Editing code of course requires the MPS editor. But generating models and running tests can be done from the command line to integrate it with automatic builds. Ant is used as the basis. In this section we explain how to use MPS from the command line via ant.
For all of the examples we use a build.properties file that defines the following two properties:
mps.home = // installation directory of MPS itself
mbeddr.home = // the root directory relative to which all other directories
// to projects etc. are specified
This build.properties file is included in all the build scripts we discuss in this section. In addition, we have to define a set of MPS-specific tasks using the taskdef element in ant. Also, a couple of JVM options are reused over and over. Consequently, the following is a skeleton of all the build files we will discuss:
<project name="com.mbeddr.core build and test" default="all">
<property file="build.properties"/>
<path id="mps.ant.path">
<pathelement location="${mps.home}/lib/mps-backend.jar"/>
<pathelement location="${mps.home}/lib/jdom.jar"/>
<pathelement location="${mps.home}/lib/log4j.jar"/>
<pathelement location="${mps.home}/lib/mps-core.jar"/>
</path>
<taskdef resource="jetbrains/mps/build/ant/antlib.xml"
classpathref="mps.ant.path"/>
<jvmargs id="myargs">
<arg value="-ea"/>
<arg value="-Xss1024k"/>
<arg value="-Xmx1024m"/>
<arg value="-XX:MaxPermSize=92m"/>
<arg value="-XX:+HeapDumpOnOutOfMemoryError"/>
</jvmargs>
<!-- here is the place where all the following example code goes -->
</project>
Building the Languages in a Project
We start by building the contents of a project. Here is the necessary ant code that has to be surrounded by the skeleton ant file shown above:
All modules within the project are generated. If only a subset of the modules in the project should be generated, a modules fileset can be used. The following code generates all the languages in a project; typically they reside in the languages directory below the project. Note how we define a different property that points to the project directory as opposed to the project (.mps) file.
Sometimes a project needs access to other languages in order to be compilable. These can be added with library elements, whose dir attribute has to point to a directory that (directly, or further below) contains the required languages.
Building solutions that contain code written in a DSL is not fundamentally different from building languages. However, it is important to set up the libraries correctly so they point to the directories that contain the languages used in the solutions.
<!-- project with solutions that should be built -->
<property name="solutionproject.dir" value="path/to/some/solution/project"/>
<!-- two projects that contain languages used by solutionproject -->
<property name="langproject.dir" value="path/to/some/project"/>
<property name="other.langproject.dir" value="path/to/other/project"/>
<target name="build-solutions">
<mps.generate>
<jvmargs refid="myargs"/>
<!-- set up libs to point to the lang projects -->
<library name="langproject" dir="${mbeddr.home}/${langproject.dir}"/>
<library name="other.langproject"
dir="${mbeddr.home}/${other.langproject.dir}"/>
<!-- generate two solutions in the project -->
<modules dir="${mbeddr.home}/${solutionproject.fit}/solutions/solution1"/>
<modules dir="${mbeddr.home}/${solutionproject.fit}/solutions/solution2"/>
</mps.generate>
</target>
Running Tests
MPS supports a special testing language that can be used for testing constraints, type system rules and editor functionality. These tests can be run from the UI using the Run option from the solution or model context menu (see the figure below).
These tests can also be run from the command line. Here is the code you need:
<!-- make this point to some temp directory -->
<property name="mps.platform.caches" value="tmp"/>
<!-- path to project .mpr with the tests in it -->
<property name="project.mpr" value="relative/path/to/project/project.mpr"/>
<target name="run-tests">
<delete dir="${mps.platform.caches}"/>
<junit haltonfailure="true" showoutput="true" fork="true" dir="${mps.home}">
<jvmarg value="-ea"/>
<jvmarg value="-Xss1024k"/>
<jvmarg value="-Xmx1200m"/>
<jvmarg value="-XX:MaxPermSize=128m"/>
<jvmarg value="-XX:+HeapDumpOnOutOfMemoryError"/>
<sysproperty key="idea.system.path" value="${mps.platform.caches}/system"/>
<sysproperty key="idea.config.path" value="${mps.platform.caches}/config"/>
<sysproperty key="idea.plugins.path" value="${mps.platform.caches}/plugins"/>
<sysproperty key="mps.junit.project"
value="${mbeddr.home}/${project.mpr}"/>
<sysproperty key="mps.junit.pathmacro.mbeddr.home"
value="${mbeddr.home}"/>
<classpath>
<fileset dir="${mps.home}/lib">
<include name="**/*.jar"/>
</fileset>
<fileset dir="${mps.home}/plugins">
<include name="**/*.jar"/>
</fileset>
</classpath>
<test name="jetbrains.mps.testbench.junit.suites.DefaultTestSuite"/>
<formatter type="xml"/>
</junit>
</target>
The important ingredients here are the two system properties mps.junit.project and mps.junit.pathmacro.mbeddr.home. The first one specifies the project that contains the tests. The second one is a bit more involved. The syntax mps.junit.pathmacro.XXX sets a value for a path variable XXX in an MPS project. To make the tests run correctly, there has to be a TestInfo node in the project that points to the project file. This one uses a path variable (defined in MPS settings) to make it portable between different machines and various locations in the file system. The mps.junit.pathmacro.mbeddr.home thingy is used to supply a value for the macro from the command line.