The industrial-strength, full-blown process of building MPS building tools goes like this:
- Use MPS to develop a bunch of domain-specific languages together with custom tooling in the form of actions, tool windows and various IDE extensions.
- Create a build script in the build language to package the languages and extensions into IDEA platform plugins and create a self-contained, branded IDE out of those plugins (called RCP for rich client platform, a name borrowed from the Eclipse world).
- Figure out a way to bring this RCP to users.
- Users use the RCP to develop their models and generate code from them.
Setting up a full-blown CI/CD pipeline for this scenario is quite complicated and could fill a rather long (and mostly boring) series of posts.
However, MPS also lets you develop languages and use them immediately to develop models, all in the same project. Sort of like developing a Java compiler together with your Java projects. Such “self-contained” projects can be useful for prototyping and in this case the development pipeline can be simplified because we don’t need to package the RCP and deal with dependencies.
Here is how one would create a CI/CD pipeline for a self-contained project.
The Ant file
First, create a new build solution by right-clicking on the project in the logical view and choosing New → Build Solution.
- Choose Plug-in for MPS as the distribution kind.
- Tick the check boxes for all the necessary modules: the languages and the solution whose generated output we are interested in.
Next, remove unnecessary stuff from the generated build solution:
- The
idea plugin
from the project structure section. - The
zip
from the default layout.
We won’t need any of these because, as explained above, we are not going to distribute our languages.
Instead, we need the build to generate and package our solution. To tell MPS to do this, add module <our-solution-name>
to the default layout.
At this point, we can generate an Ant build file by building the build solution.
We can even run this build file from within MPS by right-clicking and choosing Run from the context
menu. If all goes well, the build should produce a bunch of JAR files under build/artifacts/<script-name>
directory.
The <solution-name>-src.jar
contains the sources: the original model plus the files generated from it.
<solution-name>.jar
(without -src
) will contain the compiled Java files. (If you don’t generate Java then the second
file will probably contain another copy of the generated files or perhaps just some descriptors.)
These JAR files are the ones we want to publish to our consumers.
Adding Gradle or Maven
A limitation of the Ant file is that it needs MPS to work. You need to pass the path to MPS via
-Dmps_home=/path/to/mps
. If you want to run this script on CI you have two options: either you preinstall a particular
version of MPS on your CI server or download it from an artifact repository during the build.
Preinstalling a particular MPS version frees you from having to deal with Gradle or Maven build scripts, which could be helpful initially. However, you will need to manually install any new MPS versions that come out. You may also need to have multiple versions of MPS installed in parallel e.g. during migrations where the main branch uses an older version of MPS while a migration branch tests a newer one.
Downloading MPS during the build is more complicated but leads to better build reproducibility and less setup on the build agent side means less time spent waiting for your IT to set things up. However, Ant has no support for downloading artifacts from a repository so we will need to create a Maven or Gradle script to wrap the Ant script that was generated by MPS.
The script would do the following:
- Download and unpack the proper version of MPS (published to the itemis Nexus repository).
- Call the Ant script, passing it the MPS location.
- Publish the JARs produced by the Ant script under proper Maven coordinates.
Later on, the script could also download and unpack MPS libraries, such as the mbeddr platform, KernelF, or MPS-extensions. However, dependencies like these make things more complicated and I won’t go into that topic today.
Here are two example scripts that you can borrow or get inspired by:
- Maven: https://github.com/specificlanguages/mps-maven-sample-2019.2
- Gradle: https://github.com/coolya/mps-scaffold/blob/monolitic-example/templates/monolitic/build.gradle.kts
None of these scripts were specifically built for the self-contained scenario so you can’t copy them directly, but both scripts show how to download MPS and call out to Ant. The Gradle script does not publish any packages, use Gradle’s standard Maven Publish Plugin for that.