Skip to content

Building mbeddr extensions

Alexander Pann edited this page Aug 25, 2022 · 4 revisions

This page describes how you can setup a build for your MPS projects that either are based on the mbeddr platform or on mbeddr itself. If your project depends on mbeddr, please continue reading in Building mbeddr-based Projects, otherwise, please continue reading in Building mbeddr Platform based Projects. Further, we also explain how to setup an RCP build to package your plugins in a customized MPS.

Building mbeddr Platform based Projects

This section explains how you can setup a build for an MPS project that depends on the mbeddr platform. Such a dependency can arise if you have concepts in your languages that extend concepts from the mbeddr platform, or if you use languages from the mbeddr platform to implement your languages. In this section, we illustrate an example language extension that is implemented by using languages from the mbeddr platform, and define a build setup for this language according to the structure shown in the figure below: (1) we write an MPS build script for this language and generate an Ant script from it (bottom boxes), (2) write and Ant script that is responsible for executing the generated Ant script (box in the middle), and a Gradle script (box on top) that takes care of dependency resolving by downloading these from an artifact repository (boxes on the right) and invokes this manually written Ant script to build a plugin for the language.

Build Setup

Example Base Language Extension

In this section, we describe an example Base Language (MPS' Java implementation) extension that we have built on top of the mbeddr platform. The complete source code for this example is located in the language module com.baselanguage.unless that is part of the mbeddr.core repository and can be found at the following file system location:

mbeddr.core
└─code
  └─applications
    └─com.mbeddr.build-examples

This language comes with the concept UnlessStatement, an extension of the concept Statement (MPS Base Language). This language construct is a conditional similar to if statement, but code located inside the body of an unless statement is only executed if the condition evaluates to false. This language extension comes with a generator that reduces an unless statement to an if statement with the same body, but a negated condition. We have used the language com.mbeddr.mpsutil.iconchar from the mbeddr platform to define an icon for UnlessStatement. Thus, we have introduced a dependency from com.baselanguage.unless (our language) to com.mbeddr.mpsutil.iconchar (the mbeddr platform). The next section describes how we setup an MPS build script to build a plugin for the language module com.baselanguage.unless.

MPS Build Script

We have created a template illustrating how to setup an MPS build script for an MPS project that depends on the mbeddr platform. This template is named platform-extension.template and can be found in the com.mbeddr.build MPS project that is located at the following file system location in the mbeddr.core repository:

mbeddr.core
└─code
  └─languages
    └─com.mbeddr.build

In this section, we use this template to setup a build script for the previously described language module com.baselanguage.unless. You can use the very same template to write an MPS build script for your MPS project. If you have no or little experience with the MPS build language, please read the MPS Build Script Guide. This guide explains the semantics of the MPS build language and the conventions that we use to setup build scripts.

The screenshot below shows the build script com.baselanguage.unless.build that we have written for our language module com.baselanguage.unless by using the template that we have described before (platform-extension.template). You can find this MPS build script in the MPS project com.mbeddr.build-examples (described above). In addition to content from the template, this build script contains a macro mbeddr.github.core.home that points to the repository root and specifies the value of artifacts.root to point to a folder artifacts located right underneath the repository root. Further, the script enriches the idea plugin from the template with additional information and contains an mps group com.baselanguage.unless that contains a lanuage com.baselanguage.unless, referring to the .mpl file via the macro mbeddr.github.core.home. Running code generation on the build solution will generate an Ant script that we can directly invoke from the command-line.

Build script for the UnlessStatement

After running code generation on the build solution that contains our build script, you will find at the file system location shown below a generated file build.xml representing an Ant script:

mbeddr.core
└─build
  └─com.baselanguage.unless.build
    └─build.xml

To successfully execute this Ant script, the mbeddr platform distribution has to be available on your system. While you could build this distribution on your local machine from the mbeddr sources, we rather suggest to resolve this artifact from the mbeddr Nexus. In the next section, we describe the steps to write an Ant and a Gradle script that resolve the mbeddr platform distribution from the mbeddr Nexus and execute our generated Ant script (described above) to build a plugin for the language com.baselanguage.unless.

<a name"PlatformAntGradleScript">Ant and Gradle Script

We create in this section two build scripts: (1) an Ant script that invokes the generated Ant script that we have described before (build.xml), and (2) a Gradle script that is responsible for resolving the mbeddr platform distribution and MPS, two dependencies that we require at build time. In this section, we use the template described in Ant Script Template to write an Ant script that invokes the build.xml that we have generated from the build solution com.baselanguage.unless.build. Please read section Ant Script Template carefully to get an understanding for the Ant script template that we use in this section. The code snippet below shows the manually written Ant script that is based on this template and used for invoking our generated Ant script:

<project name="build com.baselanguage.unless" default="build" >
	<!-- validations -->
	<property name="plugins.folder.name" value="plugins"/>
	<fail unless="mps.home">mps.home must be set in your build.properties</fail>
	<fail unless="mbeddr.github.core.home">mbeddr.github.core.home must be set in your build.properties</fail>
	<fail message="Your MPS installation (${mps.home}) doesn't contain a ${plugins.folder.name} folder">
		<condition>
			<not>
				<resourcecount count="1">
					<dirset dir="${mps.home}" >
						<include name="${plugins.folder.name}"/>
					</dirset> 
				</resourcecount>
			</not>
		</condition>
	</fail>
	<!-- misc properties -->
	<property name="build-scripts.base-path" value="${mbeddr.github.core.home}/build" />
	<property name="build.dir" value="${mbeddr.github.core.home}" />
	<property name="artifacts.root" value="${build.dir}/artifacts" />
	<property name="clean-generated-code.task" value="cleanSources" /> 
	<property name="clean-plugins.task" value="clean" /> 
	<property name="generate-code.task" value="generate" /> 
	<property name="build-plugin.task" value="assemble" /> 
	<property name="run-tests.task" value="check" />
	<!-- com.baselanguage.unless -->
	<property name="com.baselanguage.unlessbuild-file.location" value="${build-scripts.base-path}/com.baselanguage.unless.build" />
	<property name="com.baselanguage.unless.build-file.name" value="build.xml" />
	<!-- targets --> 
	<target name="clean">
		<echo message=""/>
		<echo message="Deleting all source_gen, source_gen.caches, classes_gen, test_gen and test_gen.caches directories..."/>
		<delete includeemptydirs="true">
			<fileset dir="." includes="**/source_gen/,**/source_gen.caches/,**/classes_gen/,**/test_gen/,**/test_gen.caches/" defaultexcludes="false"/>
		</delete>
		<echo message="... Done"/>
		<ant antfile="${com.baselanguage.unless.build-file.name}" dir="${com.baselanguage.unless.build-file.location}" target="${clean-plugins.task}" />
		<ant antfile="${com.baselanguage.unless.build-file.name}" dir="${com.baselanguage.unless.build-file.location}" target="${clean-generated-code.task}" />
	</target>
	<target name="build" depends="clean">
		<ant antfile="${com.baselanguage.unless.build-file.name}" dir="${com.baselanguage.unless.build-file.location}" target="${generate-code.task}" />
		<ant antfile="${com.baselanguage.unless.build-file.name}" dir="${com.baselanguage.unless.build-file.location}" target="${build-plugin.task}" />
	</target>
</project>

Save this Ant script to your local file system, so we can later invoke it from the Gradle script that we describe next.

The Gradle script is also based on a template that is described in Gradle Template. We use this Gradle script to resolve the mbeddr platform and MPS from the mbeddr Nexus before the actual build starts. Currently, this Nexus only provide builds from the mbeddr master branch. In the future we plan to also provide nightly releases and regular releases. The Gradle script acts as a wrapper for the manually written Ant script and provides one target ant-build that we invoke to resolve dependencies and to build the language plugin in a single step. We do not directly invoke Ant scripts that are generated by MPS, because Gradle's Ant integration is not able to properly work with those files.

// Dependency versions
ext.mpsVersion = '3.4.2'
ext.mbeddrVersion = '1.0.+'
// URL to mbeddr Nexus
ext.dependencyRepositories = [
    'https://projects.itemis.de/nexus/content/repositories/mbeddr'
]
// Location where resolved dependencies are stored
ext.artifactsDir = new File(rootDir, 'artifacts')
apply plugin: 'base'
// Configurations to which we link depedencies
configurations {
    mpsArtifacts
    mbeddrPlatformArtifacts
}
// Dependencies + versions
dependencies {
    mpsArtifacts "com.jetbrains:mps:$mpsVersion"
    mbeddrPlatformArtifacts "com.mbeddr:platform:$mbeddrVersion"
}
// Registration of artifact repositories
repositories {
    for (repoUrl in project.dependencyRepositories) {
        maven {
            url repoUrl
        }
    }
}
// Tasks for resolving dependencies (MPS + mbeddr platform)
task resolveMps(type: Copy) {
    dependsOn configurations.mpsArtifacts
    from {
        configurations.mpsArtifacts.resolve().collect { zipTree(it) }
    }
    into "$buildDir/mps"
}
task resolveMbeddrPlatform(type: Copy) {
    from {
        configurations.mbeddrPlatformArtifacts.resolve().collect { zipTree(it) }
    }
    into artifactsDir
}
// Ant properties specifying file system paths
ant.properties['mps.home'] = resolveMps.destinationDir
ant.properties['mbeddr.github.core.home'] = rootDir
ant.properties['build.dir'] = rootDir
ant.properties['artifacts.root'] = resolveMbeddrPlatform.destinationDir
ant.importBuild('build.xml') { target -> 'ant-' + target }
// Declaring task dependencies to resolve artifact dependencies before building the MPS project
tasks['ant-build'].dependsOn resolveMps, resolveMbeddrPlatform

After saving this file on your local file system, we can now execute it by using the following command (append a leading ./, if you work with an environment that is based on Unix):

gradle ant-build

After successful executing the Gradle script, you will find the packaged MPS plugin for the language com.baselanguage.unless at the following file system location:

mbeddr.core
└─build
  └─com.baselanguage.unless.build
    └─build
      └─artifacts
        └─com.baselanguage.unless.build
          └─com.baselanguage.unless.zip

At this point, you have a build setup that allows you to perform reproducible builds. In this guide, we have not discussed how you can use Gradle to publish artifacts, which were produced during the build, to a remote artifact repository. If you are interested in this topic, please continue reading in Publishing Artifacts where we describe in detail the parts of a Gradle script that we use to publish different MPS versions to our Nexus repository.

Building mbeddr-based Projects

This section describes two MPS build script templates that we have created to illustrate how to setup a build for an MPS project that depends on mbeddr. Further, we discuss for an example mbeddr C extension how to setup a build script by using these templates.

Build Script Templates

We have created two MPS build script templates, first, a template to illustrate how to setup an MPS build script for an mbeddr language extension, second, a template to create a meta-build script that is used for generating Ant scripts from your build solutions. While you would use the former script to build and package your plugins, the latter script is used during the development for preventing developers to checkin generated Ant scripts. For build script generation (the latter script), developers checkin only one Ant script, that generates all other Ant scripts before the actual build is started.

The template for building solutions/languages is named mbeddr-extension.template, while the second template to automatically generate Ant scripts from your build solutions is named mbeddr-allScripts-extension.template.

If you use mbeddr from sources, you can find the template inside the the mbeddr build project that can be found here:

mbeddr.core
└─code
  └─languages
    └─com.mbeddr.build

The MPS Build Script Guide explains parts that can be used in a MPS build script, specifically for this template, but also for the mbeddr build script template. Please read this guide before you continue reading.

<a name"ExamplembeddrProject"> Example mbeddr-based Project

After describing the build script template in the section before, we now continue by discussing an example MPS project that we have built on top of mbeddr. The complete source code for this example is located in the language module com.mbeddr.unless that is part of the mbeddr repository and can be found at the following file system location:

mbeddr.core
└─code
  └─applications
    └─com.mbeddr.build-examples

The notion behind this example is to illustrate based on an UnlessStatement that we have written as an mbeddr extension how the previously described build script templates are used for writing a real-world build script. The language depends on mbeddr, because UnlessStatement extends mbeddr Statement and holds an Expression. More specifically, our implementation depends on the language module com.mbeddr.statement (contains Statement) and com.mbeddr.expression (contains Expression). The build script com.mbeddr.unless.build (shown in the screenshot below) for our language com.mbeddr.unless reflects this dependency and is based on the template build script for the mbeddr platform (mbeddr-extension.template) that we have described before. In addition to content from the template, this build script contains a macro mbeddr.github.core.home that points to the repository root and specifies the value of artifacts.root to point to a folder artifacts located right underneath the repository root. Further, the script enriches the idea plugin from the template with additional information and contains a mps group com.mbeddr.unless that contains a lanuage com.mbeddr.unless, referring to the .mpl file via the macro mbeddr.github.core.home. Running code generation on the build solution will generate the Ant script that we can directly invoke from the command-line.

Build script for the UnlessStatement

After running code generation on the build solution that contains our build script, we will find a generated build.xml representing an Ant script at the following file system location:

mbeddr.core
└─build
  └─com.mbeddr.unless.build
    └─build.xml

Please go through the section Ant Gradle Script to get an understanding of how a command line build is setup. This section describes in detail how you write an Ant script to execute other Ant scripts that we generate with MPS and how required dependencies are resolved by using a Gradle script. After executing these scripts as explained in this guide, you will find the packaged MPS plugin at the following file system location:

mbeddr.core
└─build
  └─com.mbeddr.unless.build
    └─build
      └─artifacts
        └─com.mbeddr.unless.build
          └─com.mbeddr.unless.zip

Besides the script for building a plugin for the com.mbeddr.unless language, the same build solution contains another build script com.mbeddr.unless-allScripts.build that we use for generating an Ant script for the former build script. The idea behind this approach is that developers do not check-in generated Ant scripts, instead, only the Ant script that has been generated from this build script. Before running the actual build, we first of all invoke this Ant script to generate the other build scripts. While this approach prevents users from checking in generated files, it also supports developers as the build gets interrupted, if dependencies change while your languages evolve.

Finally, the build solution contains the build script com.mbeddr.unless.sandbox.build that we use for building application code that uses our com.mbeddr.unless language. These three scripts give you an impression of how to run build script generation, build language plugins that base on mbeddr and build an application that uses mbeddr and your own mbeddr extension.

MPS Build Scripts

Build scripts declare on top (see screenshot below) a set of macros that we use for resolving paths during the build, e.g., mps.home refers to your MPS installation, artifacts.root points to a file system location where all of your dependencies will be located. In the screenshot below, we specify a macro platform.distribution.artifacts that refers to the location of the mbeddr platform distribution, a build artifact representing the compiled and packaged mbeddr platform release. In case you write a build script using the mbeddr template, then you will have a macro for the mbeddr build script, e.g., mbeddr.allInOne.artifacts.

Macros defined in build script template

Below the macros section is the dependencies section (see screenshot below) that defines through referenced build scripts a set of artifacts that have to be present at build time. In our templates we define dependencies on mps, mpsBuild and mpsDebuggerPlugin, all three coming with MPS. Further, we use the previously described mps.home macro to resolve these artifacts from the MPS installation. You can specify artifact locations by typing an opening parenthesis behind the build script name. It's good practice to have such an artifact locations for each dependency. Besides MPS, we define in build scripts dependencies on build scripts that bundle plugins on which we base our languages or solutions. In case of the build script examples discussed in this guide, this is either com.mbeddr.platform.distribution or com.mbeddr.allInOne. The screenshot below demonstrates an example dependency on com.mbeddr.platform.distribution.

Build time Dependencies defined in build script template

The project structure is used for configuring the build, describing plugin descriptors and listing all solutions and languages that should be built. On top of this section (see screenshot below), we usually have generator options being used for configuring the code generator with the same options that we use in mbeddr. Next, java options configures the Java compiler being used for compiling languages, and solutions containing Base Language code. Next, is a template for an idea plugin (see screenshot below) that contains dummy names, which you should replace with project-specific names. Each idea plugin ends up in an idea plugin descriptor, serialized to xml and packaged with the compiled plugin code. While these plugin descriptors specify a set of names, they additionally require a version number, optional vendor information, content referring to solutions and language described in same section and being packaged with the plugin. Finally, runtime dependencies lists other plugins that should be available in the MPS target environment where your plugin is deployed.

Below the idea plugin is a mps group <your solutions/languages group> that you can use for grouping languages and solutions that you want to build and package in a plugin. After copying the template, please change the group name accordingly to your project. Inside this group, you can either type the word language or solution on the cell containing the text <empty> to specify a module that you want to build with your build script. While you will normally list here the languages you want to build, you might also list the build solutions (solutions) for which you want to generate Ant scripts. Next, you can specify in the load from section of your instantiated module a path to the .msd (solution) or .mpl (language) file that contains meta information for your module. While you can enter this path in a relative way using the "./" prefix, we encourage you to define a macro on top of your build script for resolving the file system path. Further, you should also create a macro for your repository, e.g. <your repository name>.home, that you can use as a base path for resolving the file system location of your modules. By using macros for this purpose, you can overwrite their values from outside the build script when executing the Ant script that gets generated from your MPS build script.

Next, in default layout, we describe the structure of our packaged artifacts. Inside our template, we configure the build to package our compiled plugin (yourplugin) inside a zip file (<your plugin.zip>). Finally, below this section, we configure via mps settings the headless MPS instance being used throughout the build. In this configuration, first, we enable bootstrapping allowing MPS to break up modules (solutions/languages) into multiple smaller chunks while running code generation and compilation. This option is used in case of cyclic dependencies, and is only relevant for code generation and compilation, it does not influence the layout of classes or packaging of plugins. It's good practice to leave this options disabled, because MPS will interrupt code generation for your build script with an error message, pointing you to cycles between your modules. Finally, both heap-related options at the bottom of this configuration are important, if you run into out of memory issues during code generation.

Build time Dependencies defined in build script template

Ant Script Template

We describe in this section a template for writing Ant scripts that invoke other Ant scripts that we generate from MPS build solutions. The Ant script that you write based on this template will later be invoked by a Gradle script that takes care of dependency resolving, .e.g, resolving the mbeddr platform or mbeddr from a Nexus repository. You can find the template that we discuss in this section at the following file system location:

mbeddr.core
└─code
  └─languages
    └─com.mbeddr.build
      └─solutions
        └─com.mbeddr.templates
          └─build-template.xml

This template contains a couple of variable parts enclosed in angle brackets that you have to specify for your actual build project (described later) and is organized as following: (1) the validations section contains rules that validate the presence of required Ant properties, (2) the misc properties section embodies a set of Ant properties that specify file system paths and names of Ant targets in generated Ant scripts, (3) the your project section is created for each each build project (generated Ant script) and describes where the generated Ant scripts can be found at build time, (4) the targets section contains a clean target that is responsible for deleting previously generated files (clean-generated-code.task) and assembled plugins (clean-plugins.task), and is a dependency for the second target build, which invokes two targets on a generated Ant script to invoke code generation (generate-code.task) and assemble a plugin from the generated code (build-plugin.task). To use this template for your build project, replace the following variable parts according to your MPS project:

  • <your-repository-root.home> a property that points to the root directory of your repository
  • <your project name> the name of your build project, e.g., com.baselanguage.unless
  • <target directory> the folder where the generated Ant script can be found (just the folder, not the complete path)
  • <generated ant script name> the name of the generated Ant script (just the name, no path or file extension)

Gradle Script Template

We describe in this section a template for writing a Gradle script that we use during the build to resolve dependencies and to invoke an Ant script acting as a wrapper for Ant scripts that we generate with MPS. You can find the Gradle template at the following file system location:

mbeddr.core
└─code
  └─languages
    └─com.mbeddr.build
      └─solutions
        └─com.mbeddr.templates
          └─build-template.gradle

This template contains a couple of variable parts enclosed in angle brackets that you have to specify for your actual build project (described later). To use this template for your build project, replace the following variable parts according to your MPS project:

  • <dependency version>: the version of a dependency (e.g., ext.mbeddrVersion = '1.0.+' latest build of version 1.0.x)
  • <dependency repositories>: URLs of repositories that contain artifact dependencies
  • <dependency name>: the name of a configuration that will later be linked to an artifact dependency (e.g., mbeddrArtifact)
  • <dependency name> "<groupId>:<artifactId>:$<version>": declaration of a dependency (e.g., mbeddrArtifact "com.mbeddr:core:$mbeddrVersion")
  • <repository root macro>: the name of a macro that specifies in an MPS build script the file system location of a repository root (e.g., mbeddr.github.core.home)
  • <wrapper Ant script>: name of an Ant script that acts as a wrapper for invoking other Ant scripts that we generate with MPS (e.g., build.xml)
  • <ant target>: name of the Ant target that is located inside the wrapper Ant script (see item before) and used for building your MPS project (e.g., build)

Publishing Artifacts

So far we have described how you can resolve dependencies such as mbeddr or the mbeddr platform from an artifact repository, and how MPS build scripts are written and executed. Setting up such a build chain is sufficient for many projects, however, in your own project you might get to the point where you have to publish your build artifacts to a repository, e.g., a Nexus. This is usually the case for software components that are used by other projects as a platform (e.g., the mbeddr platform). In this section, we describe the parts of a Gradle script that we use to publish different MPS versions to our own Nexus repository. You can use this build script (located at the following location: mbeddr.core/build/thirdparty/mps/build.gradle) as a basis to introduce publishing rules in your own Gradle script. This script downloads a specific version of MPS from the web and publishes this artifact to our Nexus repository. We do not describe the code from line 1 to 29, since it is just responsible for downloading MPS. What is interesting for us is the publishMPS task (see code snippet below) that is executed after unzipping the downloaded MPS (dependsOn: unzipMPS) and is of type Zip, thus providing a zip file (the downloaded MPS) as output:

task publishMPS(type: Zip, dependsOn: unzipMPS) {
    from mpsUnpackedDir
}

Further, we define on line 36 an artifact representing the file output of publishMPS and being of type default (describes the file structure, e.g. archives, text or default):

artifacts {
    'default' publishMPS
}

At the end of the file, we define the publication to be of type MavenPublication and provide meta information such as a groupId, an artifactId and a version number. Further, we specify publishMPS as artifact to be published, hence, Gradle will publish the output that the task publishMPS provides:

publishing {
	publications {
		mps(MavenPublication) {
			groupId 'com.jetbrains'
			artifactId 'mps'
			version mpsBuild
			artifact publishMPS
		}
	}
}

Finally, what is missing in this build script is the declaration of the repository to which artifacts get published. Because the mbeddr build is setup as a multi-project build, information such as the publication declaration is spread across different build scripts. Depending on the size of your project, it usually makes sense to start with a single Gradle script, instead of setting up a multi-project build up front, which requires more effort and knowledge about Gradle. The code snippet below shows the repository declaration that can be found in another Gradle script (located at the following file system location: mbeddr.core/build/thirdparty/build.gradle). In this code snippet, we declare a maven repository and register credentials for it, if they are provided at build time (e.g., by invoking the Gradle script with -PnexusUserName='John Doe' -PnexusUserName='<password>'). The repository URL is stored in the variable releaseRepository that is declared in another Gradle script (located at the following file system location: mbeddr.core/build.gradle).

publishing {
	repositories {
		maven {
			if (project.hasProperty('nexusUsername')) {
				credentials {
					username project.getProperty('nexusUsername')
					password project.getProperty('nexusPassword')
				}
			}
			url project.releaseRepository
		}
	}
}

To publish artifacts, Gradle provides for each publication, e.g., mps, a dedicated task. You can see the list of available tasks by typing gradle tasks on the command line. For the script above, you will see a task publishMpsPublicationToMavenRepository in the list. If you have the required credentials, you can simply type gradle publishMpsPublicationToMavenRepository on the command line to download MPS and publish the resulting artifact in the mbeddr Nexus.

Packaging a custom RCP

This how-to explains how to package your languages into a custom MPS packaging that doesn't contain any "meta stuff". So you get a specialized just for your languages and nothing more. This tutorial assumes that you already know the basics of building and packaging languages/solutions with MPS and ANT.

Terminology

MPS Distribution

Is the generic MPS distribution that can be download from jetbrains here it is important to use this distribution and not a platform specific one. Only this distribution contains all the necessary files to create RCP.

Build solution

A solution primarily used for building plugins and languages. There is nothing special about it in MPS. Its more a convention that it is only used for build languages and plugins.

Build Project

A special root node the in the model which is needed to generate an XML build script from it. It contains all information needed for the build process. For instance languages that are build, dependencies on other build script and information about how to package the build artifacts.

Build Script

An XML file containing the actual steps executed by ANT. This is generated from the Build Solution.

Plugin

An Idea plugin definition created in a build script. A plugin contains several languages and solutions.

Build Layout

The Build Layout is a part of the build solution. It specifies the information how the plugins and languages should be packaged. It is used to create zip files for a single plugin or to bundle multiple plugins into a single zip file. Like any other content of a build solution these parts can be reused by other build solutions.

Branding

todo

Setting up the build project

First of all you need a build solution in your model. Now you have this open the com.mbeddr.mpsutil.rcpconfig.template solution and copy the content of the template build solution into your own. While you do this you will be asked to add some dependencies, you don't need all of them. The dependency on jetbrains.mps.ide.build is enough. Once you have finished it should look like this:

build overview

Macros

The template has two macros, the first one version is used to name the zips and later in the optional branding. The second one mps.home is a path macro that should point to your MPS distribution.

Dependencies

There is only one dependency needed for the MPS files: mpsStandalone. This is the place where you add the dependencies on your own build scripts to include your files.

Project Structure

There is no content here. But the optional Branding will be placed here.

Default Layout

The build layout contains 3 files, one for each platform. Beside the platform specific files those three zips should contain the plugins that you want to ship. We will talk about how to add them in the next section.

Adding your own plugins

Now you have a skeleton in which you can insert your plugins. The plugins reside in the plugins folder of the RCP. In this case it is easy because mbeddr already provides a complete package of its plugins. So I can directly include the contents of the zip file. To do so we need a dependency on the build solution that creates the mbeddr zip file. It is named com.mbeddr.allInOne. Then we put the content of the plugins folder to the plugins folder of our RCP build. If you have such a zip file you can do the same. If you don't, you have to package your plugins manually (don't forget to include your dependencies, like jars or other resources). The RCP Layout should looks like this:

mbeddr plugins

Make sure you include your plugins into all of the three packages.

Branding

MPS allows you to brand the RCP version of MPS that you build. The branding allows you to customize:

branding

Codename

An internal code name not shown to the user but used when fetching updates and plugins

Version

A version number presented to the user. It is also transmitted when the IDE looks for updates.

Full Name

The Name presented to the user in various places like the about screen or in the new project wizard.

Build Number

The build number of your IDE. It must have the following format {platform build number}.{applcation build number} where the platform build number must match the platform build number of your MPS. If this number doesn't match the platform build number of MPS some plugins will not load and your RCP build won't start. If have any issues loading certain plugins check if your platform build number is correct.

Build Date

A string representing the build date of your IDE. There is nothing special about this. As far as I know it is only used to show in the about screen.

Icons (16x16, 32x32 and 32x32 opaque)

Icon used in various places in the IDE and as a APP Icon of the Mac version of your RCP.

Splash Screen & Text Color

The splash screen shown at startup until the IDE is ready to use. Some information like the Fullname and the build number are rendered on it by MPS. You can change the color of the text with the text color property which must be the hex value of the color.

About Screen

The about screen used to show when the user clicks on "About {Full Name}" in some places in the RCP. The same text color specified in the splash screen is used to render some information on that screen.

Dialog Image

Image used for popup dialogs. Those appear rarely in MPS. Most likely in fatal error situations where IDE crashes completely.

Welcome Screen

welcome screen

In the left upper corner is the logo, the text in the middle is the caption and right upper corner the slogan. All of these have to be images.

Update Website

There is no official documentation availabe for this. All of these information is based on observation ann reading the MPS sources.

Check Url

The check url must point to a update.xml file which contains the update informations. The xml should look like this. You will find references to the other values in the branding there. If you plan to use this for notifying your users about updates you need to create a update.xml with similar information adapted for your RCP.

Update Url

The Url your RCP opens when it notices that there is an update though the check url.

Channel

The channel chosen from the update.xml.

Custom file locations

Once you have your custom RCP build of MPS you will notice that it will still interfere with a normal MPS installation. For instance plugins, settings and cache are stored in the same location as for a normal MPS. You can control the location of these with an idea.properties file which has to be placed in the bin directory of your RCP. A sample file might look like this:

INI
#---------------------------------------------------------------------
# Uncomment if you want to customize path to MPS config folder
#---------------------------------------------------------------------
idea.config.path=${user.home}/.mbeddr/config

#---------------------------------------------------------------------
# Uncomment this option if you want to customize path to MPS system folder
#---------------------------------------------------------------------
idea.system.path=${user.home}/.mbeddr/system

#---------------------------------------------------------------------
# Uncomment this option if you want to customize path to user installed plugins folder
#---------------------------------------------------------------------
idea.plugins.path=${user.home}/.mbeddr/config/plugins

#---------------------------------------------------------------------
# Uncomment this option if you want to customize path to MPS logs folder. Make sure you are using forward slashes
#---------------------------------------------------------------------
idea.log.path=${user.home}/.mbeddr/system/log

For your RCP you should replace the .mbeddr in the paths with something that fits your product. Once you have created a idea.properties file you have to place it in the bin folder of your RCP by adding a new copy rule to the build solution. First you need to remove the original idea.properties file from the zip:

exclude original idea.properties

Then you copy your custom file:

copy idea.properties

Troubleshooting

Creating builds for MPS projects can sometimes be challenging. This section explains what to do when some common issue appear during the build stage. Note that all of the following issues are raised when using MPS 3.3.5, in newer versions of MPS you might not experience these problems.

Inside MPS IDE

Cannot run unit tests and language tests together at solution level

At least in Windows environment, unit tests and language tests cannot be performed during the same test execution. Language tests can be executed only in the same MPS process while unit tests can be performed only in a separately started MPS instance. Hence, it is recommended to group tests into two solutions: one for unit tests, the other for language tests.

Running editor tests causes exceptions

Replacing an older MPS version with a newer version of MPS by manually copying the files on the file system can mixup libraries and plugins. The installation of your newer MPS thus may contain libraries or plugins from the older version that may cause your newer MPS to behave in a bad way possibly throwing error messages, e.g. type system or editor tests may fail with unrelated errors/exceptions. In case you experience this behavior, please reinstall your newer MPS at a different file system location and remove the invalid installation from your file system.

In TestInfo, path variables fail to denote project path

For being able to run tests on different machines, paths are usually encoded with patch variables (e.g., myproject.code), instead of using absolute paths (e.g., C:/myproject/code). For type system and editor tests, these paths are specified in a TestInfo file that lives inside a model where tests are located. Because the command line build requires information about these paths as well, you must specify them in build scripts that execute tests. This is achieved by declaring a folder macro that is prefixed with the name mps.macro., e.g. declaring for a path variable myproject.code a folder macro mps.macro.myproject.code. Thus, while executing the Ant script generated from your test build script, MPS instantiates the specified path variables with their respective values (file system paths).

Missing JAR dependency in build scripts

If a JAR file is extracted as dependency of a runtime solution, a file has to be declared within the default layout of the respective build script to indicate where this JAR file should be stored in the assembled plugin. Please make sure you resolve this JAR file on the file system by using the same folder macro that is used to resolve the .msd file of the runtime solution that depends on this JAR file.

Resolving a JAR in the default layout

In build script, already specified modules are not available as items of test modules configuration

To make specified modules available in the completion menu of your test modules configuration, please open the inspector for these modules and replace sources with sources and tests in the content part (see figure below).

Changing notion of modules

Command-Line Build

Self-packaging Build Solutions

Due to a bug in MPS' build language, MPS build scripts packaging themselves (e.g., mbeddr platform build script packages its build solution) need to have a dependency on the jetbrains.mps.build.workflow.preset solution. Not adding this dependency will result in an IllegalArgumentException when executing the ANT script that has been generated from such an MPS build script. This bug is logged in the MPS Youtrack: https://youtrack.jetbrains.com/issue/MPS-25921

Running tests aborts with errors

This may be due to the permission limit of MPS. To get over this, please modify the permission as modifiable.

The text generation failed when compiling instances of language A

This may be caused by an issue on loading mbeddr interpreter in the TextGen aspect of A (https://github.com/mbeddr/mbeddr.core/issues/1529). Using the mbeddr interpreter in the TextGen is not supported.

Language tests (both node tests and editor tests) are ignored during testing

This may be caused by the folder name in your build script's base directory. The file system path specified in the base directory should not contain the word test. The appearance of test in your base directory will cause MPS to ignore all language tests during test time.

Running tests requires dependencies on unrelated languages

This may be caused by an invoke intention test statements in one of your editor tests. This statement causes MPS to load during build time all languages that are available in your MPS project (specified in TestInfo). In case at least one of these languages is not loaded during build time, you will get an error causing your test to fail. A solution to fix this problem is to add dependencies on all unrelated languages in your build script.

Clone this wiki locally