blob: 9191b01153462392a974057689d00bae7cf6068c [file] [log] [blame]
<?php
/*******************************************************************************
* Copyright (c) 2015 Eclipse Foundation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://eclipse.org/legal/epl-v10.html
*
* Contributors:
* Eric Poirier (Eclipse Foundation) - Initial implementation
*******************************************************************************/
?>
<h1 class="article-title"><?php echo $pageTitle; ?></h1>
<p>
Xtext languages should be built, tested and deployed continuously,
just like any other piece of software. Users expect generators to
run inside popular build tools and not just inside the IDE. In
this post you will learn how to create a <a target="_blank"
href="http://maven.apache.org/">Maven</a> build for your Xtext
language and how to consume the resulting Maven artifacts from
other projects, both Maven and <a target="_blank"
href="http://www.gradle.org/">Gradle</a>.
</p>
<p>
The example language that we will be building is the <a
target="_blank"
href="https://www.eclipse.org/Xtext/7languagesDoc.html#builddsl">BuildDSL</a>
from the Seven Languages project. The language itself (i.e. the
grammar and tooling) are not important for this exercise. However,
it has several interesting properties that allow me to show you
most of the challenges you will encounter when building a
language:
</p>
<ul>
<li>It uses Xbase, linking and generating Java code</li>
<li>It has a standard library that needs to be shipped with the
language</li>
<li>It has UI tests which need some additional setup</li>
</ul>
<p>
The code for the examples is available on <a target="_blank"
href="https://github.com/oehme/xtext-maven-example">Github</a>.
</p>
<h2>Choose your path</h2>
<p>There are two ways of "mavenizing" your language build. The first
is the minimalistic approach, which relies on checking in
generated code. This means that this Maven build is not able to
generate the language infrastructure or clean the output folders
by itself. It is just there to create Maven artifacts for others
to consume. This approach is very non-intrusive for developers who
are used to an Eclipse build. There are two main disadvantages to
this. Checking in generated sources will make your diffs pretty
hard to read. And if one of your developers has a slightly
different setup than everyone else (line endings, encoding, etc.),
the build might run on his machine, but fail when he checks in.</p>
<p>The alternative is a Maven build that can generate everything
from just the source code. This means you can stop checking in
generated code. And since all important configuration is part of
the build script, you will get the same result on all platforms.
But it also means that you will have to run the Maven build once
after checkout in order for your projects to compile. In the end
this is a matter of preference and you and your team should decide
which suits you better.</p>
<h2>The starting point</h2>
<ul>
<li>Let's fist have a look at the unaltered language projects. You
will find them in the <i>language/0-initial</i> folder. I will
give you just a quick rundown what each of them does. If you
have worked with Xtext before, you should feel right at home.
</li>
<li><i>builddsl</i> contains the core language infrastructure and
code generator</li>
<li><i>builddsl.lib</i> is the standard library that is shipped
with the language</li>
<li><i>builddsl.ui</i> contains the Eclipse UI parts (like content
assist)</li>
<li><i>builddsl.tests</i> contains all tests for the language,
including UI tests</li>
<li><i>builddsl.target</i> is a target platform definition for the
language. If you don't use one, then your language will be built
against whatever is installed in your host workbench. This makes
builds highly platform dependent. So every project should have a
target platform.</li>
<li><i>builddls.repository</i> assembles the plugins into a p2
repository, from which the plugins can be installed</li>
</ul>
<h2>The minimalist approach</h2>
<p>
Next up we will create the minimal required POMs to build the
language with Maven. You can find these projects under <i>language/1-simple</i>.
The most important addition is the <i>builddsl.parent</i> project.
This is the main entrypoint for Maven. It lists all other projects
which should be built and contains common configuration for them.
The crucial part is enabling the Tycho Maven plugin.
</p>
<div>
<script
src="https://gist.github.com/oehme/9bc23b6f192aeac5f6f1.js"></script>
<span style="font-family: Arial;"><span
style="font-size: 15px; line-height: 17.25px; white-space: pre-wrap;"><br /></span></span>
</div>
<p>Tycho is a Maven extension that can read dependency information
from Manifest files and fetch these dependencies from p2
repositories. It also teaches Maven which build steps are required
for Eclipse plugins. The second important bit of configuration is
the target platform definition. We just reference our target
project and list the environments for which we want to build.
Without specifying these, Tycho would just use the currently
running system as the default, which would make the build platform
dependent.</p>
<div>
<span
style="font-family: Arial; font-size: 15px; line-height: 17.25px; white-space: pre-wrap;"><br /></span>
<script
src="https://gist.github.com/oehme/d1ee67076c3613c29a43.js"></script>
</div>
<br />
<p>The POMs for the individual projects are all very
straightforward. Each inherits from the parent and sepcifies a
packaging type. Tycho adds packaging types for Eclipse plugins,
target platforms and repositories. One of the more interesting
POMs is that of the core language plugin. It needs a dependency to
the library plugin, even though that dependency is already present
in the Manifest.</p>
<div>
<span style="font-family: Arial;"><span
style="font-size: 15px; line-height: 17.25px; white-space: pre-wrap;"><br /></span></span>
<script
src="https://gist.github.com/oehme/dbdddcecd7e28e3b0cee.js"></script>
</div>
<p>This is the downside of using Tycho. If you want Maven clients to
understand your dependencies, they always need to be in the POM
But Tycho does not "enhance" the POM with the dependency
information from the Manifest. Luckily, this is the only bit of
duplication we need. All Xtext runtime dependencies are already on
Maven Central and part of the Xtext plugins for Maven and Gradle,
which we will use later.</p>
<p>The other more elaborate POM is that of the tests plugin. You
will see that we enable the UI harness so that we can run the
content assist tests contained in the project. Sadly, we need to
add some platform specific handling for OSX, as otherwise SWT will
not run. This is the price you pay if you want your build to run
well on all platforms.</p>
<p>
Try running <i>mvn install</i> from the parent folder to install
the language into your local Maven repository. Deploying the
artifacts to some other repository like Maven Central is out of
scope for this post, but well documented and no different from any
other Maven project. Also, you will find a p2 repository in the <i>/target</i>
folder of the <i>builddsl.repository</i> project. You could
transfer this to some public server so that other people can add
your language to their Eclipse installation.
</p>
<h2>Using your language</h2>
<p>You are now ready to use your language from the client projects.
There are two example clients. Each of them contains the same
code: A builddsl file which references an Xtend file. This is just
to show that referencing existing Java code works as desired. As a
bonus, both projects also showcase how to compile Xtend files.
Note that both clients lack any Eclipse configuration, because all
the information is present in the build scripts. If you want to
look at them inside Eclipse, you need to use their respective
Eclipse integration.</p>
<p>
For Maven users, there is the xtext-maven-plugin. You just add
your core language artifact as a dependency to the plugin and add
the language configuration. This configuration contains the name
of the StandaloneSetup class and the output folders. If your
project has a large classpath, you can also filter which jars to
search for model files, greatly speeding up your build. As of now,
you manually have to add the output directory to the list of Java
source folders in order for the generated code to be picked up by
the Java compiler. This is done using the build-helper plugin. Try
running the build with <i>mvn clean verify</i>. If you want to see
the project in Eclipse. use the "Import existing Maven Project"
wizard.
</p>
<div>
<span style="font-family: Arial;"><span
style="font-size: 15px; line-height: 17.25px; white-space: pre-wrap;"><br /></span></span>
<script
src="https://gist.github.com/oehme/36377efb3d43d0556fa9.js"></script>
<span style="font-family: Arial;"><span
style="font-size: 15px; line-height: 17.25px; white-space: pre-wrap;"><br /></span></span>
</div>
<p>
For Gradle users, there is the even more convenient
xtext-gradle-plugin. Similar to Maven, you add your core language
artifact to the xtextTooling dependency configuration. Then you
configure the StandaloneSetup and output folders. You can directly
specify that an output folder produces Java code, which will
automatically make it a source folder. Try running the build with
<i>gradlew clean build</i>. If you want to see the project in
Eclipse. run <i>gradle eclipse</i> on the command line and then
use the normal "Import existing Project" wizard.
</p>
<div>
<span style="font-family: Arial;"><span
style="font-size: 15px; line-height: 17.25px; white-space: pre-wrap;"><br /></span></span>
<script
src="https://gist.github.com/oehme/1156982157729406a9ce.js"></script>
</div>
<h2>The fully mavenized way</h2>
<p>
Now for the final part of our journey: Building the language
infrastructure from scratch using Maven. You can find the projects
under <i>language/2-full</i>. In the parent POM you can see that
we have added common configuration for Xtend. This is because part
of the language infrastructure and tests are implemented using
Xtend. Subprojects now only need to add a two-line configuration
to use Xtend and inherit everything else from the parent. The
other big change happened in the core language plugin. In order to
run the mwe2 workflow to generate the language infrastructure, we
use the exec-maven plugin.
</p>
<span
style="font-family: Arial; font-size: 15px; line-height: 17.25px; white-space: pre-wrap;"><br /></span>
<script src="https://gist.github.com/oehme/019186d7b6f2ac1e720c.js"></script>
<p>
We add <i>org.eclipse.xtext.xtext</i> as a dependency, which
contains the grammar language and generator including all
dependencies like mwe2. We also depend on Xbase because our
language inherits from it. Then we just point the Mwe2Launcher at
our workflow file. For the proper cleanup, we configure the
clean-plugin to throw away everything that is generated by the
workflow. By default the clean-plugin only cleans the /target
folder. Last but not least there is a one-line change to the
workflow itself. The grammarUri cannot be a classpath Uri, since
at the time the workflow is run in Maven, the grammar file is not
on the classpath. Instead we switch to a platform resource Uri.
</p>
<span
style="font-family: Arial; font-size: 15px; line-height: 17.25px; white-space: pre-wrap;"><br /></span>
<script src="https://gist.github.com/oehme/67b6d1773d3c0896f2df.js"></script>
<p>Now try running mvn clean to see how everything but the
handwritten source gets deleted and then mvn install to build the
language from scratch. You could now .gitignore the generated
code.</p>
<h2>Conclusion</h2>
<p>
I hope this guide has helped you set up your prefered build style.
If you have any further questions, just ask them in the <a
href="https://www.eclipse.org/forums/index.php?t=thread&frm_id=27">Xtext
forum</a>. There are lots of helpful people who will probably
have an answer =)
</p>
<div class="bottomitem">
<h3>About the Authors</h3>
<div class="row">
<div class="col-sm-12">
<div class="row">
<div class="col-sm-8">
<img class="author-picture"
src="/community/eclipse_newsletter/2014/august/images/stefan1.jpg"
width="75" alt="Stefan Oehme" />
</div>
<div class="col-sm-16">
<p class="author-name">
Stefan Oehme<br />
<a target="_blank" href="http://www.itemis.com/">itemis</a>
</p>
<ul class="author-link">
<li><a target="_blank" href="http://mnmlst-dvlpr.blogspot.de/">Blog</a></li>
<!-- <li><a target="_blank" href="http://twitter.com/zxiiro">Twitter</a></li> -->
<li><a target="_blank"
href="https://plus.google.com/+StefanOehme/posts">Google +</a></li>
<?php echo $og; ?>
</ul>
</div>
</div>
</div>
</div>
</div>