diff --git a/CHANGELOG.md b/CHANGELOG.md index e6575c2..77848b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 1.25.0 + +### Added + +- `Remigrate` task type to run re-runnable migrations on a project or several projects. + +### Changed + +- Extensions now use `--plugin-root` command line argument of backends, requiring backend versions 1.15.0 or above. + ## 1.24.0 ### Changed diff --git a/api/mps-gradle-plugin.api b/api/mps-gradle-plugin.api index 217b6d1..389c720 100644 --- a/api/mps-gradle-plugin.api +++ b/api/mps-gradle-plugin.api @@ -378,3 +378,15 @@ public abstract class de/itemis/mps/gradle/tasks/MpsMigrate : org/gradle/api/Def public final fun getProjectDirectories ()Lorg/gradle/api/file/ConfigurableFileCollection; } +public class de/itemis/mps/gradle/tasks/Remigrate : org/gradle/api/tasks/JavaExec { + public fun (Lorg/gradle/api/model/ObjectFactory;Lorg/gradle/api/provider/ProviderFactory;)V + public fun exec ()V + public final fun getAdditionalClasspath ()Lorg/gradle/api/file/ConfigurableFileCollection; + protected final fun getAllProjectFiles ()Lorg/gradle/api/provider/Provider; + public final fun getFolderMacros ()Lorg/gradle/api/provider/MapProperty; + public final fun getMpsHome ()Lorg/gradle/api/file/DirectoryProperty; + public final fun getMpsVersion ()Lorg/gradle/api/provider/Property; + public final fun getPluginRoots ()Lorg/gradle/api/file/ConfigurableFileCollection; + public final fun getProjectDirectories ()Lorg/gradle/api/file/ConfigurableFileCollection; +} + diff --git a/build.gradle.kts b/build.gradle.kts index b1ac527..4dc7919 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -24,7 +24,7 @@ plugins { id("org.jetbrains.kotlinx.binary-compatibility-validator") version "0.13.2" } -val baseVersion = "1.24.0" +val baseVersion = "1.25.0" group = "de.itemis.mps" diff --git a/docs/tasks/Remigrate.md b/docs/tasks/Remigrate.md new file mode 100644 index 0000000..13d9ab3 --- /dev/null +++ b/docs/tasks/Remigrate.md @@ -0,0 +1,35 @@ +## `Remigrate` Task Type + +Execute re-runnable migrations and project migrations on a project or several projects. + +### Usage + +```groovy +import de.itemis.mps.gradle.tasks.Remigrate + +plugins { + // Required in order to use the task + id("de.itemis.mps.gradle.common") +} + +tasks.register('remigrate', Remigrate) { + mpsHome = mpsHomeDir + + // Remigrate task can run migrations on multiple projects + projectDirectories.from(projectDir1) + projectDirectories.from(projectDir2) +} +``` + +Parameters: + +* `mpsHome` - the home directory of the MPS distribution (or RCP) to use for testing. +* `mpsVersion` - the MPS version, such as "2021.3". Autodetected by reading `$mpsHome/build.properties` by default. +* `projectDirectories` - project directories to migrate. +* `folderMacros` - path variables/macros that are necessary to open the project. Path macros are not considered part of + Gradle build cache key. +* `pluginRoots` - directories that will be searched (recursively) for additional plugins to load. + +### Operation + +The task will execute re-runnable migrations and project migrations on the specified projects. diff --git a/src/main/kotlin/de/itemis/mps/gradle/BackendConfigurations.kt b/src/main/kotlin/de/itemis/mps/gradle/BackendConfigurations.kt index ebfdd58..8a82b64 100644 --- a/src/main/kotlin/de/itemis/mps/gradle/BackendConfigurations.kt +++ b/src/main/kotlin/de/itemis/mps/gradle/BackendConfigurations.kt @@ -4,4 +4,5 @@ internal object BackendConfigurations { internal const val GENERATE_BACKEND_CONFIGURATION_NAME = "generateBackend" internal const val MODELCHECK_BACKEND_CONFIGURATION_NAME = "modelcheckBackend" internal const val EXECUTE_BACKEND_CONFIGURATION_NAME = "executeBackend" + internal const val REMIGRATE_BACKEND_CONFIGURATION_NAME = "remigrateBackend" } diff --git a/src/main/kotlin/de/itemis/mps/gradle/common.gradle.kts b/src/main/kotlin/de/itemis/mps/gradle/common.gradle.kts index c4a2453..d296f72 100644 --- a/src/main/kotlin/de/itemis/mps/gradle/common.gradle.kts +++ b/src/main/kotlin/de/itemis/mps/gradle/common.gradle.kts @@ -20,3 +20,10 @@ generateBackend.defaultDependencies { executeBackend.defaultDependencies { add(dependencies.create("de.itemis.mps.build-backends:execute:${MPS_BUILD_BACKENDS_VERSION}")) } + +configurations.create(BackendConfigurations.REMIGRATE_BACKEND_CONFIGURATION_NAME) { + isCanBeConsumed = false + defaultDependencies { + add(project.dependencies.create("de.itemis.mps.build-backends:remigrate:[0,2)")) + } +} diff --git a/src/main/kotlin/de/itemis/mps/gradle/tasks/Remigrate.kt b/src/main/kotlin/de/itemis/mps/gradle/tasks/Remigrate.kt new file mode 100644 index 0000000..ef16429 --- /dev/null +++ b/src/main/kotlin/de/itemis/mps/gradle/tasks/Remigrate.kt @@ -0,0 +1,98 @@ +package de.itemis.mps.gradle.tasks + +import de.itemis.mps.gradle.BackendConfigurations +import de.itemis.mps.gradle.TaskGroups +import de.itemis.mps.gradle.launcher.MpsBackendBuilder +import de.itemis.mps.gradle.launcher.MpsVersionDetection +import org.gradle.api.Incubating +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.Directory +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.MapProperty +import org.gradle.api.provider.Property +import org.gradle.api.provider.ProviderFactory +import org.gradle.api.tasks.* +import org.gradle.kotlin.dsl.mapProperty +import org.gradle.kotlin.dsl.newInstance +import org.gradle.kotlin.dsl.property +import org.gradle.process.CommandLineArgumentProvider +import javax.inject.Inject + +@Incubating +@UntrackedTask(because = "Operates 'in place'") +open class Remigrate @Inject constructor( + objectFactory: ObjectFactory, + providerFactory: ProviderFactory +) : JavaExec() { + + @get:Internal + val mpsHome: DirectoryProperty = objectFactory.directoryProperty() + + @get:Input + @get:Optional + val mpsVersion: Property = objectFactory.property() + .convention(MpsVersionDetection.fromMpsHome(project.layout, providerFactory, mpsHome.asFile)) + + @get:Internal("covered by allProjectFiles") + val projectDirectories: ConfigurableFileCollection = objectFactory.fileCollection() + + @get:InputFiles + @get:SkipWhenEmpty + @get:IgnoreEmptyDirectories + protected val allProjectFiles = providerFactory.provider { projectDirectories.flatMap { objectFactory.fileTree().from(it) } } + + @get:Internal("Folder macros are ignored for the purposes of up-to-date checks and caching") + val folderMacros: MapProperty = objectFactory.mapProperty() + + @get:Classpath + val pluginRoots: ConfigurableFileCollection = objectFactory.fileCollection() + + @get:Internal + val additionalClasspath: ConfigurableFileCollection = + objectFactory.fileCollection().from(initialBackendClasspath()) + + + init { + val backendBuilder: MpsBackendBuilder = project.objects.newInstance(MpsBackendBuilder::class) + backendBuilder.withMpsHomeDirectory(mpsHome).withMpsVersion(mpsVersion).configure(this) + + val backendConfig = project.configurations.named(BackendConfigurations.REMIGRATE_BACKEND_CONFIGURATION_NAME) + dependsOn(backendConfig) + + argumentProviders.add(CommandLineArgumentProvider { + val result = mutableListOf() + + for (dir in projectDirectories) { + result.add("--project=$dir") + } + + addPluginRoots(result, pluginRoots) + addLogLevel(result) + addFolderMacros(result, folderMacros) + result.add("--plugin=de.itemis.mps.buildbackends.remigrate::" + + backendConfig.get().files(backendConfig.get().dependencies.first()).first()) + + result + }) + + group = TaskGroups.MIGRATION + + classpath(backendConfig) + classpath(additionalClasspath) + + mainClass.set("de.itemis.mps.gradle.remigrate.MainKt") + } + + @TaskAction + override fun exec() { + for (dir in projectDirectories) { + checkProjectLocation(dir) + } + super.exec() + } + + private fun initialBackendClasspath() = mpsHome.asFileTree.matching { + include("lib/**/*.jar") + } +} diff --git a/src/test/kotlin/test/migration/RunMigrationsTest.kt b/src/test/kotlin/test/migration/RunMigrationsTest.kt index cade9e1..13f2479 100644 --- a/src/test/kotlin/test/migration/RunMigrationsTest.kt +++ b/src/test/kotlin/test/migration/RunMigrationsTest.kt @@ -211,6 +211,43 @@ class RunMigrationsTest { assertEquals(TaskOutcome.SUCCESS, result.task(":migrate")?.outcome) } + @Test + fun `Remigrate task works`() { + buildFile.writeText( + """ + import de.itemis.mps.gradle.tasks.Remigrate + + plugins { + id("de.itemis.mps.gradle.common") + } + + repositories { + mavenCentral() + maven("https://artifacts.itemis.cloud/repository/maven-mps") + } + + val mps = configurations.create("mps") + dependencies { + mps("com.jetbrains:mps:2021.3.2") + } + + val resolveMps by tasks.registering(Sync::class) { + from(Callable { zipTree(mps.singleFile) }) + into(layout.buildDirectory.dir("mps")) + } + + val remigrate by tasks.registering(Remigrate::class) { + projectDirectories.from("$mpsTestPrjLocation") + mpsHome.set(layout.dir(resolveMps.map { it.destinationDir })) + } + """.trimIndent() + ) + + val result = gradleRunner().withArguments("remigrate").forwardOutput().build() + + assertEquals(TaskOutcome.SUCCESS, result.task(":remigrate")?.outcome) + } + private fun gradleRunner(): GradleRunner = GradleRunner.create() .withProjectDir(testProjectDir.root) .withPluginClasspath()