Skip to content

Commit bc47810

Browse files
1.24.0: allow overriding version by setting mpsVersion
This enables support of MPS prereleases or custom RCPs. Also, include extension name in error messages from extensions.
1 parent 614437e commit bc47810

File tree

11 files changed

+103
-63
lines changed

11 files changed

+103
-63
lines changed

CHANGELOG.md

+12
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,18 @@
22
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
33
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
44

5+
## 1.24.0
6+
7+
### Changed
8+
9+
- Extensions (`generate`, `modelcheck`, `runMigrations`) will use `mpsVersion` if specified, instead of relying on
10+
auto-detection logic. This makes it possible to use extensions with a non-standard MPS dependency (such as a
11+
pre-release).
12+
- Setting `mpsVersion` but not `mpsLocation` was not supported previously but is supported now. The default location
13+
(`$buildDir/mps`) will be used and `mpsConfig` will be unpacked there. At least one of `mpsLocation` and `mpsConfig`
14+
must still be specified.
15+
- Extension-related error messages now include the name of the extension.
16+
517
## 1.23.1
618

719
### Fixed

README.md

+26-20
Original file line numberDiff line numberDiff line change
@@ -279,10 +279,11 @@ dependencies {
279279
```
280280

281281
Parameters:
282-
* `mpsConfig` - the configuration used to resolve MPS. Currently only vanilla MPS is supported and no custom RCPs.
283-
Custom plugins are supported via the `pluginLocation` parameter.
284-
* `mpsLocation` - optional location where to place the MPS files.
285-
* `mpsVersion` - optional if you use a [custom distribution](#Custom MPS Distribution) of MPS
282+
* `mpsConfig` - the configuration used to resolve MPS. Custom plugins are supported via the `pluginLocation` parameter.
283+
* `mpsLocation` - optional location where to place the MPS files if `mpsConfig` is specified, or where to take them from
284+
otherwise.
285+
* `mpsVersion` - optionally overrides automated version detection from `mpsConfig`. Required if you use
286+
a [custom distribution](#Custom MPS Distribution) of MPS.
286287
* `javaExec` - optional `java` executable to use.
287288
* `pluginLocation` - location where to load the plugins from. Structure needs to be a flat folder structure similar to the
288289
`plugins` directory inside of the MPS installation.
@@ -348,10 +349,11 @@ modelcheck {
348349
```
349350

350351
Parameters:
351-
* `mpsConfig` - the configuration used to resolve MPS. Currently only vanilla MPS is supported and no custom RCPs.
352-
Custom plugins are supported via the `pluginLocation` parameter.
353-
* `mpsLocation` - optional location where to place the MPS files.
354-
* `mpsVersion` - optional if you use a [custom distribution](#Custom MPS Distribution) of MPS
352+
* `mpsConfig` - the configuration used to resolve MPS. Custom plugins are supported via the `pluginLocation` parameter.
353+
* `mpsLocation` - optional location where to place the MPS files if `mpsConfig` is specified, or where to take them from
354+
otherwise.
355+
* `mpsVersion` - optionally overrides automated version detection from `mpsConfig`. Required if you use
356+
a [custom distribution](#Custom MPS Distribution) of MPS.
355357
* `javaExec` - optional `java` executable to use.
356358
* `pluginLocation` - location where to load the plugins from. Structure needs to be a flat folder structure similar to the
357359
`plugins` directory inside of the MPS installation.
@@ -607,9 +609,11 @@ runMigrations {
607609
```
608610

609611
Parameters:
610-
* `mpsConfig` - configuration used to resolve MPS.
611-
* `mpsLocation` - location where to place the MPS files.
612-
* `mpsVersion` - if you use a [custom distribution](#custom-mps-distribution) of MPS.
612+
* `mpsConfig` - the configuration used to resolve MPS.
613+
* `mpsLocation` - optional location where to place the MPS files if `mpsConfig` is specified, or where to take them from
614+
otherwise.
615+
* `mpsVersion` - optionally overrides automated version detection from `mpsConfig`. Required if you use
616+
a [custom distribution](#Custom MPS Distribution) of MPS.
613617
* `projectLocation` - location of the project that should be migrated.
614618
* `force` - ignores the marker files for projects which allow pending migrations, migrate them anyway. Supported in 2021.3.0 and higher.
615619
* `haltOnPrecheckFailure` - controls whether migration is aborted if pre-checks fail (except the check for migrated dependecies) Default: `true`. Supported in 2021.1 and higher.
@@ -681,17 +685,19 @@ downloadJbr {
681685
## Custom MPS Distribution
682686

683687
Features that perform an action inside an MPS project, like the `modelcheck` or `generate-models` plugin, require
684-
an MPS available to them. While for vanilla MPS it is enough to pass in a reference to the MPS dependency via the
685-
`mpsConfig` property this doesn't work for custom distributions of MPS. A custom distribution of MPS is also called
686-
a MPS RCP. If you like to use your own MPS distribution with preinstalled plugins and your own versioning scheme
687-
then this is possible but requires additional steps in the build script.
688+
an MPS available to them. While for vanilla MPS it is enough to pass in a reference to the MPS dependency via the
689+
`mpsConfig` property, this doesn't work for custom distributions of MPS. A custom distribution of MPS is also called
690+
an MPS RCP. If you like to use your own MPS distribution with preinstalled plugins and your own versioning scheme
691+
then this is possible but requires additional steps in the build script.
688692

689-
When you are using a custom distribution of MPS you can no longer use the `mpsConfig` property and rely on
690-
the plugin resolving it. The plugin needs to be configured with the properties `mpsVersion` and `mpsLocation`
691-
being set and no value set for `mpsConfig`. If you set `mpsVersion` but also set `mpsConfig` then `mpsConfig`
692-
will take precedence over `mpsVersion` and the plugin will resolve that configuration into `mpsLocation`.
693+
When you are using a custom distribution of MPS you can still use the `mpsConfig` property and rely on
694+
the plugin resolving it. However, you may need to configure explicit `mpsVersion` for the plugin. You can also use a
695+
custom `mpsLocation` with no value set for `mpsConfig`. In this case you _must_ configure `mpsVersion` as well.
693696

694-
`mpsVersion` needs to be set to the exact MPS version your custom distribution is based on e.g. if you build a
697+
If you set `mpsVersion` but also set `mpsConfig` then `mpsVersion` will take precedence over the version of the
698+
dependency in the configuration. The plugin will resolve the specified configuration into `mpsLocation`.
699+
700+
`mpsVersion` needs to be set to the exact MPS version your custom distribution is based on. For example, if you build an
695701
RCP with MPS 2020.3.3 you need to set this property to `2020.3.3`. `mpsLocation` needs to point to the location
696702
where you extracted your custom MPS distribution into e.g. `$buildDir/myAwesomeMPS` if you extracted into that location.
697703

api/mps-gradle-plugin.api

+1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ public final class de/itemis/mps/gradle/CommonKt {
6262
public static final field MPS_SUPPORT_MSG Ljava/lang/String;
6363
public static final fun argsFromBaseExtension (Lde/itemis/mps/gradle/BasePluginExtensions;)Lorg/gradle/process/CommandLineArgumentProvider;
6464
public static final fun getMPSVersion (Lde/itemis/mps/gradle/BasePluginExtensions;)Ljava/lang/String;
65+
public static final fun getMPSVersion (Lde/itemis/mps/gradle/BasePluginExtensions;Ljava/lang/String;)Ljava/lang/String;
6566
public static final fun validateDefaultJvm ()V
6667
}
6768

build.gradle.kts

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ plugins {
2424
id("org.jetbrains.kotlinx.binary-compatibility-validator") version "0.13.2"
2525
}
2626

27-
val baseVersion = "1.23.1"
27+
val baseVersion = "1.24.0"
2828

2929
group = "de.itemis.mps"
3030

src/main/kotlin/de/itemis/mps/gradle/Common.kt

+21-21
Original file line numberDiff line numberDiff line change
@@ -89,26 +89,26 @@ fun argsFromBaseExtension(extensions: BasePluginExtensions): CommandLineArgument
8989
result
9090
}
9191

92-
fun BasePluginExtensions.getMPSVersion(): String {
93-
/*
94-
If the user supplies a MPS config we use this one to resolve MPS and get the version. For other scenarios the user
95-
can supply mpsLocation and mpsVersion then we do not resolve anything and the users build script is responsible for
96-
resolving a compatible MPS into th mpsLocation before the
97-
*/
98-
if(mpsConfig != null) {
99-
return mpsConfig!!
100-
.resolvedConfiguration
101-
.firstLevelModuleDependencies.find { it.moduleGroup == "com.jetbrains" && it.moduleName == "mps" }
102-
?.moduleVersion ?: throw GradleException("MPS configuration doesn't contain MPS")
103-
}
104-
105-
if(mpsVersion != null) {
106-
if(mpsLocation == null) {
107-
throw GradleException(ErrorMessages.MUST_SET_VERSION_AND_LOCATION)
108-
}
109-
return mpsVersion!!
92+
@Deprecated("Use getMPSVersion(extensionName)", replaceWith = ReplaceWith("getMPSVersion(this.javaClass.name)"))
93+
fun BasePluginExtensions.getMPSVersion(): String = getMPSVersion(this.javaClass.name)
94+
95+
/**
96+
* [extensionName]: extension name, for diagnostics.
97+
*/
98+
fun BasePluginExtensions.getMPSVersion(extensionName: String): String {
99+
// If the user supplies explicit mpsVersion, we use it.
100+
if (mpsVersion != null) return mpsVersion!!
101+
102+
val mpsConfig = mpsConfig
103+
if (mpsConfig != null) {
104+
// If the user supplies a configuration, we use it to detect MPS version.
105+
return mpsConfig.resolvedConfiguration.firstLevelModuleDependencies
106+
.find { it.moduleGroup == "com.jetbrains" && it.moduleName == "mps" }
107+
?.moduleVersion
108+
?: throw GradleException(ErrorMessages.couldNotDetermineMpsVersionFromConfiguration(mpsConfig)
109+
)
110110
}
111111

112-
throw GradleException(ErrorMessages.MUST_SET_CONFIG_OR_VERSION)
113-
114-
}
112+
// Otherwise, the version has to be provided explicitly.
113+
throw GradleException(ErrorMessages.mustSetVersionWhenNoMpsConfiguration(extensionName))
114+
}
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
package de.itemis.mps.gradle
22

3+
import org.gradle.api.artifacts.Configuration
34
import java.io.File
45

56
internal object ErrorMessages {
6-
const val MUST_SET_CONFIG_OR_VERSION = "Either mpsConfig or mpsVersion needs to specified!"
7-
const val MUST_SET_VERSION_AND_LOCATION = "Setting an MPS version but no MPS location is not supported!"
87
const val MPS_VERSION_NOT_SUPPORTED = "This version of mps-gradle-plugin only supports MPS 2020.1 and above. Please use version 1.4 with an older version of MPS."
98

10-
fun noMpsProjectIn(dir: File): String = "Directory does not contain an MPS project: $dir"
9+
internal fun noMpsProjectIn(dir: File): String = "Directory does not contain an MPS project: $dir"
10+
11+
internal fun couldNotDetermineMpsVersionFromConfiguration(mpsConfig: Configuration) =
12+
"Could not determine MPS version from configuration ${mpsConfig.name} (configuration must contain com.jetbrains:mps dependency)."
13+
14+
internal fun mustSetConfigOrLocation(extensionName: String) = "Either mpsConfig or mpsLocation needs to specified for extension $extensionName."
15+
internal fun mustSetVersionWhenNoMpsConfiguration(extensionName: String) =
16+
"Could not determine MPS version because mpsConfiguration was not specified. Set mpsVersion of $extensionName" +
17+
" extension explicitly."
1118
}

src/main/kotlin/de/itemis/mps/gradle/generate/Plugin.kt

+7-4
Original file line numberDiff line numberDiff line change
@@ -29,27 +29,30 @@ open class GenerateMpsProjectPlugin : Plugin<Project> {
2929

3030
override fun apply(project: Project) {
3131
project.run {
32-
val extension = extensions.create("generate", GeneratePluginExtensions::class.java)
32+
val extensionName = "generate"
33+
val extension = extensions.create(extensionName, GeneratePluginExtensions::class.java)
3334
val generate = tasks.register("generate", JavaExec::class.java)
3435
val fake = tasks.register("fakeBuildNumber", FakeBuildNumberTask::class.java)
3536

3637
afterEvaluate {
37-
val mpsLocation = extension.mpsLocation ?: File(project.buildDir, "mps")
38-
val mpsVersion = extension.getMPSVersion()
38+
val mpsVersion = extension.getMPSVersion(extensionName)
3939

4040
val genConfig = extension.backendConfig ?: createDetachedBackendConfig(project)
4141

4242
if(mpsVersion.substring(0..3).toInt() < 2020) {
4343
throw GradleException(MPS_SUPPORT_MSG)
4444
}
4545

46+
val mpsLocation = extension.mpsLocation ?: File(project.buildDir, "mps")
4647
val resolveMps = if (extension.mpsConfig != null) {
4748
tasks.register("resolveMpsForGeneration", Copy::class.java) {
4849
from({ extension.mpsConfig!!.resolve().map(::zipTree) })
4950
into(mpsLocation)
5051
}
51-
} else {
52+
} else if (extension.mpsLocation != null) {
5253
tasks.register("resolveMpsForGeneration")
54+
} else {
55+
throw GradleException(ErrorMessages.mustSetConfigOrLocation(extensionName))
5356
}
5457

5558
/*

src/main/kotlin/de/itemis/mps/gradle/modelcheck/Plugin.kt

+7-5
Original file line numberDiff line numberDiff line change
@@ -30,27 +30,29 @@ open class ModelCheckPluginExtensions(objectFactory: ObjectFactory) : BasePlugin
3030
open class ModelcheckMpsProjectPlugin : Plugin<Project> {
3131
override fun apply(project: Project) {
3232
project.run {
33-
val extension = extensions.create("modelcheck", ModelCheckPluginExtensions::class.java)
33+
val extensionName = "modelcheck"
34+
val extension = extensions.create(extensionName, ModelCheckPluginExtensions::class.java)
3435
val checkmodels = tasks.register("checkmodels", JavaExec::class.java)
3536

3637
afterEvaluate {
37-
val mpsLocation = extension.mpsLocation ?: File(project.buildDir, "mps")
38-
39-
val mpsVersion = extension.getMPSVersion()
38+
val mpsVersion = extension.getMPSVersion(extensionName)
4039

4140
val genConfig = extension.backendConfig ?: createDetachedBackendConfig(project)
4241

4342
if(mpsVersion.substring(0..3).toInt() < 2020) {
4443
throw GradleException(MPS_SUPPORT_MSG)
4544
}
4645

46+
val mpsLocation = extension.mpsLocation ?: File(project.buildDir, "mps")
4747
val resolveMps = if (extension.mpsConfig != null) {
4848
tasks.register("resolveMpsForModelcheck", Copy::class.java) {
4949
from({ extension.mpsConfig!!.resolve().map(::zipTree) })
5050
into(mpsLocation)
5151
}
52-
} else {
52+
} else if (extension.mpsLocation != null) {
5353
tasks.register("resolveMpsForModelcheck")
54+
} else {
55+
throw GradleException(ErrorMessages.mustSetConfigOrLocation(extensionName))
5456
}
5557

5658
checkmodels.configure {

src/main/kotlin/de/itemis/mps/gradle/runmigrations/Plugin.kt

+9-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package de.itemis.mps.gradle.runmigrations
22

33
import de.itemis.mps.gradle.BasePluginExtensions
4+
import de.itemis.mps.gradle.ErrorMessages
45
import de.itemis.mps.gradle.getMPSVersion
56
import de.itemis.mps.gradle.runAnt
67
import groovy.xml.MarkupBuilder
@@ -43,17 +44,18 @@ open class RunMigrationsMpsProjectPlugin : Plugin<Project> {
4344

4445
override fun apply(project: Project) {
4546
project.run {
46-
val extension = extensions.create("runMigrations", MigrationExecutorPluginExtensions::class.java)
47+
val extensionName = "runMigrations"
48+
val extension = extensions.create(extensionName, MigrationExecutorPluginExtensions::class.java)
49+
4750
tasks.register("runMigrations")
4851

4952
afterEvaluate {
50-
val mpsLocation = extension.mpsLocation ?: File(project.buildDir, "mps")
5153
val projectLocation = extension.projectLocation ?: throw GradleException("No project path set")
5254
if (!file(projectLocation).exists()) {
5355
throw GradleException("The path to the project doesn't exist: $projectLocation")
5456
}
5557

56-
val mpsVersion = extension.getMPSVersion()
58+
val mpsVersion = extension.getMPSVersion(extensionName)
5759
val parsedMPSVersion = SemVer.parse(mpsVersion)
5860

5961
if (extension.force != null && parsedMPSVersion < MIN_VERSION_FOR_FORCE) {
@@ -68,13 +70,16 @@ open class RunMigrationsMpsProjectPlugin : Plugin<Project> {
6870
throw GradleException("The 'do not halt on dependency error' option is only supported for MPS version $MIN_VERSION_FOR_HALT_ON_DEPENDENCY_ERROR and higher.")
6971
}
7072

73+
val mpsLocation = extension.mpsLocation ?: File(project.buildDir, "mps")
7174
val resolveMps: Task = if (extension.mpsConfig != null) {
7275
tasks.create("resolveMpsForMigrations", Copy::class.java) {
7376
from({ extension.mpsConfig!!.resolve().map(::zipTree) })
7477
into(mpsLocation)
7578
}
76-
} else {
79+
} else if (extension.mpsLocation != null) {
7780
tasks.create("resolveMpsForMigrations")
81+
} else {
82+
throw GradleException(ErrorMessages.mustSetConfigOrLocation(extensionName))
7883
}
7984

8085
tasks.named("runMigrations") {

src/test/kotlin/test/de/itemis/mps/gradle/GenerateModelsTest.kt

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package test.de.itemis.mps.gradle
22

33
import de.itemis.mps.gradle.ErrorMessages
4+
import de.itemis.mps.gradle.generate.GeneratePluginExtensions
45
import org.gradle.testkit.runner.GradleRunner
56
import org.gradle.testkit.runner.TaskOutcome
67
import org.hamcrest.CoreMatchers
@@ -256,7 +257,7 @@ class GenerateModelsTest {
256257
.withArguments()
257258
.withPluginClasspath()
258259
.buildAndFail()
259-
MatcherAssert.assertThat(result.output, CoreMatchers.containsString(ErrorMessages.MUST_SET_VERSION_AND_LOCATION))
260+
MatcherAssert.assertThat(result.output, CoreMatchers.containsString(ErrorMessages.mustSetConfigOrLocation("generate")))
260261
}
261262
@Test
262263
fun `generate fails with only MPS path set`() {
@@ -299,7 +300,7 @@ class GenerateModelsTest {
299300
.withArguments()
300301
.withPluginClasspath()
301302
.buildAndFail()
302-
MatcherAssert.assertThat(result.output, CoreMatchers.containsString(ErrorMessages.MUST_SET_CONFIG_OR_VERSION))
303+
MatcherAssert.assertThat(result.output, CoreMatchers.containsString(ErrorMessages.mustSetVersionWhenNoMpsConfiguration("generate")))
303304
}
304305

305306
@Test
@@ -345,4 +346,4 @@ class GenerateModelsTest {
345346
Assert.assertTrue("generate.javaLauncher should not be present",
346347
result.output.contains("generate.javaLauncher.isPresent: false"))
347348
}
348-
}
349+
}

src/test/kotlin/test/de/itemis/mps/gradle/ModelCheckWithPluginTest.kt

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package test.de.itemis.mps.gradle
22

33
import de.itemis.mps.gradle.ErrorMessages
4+
import de.itemis.mps.gradle.modelcheck.ModelCheckPluginExtensions
45
import org.gradle.testkit.runner.GradleRunner
56
import org.gradle.testkit.runner.TaskOutcome
67
import org.hamcrest.CoreMatchers
@@ -273,8 +274,9 @@ class ModelCheckWithPluginTest {
273274
.withPluginClasspath()
274275
.buildAndFail()
275276

276-
MatcherAssert.assertThat(result.output, CoreMatchers.containsString(ErrorMessages.MUST_SET_VERSION_AND_LOCATION))
277+
MatcherAssert.assertThat(result.output, CoreMatchers.containsString(ErrorMessages.mustSetConfigOrLocation("modelcheck")))
277278
}
279+
278280
@Test
279281
fun `check model fails with only MPS path set`() {
280282
settingsFile.writeText(settingsBoilerplate())
@@ -296,6 +298,7 @@ class ModelCheckWithPluginTest {
296298
.withPluginClasspath()
297299
.buildAndFail()
298300

299-
MatcherAssert.assertThat(result.output, CoreMatchers.containsString(ErrorMessages.MUST_SET_CONFIG_OR_VERSION))
301+
MatcherAssert.assertThat(result.output, CoreMatchers.containsString(
302+
ErrorMessages.mustSetVersionWhenNoMpsConfiguration("modelcheck")))
300303
}
301304
}

0 commit comments

Comments
 (0)