diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index a3ba9a86..69d04de8 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -19,36 +19,31 @@ concurrency: jobs: build: - name: Build (JDK ${{ matrix.java }}) + name: Build runs-on: ubuntu-latest permissions: contents: read - strategy: - matrix: - java: [ 11, 17, 21 ] steps: - - uses: actions/checkout@v4 - # Java 11 is always needed to run the tests because they use MPS 2021.x - - name: Set up Java 11 - uses: actions/setup-java@v3 - with: - distribution: temurin - java-version: 11 - - uses: actions/setup-java@v3 + - uses: actions/checkout@v6 + + # Set up Java to run Gradle. Gradle will then download the appropriate version(s) to compile the projects or run + # tests. + - uses: actions/setup-java@v5 with: distribution: temurin - java-version: ${{ matrix.java }} - - uses: gradle/actions/setup-gradle@v3 + java-version: 17 + + - uses: gradle/actions/setup-gradle@v6 - name: Build with Gradle run: ./gradlew build - name: Upload JUnit XMLs - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 if: failure() with: name: junit-xmls path: '**/build/test-results/**/TEST-*.xml' retention-days: 7 - - uses: mikepenz/action-junit-report@v5 + - uses: mikepenz/action-junit-report@v6 if: success() || failure() with: report_paths: '**/build/test-results/**/TEST-*.xml' diff --git a/.java-version b/.java-version deleted file mode 100644 index 2dbc24b3..00000000 --- a/.java-version +++ /dev/null @@ -1 +0,0 @@ -11.0 diff --git a/api/mps-gradle-plugin.api b/api/mps-gradle-plugin.api deleted file mode 100644 index 51b6f915..00000000 --- a/api/mps-gradle-plugin.api +++ /dev/null @@ -1,416 +0,0 @@ -public class de/itemis/mps/gradle/BasePluginExtensions { - public fun (Lorg/gradle/api/model/ObjectFactory;)V - public final fun getBackendConfig ()Lorg/gradle/api/artifacts/Configuration; - public final fun getDebug ()Z - public final fun getEnvironmentKind ()Lorg/gradle/api/provider/Property; - public final fun getJavaExec ()Ljava/io/File; - public final fun getMacros ()Ljava/util/List; - public final fun getMaxHeap ()Ljava/lang/String; - public final fun getMpsConfig ()Lorg/gradle/api/artifacts/Configuration; - public final fun getMpsLocation ()Ljava/io/File; - public final fun getMpsVersion ()Ljava/lang/String; - public final fun getPluginLocation ()Ljava/io/File; - public final fun getPlugins ()Ljava/util/List; - public final fun getPluginsProperty ()Lorg/gradle/api/provider/ListProperty; - public final fun getProjectLocation ()Ljava/io/File; - public final fun setBackendConfig (Lorg/gradle/api/artifacts/Configuration;)V - public final fun setDebug (Z)V - public final fun setJavaExec (Ljava/io/File;)V - public final fun setMacros (Ljava/util/List;)V - public final fun setMaxHeap (Ljava/lang/String;)V - public final fun setMpsConfig (Lorg/gradle/api/artifacts/Configuration;)V - public final fun setMpsLocation (Ljava/io/File;)V - public final fun setMpsVersion (Ljava/lang/String;)V - public final fun setPluginLocation (Ljava/io/File;)V - public final fun setPlugins (Ljava/util/List;)V - public final fun setProjectLocation (Ljava/io/File;)V -} - -public abstract class de/itemis/mps/gradle/BuildLanguages : de/itemis/mps/gradle/RunAntScript { - public fun ()V -} - -public class de/itemis/mps/gradle/BundleMacosJdk : org/gradle/api/DefaultTask, groovy/lang/GroovyObject { - public static synthetic field __$stMC Z - protected synthetic fun $getStaticMetaClass ()Lgroovy/lang/MetaClass; - public fun ()V - public fun build ()Ljava/lang/Object; - public fun getJdk ()Ljava/io/File; - public fun getJdkDirname ()Ljava/lang/String; - public fun getMetaClass ()Lgroovy/lang/MetaClass; - public fun getOutputFile ()Ljava/io/File; - public fun getRcpArtifact ()Ljava/io/File; - public fun setJdk (Ljava/lang/Object;)Ljava/lang/Object; - public fun setJdkDependency (Ljava/lang/Object;)Ljava/lang/Object; - public fun setJdkDirname (Ljava/lang/String;)Ljava/lang/Object; - public fun setMetaClass (Lgroovy/lang/MetaClass;)V - public fun setOutputFile (Ljava/lang/Object;)Ljava/lang/Object; - public fun setRcpArtifact (Ljava/lang/Object;)Ljava/lang/Object; -} - -public class de/itemis/mps/gradle/BundledScripts : groovy/lang/GroovyObject { - public static synthetic field __$stMC Z - protected synthetic fun $getStaticMetaClass ()Lgroovy/lang/MetaClass; - public fun ()V - public static fun extractScriptsToDir (Ljava/io/File;[Ljava/lang/String;)V - public fun getMetaClass ()Lgroovy/lang/MetaClass; - public fun setMetaClass (Lgroovy/lang/MetaClass;)V -} - -public final class de/itemis/mps/gradle/CommonKt { - public static final field MPS_BUILD_BACKENDS_VERSION Ljava/lang/String; - public static final field MPS_SUPPORT_MSG Ljava/lang/String; - public static final fun argsFromBaseExtension (Lde/itemis/mps/gradle/BasePluginExtensions;)Lorg/gradle/process/CommandLineArgumentProvider; - public static final fun getMPSVersion (Lde/itemis/mps/gradle/BasePluginExtensions;)Ljava/lang/String; - public static final fun getMPSVersion (Lde/itemis/mps/gradle/BasePluginExtensions;Ljava/lang/String;)Ljava/lang/String; - public static final fun validateDefaultJvm ()V -} - -public final class de/itemis/mps/gradle/CommonPlugin : org/gradle/api/Plugin { - public fun ()V - public synthetic fun apply (Ljava/lang/Object;)V - public fun apply (Lorg/gradle/api/Project;)V -} - -public class de/itemis/mps/gradle/CreateDmg : org/gradle/api/DefaultTask, groovy/lang/GroovyObject { - public static synthetic field __$stMC Z - protected synthetic fun $getStaticMetaClass ()Lgroovy/lang/MetaClass; - public fun ()V - public fun build ()Ljava/lang/Object; - public fun getBackgroundImage ()Ljava/io/File; - public fun getDmgFile ()Ljava/io/File; - public fun getJdk ()Ljava/io/File; - public fun getMetaClass ()Lgroovy/lang/MetaClass; - public fun getRcpArtifact ()Ljava/io/File; - public fun getSignIdentity ()Ljava/lang/String; - public fun getSignKeyChain ()Ljava/io/File; - public fun getSignKeyChainPassword ()Ljava/lang/String; - public fun setBackgroundImage (Ljava/lang/Object;)Ljava/lang/Object; - public fun setDmgFile (Ljava/lang/Object;)Ljava/lang/Object; - public fun setJdk (Ljava/lang/Object;)Ljava/lang/Object; - public fun setJdkDependency (Ljava/lang/Object;)Ljava/lang/Object; - public fun setMetaClass (Lgroovy/lang/MetaClass;)V - public fun setRcpArtifact (Ljava/lang/Object;)Ljava/lang/Object; - public fun setSignIdentity (Ljava/lang/String;)V - public fun setSignKeyChain (Ljava/lang/Object;)Ljava/lang/Object; - public fun setSignKeyChainPassword (Ljava/lang/String;)V -} - -public final class de/itemis/mps/gradle/EnvironmentKind : java/lang/Enum { - public static final field IDEA Lde/itemis/mps/gradle/EnvironmentKind; - public static final field MPS Lde/itemis/mps/gradle/EnvironmentKind; - public static fun valueOf (Ljava/lang/String;)Lde/itemis/mps/gradle/EnvironmentKind; - public static fun values ()[Lde/itemis/mps/gradle/EnvironmentKind; -} - -public class de/itemis/mps/gradle/GenerateLibrariesXml : org/gradle/api/DefaultTask, groovy/lang/GroovyObject { - public static synthetic field __$stMC Z - protected synthetic fun $getStaticMetaClass ()Lgroovy/lang/MetaClass; - public fun ()V - public fun generate ()Ljava/lang/Object; - public fun getDefaults ()Ljava/io/File; - public fun getDestination ()Ljava/io/File; - public fun getMetaClass ()Lgroovy/lang/MetaClass; - public fun getOverrides ()Ljava/io/File; - public fun setDefaults (Ljava/io/File;)V - public fun setDestination (Ljava/io/File;)V - public fun setMetaClass (Lgroovy/lang/MetaClass;)V - public fun setOverrides (Ljava/lang/Object;)V -} - -public class de/itemis/mps/gradle/GetMpsInBrowser : org/gradle/api/DefaultTask, groovy/lang/GroovyObject { - public static synthetic field __$stMC Z - protected synthetic fun $getStaticMetaClass ()Lgroovy/lang/MetaClass; - public fun ()V - public fun build ()Ljava/lang/Object; - public fun getMetaClass ()Lgroovy/lang/MetaClass; - public fun getVersion ()Ljava/lang/String; - public fun setMetaClass (Lgroovy/lang/MetaClass;)V - public fun setVersion (Ljava/lang/String;)Ljava/lang/Object; -} - -public final class de/itemis/mps/gradle/Macro { - public fun (Ljava/lang/String;Ljava/lang/String;)V - public final fun component1 ()Ljava/lang/String; - public final fun component2 ()Ljava/lang/String; - public final fun copy (Ljava/lang/String;Ljava/lang/String;)Lde/itemis/mps/gradle/Macro; - public static synthetic fun copy$default (Lde/itemis/mps/gradle/Macro;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lde/itemis/mps/gradle/Macro; - public fun equals (Ljava/lang/Object;)Z - public final fun getName ()Ljava/lang/String; - public final fun getValue ()Ljava/lang/String; - public fun hashCode ()I - public final fun setName (Ljava/lang/String;)V - public final fun setValue (Ljava/lang/String;)V - public fun toString ()Ljava/lang/String; -} - -public final class de/itemis/mps/gradle/Plugin { - public fun (Ljava/lang/String;Ljava/lang/String;)V - public final fun component1 ()Ljava/lang/String; - public final fun component2 ()Ljava/lang/String; - public final fun copy (Ljava/lang/String;Ljava/lang/String;)Lde/itemis/mps/gradle/Plugin; - public static synthetic fun copy$default (Lde/itemis/mps/gradle/Plugin;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lde/itemis/mps/gradle/Plugin; - public fun equals (Ljava/lang/Object;)Z - public final fun getId ()Ljava/lang/String; - public final fun getPath ()Ljava/lang/String; - public fun hashCode ()I - public final fun setId (Ljava/lang/String;)V - public final fun setPath (Ljava/lang/String;)V - public fun toString ()Ljava/lang/String; -} - -public class de/itemis/mps/gradle/Pom : groovy/lang/GroovyObject { - public static synthetic field __$stMC Z - protected synthetic fun $getStaticMetaClass ()Lgroovy/lang/MetaClass; - public fun ()V - public fun getMetaClass ()Lgroovy/lang/MetaClass; - public fun setMetaClass (Lgroovy/lang/MetaClass;)V - public fun withDep (Lorg/gradle/api/publish/maven/MavenPom;Lorg/gradle/api/artifacts/Configuration;)Ljava/lang/Object; - public fun withProvidedDep (Lorg/gradle/api/publish/maven/MavenPom;Lorg/gradle/api/artifacts/Configuration;)Ljava/lang/Object; -} - -public abstract class de/itemis/mps/gradle/RunAntScript : org/gradle/api/DefaultTask { - public field script Ljava/lang/Object; - public fun ()V - public final fun build ()V - public final fun executable (Ljava/lang/Object;)V - protected abstract fun getExecOperations ()Lorg/gradle/process/ExecOperations; - public final fun getExecutable ()Ljava/lang/Object; - public final fun getIncludeDefaultArgs ()Z - public final fun getIncludeDefaultClasspath ()Z - public final fun getIncremental ()Z - public final fun getScript ()Ljava/lang/Object; - public final fun getScriptArgs ()Ljava/util/List; - public final fun getScriptClasspath ()Lorg/gradle/api/file/FileCollection; - public final fun getTargets ()Ljava/util/List; - public final fun setExecutable (Ljava/lang/Object;)V - public final fun setIncludeDefaultArgs (Z)V - public final fun setIncludeDefaultClasspath (Z)V - public final fun setIncremental (Z)V - public final fun setScript (Ljava/lang/Object;)V - public final fun setScriptArgs (Ljava/util/List;)V - public final fun setScriptClasspath (Lorg/gradle/api/file/FileCollection;)V - public final fun setTargets (Ljava/util/List;)V - public final fun targets ([Ljava/lang/String;)V -} - -public abstract class de/itemis/mps/gradle/TestLanguages : de/itemis/mps/gradle/RunAntScript { - public fun ()V -} - -public class de/itemis/mps/gradle/downloadJBR/DownloadJbrConfiguration { - public field jbrVersion Ljava/lang/String; - public fun (Lorg/gradle/api/model/ObjectFactory;)V - public final fun getDistributionType ()Ljava/lang/String; - public final fun getDownloadDir ()Ljava/io/File; - public final fun getJbrVersion ()Ljava/lang/String; - public final fun setDistributionType (Ljava/lang/String;)V - public final fun setDownloadDir (Ljava/io/File;)V - public final fun setJbrVersion (Ljava/lang/String;)V -} - -public class de/itemis/mps/gradle/downloadJBR/DownloadJbrForPlatform : org/gradle/api/DefaultTask { - public fun (Lorg/gradle/api/model/ObjectFactory;Lorg/gradle/jvm/toolchain/JavaToolchainService;)V - public final fun getJavaExecutable ()Ljava/io/File; - public final fun getJavaLauncher ()Lorg/gradle/api/provider/Provider; - public final fun getJbrDir ()Ljava/io/File; - public final fun getToolchainSpec ()Lorg/gradle/api/provider/Provider; - public final fun setJavaExecutable (Ljava/io/File;)V - public final fun setJbrDir (Ljava/io/File;)V -} - -public abstract class de/itemis/mps/gradle/downloadJBR/DownloadJbrProjectPlugin : org/gradle/api/Plugin { - public fun ()V - public synthetic fun apply (Ljava/lang/Object;)V - public fun apply (Lorg/gradle/api/Project;)V - protected abstract fun getExecOperations ()Lorg/gradle/process/ExecOperations; -} - -public abstract class de/itemis/mps/gradle/generate/FakeBuildNumberTask : org/gradle/api/DefaultTask { - public fun ()V - public final fun fakeBuildNumber ()V - public final fun getBuildTxt ()Lorg/gradle/api/provider/Provider; - public abstract fun getMpsDir ()Lorg/gradle/api/file/DirectoryProperty; -} - -public class de/itemis/mps/gradle/generate/GenerateMpsProjectPlugin : org/gradle/api/Plugin { - public fun ()V - public synthetic fun apply (Ljava/lang/Object;)V - public fun apply (Lorg/gradle/api/Project;)V -} - -public class de/itemis/mps/gradle/generate/GeneratePluginExtensions : de/itemis/mps/gradle/BasePluginExtensions { - public fun (Lorg/gradle/api/model/ObjectFactory;)V - public final fun getExcludeModels ()Ljava/util/List; - public final fun getExcludeModules ()Ljava/util/List; - public final fun getModels ()Ljava/util/List; - public final fun getModules ()Ljava/util/List; - public final fun getParallelGenerationThreads ()I - public final fun setExcludeModels (Ljava/util/List;)V - public final fun setExcludeModules (Ljava/util/List;)V - public final fun setModels (Ljava/util/List;)V - public final fun setModules (Ljava/util/List;)V - public final fun setParallelGenerationThreads (I)V -} - -public class de/itemis/mps/gradle/modelcheck/ModelCheckPluginExtensions : de/itemis/mps/gradle/BasePluginExtensions { - public fun (Lorg/gradle/api/model/ObjectFactory;)V - public final fun getErrorNoFail ()Z - public final fun getExcludeModels ()Ljava/util/List; - public final fun getExcludeModules ()Ljava/util/List; - public final fun getJunitFile ()Ljava/io/File; - public final fun getJunitFormat ()Ljava/lang/String; - public final fun getModels ()Ljava/util/List; - public final fun getModules ()Ljava/util/List; - public final fun getParallel ()Z - public final fun getWarningAsError ()Z - public final fun setErrorNoFail (Z)V - public final fun setExcludeModels (Ljava/util/List;)V - public final fun setExcludeModules (Ljava/util/List;)V - public final fun setJunitFile (Ljava/io/File;)V - public final fun setJunitFormat (Ljava/lang/String;)V - public final fun setModels (Ljava/util/List;)V - public final fun setModules (Ljava/util/List;)V - public final fun setParallel (Z)V - public final fun setWarningAsError (Z)V -} - -public class de/itemis/mps/gradle/modelcheck/ModelcheckMpsProjectPlugin : org/gradle/api/Plugin { - public fun ()V - public synthetic fun apply (Ljava/lang/Object;)V - public fun apply (Lorg/gradle/api/Project;)V -} - -public class de/itemis/mps/gradle/runmigrations/MigrationExecutorPluginExtensions : de/itemis/mps/gradle/BasePluginExtensions { - public fun (Lorg/gradle/api/model/ObjectFactory;)V - public final fun getForce ()Ljava/lang/Boolean; - public final fun getHaltOnDependencyError ()Ljava/lang/Boolean; - public final fun getHaltOnPrecheckFailure ()Ljava/lang/Boolean; - public final fun setForce (Ljava/lang/Boolean;)V - public final fun setHaltOnDependencyError (Ljava/lang/Boolean;)V - public final fun setHaltOnPrecheckFailure (Ljava/lang/Boolean;)V -} - -public abstract class de/itemis/mps/gradle/runmigrations/RunMigrationsMpsProjectPlugin : org/gradle/api/Plugin { - public static final field Companion Lde/itemis/mps/gradle/runmigrations/RunMigrationsMpsProjectPlugin$Companion; - public fun ()V - public synthetic fun apply (Ljava/lang/Object;)V - public fun apply (Lorg/gradle/api/Project;)V - protected abstract fun getExecOperations ()Lorg/gradle/process/ExecOperations; -} - -public final class de/itemis/mps/gradle/runmigrations/RunMigrationsMpsProjectPlugin$Companion { - public final fun getMIN_VERSION_FOR_FORCE ()Lnet/swiftzer/semver/SemVer; - public final fun getMIN_VERSION_FOR_HALT_ON_DEPENDENCY_ERROR ()Lnet/swiftzer/semver/SemVer; - public final fun getMIN_VERSION_FOR_HALT_ON_PRECHECK_FAILURE ()Lnet/swiftzer/semver/SemVer; -} - -public final class de/itemis/mps/gradle/tasks/ExcludedModuleMigration { - public fun (Ljava/lang/String;I)V - public final fun component1 ()Ljava/lang/String; - public final fun component2 ()I - public final fun copy (Ljava/lang/String;I)Lde/itemis/mps/gradle/tasks/ExcludedModuleMigration; - public static synthetic fun copy$default (Lde/itemis/mps/gradle/tasks/ExcludedModuleMigration;Ljava/lang/String;IILjava/lang/Object;)Lde/itemis/mps/gradle/tasks/ExcludedModuleMigration; - public fun equals (Ljava/lang/Object;)Z - public final fun getLanguage ()Ljava/lang/String; - public final fun getVersion ()I - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - -public abstract class de/itemis/mps/gradle/tasks/MpsCheck : org/gradle/api/tasks/JavaExec, org/gradle/api/tasks/VerificationTask { - public fun ()V - public fun exec ()V - public final fun getAdditionalModelcheckBackendClasspath ()Lorg/gradle/api/file/ConfigurableFileCollection; - protected final fun getCompiledClasses ()Lorg/gradle/api/file/FileTree; - public final fun getExcludeModels ()Lorg/gradle/api/provider/ListProperty; - public final fun getExcludeModules ()Lorg/gradle/api/provider/ListProperty; - public final fun getFolderMacros ()Lorg/gradle/api/provider/MapProperty; - public final fun getJunitFile ()Lorg/gradle/api/file/RegularFileProperty; - public final fun getJunitFormat ()Lorg/gradle/api/provider/Property; - public final fun getLogLevel ()Lorg/gradle/api/provider/Property; - public final fun getModels ()Lorg/gradle/api/provider/ListProperty; - public final fun getModules ()Lorg/gradle/api/provider/ListProperty; - public final fun getMpsHome ()Lorg/gradle/api/file/DirectoryProperty; - public final fun getMpsVersion ()Lorg/gradle/api/provider/Property; - public final fun getParallel ()Lorg/gradle/api/provider/Property; - public final fun getPluginRoots ()Lorg/gradle/api/provider/SetProperty; - public final fun getProjectLocation ()Lorg/gradle/api/file/DirectoryProperty; - protected final fun getSources ()Lorg/gradle/api/file/FileTree; - public final fun getVarMacros ()Lorg/gradle/api/provider/MapProperty; - public final fun getWarningAsError ()Lorg/gradle/api/provider/Property; -} - -public abstract class de/itemis/mps/gradle/tasks/MpsExecute : org/gradle/api/tasks/JavaExec { - public fun ()V - public fun exec ()V - public final fun getAdditionalExecuteBackendClasspath ()Lorg/gradle/api/file/ConfigurableFileCollection; - public abstract fun getClassName ()Lorg/gradle/api/provider/Property; - public abstract fun getLogLevel ()Lorg/gradle/api/provider/Property; - public abstract fun getMacros ()Lorg/gradle/api/provider/MapProperty; - public abstract fun getMethod ()Lorg/gradle/api/provider/Property; - public abstract fun getMethodArguments ()Lorg/gradle/api/provider/ListProperty; - public abstract fun getModule ()Lorg/gradle/api/provider/Property; - public abstract fun getMpsHome ()Lorg/gradle/api/file/DirectoryProperty; - public abstract fun getMpsVersion ()Lorg/gradle/api/provider/Property; - public abstract fun getPluginRoots ()Lorg/gradle/api/provider/SetProperty; - public abstract fun getProjectLocation ()Lorg/gradle/api/file/DirectoryProperty; -} - -public abstract class de/itemis/mps/gradle/tasks/MpsGenerate : org/gradle/api/tasks/JavaExec { - public fun ()V - public fun exec ()V - public final fun getAdditionalGenerateBackendClasspath ()Lorg/gradle/api/file/ConfigurableFileCollection; - protected final fun getCompiledClasses ()Lorg/gradle/api/file/FileTree; - public final fun getEnvironmentKind ()Lorg/gradle/api/provider/Property; - public final fun getExcludeModels ()Lorg/gradle/api/provider/ListProperty; - public final fun getExcludeModules ()Lorg/gradle/api/provider/ListProperty; - public final fun getFolderMacros ()Lorg/gradle/api/provider/MapProperty; - public final fun getLogLevel ()Lorg/gradle/api/provider/Property; - public final fun getModels ()Lorg/gradle/api/provider/ListProperty; - public final fun getModules ()Lorg/gradle/api/provider/ListProperty; - public final fun getMpsHome ()Lorg/gradle/api/file/DirectoryProperty; - public final fun getMpsVersion ()Lorg/gradle/api/provider/Property; - public final fun getParallelGenerationThreads ()Lorg/gradle/api/provider/Property; - public final fun getPluginRoots ()Lorg/gradle/api/file/ConfigurableFileCollection; - public final fun getProjectLocation ()Lorg/gradle/api/file/DirectoryProperty; - protected final fun getSources ()Lorg/gradle/api/file/FileTree; - public final fun getStrictMode ()Lorg/gradle/api/provider/Property; - public final fun getVarMacros ()Lorg/gradle/api/provider/MapProperty; -} - -public abstract class de/itemis/mps/gradle/tasks/MpsMigrate : org/gradle/api/DefaultTask { - public fun (Lorg/gradle/api/model/ObjectFactory;Lorg/gradle/api/provider/ProviderFactory;)V - public final fun execute ()V - protected final fun getAllProjectFiles ()Lorg/gradle/api/provider/Provider; - public final fun getAntJvmArgs ()Lorg/gradle/api/provider/ListProperty; - protected abstract fun getExecOperations ()Lorg/gradle/process/ExecOperations; - public final fun getFolderMacros ()Lorg/gradle/api/provider/MapProperty; - public final fun getHaltOnDependencyError ()Lorg/gradle/api/provider/Property; - public final fun getHaltOnPrecheckFailure ()Lorg/gradle/api/provider/Property; - public final fun getJavaExecutable ()Lorg/gradle/api/file/RegularFileProperty; - public final fun getJavaLauncher ()Lorg/gradle/api/provider/Property; - public final fun getJvmArgs ()Lorg/gradle/api/provider/ListProperty; - public final fun getLogLevel ()Lorg/gradle/api/provider/Property; - public final fun getMaxHeapSize ()Lorg/gradle/api/provider/Property; - 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; -} - -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 final fun excludeModuleMigration (Ljava/lang/String;I)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 getExcludedModuleMigrations ()Lorg/gradle/api/provider/SetProperty; - public final fun getFolderMacros ()Lorg/gradle/api/provider/MapProperty; - public final fun getLogLevel ()Lorg/gradle/api/provider/Property; - 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 bd0dfaa1..be69aee5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,163 +1,9 @@ -import de.itemis.mps.gradle.GitBasedVersioning -import org.jetbrains.kotlin.gradle.dsl.JvmTarget -import org.jetbrains.kotlin.gradle.dsl.KotlinVersion - -buildscript { - configurations.classpath { - resolutionStrategy.activateDependencyLocking() - } - - dependencies { - classpath("de.itemis.mps.gradle:git-based-versioning") - } -} - -plugins { - groovy - `java-gradle-plugin` - `kotlin-dsl` - `maven-publish` - kotlin("jvm") version libs.versions.kotlin - alias(libs.plugins.kotlin.compatibility.validator) -} - -group = "de.itemis.mps" - -val baseVersion = "3.0.0-dev" -val currentBranch : String? = GitBasedVersioning.getGitBranch() - -version = if (!project.hasProperty("useSnapshot") && - (project.hasProperty("forceCI") || project.hasProperty("teamcity")) -) { - val prefix = when (currentBranch) { - null, "", "v1.x", "HEAD", "master", "main" -> "" - else -> "$currentBranch." - } - - val suffix = ".${GitBasedVersioning.getGitCommitCount()}.${GitBasedVersioning.getGitShortCommitHash()}" - - prefix + baseVersion + suffix -} else { - "$baseVersion-SNAPSHOT" -} - -val mpsConfiguration = configurations.create("mps") - -repositories { - mavenCentral() - // For mps-build-backends, during tests - maven(url = "https://artifacts.itemis.cloud/repository/maven-mps") -} - -dependencyLocking { - lockAllConfigurations() -} - -dependencies { - api(libs.itemis.gradle.git.based.versioning) - implementation(libs.kotlin.stdlib) - implementation(libs.swiftzer.semver) - implementation(libs.itemis.gradle.build.backends.launcher) - testImplementation(libs.junit) -} - -tasks.test { - useJUnit() -} - -gradlePlugin { - plugins { - register("generate-models") { - id = "generate-models" - implementationClass = "de.itemis.mps.gradle.generate.GenerateMpsProjectPlugin" - } - register("modelcheck") { - id = "modelcheck" - implementationClass = "de.itemis.mps.gradle.modelcheck.ModelcheckMpsProjectPlugin" - } - register("migrations-executor") { - id = "run-migrations" - implementationClass = "de.itemis.mps.gradle.runmigrations.RunMigrationsMpsProjectPlugin" - } - register("download-jbr") { - id = "download-jbr" - implementationClass = "de.itemis.mps.gradle.downloadJBR.DownloadJbrProjectPlugin" - } - } -} - -tasks.register("setTeamCityBuildNumber") { - doLast { - println("##teamcity[buildNumber '$version']") - } -} - -publishing { - repositories { - maven { - name = "itemisCloud" - url = uri("https://artifacts.itemis.cloud/repository/maven-mps-releases/") - if (project.hasProperty("artifacts.itemis.cloud.user") && project.hasProperty("artifacts.itemis.cloud.pw")) { - credentials { - username = project.findProperty("artifacts.itemis.cloud.user") as String? - password = project.findProperty("artifacts.itemis.cloud.pw") as String? - } - } - } - if (currentBranch == "master" || currentBranch == "v1.x") { - maven { - name = "GitHubPackages" - url = uri("https://maven.pkg.github.com/mbeddr/mps-gradle-plugin") - if(project.hasProperty("gpr.token")) { - credentials { - username = project.findProperty("gpr.user") as String? - password = project.findProperty("gpr.token") as String? - } - } - } - } - } - - publications.withType().configureEach { - versionMapping { - allVariants { - fromResolutionResult() - } - } - pom { - url.set("https://github.com/mbeddr/mps-gradle-plugin") - licenses { - license { - name.set("The Apache License, Version 2.0") - url.set("https://www.apache.org/licenses/LICENSE-2.0.txt") - } - } - scm { - connection.set("scm:git:git://github.com/mbeddr/mps-gradle-plugin.git") - developerConnection.set("scm:git:ssh://github.com/mbeddr/mps-gradle-plugin.git") - url.set("https://github.com/mbeddr/mps-gradle-plugin") - } - } - } -} - -java { - targetCompatibility = JavaVersion.VERSION_11 - withSourcesJar() -} - -kotlin { - compilerOptions { - jvmTarget = JvmTarget.fromTarget(libs.versions.kotlinJvmTarget.get()) - apiVersion = KotlinVersion.fromVersion(libs.versions.kotlinApi.get()) - allWarningsAsErrors = true - } -} - -apiValidation { - ignoredClasses.add("de.itemis.mps.gradle.Common_gradle") +tasks.register("build") { + description = "Aggregates the build task of each included build." + dependsOn(gradle.includedBuilds.map { it.task(":build") }) } -tasks.test { - maxParallelForks = (Runtime.getRuntime().availableProcessors() * 2 / 3).coerceAtLeast(1) +tasks.register("publishToMavenLocal") { + description = "Aggregates the publishToMavenLocal task of each included build." + dependsOn(gradle.includedBuilds.map { it.task(":publishToMavenLocal") }) } diff --git a/buildscript-gradle.lockfile b/buildscript-gradle.lockfile deleted file mode 100644 index 13545f8b..00000000 --- a/buildscript-gradle.lockfile +++ /dev/null @@ -1,35 +0,0 @@ -# This is a Gradle generated file for dependency locking. -# Manual edits can break the build and are not advised. -# This file is expected to be part of source control. -com.google.code.gson:gson:2.8.9=classpath -io.github.java-diff-utils:java-diff-utils:4.12=classpath -org.gradle.kotlin.kotlin-dsl:org.gradle.kotlin.kotlin-dsl.gradle.plugin:5.2.0=classpath -org.gradle.kotlin:gradle-kotlin-dsl-plugins:5.2.0=classpath -org.jetbrains.intellij.deps:trove4j:1.0.20200330=classpath -org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:1.7.10=classpath -org.jetbrains.kotlin:kotlin-assignment:2.0.21=classpath -org.jetbrains.kotlin:kotlin-build-statistics:2.0.21=classpath -org.jetbrains.kotlin:kotlin-build-tools-api:2.0.21=classpath -org.jetbrains.kotlin:kotlin-compiler-embeddable:2.0.21=classpath -org.jetbrains.kotlin:kotlin-compiler-runner:2.0.21=classpath -org.jetbrains.kotlin:kotlin-daemon-client:2.0.21=classpath -org.jetbrains.kotlin:kotlin-daemon-embeddable:2.0.21=classpath -org.jetbrains.kotlin:kotlin-gradle-plugin-annotations:2.0.21=classpath -org.jetbrains.kotlin:kotlin-gradle-plugin-api:2.0.21=classpath -org.jetbrains.kotlin:kotlin-gradle-plugin-idea-proto:2.0.21=classpath -org.jetbrains.kotlin:kotlin-gradle-plugin-idea:2.0.21=classpath -org.jetbrains.kotlin:kotlin-gradle-plugin-model:2.0.21=classpath -org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.21=classpath -org.jetbrains.kotlin:kotlin-gradle-plugins-bom:2.0.21=classpath -org.jetbrains.kotlin:kotlin-klib-commonizer-api:2.0.21=classpath -org.jetbrains.kotlin:kotlin-native-utils:2.0.21=classpath -org.jetbrains.kotlin:kotlin-sam-with-receiver:2.0.21=classpath -org.jetbrains.kotlin:kotlin-stdlib:2.0.21=classpath -org.jetbrains.kotlin:kotlin-tooling-core:2.0.21=classpath -org.jetbrains.kotlin:kotlin-util-io:2.0.21=classpath -org.jetbrains.kotlin:kotlin-util-klib:2.0.21=classpath -org.jetbrains.kotlinx.binary-compatibility-validator:org.jetbrains.kotlinx.binary-compatibility-validator.gradle.plugin:0.18.1=classpath -org.jetbrains.kotlinx:binary-compatibility-validator:0.18.1=classpath -org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.6.4=classpath -org.jetbrains:annotations:13.0=classpath -empty= diff --git a/docs/plugins/download-jbr.md b/docs/plugins/download-jbr.md deleted file mode 100644 index ea295d73..00000000 --- a/docs/plugins/download-jbr.md +++ /dev/null @@ -1,57 +0,0 @@ -## Download JetBrains Runtime - -When building MPS projects with the JetBrains Runtime, the JDK/JRE used by MPS and other JetBrains IDEs, it's -required to download the correct version of the runtime. Since the runtime is platform-dependent it is required to -download a platform-dependent binary. While it's possible to add the logic to your own build script, we provide -a convenient way of doing this with a Gradle plugin. - -The `download-jbr` plugin will add new dependencies and a task to your build. It will add a dependency to -`com.jetbrains.jdk:jbr` to your build, you need to make sure that it is available in your dependency repositories. The -[itemis Maven repository](https://artifacts.itemis.cloud/repository/maven-mps) provides this dependency, but you can -create your own with the scripts located in [mbeddr/build.publish.jdk](https://github.com/mbeddr/build.publish.jdk). - -For easy consumption and incremental build support the plugin creates a task `downloadJbr` which exposes the location of -the java executable via the `javaExecutable` and `javaLauncher` properties. See -[the tests](../../src/test/kotlin/test/others/JBRDownloadTest.kt) for an example of how to use it. - -### Usage - - -Kotlin: -``` -plugins { - id("download-jbr") -} - -repositories { - mavenCentral() - maven("https://artifacts.itemis.cloud/repository/maven-mps") -} - -downloadJbr { - jbrVersion = "11_0_6-b520.66" -} -``` - -Groovy: -``` -apply plugin: 'download-jbr' -... - -repositories { - maven { url 'https://artifacts.itemis.cloud/repository/maven-mps' } - mavenCentral() -} - -downloadJbr { - jbrVersion = '11_0_6-b520.66' -} -``` - -### Parameters - -* `jbrVersion` - version of the JBR to download. While this supports maven version selectors we highly recommend not - using wildcards like `*` or `+` in there for reproducible builds. -* `distributionType` - optional distribution type for the JBR to use. Will default to `jbr_jcef` if omitted. -* `downloadDir` - optional directory where the downloaded JBR is downloaded and extracted to. The plugin defaults to - `build/jbrDownload`. diff --git a/docs/plugins/generate.md b/docs/plugins/generate.md deleted file mode 100644 index b1e6e367..00000000 --- a/docs/plugins/generate.md +++ /dev/null @@ -1,67 +0,0 @@ -## `generate` - -Generate a specific or all models in a project without the need for an MPS model. - -While technically possible generating languages with this task makes little sense as there is no way of packaging the -generated artifacts into JAR files. We only recommend using this for simple tasks where user defined models should be -generated in the CI build or from the commandline. - -### Usage - -A minimal build script to generate an MPS project with no external plugins would look like this: - -``` -apply plugin: 'generate-models' - -configurations { - mps -} - -ext.mpsVersion = '2018.3.6' - -generate { - projectLocation = new File("./mps-prj") - mpsConfig = configurations.mps -} - -dependencies { - mps "com.jetbrains:mps:$mpsVersion" -} -``` - -Parameters: -* `mpsConfig` - the configuration used to resolve MPS. Custom plugins are supported via the `pluginLocation` parameter. -* `mpsLocation` - optional location where to place the MPS files if `mpsConfig` is specified, or where to take them from - otherwise. -* `mpsVersion` - optionally overrides automated version detection from `mpsConfig`. Required if you use - a [custom distribution](../notes/custom-mps-distribution.md) of MPS. -* `javaExec` - optional `java` executable to use. -* `pluginLocation` - location where to load the plugins from. Structure needs to be a flat folder structure similar to the - `plugins` directory inside of the MPS installation. -* `plugins` - deprecated, use `pluginsProperty`. -* `pluginsProperty` - optional list of plugins to load before generation is attempted. - The notation is `new Plugin("pluginID", "somePath")`. The first parameter is the plugin id. - For the second parameter `"somePath"` there are several options: - * if it's an absolute path, the plugin is loaded from that path - * if it's a folder located under `pluginLocation` the plugin is loaded from that folder - * otherwise it should be a plugin folder located under the default `mps/plugins` -* `models` - optional list of models to generate. If omitted all models in the project will be generated. Only full name - matched are supported and no RegEx or partial name matching. -* `excludeModels` (since 1.14) - optional list of models to exclude from generating. RegEx can be used for matching multiple models. -* `modules` (since 1.14) - optional list of modules to generate. Expects ordinary name (w/o virtual folders). RegEx can be used for matching multiple modules. - If both parameters, `models` and `modules`, are omitted - all models in the project will be generated, except as - excluded by `excludeModels` and `excludeModules`. -* `excludeModules` (since 1.14) - optional list of modules to exclude from generate. RegEx can be used for matching multiple modules. -* `macros` - optional list of path macros. The notation is `new Macro("name", "value")`. -* `projectLocation` - location of the MPS project to generate. -* `parallelGenerationThreads` (since 1.17) - optional number of threads to use for parallel generation. Defaults to `0`, - which means that parallel generation is turned off. -* `debug` - optionally allows to start the JVM that is used to generated with a debugger. Setting it to `true` will cause - the started JVM to suspend until a debugger is attached. Useful for debugging classloading problems or exceptions during - the build. -* `backendConfig` - optional configuration providing the backend. If not given, the `execute-generators` backend from - [mps-build-backends](https://github.com/mbeddr/mps-build-backends) will be used. -* `environmentKind` - optional kind of environment (MPS or IDEA) to execute the generators in. IDEA environment is used - by default for backwards compatibility but MPS environment may be faster. See [MPS vs IDEA environment](../notes/mps-vs-idea-environment.md). -* `maxHeap` (since 1.15) - maximum heap size setting for the JVM that executes the generator. The value is a string - understood by the JVM command line argument `-Xmx` e.g. `3G` or `512M`. diff --git a/docs/plugins/modelcheck.md b/docs/plugins/modelcheck.md deleted file mode 100644 index 3b1024c0..00000000 --- a/docs/plugins/modelcheck.md +++ /dev/null @@ -1,102 +0,0 @@ -## `checkModels` - -Run the model check on a subset or all models in a project directly from gradle. - -This functionality currently runs all model checks (typesystem, structure, constrains, etc.) from Gradle. By default, if -any of checks fails, the complete build is failed. All messages (Info, Warning or Error) are reported through log4j to -the command line. - -### Usage - -A minimal build script to check all models in an MPS project with no external plugins would look like this: - -``` -apply plugin: 'modelcheck' - -configurations { - mps -} - -dependencies { - mps "com.jetbrains:mps:$mpsVersion" -} - -ext.mpsVersion = '2018.3.6' - -modelcheck { - projectLocation = new File("./mps-prj") - mpsConfig = configurations.mps - macros = [Macro("mypath", "/your/path")] -} -``` - -Parameters: -* `mpsConfig` - the configuration used to resolve MPS. Custom plugins are supported via the `pluginLocation` parameter. -* `mpsLocation` - optional location where to place the MPS files if `mpsConfig` is specified, or where to take them from - otherwise. -* `mpsVersion` - optionally overrides automated version detection from `mpsConfig`. Required if you use - a [custom distribution](../notes/custom-mps-distribution.md) of MPS. -* `javaExec` - optional `java` executable to use. -* `pluginLocation` - location where to load the plugins from. Structure needs to be a flat folder structure similar to the - `plugins` directory inside of the MPS installation. -* `plugins` - deprecated, use `pluginsProperty`. -* `pluginsProperty` - optional list of plugins to load before generation is attempted. - The notation is `new Plugin("pluginID", "somePath")`. The first parameter is the plugin id. - For the second parameter `"somePath"` there are several options: - * if it's an absolute path, the plugin is loaded from that path - * if it's a folder located under `pluginLocation` the plugin is loaded from that folder - * otherwise it should be a plugin folder located under the default `mps/plugins` -* `models` - optional list of models to check. RegEx can be used for matching multiple models. -* `excludeModels` - optional list of models to exclude from checking. RegEx can be used for matching multiple models. -* `modules` - optional list of modules to check. Expects ordinary name (w/o virtual folders). RegEx can be used for matching multiple modules. - If both parameters, `models` and `modules`, are omitted - all models in the project will be checked, except as - excluded by `excludeModels` and `excludeModules`. -* `excludeModules` - optional list of modules to exclude from checking. RegEx can be used for matching multiple modules. -* `macros` - optional list of path macros. The notation is `new Macro("name", "value")`. -* `projectLocation` - location of the MPS project to check. -* `errorNoFail` - report errors but do not fail the build. -* `warningAsError` - handles warnings as errors and will fail the build if any is found when `errorNoFail` is not set. -* `debug` - optionally allows to start the JVM that is used to load MPS project with a debugger. Setting it to `true` will cause - the started JVM to suspend until a debugger is attached. Useful for debugging classloading problems or exceptions during - the build. -* `junitFile` - allows storing the results of the model check as a JUnit XML file. By default, the file will contain one - testcase for each model that was checked (s. `junitFormat`). -* `junitFormat` - specifies how errors are reported in the JUnit XML file. Possible options: - * `model` (default, deprecated) - generates one test case for each model that was checked. If the model check reported - any error for the model, the test case will contain a failure with the error message. - * `module-and-model` (preferred) - generates one test case for each module and model that was checked. If the model - check reported any error for the model or module, the test case will contain a failure with the error message. - * `message` - generates one testcase for each model check error. For uniqueness reasons, the name of the testcase will - reflect the specific model check error and the name of the test class will be constructed from the checked node ID - and its containing root node. Full error message and the node URL will be reported in the testcase failure. Checked - models will be mapped to test suites with this option. -* `parallel` (since 1.20) - runs model checker in parallel mode. Supported in MPS 2021.3.4. Default is `false`. -* `maxHeap` - maximum heap size setting for the JVM that executes the model checker. This is useful to limit the heap usage - in scenarios like containerized build agents where the OS reported memory limit is not the maximum - to be consumed by the container. The value is a string understood by the JVM command line argument `-Xmx` e.g. `3G` or `512M`. -* `backendConfig` - optional configuration providing the backend. If not given, the `modelcheck` backend from - [mps-build-backends](https://github.com/mbeddr/mps-build-backends) will be used. -* `environmentKind` - optional kind of environment (MPS or IDEA) to execute the generators in. IDEA environment is used - by default for backwards compatibility but MPS environment may be faster. See [MPS vs IDEA environment](../notes/mps-vs-idea-environment.md). - -### Additional Plugins - -By default, only the minimum required set of plugins is loaded. This includes base language and some utilities like the -HTTP server from MPS. If your project requires additional plugins to be loaded this is done by setting plugin location -to the place where your jar files are placed and adding your plugin id and folder name to the `plugins` list: - -``` -apply plugin: 'modelcheck' -... - -modelcheck { - pluginLocation = new File("path/to/my/plugins") - pluginsProperty = [new Plugin("com.mbeddr.core", "mbeddr.core")] - projectLocation = new File("./mps-prj") - mpsConfig = configurations.mps -} - -``` - -Dependencies of the specified plugins are automatically loaded from the `pluginLocation` and the plugins directory of -MPS. If they are not found the build will fail. diff --git a/docs/tasks/BundleMacosJdk.md b/docs/tasks/BundleMacosJdk.md deleted file mode 100644 index b9f940ea..00000000 --- a/docs/tasks/BundleMacosJdk.md +++ /dev/null @@ -1,33 +0,0 @@ -## BundleMacosJdk - -(Linux/macOS) Creates a .tar.gz by combining an RCP artifact and a JDK. -This task is intended as a substitute for the macOS-specific CreateDmg -task. - -### Usage - -``` -task bundleMacosJdk(type: de.itemis.mps.gradle.BundleMacosJdk) { - rcpArtifact file('path/to/RCP.tgz') - - jdkDependency "com.jetbrains.jdk:jdk:${jdkVersion}:osx_x64@tgz" - // -or - - jdk file('path/to/jdk.tgz') - - outputFile file('output.tar.gz') -} -``` - -Parameters: -* `rcpArtifact` - the path to the RCP artifact produced by a build script. -* `jdkDependency` - the coordinates of a JDK in case it's available in - a repository and can be resolved as a Gradle dependency. -* `jdk` - the path to a JDK .tgz file. -* `outputFile` - the path and file name of the output gzipped tar archive. - -### Operation - -The task unpacks `rcpArtifact` into a temporary directory, unpacks -the JDK given by `jdkDependency`/`jdk` under the `jre` subdirectory of -the unpacked RCP artifact, fixes file permissions and creates missing -symlinks. Finally, the file is repackaged again as tar/gzip. diff --git a/docs/tasks/CreateDmg.md b/docs/tasks/CreateDmg.md deleted file mode 100644 index 94d1106a..00000000 --- a/docs/tasks/CreateDmg.md +++ /dev/null @@ -1,46 +0,0 @@ -## CreateDmg - -(macOS only) Creates a .dmg installer by combining an RCP artifact (as -created by an MPS-generated Ant script), a JDK, and a background image. - -### Usage - -``` -task buildDmg(type: de.itemis.mps.gradle.CreateDmg) { - rcpArtifact file('path/to/RCP.tgz') - - jdkDependency "com.jetbrains.jdk:jdk:${jdkVersion}:osx_x64@tgz" - // -or - - jdk file('path/to/jdk.tgz') - - backgroundImage file('path/to/background.png') - dmgFile file('output.dmg') - - signKeyChain file("/path/to/my.keychain-db") - - signKeyChainPassword "my.keychain-db-password" - - signIdentity "my Application ID Name" -} -``` - -Parameters: -* `rcpArtifact` - the path to the RCP artifact produced by a build script. -* `jdkDependency` - the coordinates of a JDK in case it's available in - a repository and can be resolved as a Gradle dependency. -* `jdk` - the path to a JDK .tgz file. -* `backgroundImage` - the path to the background image. -* `dmgFile` - the path and file name of the output DMG image. Must end - with `.dmg`. -* `signKeyChain (optional)` - the path and file name of the keychain which contains a code signing certificate. -* `signKeyChainPassword (optional)` - the password which should be used to unlock the keychain. -* `signIdentity (optional)` - the application ID of the code signing certificate. - -### Operation - -The task unpacks `rcpArtifact` into a temporary directory, unpacks -the JDK given by `jdkDependency`/`jdk` under the `jre` subdirectory of -the unpacked RCP artifact, fixes file permissions and creates missing -symlinks. If the additional properties for code signing (`signKeyChain`, `signKeyChainPassword`, `signIdentity`) are defined, -the application will be signed with the given certificate. Afterwards a DMG image is created and its layout is configured using the -background image. Finally, the DMG is copied to `dmgFile`. diff --git a/docs/tasks/MpsMigrate.md b/docs/tasks/MpsMigrate.md deleted file mode 100644 index ab3d7f24..00000000 --- a/docs/tasks/MpsMigrate.md +++ /dev/null @@ -1,74 +0,0 @@ -## `MpsMigrate` Task Type - -Migrates the specified projects. - -### Usage - -```groovy -import de.itemis.mps.gradle.tasks.MpsMigrate - -plugins { - // Required in order to use the task - id("de.itemis.mps.gradle.common") -} - -tasks.register('migrate', MpsMigrate) { - mpsHome = mpsHomeDir - - // MpsMigrate task can migrate multiple projects at once - projectDirectories.from(projectDir) - - ... -} -``` - -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. -* `haltOnPrecheckFailure` - fail if the migration pre-check (e.g. broken references) fails. -* `haltOnDependencyError` - fail if non-migrated dependencies are found. -* `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. - -## Run migrations - -Run all pending migrations in the project. - -### Usage - -A minimal build script to check all models in an MPS project with no external plugins would look like this: - -``` -apply plugin: 'run-migrations"' - -configurations { - mps -} - -dependencies { - mps "com.jetbrains:mps:$mpsVersion" -} - -runMigrations { - projectLocation = new File("./mps-prj") - mpsConfig = configurations.mps -} -``` - -Parameters: -* `mpsConfig` - the configuration used to resolve MPS. -* `mpsLocation` - optional location where to place the MPS files if `mpsConfig` is specified, or where to take them from - otherwise. -* `mpsVersion` - optionally overrides automated version detection from `mpsConfig`. Required if you use - a [custom distribution](../notes/custom-mps-distribution.md) of MPS. -* `projectLocation` - location of the project that should be migrated. -* `force` - ignores the marker files for projects which allow pending migrations, migrate them anyway. Supported in 2021.3.0 and higher. -* `haltOnPrecheckFailure` - controls whether migration is aborted if pre-checks fail (except the check for migrated dependencies) Default: `true`. Supported in 2021.1 and higher. -* `haltOnDependencyError` - controls whether migration is aborted when non-migrated dependencies are discovered. Default: `true`. Supported in 2021.3.4 and 2023.2 and higher. -* `maxHeap` (since 1.15) - maximum heap size setting for the JVM that executes the migrations. The value is a string - understood by the JVM command line argument `-Xmx` e.g. `3G` or `512M`. - -At least `mpsConfig` or `mpsLocation` + `mpsVersion` must be set. diff --git a/docs/tasks/RunAntScript.md b/docs/tasks/RunAntScript.md deleted file mode 100644 index dbfd1c7c..00000000 --- a/docs/tasks/RunAntScript.md +++ /dev/null @@ -1,66 +0,0 @@ -## RunAntScript - -This task is the base for other tasks that run MPS-generated Ant scripts (`BuildLanguages`, `TestLanguages`). - -The custom tasks are useful when you don't check in the build scripts generated by MPS into source control but want to -generate them during the Gradle build. In that case you can't use the Ant integration of Gradle to run these files -because they may not exist yet when the build is started. - -### Usage - -Parameters: - -- `script`: path to the ANT to execute -- `scriptClasspath`: classpath used for the JVM that will execute the generated ANT script. Needs to contain ANT to be - able to run the build script. See below section "Providing Global Defaults" for project wide defaults. -- `scriptArgs`: additional command line arguments provided to the JVM that will execute the generated ANT scripts. This - is often used to provide property valued via `-Dprop=value`. See below section "Providing Global Defaults" for project wide defaults. -- `executable`: the `java` executable to use. Optional. If `itemis.mps.gradle.ant.defaultJavaExecutable` extended - property is set, its value is used as the default value for the parameter. -- `includeDefaultArgs`: controls whether the project-wide default values for arguments are used. - It's set to `true` by default. -- `includeDefaultClasspath`: controls whether the project-wide default values for the classpath are used. - It's set to `true` by default. -- `targets`: the targets to execute of the ANT files. -- `incremental`: enable incremental build, see below. (Since 1.6.) - -### Providing Global Defaults For Class Path And Arguments - -All tasks derived from the `RunAntScript` base class allow to specify default values for the classpath and script arguments -via project properties. By default, these values are added to the value specified for the parameters `scriptArgs` and -`scriptClasspath` if they are present. To opt out from the defaults see above the parameters `includeDefaultArgs` and -`includeDefaultClasspath`. - -The property `itemis.mps.gradle.ant.defaultScriptArgs` controls the default arguments provided to the build scripts -execution. In belows example the default arguments contain the version and build date. At runtime the default arguments -are combined with the arguments defined via `scriptArgs`. - -The property `itemis.mps.gradle.ant.defaultScriptClasspath` controls the default classpath provided to the build scripts -execution. In belows example the classpath contains ANT (via dependency configuration) and the tools jar from the JDK. -At runtime the default classpath are combined with the classpath defined via `scriptClasspath`. -``` -def defaultScriptArgs = ["-Dversion=$version", "-DbuildDate=${new Date().toString()}"] -def buildScriptClasspath = project.configurations.ant_lib.fileCollection({true}) + project.files("$project.jdk_home/lib/tools.jar") - -ext["itemis.mps.gradle.ant.defaultScriptArgs"] = defaultScriptArgs -ext["itemis.mps.gradle.ant.defaultScriptClasspath"] = buildScriptClasspath -``` - -### Providing Global Defaults For The Java Executable - -The `itemis.mps.gradle.ant.defaultJavaExecutable` property specifies the value to use as the underlying -`JavaExec.executable`. The `executable` parameter of each individual task takes precedence over the global default. - -### Incremental Builds - -Incremental builds can be enabled by setting the `incremental` property to `true`. This has the following effects: -* The `clean` target is removed from the `targets` list. -* Argument `-Dmps.generator.skipUnmodifiedModels=true` is passed to Ant. This property tells the MPS generator to skip - generating and compiling models that have not been modified. - -NOTE: While incremental builds are convenient, it is necessary to be aware of their limitations. To determine whether -a model should be regenerated the generator only looks at the hash of the model contents. If the contents have not -changed since the last generation the generation is skipped. This may not be fully correct in the general case. Changing -the generator of a language used by the model may affect the generated code for the model, for example. Changes in -imported models may affect the generation output of this model as well. None of these changes would be detected via the -model contents hash. diff --git a/gradle.lockfile b/gradle.lockfile deleted file mode 100644 index bec2bf9e..00000000 --- a/gradle.lockfile +++ /dev/null @@ -1,35 +0,0 @@ -# This is a Gradle generated file for dependency locking. -# Manual edits can break the build and are not advised. -# This file is expected to be part of source control. -de.itemis.mps.build-backends:launcher:2.8.0.167.c51149b=compileClasspath,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath -junit:junit:4.13.2=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath -net.swiftzer.semver:semver:1.1.2=compileClasspath,implementationDependenciesMetadata,runtimeClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath -org.hamcrest:hamcrest-core:1.3=testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath -org.jetbrains.intellij.deps:trove4j:1.0.20200330=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinKlibCommonizerClasspath -org.jetbrains.kotlin:kotlin-assignment-compiler-plugin-embeddable:2.0.21=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest -org.jetbrains.kotlin:kotlin-build-common:2.0.21=kotlinBuildToolsApiClasspath -org.jetbrains.kotlin:kotlin-build-tools-api:2.0.21=kotlinBuildToolsApiClasspath -org.jetbrains.kotlin:kotlin-build-tools-impl:2.0.21=kotlinBuildToolsApiClasspath -org.jetbrains.kotlin:kotlin-compiler-embeddable:2.0.21=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinKlibCommonizerClasspath -org.jetbrains.kotlin:kotlin-compiler-runner:2.0.21=kotlinBuildToolsApiClasspath -org.jetbrains.kotlin:kotlin-daemon-client:2.0.21=kotlinBuildToolsApiClasspath -org.jetbrains.kotlin:kotlin-daemon-embeddable:2.0.21=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinKlibCommonizerClasspath -org.jetbrains.kotlin:kotlin-klib-commonizer-embeddable:2.0.21=kotlinKlibCommonizerClasspath -org.jetbrains.kotlin:kotlin-metadata-jvm:2.0.21=bcv-rt-jvm-cp-resolver -org.jetbrains.kotlin:kotlin-native-prebuilt:2.0.21=kotlinNativeBundleConfiguration -org.jetbrains.kotlin:kotlin-reflect:1.6.10=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinKlibCommonizerClasspath -org.jetbrains.kotlin:kotlin-reflect:2.0.21=compileClasspath,compileOnlyDependenciesMetadata,embeddedKotlin,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath -org.jetbrains.kotlin:kotlin-sam-with-receiver-compiler-plugin-embeddable:2.0.21=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest -org.jetbrains.kotlin:kotlin-script-runtime:2.0.21=compilePluginsBlocksPluginClasspathElements,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,kotlinKlibCommonizerClasspath -org.jetbrains.kotlin:kotlin-scripting-common:2.0.21=compilePluginsBlocksPluginClasspathElements,kotlinBuildToolsApiClasspath,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest -org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:2.0.21=compilePluginsBlocksPluginClasspathElements,kotlinBuildToolsApiClasspath,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest -org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:2.0.21=compilePluginsBlocksPluginClasspathElements,kotlinBuildToolsApiClasspath,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest -org.jetbrains.kotlin:kotlin-scripting-jvm:2.0.21=compilePluginsBlocksPluginClasspathElements,kotlinBuildToolsApiClasspath,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest -org.jetbrains.kotlin:kotlin-stdlib-common:1.7.10=implementationDependenciesMetadata,runtimeClasspath -org.jetbrains.kotlin:kotlin-stdlib:1.7.10=implementationDependenciesMetadata,runtimeClasspath -org.jetbrains.kotlin:kotlin-stdlib:2.0.21=bcv-rt-jvm-cp-resolver,compileClasspath,compileOnlyDependenciesMetadata,compilePluginsBlocksPluginClasspathElements,embeddedKotlin,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,kotlinKlibCommonizerClasspath,testCompileClasspath,testImplementationDependenciesMetadata,testRuntimeClasspath -org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.6.4=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinKlibCommonizerClasspath -org.jetbrains:annotations:13.0=bcv-rt-jvm-cp-resolver,compileClasspath,compilePluginsBlocksPluginClasspathElements,embeddedKotlin,implementationDependenciesMetadata,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,kotlinKlibCommonizerClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-tree:9.6=bcv-rt-jvm-cp-resolver -org.ow2.asm:asm:9.6=bcv-rt-jvm-cp-resolver -empty=annotationProcessor,apiDependenciesMetadata,intransitiveDependenciesMetadata,kotlinCompilerPluginClasspath,kotlinNativeCompilerPluginClasspath,kotlinScriptDefExtensions,mps,testAnnotationProcessor,testApiDependenciesMetadata,testCompileOnlyDependenciesMetadata,testIntransitiveDependenciesMetadata,testKotlinScriptDefExtensions diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2ed1c4a0..30198e15 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,9 +1,4 @@ [versions] -#versions -kotlinApi = "1.7" -kotlin = "1.7.10" -kotlinJvmTarget = "11" - # plugins kotlinCompatibilityValidator = "0.18.1" @@ -17,7 +12,6 @@ launcher = "2.8.0.+" kotlin-compatibility-validator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "kotlinCompatibilityValidator" } [libraries] -kotlin-stdlib = {group = "org.jetbrains.kotlin", name = "kotlin-stdlib", version.ref = "kotlin"} swiftzer-semver = {group = "net.swiftzer.semver", name="semver", version.ref="semver"} junit = {group = "junit", name="junit", version.ref="junit"} itemis-gradle-git-based-versioning = {group= "de.itemis.mps.gradle", name = "git-based-versioning", version.ref="gitBasedVersioning"} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 78cb6e16..8e61ef12 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=bd71102213493060956ec229d946beee57158dbd89d0e62b91bca0fa2c5f3531 -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip +distributionSha256Sum=2ab2958f2a1e51120c326cad6f385153bb11ee93b3c216c5fccebfdfbb7ec6cb +distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/mps-gradle-plugin-api/CHANGELOG.md b/mps-gradle-plugin-api/CHANGELOG.md new file mode 100644 index 00000000..612ba748 --- /dev/null +++ b/mps-gradle-plugin-api/CHANGELOG.md @@ -0,0 +1,14 @@ +# Changelog +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.0.0 + +Initial release, extracted from `mps-gradle-plugin`. + +### Added + +- `MpsTask` interface for any task that operates on an MPS installation. Provides `mpsHome`, `mpsVersion`, and + `javaLauncher` properties. +- `MpsProjectTask` interface (extends `MpsTask`) for tasks that operate on MPS projects. Provides `projectLocation`, + `pluginRoots`, `folderMacros`, and `logLevel` properties. diff --git a/mps-gradle-plugin-api/README.md b/mps-gradle-plugin-api/README.md new file mode 100644 index 00000000..153b19e4 --- /dev/null +++ b/mps-gradle-plugin-api/README.md @@ -0,0 +1,51 @@ +# mps-gradle-plugin-api + +Task-type interfaces shared across the MPS task types provided by [`mps-gradle-plugin`](../mps-gradle-plugin). + +## Coordinates + +```kotlin +dependencies { + compileOnly("de.itemis.mps:mps-gradle-plugin-api:1.0.0") +} +``` + +Requires Java 17. + +## Interfaces + +### `MpsTask` + +Any task that operates on an MPS installation. Provides: + +- `mpsHome: DirectoryProperty` — the MPS installation directory. +- `mpsVersion: Property` — MPS version string (e.g. `2021.3.3`). + Auto-detected from `mpsHome` when not set explicitly. +- `javaLauncher: Property` — Java launcher used to run MPS. +- `pluginRoots: ConfigurableFileCollection` — root directories containing MPS + plugins to load. +- `folderMacros: MapProperty` — folder macros (path + variables) to pass to MPS. +- `logLevel: Property` — log level for the MPS backend process. + +### `MpsProjectTask` + +Extends `MpsTask` for tasks that operate on an MPS project. Adds: + +- `projectLocation: DirectoryProperty` — the MPS project directory. + +## Bulk configuration + +Use `tasks.withType` to configure every MPS task at once, for example to share +an MPS home and plugin roots across the whole build: + +```kotlin +tasks.withType().configureEach { + mpsHome = layout.projectDirectory.dir("mps") + pluginRoots.from(mpsHome.dir("plugins")) +} + +tasks.withType().configureEach { + projectLocation = layout.projectDirectory.dir("my-mps-project") +} +``` diff --git a/mps-gradle-plugin-api/build.gradle.kts b/mps-gradle-plugin-api/build.gradle.kts new file mode 100644 index 00000000..6f6a44a3 --- /dev/null +++ b/mps-gradle-plugin-api/build.gradle.kts @@ -0,0 +1,92 @@ +plugins { + `java-library` + `maven-publish` +} + +group = "de.itemis.mps" + +version = "1.0.0" + +repositories { + mavenCentral() +} + +dependencyLocking { + lockAllConfigurations() +} + +dependencies { + compileOnly(gradleApi()) +} + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + withSourcesJar() +} + +tasks.withType().configureEach { + options.compilerArgs.addAll(listOf("-Xlint:all", "-Werror")) +} + +publishing { + repositories { + val isSnapshot = project.version.toString().endsWith("SNAPSHOT") + if (project.hasProperty("artifacts.itemis.cloud.user") && project.hasProperty("artifacts.itemis.cloud.pw")) { + maven { + name = "itemisCloud" + if (isSnapshot) { + url = uri("https://artifacts.itemis.cloud/repository/maven-mps-snapshots/") + } else { + url = uri("https://artifacts.itemis.cloud/repository/maven-mps-releases/") + } + credentials { + username = project.findProperty("artifacts.itemis.cloud.user") as String? + password = project.findProperty("artifacts.itemis.cloud.pw") as String? + } + } + } + + if (!isSnapshot && project.hasProperty("gpr.token")) { + maven { + name = "GitHubPackages" + url = uri("https://maven.pkg.github.com/mbeddr/mps-gradle-plugin") + credentials { + username = project.findProperty("gpr.user") as String? + password = project.findProperty("gpr.token") as String? + } + } + } + } + + publications { + create("mavenJava") { + from(components["java"]) + versionMapping { + allVariants { + fromResolutionResult() + } + } + pom { + url = "https://github.com/mbeddr/mps-gradle-plugin" + licenses { + license { + name = "The Apache License, Version 2.0" + url = "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } + scm { + connection = "scm:git:git://github.com/mbeddr/mps-gradle-plugin.git" + developerConnection = "scm:git:ssh://github.com/mbeddr/mps-gradle-plugin.git" + url = "https://github.com/mbeddr/mps-gradle-plugin" + } + } + } + } +} + +tasks.register("setTeamCityBuildNumber") { + doLast { + println("##teamcity[buildNumber '$version']") + } +} diff --git a/mps-gradle-plugin-api/gradle.lockfile b/mps-gradle-plugin-api/gradle.lockfile new file mode 100644 index 00000000..f4a90db1 --- /dev/null +++ b/mps-gradle-plugin-api/gradle.lockfile @@ -0,0 +1,35 @@ +# This is a Gradle generated file for dependency locking. +# Manual edits can break the build and are not advised. +# This file is expected to be part of source control. +io.github.java-diff-utils:java-diff-utils:4.12=kotlinInternalAbiValidation +org.jetbrains.kotlin:abi-tools-api:2.3.0=kotlinInternalAbiValidation +org.jetbrains.kotlin:abi-tools:2.3.0=kotlinInternalAbiValidation +org.jetbrains.kotlin:kotlin-assignment-compiler-plugin-embeddable:2.3.0=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest +org.jetbrains.kotlin:kotlin-build-tools-api:2.3.0=kotlinBuildToolsApiClasspath +org.jetbrains.kotlin:kotlin-build-tools-compat:2.3.0=kotlinBuildToolsApiClasspath +org.jetbrains.kotlin:kotlin-build-tools-impl:2.3.0=kotlinBuildToolsApiClasspath +org.jetbrains.kotlin:kotlin-compiler-embeddable:2.3.0=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinKlibCommonizerClasspath +org.jetbrains.kotlin:kotlin-compiler-runner:2.3.0=kotlinBuildToolsApiClasspath +org.jetbrains.kotlin:kotlin-daemon-client:2.3.0=kotlinBuildToolsApiClasspath +org.jetbrains.kotlin:kotlin-daemon-embeddable:2.3.0=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinKlibCommonizerClasspath +org.jetbrains.kotlin:kotlin-klib-abi-reader:2.3.0=kotlinInternalAbiValidation +org.jetbrains.kotlin:kotlin-klib-commonizer-embeddable:2.3.0=kotlinKlibCommonizerClasspath +org.jetbrains.kotlin:kotlin-metadata-jvm:2.3.0=bcv-rt-jvm-cp-resolver,kotlinInternalAbiValidation +org.jetbrains.kotlin:kotlin-native-prebuilt:2.0.21=kotlinNativeBundleConfiguration +org.jetbrains.kotlin:kotlin-reflect:1.6.10=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinKlibCommonizerClasspath +org.jetbrains.kotlin:kotlin-reflect:2.0.21=compileOnlyDependenciesMetadata +org.jetbrains.kotlin:kotlin-reflect:2.3.0=embeddedKotlin +org.jetbrains.kotlin:kotlin-sam-with-receiver-compiler-plugin-embeddable:2.3.0=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest +org.jetbrains.kotlin:kotlin-script-runtime:2.3.0=compilePluginsBlocksPluginClasspathElements,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,kotlinKlibCommonizerClasspath +org.jetbrains.kotlin:kotlin-scripting-common:2.3.0=compilePluginsBlocksPluginClasspathElements,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest +org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:2.3.0=compilePluginsBlocksPluginClasspathElements,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest +org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:2.3.0=compilePluginsBlocksPluginClasspathElements,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest +org.jetbrains.kotlin:kotlin-scripting-jvm:2.3.0=compilePluginsBlocksPluginClasspathElements,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest +org.jetbrains.kotlin:kotlin-stdlib:2.0.21=compileOnlyDependenciesMetadata +org.jetbrains.kotlin:kotlin-stdlib:2.3.0=bcv-rt-jvm-cp-resolver,compilePluginsBlocksPluginClasspathElements,embeddedKotlin,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,kotlinInternalAbiValidation,kotlinKlibCommonizerClasspath +org.jetbrains.kotlin:kotlin-tooling-core:2.3.0=kotlinBuildToolsApiClasspath +org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.8.0=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinKlibCommonizerClasspath +org.jetbrains:annotations:13.0=bcv-rt-jvm-cp-resolver,compilePluginsBlocksPluginClasspathElements,embeddedKotlin,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,kotlinInternalAbiValidation,kotlinKlibCommonizerClasspath +org.ow2.asm:asm-tree:9.6=bcv-rt-jvm-cp-resolver +org.ow2.asm:asm:9.6=bcv-rt-jvm-cp-resolver +empty=annotationProcessor,apiDependenciesMetadata,compileClasspath,implementationDependenciesMetadata,intransitiveDependenciesMetadata,kotlinCompilerPluginClasspath,kotlinNativeCompilerPluginClasspath,kotlinScriptDefExtensions,runtimeClasspath,testAnnotationProcessor,testApiDependenciesMetadata,testCompileClasspath,testCompileOnlyDependenciesMetadata,testImplementationDependenciesMetadata,testIntransitiveDependenciesMetadata,testKotlinScriptDefExtensions,testRuntimeClasspath diff --git a/mps-gradle-plugin-api/settings.gradle.kts b/mps-gradle-plugin-api/settings.gradle.kts new file mode 100644 index 00000000..4edc5e07 --- /dev/null +++ b/mps-gradle-plugin-api/settings.gradle.kts @@ -0,0 +1,13 @@ +plugins { + id("org.gradle.toolchains.foojay-resolver-convention") version ("1.0.0") +} + +dependencyResolutionManagement { + versionCatalogs { + create("libs") { + from(files("../gradle/libs.versions.toml")) + } + } +} + +rootProject.name = "mps-gradle-plugin-api" diff --git a/mps-gradle-plugin-api/src/main/java/de/itemis/mps/gradle/tasks/MpsProjectTask.java b/mps-gradle-plugin-api/src/main/java/de/itemis/mps/gradle/tasks/MpsProjectTask.java new file mode 100644 index 00000000..71494f6e --- /dev/null +++ b/mps-gradle-plugin-api/src/main/java/de/itemis/mps/gradle/tasks/MpsProjectTask.java @@ -0,0 +1,28 @@ +package de.itemis.mps.gradle.tasks; + +import org.gradle.api.Incubating; +import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.tasks.Internal; + +/** + * A Gradle task that operates on an MPS project. + * + *

Adds {@link #getProjectLocation()} to the properties inherited from {@link MpsTask}. Use + * {@link org.gradle.api.tasks.TaskCollection#withType(Class)} to configure all MPS project tasks at once: + * + *

{@code
+ * tasks.withType(MpsProjectTask.class).configureEach(task -> {
+ *     task.getProjectLocation().set(layout.getProjectDirectory().dir("my-mps-project"));
+ * });
+ * }
+ */ +@Incubating +public interface MpsProjectTask extends MpsTask { + /** + * The MPS project directory. Convention: the Gradle project directory (but no convention is set on + * multi-project tasks like MpsMigrate and Remigrate, so that the mutual exclusivity check with + * {@code projectLocations} works correctly). + */ + @Internal("too coarse to be used as input") + DirectoryProperty getProjectLocation(); +} diff --git a/mps-gradle-plugin-api/src/main/java/de/itemis/mps/gradle/tasks/MpsTask.java b/mps-gradle-plugin-api/src/main/java/de/itemis/mps/gradle/tasks/MpsTask.java new file mode 100644 index 00000000..7f138f0e --- /dev/null +++ b/mps-gradle-plugin-api/src/main/java/de/itemis/mps/gradle/tasks/MpsTask.java @@ -0,0 +1,68 @@ +package de.itemis.mps.gradle.tasks; + +import org.gradle.api.Incubating; +import org.gradle.api.Task; +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.file.Directory; +import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.logging.LogLevel; +import org.gradle.api.provider.MapProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.Classpath; +import org.gradle.api.tasks.Console; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.Internal; +import org.gradle.api.tasks.Nested; +import org.gradle.jvm.toolchain.JavaLauncher; + +/** + * A Gradle task that operates on an MPS installation. + * + *

Use {@link org.gradle.api.tasks.TaskCollection#withType(Class)} to configure all MPS tasks at once: + * + *

{@code
+ * tasks.withType(MpsTask.class).configureEach(task -> {
+ *     task.getMpsHome().set(layout.getProjectDirectory().dir("mps"));
+ *     task.getPluginRoots().from(task.getMpsHome().dir("plugins"));
+ * });
+ * }
+ */ +@Incubating +public interface MpsTask extends Task { + /** + * The MPS installation directory. + */ + @Internal("not considered an input by itself") + DirectoryProperty getMpsHome(); + + /** + * The MPS version string (e.g. "2021.3.3"). Detected automatically from {@link #getMpsHome()} if not set + * explicitly. + */ + @Input + Property getMpsVersion(); + + /** + * The Java launcher to use for running MPS. Each MPS version requires a specific Java version. + */ + @Nested + Property getJavaLauncher(); + + /** + * Root directories containing MPS plugins to load. + */ + @Classpath + ConfigurableFileCollection getPluginRoots(); + + /** + * Folder macros (path variables) to pass to MPS. Keys are macro names, values are directories. + */ + @Internal("not considered input") + MapProperty getFolderMacros(); + + /** + * Log level for the MPS backend process. + */ + @Console + Property getLogLevel(); +} diff --git a/CHANGELOG.md b/mps-gradle-plugin/CHANGELOG.md similarity index 72% rename from CHANGELOG.md rename to mps-gradle-plugin/CHANGELOG.md index 4e297aee..346864ad 100644 --- a/CHANGELOG.md +++ b/mps-gradle-plugin/CHANGELOG.md @@ -4,7 +4,51 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## 3.0.0 (Unreleased) -- TBD +### Removed + +- Old plugins `generate-models`, `modelcheck`, and `run-migrations` have been removed. Use the task types + (`MpsGenerate`, `MpsCheck`, `MpsMigrate`, `Remigrate`) directly instead. +- `download-jbr` plugin, `DownloadJbrForPlatform` task, and the `downloadJBR` package have been removed. Use the + [`com.specificlanguages.jbr-toolchain`](https://plugins.gradle.org/plugin/com.specificlanguages.jbr-toolchain) + plugin to download the JetBrains Runtime. +- `CreateDmg` and `BundleMacosJdk` tasks have been removed. Building macOS installers is no longer in scope for this + plugin. +- `GetMpsInBrowser` task has been removed. It only opened the JetBrains download page in a browser and did not pull its + weight. + +### Added + +- `MpsTask` interface for any task that operates on an MPS installation. Provides `mpsHome`, `mpsVersion`, + `javaLauncher`, `pluginRoots`, `folderMacros`, and `logLevel` properties. Implemented by all MPS task types + including `RunAntScript`. Published separately in the new `de.itemis.mps:mps-gradle-plugin-api` artifact. +- `MpsProjectTask` interface (extends `MpsTask`) for tasks that operate on an MPS project. Adds `projectLocation`. + Implemented by `MpsGenerate`, `MpsCheck`, `MpsExecute`, `MpsMigrate`, and `Remigrate`. +- Both interfaces allow bulk configuration via `tasks.withType()` and `tasks.withType()`. + +### Changed + +- The plugin now requires Gradle 9 and Java 17 (previously Gradle 8 and Java 11). +- `MpsCheck`, `MpsExecute`: `pluginRoots` changed from `SetProperty` to `ConfigurableFileCollection`. +- `MpsCheck`, `MpsExecute`: `macros`/`varMacros` replaced with `folderMacros: MapProperty`. +- `MpsMigrate`, `Remigrate`: `projectDirectories` renamed to `projectLocations`. +- `MpsMigrate`: `javaExecutable` removed in favor of `javaLauncher` from `MpsTask`. +- `RunAntScript`: now requires `mpsHome`. Derives the Ant classpath from `mpsHome` when `scriptClasspath` is not set, + and passes `mps.home` and `mps_home` as Ant properties. +- `RunAntScript`: `scriptClasspath`, `scriptArgs`, `targets`, and `script` are now lazy Gradle properties + (`ConfigurableFileCollection`, `ListProperty`, `RegularFileProperty`). Use `set`/`from`/`addAll` rather than + direct assignment. `script` is tracked as a proper `@InputFile`, so wiring it via + `script.set(otherTask.flatMap { ... })` establishes the task dependency automatically. +- `RunAntScript`: added `workingDirectory: DirectoryProperty` (defaults to the root project directory) so the task + no longer reaches through `project` at execution time. +- `RunAntScript`: uses the inherited `javaLauncher` and `logLevel` properties. The `executable` property has been + removed (use `javaLauncher` instead); `-Dmps.ant.log` is derived from `logLevel` rather than from `task.logging.level`. +- `RunAntScript`: removed the `includeDefaultArgs`, `includeDefaultClasspath`, `itemis.mps.gradle.ant.defaultScriptArgs`, + `itemis.mps.gradle.ant.defaultScriptClasspath`, and `itemis.mps.gradle.ant.defaultJavaExecutable` escape hatches. + Use `tasks.withType().configureEach { ... }` to share configuration across tasks. +- `RunAntScript`: removed the `incremental` property. Pass `-Dmps.generator.skipUnmodifiedModels=true` via `scriptArgs` + and adjust `targets` directly if you need the previous behavior. +- The `de.itemis.mps.gradle.common` plugin sets the convention for `MpsTask.logLevel` to Gradle's + `gradle.startParameter.logLevel`, so MPS subprocesses log at the same level as Gradle by default. ## 1.30.1 diff --git a/README.md b/mps-gradle-plugin/README.md similarity index 74% rename from README.md rename to mps-gradle-plugin/README.md index a9d4523e..8577db39 100644 --- a/README.md +++ b/mps-gradle-plugin/README.md @@ -37,8 +37,6 @@ reproducibility. Tasks: * [RunAntScript](docs/tasks/RunAntScript.md) -- run an MPS-generated Ant script. -* [CreateDmg](docs/tasks/CreateDmg.md) -- (macOS only) create a .dmg installer. -* [BundleMacosJdk](docs/tasks/BundleMacosJdk.md) -- (Linux/macOS) create a .tar.gz by combining an RCP artifact and a JDK. * [GenerateLibrariesXml](docs/tasks/GenerateLibrariesXml.md) -- generate a `.mps/libraries.xml` file from property files. * [MpsCheck](docs/tasks/MpsCheck.md) -- check (a subset of) models in a project. * [MpsExecute](docs/tasks/MpsExecute.md) -- execute a specified method in a generated class in the context of a running @@ -47,9 +45,3 @@ Tasks: build model. * [MpsMigrate](docs/tasks/MpsMigrate.md) -- Run pending migrations on one or several MPS projects. -Plugins: - -* [generate](docs/plugins/generate.md) -- Deprecated. Generate (a subset of) models in a project without the need for a MPS - build model. -* [modelcheck/checkmodels](docs/plugins/modelcheck.md) -- Deprecated. Check (a subset of) models in a project. -* [download-jbr/downloadJbr](docs/plugins/download-jbr.md) -- Download JetBrains Runtime. diff --git a/mps-gradle-plugin/api/mps-gradle-plugin.api b/mps-gradle-plugin/api/mps-gradle-plugin.api new file mode 100644 index 00000000..9d8fde32 --- /dev/null +++ b/mps-gradle-plugin/api/mps-gradle-plugin.api @@ -0,0 +1,175 @@ +public abstract class de/itemis/mps/gradle/BuildLanguages : de/itemis/mps/gradle/RunAntScript { + public fun ()V +} + +public final class de/itemis/mps/gradle/CommonKt { + public static final field MPS_BUILD_BACKENDS_VERSION Ljava/lang/String; +} + +public final class de/itemis/mps/gradle/CommonPlugin : org/gradle/api/Plugin { + public fun ()V + public synthetic fun apply (Ljava/lang/Object;)V + public fun apply (Lorg/gradle/api/Project;)V +} + +public final class de/itemis/mps/gradle/EnvironmentKind : java/lang/Enum { + public static final field IDEA Lde/itemis/mps/gradle/EnvironmentKind; + public static final field MPS Lde/itemis/mps/gradle/EnvironmentKind; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lde/itemis/mps/gradle/EnvironmentKind; + public static fun values ()[Lde/itemis/mps/gradle/EnvironmentKind; +} + +public class de/itemis/mps/gradle/GenerateLibrariesXml : org/gradle/api/DefaultTask, groovy/lang/GroovyObject { + public static synthetic field __$stMC Z + public static synthetic fun $getLookup ()Ljava/lang/invoke/MethodHandles$Lookup; + protected synthetic fun $getStaticMetaClass ()Lgroovy/lang/MetaClass; + public fun ()V + public fun generate ()Ljava/lang/Object; + public fun getDefaults ()Ljava/io/File; + public fun getDestination ()Ljava/io/File; + public fun getMetaClass ()Lgroovy/lang/MetaClass; + public fun getOverrides ()Ljava/io/File; + public fun setDefaults (Ljava/io/File;)V + public fun setDestination (Ljava/io/File;)V + public fun setMetaClass (Lgroovy/lang/MetaClass;)V + public fun setOverrides (Ljava/lang/Object;)V +} + +public class de/itemis/mps/gradle/Pom : groovy/lang/GroovyObject { + public static synthetic field __$stMC Z + public static synthetic fun $getLookup ()Ljava/lang/invoke/MethodHandles$Lookup; + protected synthetic fun $getStaticMetaClass ()Lgroovy/lang/MetaClass; + public fun ()V + public fun getMetaClass ()Lgroovy/lang/MetaClass; + public fun setMetaClass (Lgroovy/lang/MetaClass;)V + public fun withDep (Lorg/gradle/api/publish/maven/MavenPom;Lorg/gradle/api/artifacts/Configuration;)Ljava/lang/Object; + public fun withProvidedDep (Lorg/gradle/api/publish/maven/MavenPom;Lorg/gradle/api/artifacts/Configuration;)Ljava/lang/Object; +} + +public abstract class de/itemis/mps/gradle/RunAntScript : org/gradle/api/DefaultTask, de/itemis/mps/gradle/tasks/MpsTask { + public fun ()V + public final fun build ()V + protected abstract fun getExecOperations ()Lorg/gradle/process/ExecOperations; + public abstract fun getJavaLauncher ()Lorg/gradle/api/provider/Property; + public abstract fun getScript ()Lorg/gradle/api/file/RegularFileProperty; + public abstract fun getScriptArgs ()Lorg/gradle/api/provider/ListProperty; + public abstract fun getScriptClasspath ()Lorg/gradle/api/file/ConfigurableFileCollection; + public abstract fun getTargets ()Lorg/gradle/api/provider/ListProperty; + public abstract fun getWorkingDirectory ()Lorg/gradle/api/file/DirectoryProperty; +} + +public abstract class de/itemis/mps/gradle/TestLanguages : de/itemis/mps/gradle/RunAntScript { + public fun ()V +} + +public final class de/itemis/mps/gradle/tasks/ExcludedModuleMigration { + public fun (Ljava/lang/String;I)V + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()I + public final fun copy (Ljava/lang/String;I)Lde/itemis/mps/gradle/tasks/ExcludedModuleMigration; + public static synthetic fun copy$default (Lde/itemis/mps/gradle/tasks/ExcludedModuleMigration;Ljava/lang/String;IILjava/lang/Object;)Lde/itemis/mps/gradle/tasks/ExcludedModuleMigration; + public fun equals (Ljava/lang/Object;)Z + public final fun getLanguage ()Ljava/lang/String; + public final fun getVersion ()I + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public abstract class de/itemis/mps/gradle/tasks/MpsCheck : org/gradle/api/tasks/JavaExec, de/itemis/mps/gradle/tasks/MpsProjectTask, org/gradle/api/tasks/VerificationTask { + public fun ()V + public fun exec ()V + public abstract fun getAdditionalModelcheckBackendClasspath ()Lorg/gradle/api/file/ConfigurableFileCollection; + protected final fun getCompiledClasses ()Lorg/gradle/api/file/FileTree; + public abstract fun getExcludeModels ()Lorg/gradle/api/provider/ListProperty; + public abstract fun getExcludeModules ()Lorg/gradle/api/provider/ListProperty; + public abstract fun getFolderMacros ()Lorg/gradle/api/provider/MapProperty; + public abstract fun getJunitFile ()Lorg/gradle/api/file/RegularFileProperty; + public abstract fun getJunitFormat ()Lorg/gradle/api/provider/Property; + public abstract fun getLogLevel ()Lorg/gradle/api/provider/Property; + public abstract fun getModels ()Lorg/gradle/api/provider/ListProperty; + public abstract fun getModules ()Lorg/gradle/api/provider/ListProperty; + public abstract fun getMpsHome ()Lorg/gradle/api/file/DirectoryProperty; + public abstract fun getMpsVersion ()Lorg/gradle/api/provider/Property; + public abstract fun getParallel ()Lorg/gradle/api/provider/Property; + public abstract fun getPluginRoots ()Lorg/gradle/api/file/ConfigurableFileCollection; + public abstract fun getProjectLocation ()Lorg/gradle/api/file/DirectoryProperty; + protected final fun getSources ()Lorg/gradle/api/file/FileTree; + public abstract fun getWarningAsError ()Lorg/gradle/api/provider/Property; +} + +public abstract class de/itemis/mps/gradle/tasks/MpsExecute : org/gradle/api/tasks/JavaExec, de/itemis/mps/gradle/tasks/MpsProjectTask { + public fun ()V + public fun exec ()V + public abstract fun getAdditionalExecuteBackendClasspath ()Lorg/gradle/api/file/ConfigurableFileCollection; + public abstract fun getClassName ()Lorg/gradle/api/provider/Property; + public abstract fun getFolderMacros ()Lorg/gradle/api/provider/MapProperty; + public abstract fun getLogLevel ()Lorg/gradle/api/provider/Property; + public abstract fun getMethod ()Lorg/gradle/api/provider/Property; + public abstract fun getMethodArguments ()Lorg/gradle/api/provider/ListProperty; + public abstract fun getModule ()Lorg/gradle/api/provider/Property; + public abstract fun getMpsHome ()Lorg/gradle/api/file/DirectoryProperty; + public abstract fun getMpsVersion ()Lorg/gradle/api/provider/Property; + public abstract fun getPluginRoots ()Lorg/gradle/api/file/ConfigurableFileCollection; + public abstract fun getProjectLocation ()Lorg/gradle/api/file/DirectoryProperty; +} + +public abstract class de/itemis/mps/gradle/tasks/MpsGenerate : org/gradle/api/tasks/JavaExec, de/itemis/mps/gradle/tasks/MpsProjectTask { + public fun ()V + public fun exec ()V + public abstract fun getAdditionalGenerateBackendClasspath ()Lorg/gradle/api/file/ConfigurableFileCollection; + protected final fun getCompiledClasses ()Lorg/gradle/api/file/FileTree; + public abstract fun getEnvironmentKind ()Lorg/gradle/api/provider/Property; + public abstract fun getExcludeModels ()Lorg/gradle/api/provider/ListProperty; + public abstract fun getExcludeModules ()Lorg/gradle/api/provider/ListProperty; + public abstract fun getFolderMacros ()Lorg/gradle/api/provider/MapProperty; + public abstract fun getLogLevel ()Lorg/gradle/api/provider/Property; + public abstract fun getModels ()Lorg/gradle/api/provider/ListProperty; + public abstract fun getModules ()Lorg/gradle/api/provider/ListProperty; + public abstract fun getMpsHome ()Lorg/gradle/api/file/DirectoryProperty; + public abstract fun getMpsVersion ()Lorg/gradle/api/provider/Property; + public abstract fun getParallelGenerationThreads ()Lorg/gradle/api/provider/Property; + public abstract fun getPluginRoots ()Lorg/gradle/api/file/ConfigurableFileCollection; + public abstract fun getProjectLocation ()Lorg/gradle/api/file/DirectoryProperty; + protected final fun getSources ()Lorg/gradle/api/file/FileTree; + public abstract fun getStrictMode ()Lorg/gradle/api/provider/Property; +} + +public abstract class de/itemis/mps/gradle/tasks/MpsMigrate : org/gradle/api/DefaultTask, de/itemis/mps/gradle/tasks/MpsProjectTask { + public fun ()V + public final fun execute ()V + protected final fun getAllProjectFiles ()Lorg/gradle/api/provider/Provider; + public abstract fun getAntJvmArgs ()Lorg/gradle/api/provider/ListProperty; + protected abstract fun getExecOperations ()Lorg/gradle/process/ExecOperations; + public abstract fun getFolderMacros ()Lorg/gradle/api/provider/MapProperty; + public abstract fun getHaltOnDependencyError ()Lorg/gradle/api/provider/Property; + public abstract fun getHaltOnPrecheckFailure ()Lorg/gradle/api/provider/Property; + public abstract fun getJavaLauncher ()Lorg/gradle/api/provider/Property; + public abstract fun getJvmArgs ()Lorg/gradle/api/provider/ListProperty; + public abstract fun getLogLevel ()Lorg/gradle/api/provider/Property; + public abstract fun getMaxHeapSize ()Lorg/gradle/api/provider/Property; + public abstract fun getMpsHome ()Lorg/gradle/api/file/DirectoryProperty; + public abstract fun getMpsVersion ()Lorg/gradle/api/provider/Property; + protected abstract fun getObjectFactory ()Lorg/gradle/api/model/ObjectFactory; + public abstract fun getPluginRoots ()Lorg/gradle/api/file/ConfigurableFileCollection; + public abstract fun getProjectLocation ()Lorg/gradle/api/file/DirectoryProperty; + public abstract fun getProjectLocations ()Lorg/gradle/api/file/ConfigurableFileCollection; + protected abstract fun getProviderFactory ()Lorg/gradle/api/provider/ProviderFactory; +} + +public abstract class de/itemis/mps/gradle/tasks/Remigrate : org/gradle/api/tasks/JavaExec, de/itemis/mps/gradle/tasks/MpsProjectTask { + public fun ()V + public final fun excludeModuleMigration (Ljava/lang/String;I)V + public fun exec ()V + public abstract fun getAdditionalClasspath ()Lorg/gradle/api/file/ConfigurableFileCollection; + protected final fun getAllProjectFiles ()Lorg/gradle/api/provider/Provider; + public abstract fun getExcludedModuleMigrations ()Lorg/gradle/api/provider/SetProperty; + public abstract fun getFolderMacros ()Lorg/gradle/api/provider/MapProperty; + public abstract fun getLogLevel ()Lorg/gradle/api/provider/Property; + public abstract fun getMpsHome ()Lorg/gradle/api/file/DirectoryProperty; + public abstract fun getMpsVersion ()Lorg/gradle/api/provider/Property; + public abstract fun getPluginRoots ()Lorg/gradle/api/file/ConfigurableFileCollection; + public abstract fun getProjectLocation ()Lorg/gradle/api/file/DirectoryProperty; + public abstract fun getProjectLocations ()Lorg/gradle/api/file/ConfigurableFileCollection; +} + diff --git a/mps-gradle-plugin/build.gradle.kts b/mps-gradle-plugin/build.gradle.kts new file mode 100644 index 00000000..63abd5e9 --- /dev/null +++ b/mps-gradle-plugin/build.gradle.kts @@ -0,0 +1,118 @@ +plugins { + groovy + `java-gradle-plugin` + `kotlin-dsl` + `maven-publish` + alias(libs.plugins.kotlin.compatibility.validator) +} + +group = "de.itemis.mps" + +version = "3.0.0-SNAPSHOT" + +repositories { + mavenCentral() + // For mps-build-backends, during tests + maven(url = "https://artifacts.itemis.cloud/repository/maven-mps") +} + +dependencyLocking { + lockAllConfigurations() +} + +dependencies { + api(libs.itemis.gradle.git.based.versioning) + api("de.itemis.mps:mps-gradle-plugin-api") + implementation(libs.swiftzer.semver) + implementation(libs.itemis.gradle.build.backends.launcher) + testImplementation(libs.junit) +} + +tasks.test { + useJUnit() +} + +gradlePlugin { + plugins { + // Plugins are provided by the common.gradle.kts precompiled script plugin. + // Task types are used directly by consumers. + } +} + +tasks.register("setTeamCityBuildNumber") { + doLast { + println("##teamcity[buildNumber '$version']") + } +} + +publishing { + repositories { + val isSnapshot = project.version.toString().endsWith("SNAPSHOT") + if (project.hasProperty("artifacts.itemis.cloud.user") && project.hasProperty("artifacts.itemis.cloud.pw")) { + maven { + name = "itemisCloud" + if (isSnapshot) { + url = uri("https://artifacts.itemis.cloud/repository/maven-mps-snapshots/") + } else { + url = uri("https://artifacts.itemis.cloud/repository/maven-mps-releases/") + } + credentials { + username = project.findProperty("artifacts.itemis.cloud.user") as String? + password = project.findProperty("artifacts.itemis.cloud.pw") as String? + } + } + } + + if (!isSnapshot && project.hasProperty("gpr.token")) { + maven { + name = "GitHubPackages" + url = uri("https://maven.pkg.github.com/mbeddr/mps-gradle-plugin") + credentials { + username = project.findProperty("gpr.user") as String? + password = project.findProperty("gpr.token") as String? + } + } + } + } + + publications.withType().configureEach { + versionMapping { + allVariants { + fromResolutionResult() + } + } + pom { + url = "https://github.com/mbeddr/mps-gradle-plugin" + licenses { + license { + name = "The Apache License, Version 2.0" + url = "https://www.apache.org/licenses/LICENSE-2.0.txt" + } + } + scm { + connection = "scm:git:git://github.com/mbeddr/mps-gradle-plugin.git" + developerConnection = "scm:git:ssh://github.com/mbeddr/mps-gradle-plugin.git" + url = "https://github.com/mbeddr/mps-gradle-plugin" + } + } + } +} + +java { + targetCompatibility = JavaVersion.VERSION_17 + withSourcesJar() +} + +kotlin { + compilerOptions { + allWarningsAsErrors = true + } +} + +apiValidation { + ignoredClasses.add("de.itemis.mps.gradle.Common_gradle") +} + +tasks.test { + maxParallelForks = (Runtime.getRuntime().availableProcessors() * 2 / 3).coerceAtLeast(1) +} diff --git a/docs/notes/custom-mps-distribution.md b/mps-gradle-plugin/docs/notes/custom-mps-distribution.md similarity index 100% rename from docs/notes/custom-mps-distribution.md rename to mps-gradle-plugin/docs/notes/custom-mps-distribution.md diff --git a/docs/notes/mps-vs-idea-environment.md b/mps-gradle-plugin/docs/notes/mps-vs-idea-environment.md similarity index 100% rename from docs/notes/mps-vs-idea-environment.md rename to mps-gradle-plugin/docs/notes/mps-vs-idea-environment.md diff --git a/docs/tasks/GenerateLibrariesXml.md b/mps-gradle-plugin/docs/tasks/GenerateLibrariesXml.md similarity index 100% rename from docs/tasks/GenerateLibrariesXml.md rename to mps-gradle-plugin/docs/tasks/GenerateLibrariesXml.md diff --git a/docs/tasks/MpsCheck.md b/mps-gradle-plugin/docs/tasks/MpsCheck.md similarity index 75% rename from docs/tasks/MpsCheck.md rename to mps-gradle-plugin/docs/tasks/MpsCheck.md index 26df2909..bcce461b 100644 --- a/docs/tasks/MpsCheck.md +++ b/mps-gradle-plugin/docs/tasks/MpsCheck.md @@ -1,13 +1,6 @@ ## `MpsCheck` Task Type -This task improves over the [`modelcheck` plugin](../plugins/modelcheck.md) and fixes some of its deficiencies. - -The `modelcheck` extension provided by the eponymous plugin can only be configured once per Gradle project. Checking -multiple subprojects is not possible without resorting to tricks. In addition, the extension only has limited support -for lazy configuration and does not support Gradle build cache. - -The `MpsCheck` task works similarly to the `checkmodels` task of `modelcheck` plugin but allows defining multiple -instances of itself, supports lazy configuration and caching. +Run the model check on a subset or all models in a project. ### Usage @@ -32,10 +25,8 @@ Parameters: included or excluded from checking. * `additionalModelcheckBackendClasspath` - any extra libraries that should be on the classpath of the modelcheck backend. -* `folderMacros` - path variables/macros that are necessary to open the project. Path macros are not considered part of - Gradle build cache key. -* `varMacros` - non-path variables/macros that are necessary to open the project. Variable macros *are* considered part - of Gradle build cache key. +* `folderMacros` - path variables/macros that are necessary to open the project. Keys are macro names, values are + directories. Path macros are not considered part of Gradle build cache key. * `junitFile` - the JUnit XML file to produce. Defaults to `$buildDir/TEST-${task.name}.xml` * `junitFormat` - specifies how errors are reported in the JUnit XML file. Possible options: * `model` (default, deprecated) - generates one test case for each model that was checked. If the model check reported diff --git a/docs/tasks/MpsExecute.md b/mps-gradle-plugin/docs/tasks/MpsExecute.md similarity index 92% rename from docs/tasks/MpsExecute.md rename to mps-gradle-plugin/docs/tasks/MpsExecute.md index 528c4809..f18e7412 100644 --- a/docs/tasks/MpsExecute.md +++ b/mps-gradle-plugin/docs/tasks/MpsExecute.md @@ -27,7 +27,8 @@ Parameters: * `projectLocation` - the location of the MPS project. Default is the Gradle project directory. * `additionalExecuteBackendClasspath` - any extra libraries that should be on the classpath of the execute backend. -* `macros` - variables/macros that are necessary to open the project. +* `folderMacros` - path variables/macros that are necessary to open the project. Keys are macro names, values are + directories. * `mpsHome` - the home directory of the MPS distribution (or RCP) to use for testing. * `mpsVersion` - the MPS version, such as "2021.3". Default is autodetection by reading `$mpsHome/build.properties`. * `pluginRoots` - directories containing additional plugins to load. diff --git a/docs/tasks/MpsGenerate.md b/mps-gradle-plugin/docs/tasks/MpsGenerate.md similarity index 66% rename from docs/tasks/MpsGenerate.md rename to mps-gradle-plugin/docs/tasks/MpsGenerate.md index 2eb75cce..bf91ad29 100644 --- a/docs/tasks/MpsGenerate.md +++ b/mps-gradle-plugin/docs/tasks/MpsGenerate.md @@ -1,13 +1,6 @@ ## `MpsGenerate` Task Type -This task improves over the [`generate` plugin](../plugins/generate.md) above and fixes some of its deficiencies. - -The `generate` extension provided by the eponymous plugin can only be configured once per Gradle project. Generating -multiple subprojects or multiple sets of models is not possible without resorting to tricks. In addition, the extension -only has limited support for lazy configuration and does not support Gradle build cache. - -The `MpsGenerate` task works similarly to the `generate` task created by the `generate` plugin but allows defining -multiple instances of itself, supports lazy configuration and caching. +Generate a specific or all models in a project. ### Usage @@ -32,10 +25,8 @@ Parameters: included or excluded from generation. * `additionalGenerateBackendClasspath` - any extra libraries that should be on the classpath of the generate backend. -* `folderMacros` - path variables/macros that are necessary to open the project. Path macros are not considered part of - Gradle build cache key. -* `varMacros` - non-path variables/macros that are necessary to open the project. Variable macros *are* considered part - of Gradle build cache key. +* `folderMacros` - path variables/macros that are necessary to open the project. Keys are macro names, values are + directories. Path macros are not considered part of Gradle build cache key. * `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. * `pluginRoots` - directories with additional plugins to load. diff --git a/mps-gradle-plugin/docs/tasks/MpsMigrate.md b/mps-gradle-plugin/docs/tasks/MpsMigrate.md new file mode 100644 index 00000000..8b6f0b3a --- /dev/null +++ b/mps-gradle-plugin/docs/tasks/MpsMigrate.md @@ -0,0 +1,37 @@ +## `MpsMigrate` Task Type + +Migrates the specified projects. + +### Usage + +```groovy +import de.itemis.mps.gradle.tasks.MpsMigrate + +plugins { + // Required in order to use the task + id("de.itemis.mps.gradle.common") +} + +tasks.register('migrate', MpsMigrate) { + mpsHome = mpsHomeDir + + // MpsMigrate task can migrate multiple projects at once + projectLocations.from(projectDir) + + ... +} +``` + +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. +* `haltOnPrecheckFailure` - fail if the migration pre-check (e.g. broken references) fails. +* `haltOnDependencyError` - fail if non-migrated dependencies are found. +* `projectLocation` or `projectLocations` - the project or projects to migrate. The properties are mutually exclusive, + exactly one should be set. +* `folderMacros` - path variables/macros that are necessary to open the project. Keys are macro names, values are + directories. Path macros are not considered part of Gradle build cache key. +* `pluginRoots` - directories that will be searched (recursively) for additional plugins to load. +* `maxHeapSize` - maximum heap size setting for the JVM that executes the migrations. The value is a string + understood by the JVM command line argument `-Xmx` e.g. `3G` or `512M`. diff --git a/docs/tasks/Remigrate.md b/mps-gradle-plugin/docs/tasks/Remigrate.md similarity index 71% rename from docs/tasks/Remigrate.md rename to mps-gradle-plugin/docs/tasks/Remigrate.md index 13d9ab34..f74ead1f 100644 --- a/docs/tasks/Remigrate.md +++ b/mps-gradle-plugin/docs/tasks/Remigrate.md @@ -16,8 +16,8 @@ tasks.register('remigrate', Remigrate) { mpsHome = mpsHomeDir // Remigrate task can run migrations on multiple projects - projectDirectories.from(projectDir1) - projectDirectories.from(projectDir2) + projectLocations.from(projectDir1) + projectLocations.from(projectDir2) } ``` @@ -25,9 +25,10 @@ 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. +* `projectLocation` or `projectLocations` - the project or projects to migrate. The properties are mutually exclusive, + exactly one should be set. +* `folderMacros` - path variables/macros that are necessary to open the project. Keys are macro names, values are + directories. Path macros are not considered part of Gradle build cache key. * `pluginRoots` - directories that will be searched (recursively) for additional plugins to load. ### Operation diff --git a/mps-gradle-plugin/docs/tasks/RunAntScript.md b/mps-gradle-plugin/docs/tasks/RunAntScript.md new file mode 100644 index 00000000..8a9a5333 --- /dev/null +++ b/mps-gradle-plugin/docs/tasks/RunAntScript.md @@ -0,0 +1,39 @@ +## RunAntScript + +This task is the base for other tasks that run MPS-generated Ant scripts (`BuildLanguages`, `TestLanguages`). + +The custom tasks are useful when you don't check in the build scripts generated by MPS into source control but want to +generate them during the Gradle build. In that case you can't use the Ant integration of Gradle to run these files +because they may not exist yet when the build is started. + +### Usage + +Properties: + +- `script`: the Ant script to execute (`RegularFileProperty`). Wire via `script.set(otherTask.flatMap { ... })` to + depend on a task that generates the script. +- `workingDirectory`: working directory for the Ant invocation. Defaults to the root project directory. +- `scriptClasspath`: classpath used for the JVM that will execute the generated Ant script. Needs to contain Ant to be + able to run the build script. Defaults to `lib/ant/lib/*.jar` and `lib/*.jar` under `mpsHome`. +- `scriptArgs`: additional command line arguments provided to the JVM that will execute the generated Ant scripts. This + is often used to provide property valued via `-Dprop=value`. +- `mpsHome`: the MPS installation directory. Required. `mps.home` and `mps_home` are passed as Ant properties + automatically, and the Ant classpath is derived from the MPS installation if `scriptClasspath` is not set. +- `javaLauncher`: the Java launcher used to run the Ant script. Optional. +- `targets`: the targets to execute of the Ant files. + +### Project-Wide Defaults + +To share configuration across all `RunAntScript` tasks (and its subclasses), use Gradle's standard +`tasks.withType` mechanism: + +```kotlin +tasks.withType().configureEach { + scriptArgs.addAll("-Dversion=$version", "-DbuildDate=${java.time.Instant.now()}") + scriptClasspath.from(configurations.named("antLib")) + javaLauncher = javaToolchains.launcherFor { + languageVersion = JavaLanguageVersion.of(17) + } +} +``` + diff --git a/mps-gradle-plugin/gradle.lockfile b/mps-gradle-plugin/gradle.lockfile new file mode 100644 index 00000000..cdc921d1 --- /dev/null +++ b/mps-gradle-plugin/gradle.lockfile @@ -0,0 +1,39 @@ +# This is a Gradle generated file for dependency locking. +# Manual edits can break the build and are not advised. +# This file is expected to be part of source control. +de.itemis.mps.build-backends:launcher:2.8.0.171.f987265=compileClasspath,precompiledScriptPluginAccessorsGenerationClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +io.github.java-diff-utils:java-diff-utils:4.12=kotlinInternalAbiValidation +junit:junit:4.13.2=testCompileClasspath,testRuntimeClasspath +net.swiftzer.semver:semver:1.1.2=compileClasspath,precompiledScriptPluginAccessorsGenerationClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +org.hamcrest:hamcrest-core:1.3=testCompileClasspath,testRuntimeClasspath +org.jetbrains.kotlin:abi-tools-api:2.3.0=kotlinInternalAbiValidation +org.jetbrains.kotlin:abi-tools:2.3.0=kotlinInternalAbiValidation +org.jetbrains.kotlin:kotlin-assignment-compiler-plugin-embeddable:2.3.0=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest +org.jetbrains.kotlin:kotlin-build-tools-api:2.3.0=kotlinBuildToolsApiClasspath +org.jetbrains.kotlin:kotlin-build-tools-compat:2.3.0=kotlinBuildToolsApiClasspath +org.jetbrains.kotlin:kotlin-build-tools-impl:2.3.0=kotlinBuildToolsApiClasspath +org.jetbrains.kotlin:kotlin-compiler-embeddable:2.3.0=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinKlibCommonizerClasspath +org.jetbrains.kotlin:kotlin-compiler-runner:2.3.0=kotlinBuildToolsApiClasspath +org.jetbrains.kotlin:kotlin-daemon-client:2.3.0=kotlinBuildToolsApiClasspath +org.jetbrains.kotlin:kotlin-daemon-embeddable:2.3.0=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinKlibCommonizerClasspath +org.jetbrains.kotlin:kotlin-klib-abi-reader:2.3.0=kotlinInternalAbiValidation +org.jetbrains.kotlin:kotlin-klib-commonizer-embeddable:2.3.0=kotlinKlibCommonizerClasspath +org.jetbrains.kotlin:kotlin-metadata-jvm:2.3.0=bcv-rt-jvm-cp-resolver,kotlinInternalAbiValidation +org.jetbrains.kotlin:kotlin-native-prebuilt:2.0.21=kotlinNativeBundleConfiguration +org.jetbrains.kotlin:kotlin-reflect:1.6.10=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinKlibCommonizerClasspath +org.jetbrains.kotlin:kotlin-reflect:2.0.21=compileOnlyDependenciesMetadata +org.jetbrains.kotlin:kotlin-reflect:2.3.0=compileClasspath,embeddedKotlin,precompiledScriptPluginAccessorsGenerationClasspath,testCompileClasspath,testRuntimeClasspath +org.jetbrains.kotlin:kotlin-sam-with-receiver-compiler-plugin-embeddable:2.3.0=kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest +org.jetbrains.kotlin:kotlin-script-runtime:2.3.0=compilePluginsBlocksPluginClasspathElements,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,kotlinKlibCommonizerClasspath +org.jetbrains.kotlin:kotlin-scripting-common:2.3.0=compilePluginsBlocksPluginClasspathElements,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest +org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:2.3.0=compilePluginsBlocksPluginClasspathElements,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest +org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:2.3.0=compilePluginsBlocksPluginClasspathElements,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest +org.jetbrains.kotlin:kotlin-scripting-jvm:2.3.0=compilePluginsBlocksPluginClasspathElements,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest +org.jetbrains.kotlin:kotlin-stdlib:2.0.21=compileOnlyDependenciesMetadata +org.jetbrains.kotlin:kotlin-stdlib:2.3.0=bcv-rt-jvm-cp-resolver,compileClasspath,compilePluginsBlocksPluginClasspathElements,embeddedKotlin,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,kotlinInternalAbiValidation,kotlinKlibCommonizerClasspath,precompiledScriptPluginAccessorsGenerationClasspath,testCompileClasspath,testRuntimeClasspath +org.jetbrains.kotlin:kotlin-tooling-core:2.3.0=kotlinBuildToolsApiClasspath +org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.8.0=kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinKlibCommonizerClasspath +org.jetbrains:annotations:13.0=bcv-rt-jvm-cp-resolver,compileClasspath,compilePluginsBlocksPluginClasspathElements,embeddedKotlin,kotlinBuildToolsApiClasspath,kotlinCompilerClasspath,kotlinCompilerPluginClasspathMain,kotlinCompilerPluginClasspathTest,kotlinInternalAbiValidation,kotlinKlibCommonizerClasspath,precompiledScriptPluginAccessorsGenerationClasspath,testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-tree:9.6=bcv-rt-jvm-cp-resolver +org.ow2.asm:asm:9.6=bcv-rt-jvm-cp-resolver +empty=annotationProcessor,apiDependenciesMetadata,implementationDependenciesMetadata,intransitiveDependenciesMetadata,kotlinCompilerPluginClasspath,kotlinNativeCompilerPluginClasspath,kotlinScriptDefExtensions,testAnnotationProcessor,testApiDependenciesMetadata,testCompileOnlyDependenciesMetadata,testImplementationDependenciesMetadata,testIntransitiveDependenciesMetadata,testKotlinScriptDefExtensions diff --git a/mps-gradle-plugin/settings.gradle.kts b/mps-gradle-plugin/settings.gradle.kts new file mode 100644 index 00000000..4bed93f9 --- /dev/null +++ b/mps-gradle-plugin/settings.gradle.kts @@ -0,0 +1,14 @@ +plugins { + id("org.gradle.toolchains.foojay-resolver-convention") version ("1.0.0") +} + +dependencyResolutionManagement { + versionCatalogs { + create("libs") { + from(files("../gradle/libs.versions.toml")) + } + } +} + +includeBuild("../git-based-versioning") +includeBuild("../mps-gradle-plugin-api") diff --git a/src/main/groovy/de/itemis/mps/gradle/GenerateLibrariesXml.groovy b/mps-gradle-plugin/src/main/groovy/de/itemis/mps/gradle/GenerateLibrariesXml.groovy similarity index 87% rename from src/main/groovy/de/itemis/mps/gradle/GenerateLibrariesXml.groovy rename to mps-gradle-plugin/src/main/groovy/de/itemis/mps/gradle/GenerateLibrariesXml.groovy index ba618d5b..a63fa98b 100644 --- a/src/main/groovy/de/itemis/mps/gradle/GenerateLibrariesXml.groovy +++ b/mps-gradle-plugin/src/main/groovy/de/itemis/mps/gradle/GenerateLibrariesXml.groovy @@ -7,10 +7,14 @@ import org.gradle.api.tasks.InputFile import org.gradle.api.tasks.Internal import org.gradle.api.tasks.Optional import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity import org.gradle.api.tasks.TaskAction +import org.gradle.work.DisableCachingByDefault +@DisableCachingByDefault(because = "Not worth caching: generates a small XML file from a properties file") class GenerateLibrariesXml extends DefaultTask { - @InputFile + @InputFile @PathSensitive(PathSensitivity.NONE) File defaults @Internal diff --git a/src/main/groovy/de/itemis/mps/gradle/Pom.groovy b/mps-gradle-plugin/src/main/groovy/de/itemis/mps/gradle/Pom.groovy similarity index 100% rename from src/main/groovy/de/itemis/mps/gradle/Pom.groovy rename to mps-gradle-plugin/src/main/groovy/de/itemis/mps/gradle/Pom.groovy diff --git a/src/main/kotlin/de/itemis/mps/gradle/BackendConfigurations.kt b/mps-gradle-plugin/src/main/kotlin/de/itemis/mps/gradle/BackendConfigurations.kt similarity index 100% rename from src/main/kotlin/de/itemis/mps/gradle/BackendConfigurations.kt rename to mps-gradle-plugin/src/main/kotlin/de/itemis/mps/gradle/BackendConfigurations.kt diff --git a/mps-gradle-plugin/src/main/kotlin/de/itemis/mps/gradle/Common.kt b/mps-gradle-plugin/src/main/kotlin/de/itemis/mps/gradle/Common.kt new file mode 100644 index 00000000..99edbdc0 --- /dev/null +++ b/mps-gradle-plugin/src/main/kotlin/de/itemis/mps/gradle/Common.kt @@ -0,0 +1,5 @@ +package de.itemis.mps.gradle + +const val MPS_BUILD_BACKENDS_VERSION = "[1.15,2.0)" // 1.15 required for --plugin-root support. + +enum class EnvironmentKind { MPS, IDEA } diff --git a/mps-gradle-plugin/src/main/kotlin/de/itemis/mps/gradle/ErrorMessages.kt b/mps-gradle-plugin/src/main/kotlin/de/itemis/mps/gradle/ErrorMessages.kt new file mode 100644 index 00000000..418e360d --- /dev/null +++ b/mps-gradle-plugin/src/main/kotlin/de/itemis/mps/gradle/ErrorMessages.kt @@ -0,0 +1,7 @@ +package de.itemis.mps.gradle + +import java.io.File + +internal object ErrorMessages { + internal fun noMpsProjectIn(dir: File): String = "Directory does not contain an MPS project: $dir" +} diff --git a/mps-gradle-plugin/src/main/kotlin/de/itemis/mps/gradle/RunAntScript.kt b/mps-gradle-plugin/src/main/kotlin/de/itemis/mps/gradle/RunAntScript.kt new file mode 100644 index 00000000..891a7c70 --- /dev/null +++ b/mps-gradle-plugin/src/main/kotlin/de/itemis/mps/gradle/RunAntScript.kt @@ -0,0 +1,98 @@ +package de.itemis.mps.gradle; + +import de.itemis.mps.gradle.tasks.MpsTask +import org.gradle.api.DefaultTask +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.logging.LogLevel +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.* +import org.gradle.jvm.toolchain.JavaLauncher +import org.gradle.process.ExecOperations +import org.gradle.work.DisableCachingByDefault +import javax.inject.Inject + +@DisableCachingByDefault(because = "Runs an Ant script that builds MPS languages and has non-trivial, external outputs") +abstract class RunAntScript : DefaultTask(), MpsTask { + @get:InputFile + @get:PathSensitive(PathSensitivity.NONE) + abstract val script: RegularFileProperty + + @get:Input + abstract val targets: ListProperty + + @get:Classpath + abstract val scriptClasspath: ConfigurableFileCollection + + @get:Input + abstract val scriptArgs: ListProperty + + @get:Internal("working directory has no effect on the output") + abstract val workingDirectory: DirectoryProperty + + @get:Inject + protected abstract val execOperations: ExecOperations + + @Nested + @Optional + abstract override fun getJavaLauncher(): Property + + init { + scriptClasspath.convention(mpsHome.asFileTree.matching { + include("lib/ant/lib/*.jar") + include("lib/*.jar") + }) + workingDirectory.convention(project.rootProject.layout.projectDirectory) + } + + @TaskAction + fun build() { + val allArgs = scriptArgs.get().toMutableList() + + val level = logLevel.get() + if (level != LogLevel.LIFECYCLE && !allArgs.any { it.startsWith("-Dmps.ant.log=") }) { + allArgs += "-Dmps.ant.log=${level.toString().lowercase()}" + } + + val mpsHomePath = mpsHome.get().asFile.absolutePath + if (!allArgs.any { it.startsWith("-Dmps.home=") }) { + allArgs += "-Dmps.home=$mpsHomePath" + } + if (!allArgs.any { it.startsWith("-Dmps_home=") }) { + allArgs += "-Dmps_home=$mpsHomePath" + } + + allArgs += "-buildfile" + allArgs += script.get().asFile.absolutePath + allArgs += targets.get() + + execOperations.javaexec { + if (javaLauncher.isPresent) { + executable(javaLauncher.get().executablePath) + } + + mainClass.set("org.apache.tools.ant.launch.Launcher") + workingDir = workingDirectory.get().asFile + + classpath(scriptClasspath) + + args(allArgs) + } + } +} + +@DisableCachingByDefault(because = "Runs an Ant script that builds MPS languages and has non-trivial, external outputs") +abstract class BuildLanguages : RunAntScript() { + init { + targets.convention(listOf("clean", "generate", "assemble")) + } +} + +@DisableCachingByDefault(because = "Runs an Ant script that tests MPS languages and has non-trivial, external outputs") +abstract class TestLanguages : RunAntScript() { + init { + targets.convention(listOf("clean", "generate", "assemble", "check")) + } +} diff --git a/src/main/kotlin/de/itemis/mps/gradle/TaskGroups.kt b/mps-gradle-plugin/src/main/kotlin/de/itemis/mps/gradle/TaskGroups.kt similarity index 100% rename from src/main/kotlin/de/itemis/mps/gradle/TaskGroups.kt rename to mps-gradle-plugin/src/main/kotlin/de/itemis/mps/gradle/TaskGroups.kt diff --git a/src/main/kotlin/de/itemis/mps/gradle/common.gradle.kts b/mps-gradle-plugin/src/main/kotlin/de/itemis/mps/gradle/common.gradle.kts similarity index 89% rename from src/main/kotlin/de/itemis/mps/gradle/common.gradle.kts rename to mps-gradle-plugin/src/main/kotlin/de/itemis/mps/gradle/common.gradle.kts index d296f72d..3f3a4973 100644 --- a/src/main/kotlin/de/itemis/mps/gradle/common.gradle.kts +++ b/mps-gradle-plugin/src/main/kotlin/de/itemis/mps/gradle/common.gradle.kts @@ -1,10 +1,16 @@ package de.itemis.mps.gradle +import de.itemis.mps.gradle.tasks.MpsTask + /** * A side effect of this plugin is that it lets us use `plugins` block rather than `buildscript` to put the task classes * ([RunAntScript], [BuildLanguages], etc.) onto the classpath. */ +tasks.withType().configureEach { + logLevel.convention(gradle.startParameter.logLevel) +} + val modelcheckBackend = configurations.create(BackendConfigurations.MODELCHECK_BACKEND_CONFIGURATION_NAME) val generateBackend = configurations.create(BackendConfigurations.GENERATE_BACKEND_CONFIGURATION_NAME) val executeBackend = configurations.create(BackendConfigurations.EXECUTE_BACKEND_CONFIGURATION_NAME) diff --git a/src/main/kotlin/de/itemis/mps/gradle/tasks/ExcludedModuleMigration.kt b/mps-gradle-plugin/src/main/kotlin/de/itemis/mps/gradle/tasks/ExcludedModuleMigration.kt similarity index 100% rename from src/main/kotlin/de/itemis/mps/gradle/tasks/ExcludedModuleMigration.kt rename to mps-gradle-plugin/src/main/kotlin/de/itemis/mps/gradle/tasks/ExcludedModuleMigration.kt diff --git a/src/main/kotlin/de/itemis/mps/gradle/tasks/Libraries.kt b/mps-gradle-plugin/src/main/kotlin/de/itemis/mps/gradle/tasks/Libraries.kt similarity index 100% rename from src/main/kotlin/de/itemis/mps/gradle/tasks/Libraries.kt rename to mps-gradle-plugin/src/main/kotlin/de/itemis/mps/gradle/tasks/Libraries.kt diff --git a/src/main/kotlin/de/itemis/mps/gradle/tasks/MpsCheck.kt b/mps-gradle-plugin/src/main/kotlin/de/itemis/mps/gradle/tasks/MpsCheck.kt similarity index 65% rename from src/main/kotlin/de/itemis/mps/gradle/tasks/MpsCheck.kt rename to mps-gradle-plugin/src/main/kotlin/de/itemis/mps/gradle/tasks/MpsCheck.kt index b4a3091f..31d7f36f 100644 --- a/src/main/kotlin/de/itemis/mps/gradle/tasks/MpsCheck.kt +++ b/mps-gradle-plugin/src/main/kotlin/de/itemis/mps/gradle/tasks/MpsCheck.kt @@ -5,71 +5,77 @@ 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.* +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.Directory +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.FileTree +import org.gradle.api.file.RegularFileProperty import org.gradle.api.logging.LogLevel import org.gradle.api.provider.ListProperty import org.gradle.api.provider.MapProperty import org.gradle.api.provider.Property -import org.gradle.api.provider.SetProperty import org.gradle.api.tasks.* -import org.gradle.kotlin.dsl.* +import org.gradle.kotlin.dsl.newInstance import org.gradle.process.CommandLineArgumentProvider @CacheableTask @Incubating -abstract class MpsCheck : JavaExec(), VerificationTask { +abstract class MpsCheck : JavaExec(), VerificationTask, MpsProjectTask { - @get:Input - val logLevel: Property = objectFactory.property().convention(project.gradle.startParameter.logLevel) - - @get:Internal("covered by mpsVersion, classpath") - val mpsHome: DirectoryProperty = objectFactory.directoryProperty() + @Input + abstract override fun getLogLevel(): Property - @get:Input - @get:Optional - val mpsVersion: Property = objectFactory.property() - .convention(MpsVersionDetection.fromMpsHome(project.layout, providerFactory, mpsHome.asFile)) + @Internal("covered by mpsVersion and classpath") + abstract override fun getMpsHome(): DirectoryProperty - @get:Internal("only modules and models matter, covered by #sources") - val projectLocation: DirectoryProperty = - objectFactory.directoryProperty().convention(project.layout.projectDirectory) + @Input + @Optional + abstract override fun getMpsVersion(): Property - @get:Classpath - val pluginRoots: SetProperty = objectFactory.setProperty() + @Internal("covered by sources") + abstract override fun getProjectLocation(): DirectoryProperty - @get:Internal("Folder macros are ignored for the purposes of up-to-date checks and caching") - val folderMacros: MapProperty = objectFactory.mapProperty() + @Classpath + abstract override fun getPluginRoots(): ConfigurableFileCollection - @get:Input - val varMacros: MapProperty = objectFactory.mapProperty() + @Internal("Folder macros are ignored for the purposes of up-to-date checks and caching") + abstract override fun getFolderMacros(): MapProperty @get:Input - val models: ListProperty = objectFactory.listProperty() + abstract val models: ListProperty @get:Input - val modules: ListProperty = objectFactory.listProperty() + abstract val modules: ListProperty @get:Input - val excludeModels: ListProperty = objectFactory.listProperty() + abstract val excludeModels: ListProperty @get:Input - val excludeModules: ListProperty = objectFactory.listProperty() + abstract val excludeModules: ListProperty @get:Input - val warningAsError: Property = objectFactory.property().convention(false) + abstract val warningAsError: Property @get:OutputFile - val junitFile: RegularFileProperty = objectFactory.fileProperty() - .convention(project.layout.buildDirectory.map { it.file("TEST-${this@MpsCheck.name}.xml") }) + abstract val junitFile: RegularFileProperty @get:Input - val junitFormat: Property = objectFactory.property().convention("module-and-model") + abstract val junitFormat: Property @get:Input - val parallel: Property = objectFactory.property().convention(false) + abstract val parallel: Property @get:Internal("covered by classpath") - val additionalModelcheckBackendClasspath: ConfigurableFileCollection = objectFactory.fileCollection() + abstract val additionalModelcheckBackendClasspath: ConfigurableFileCollection + + init { + mpsVersion.convention(MpsVersionDetection.fromMpsHome(project.layout, providerFactory, mpsHome.asFile)) + projectLocation.convention(project.layout.projectDirectory) + warningAsError.convention(false) + junitFile.convention(project.layout.buildDirectory.map { it.file("TEST-${this@MpsCheck.name}.xml") }) + junitFormat.convention("module-and-model") + parallel.convention(false) + } @Suppress("unused") @get:InputFiles @@ -101,9 +107,8 @@ abstract class MpsCheck : JavaExec(), VerificationTask { result.add("--project=${projectLocation.get().asFile}") - addPluginRoots(result, pluginRoots.get()) + addPluginRoots(result, pluginRoots) addFolderMacros(result, folderMacros) - addVarMacros(result, varMacros) // Only a limited subset of checkers is registered in MPS environment, IDEA environment is necessary for // proper checking. diff --git a/src/main/kotlin/de/itemis/mps/gradle/tasks/MpsExecute.kt b/mps-gradle-plugin/src/main/kotlin/de/itemis/mps/gradle/tasks/MpsExecute.kt similarity index 74% rename from src/main/kotlin/de/itemis/mps/gradle/tasks/MpsExecute.kt rename to mps-gradle-plugin/src/main/kotlin/de/itemis/mps/gradle/tasks/MpsExecute.kt index dbcc0d76..c33c1fca 100644 --- a/src/main/kotlin/de/itemis/mps/gradle/tasks/MpsExecute.kt +++ b/mps-gradle-plugin/src/main/kotlin/de/itemis/mps/gradle/tasks/MpsExecute.kt @@ -11,7 +11,6 @@ import org.gradle.api.logging.LogLevel import org.gradle.api.provider.ListProperty import org.gradle.api.provider.MapProperty import org.gradle.api.provider.Property -import org.gradle.api.provider.SetProperty import org.gradle.api.tasks.* import org.gradle.kotlin.dsl.newInstance import org.gradle.work.DisableCachingByDefault @@ -19,25 +18,26 @@ import org.gradle.work.DisableCachingByDefault @DisableCachingByDefault(because = "calls arbitrary user code") @Incubating -abstract class MpsExecute : JavaExec() { +abstract class MpsExecute : JavaExec(), MpsProjectTask { - @get:Input - abstract val logLevel: Property + @Input + abstract override fun getLogLevel(): Property - @get:Internal - abstract val mpsHome: DirectoryProperty + @Internal("covered by mpsVersion and classpath") + abstract override fun getMpsHome(): DirectoryProperty - @get:Internal - abstract val mpsVersion: Property + @Input + @Optional + abstract override fun getMpsVersion(): Property - @get:Internal - abstract val projectLocation: DirectoryProperty + @Internal("covered by sources") + abstract override fun getProjectLocation(): DirectoryProperty - @get:Classpath - abstract val pluginRoots: SetProperty + @Classpath + abstract override fun getPluginRoots(): ConfigurableFileCollection - @get:Internal - abstract val macros: MapProperty + @Internal("Folder macros are ignored for the purposes of up-to-date checks and caching") + abstract override fun getFolderMacros(): MapProperty @get:Internal abstract val module: Property @@ -52,10 +52,9 @@ abstract class MpsExecute : JavaExec() { abstract val methodArguments: ListProperty @get:Internal - val additionalExecuteBackendClasspath: ConfigurableFileCollection = objectFactory.fileCollection() + abstract val additionalExecuteBackendClasspath: ConfigurableFileCollection init { - logLevel.convention(project.gradle.startParameter.logLevel) mpsVersion.convention(MpsVersionDetection.fromMpsHome(project.layout, providerFactory, mpsHome.asFile)) projectLocation.convention(project.layout.projectDirectory) @@ -68,8 +67,8 @@ abstract class MpsExecute : JavaExec() { mutableListOf().apply { add("--project=${projectLocation.get().asFile}") - addPluginRoots(this, pluginRoots.get()) - addVarMacros(this, macros) + addPluginRoots(this, pluginRoots) + addFolderMacros(this, folderMacros) add("--module=${module.get()}") add("--class=${className.get()}") diff --git a/src/main/kotlin/de/itemis/mps/gradle/tasks/MpsGenerate.kt b/mps-gradle-plugin/src/main/kotlin/de/itemis/mps/gradle/tasks/MpsGenerate.kt similarity index 63% rename from src/main/kotlin/de/itemis/mps/gradle/tasks/MpsGenerate.kt rename to mps-gradle-plugin/src/main/kotlin/de/itemis/mps/gradle/tasks/MpsGenerate.kt index d3108027..aab07e0f 100644 --- a/src/main/kotlin/de/itemis/mps/gradle/tasks/MpsGenerate.kt +++ b/mps-gradle-plugin/src/main/kotlin/de/itemis/mps/gradle/tasks/MpsGenerate.kt @@ -15,64 +15,63 @@ import org.gradle.api.provider.ListProperty import org.gradle.api.provider.MapProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.* -import org.gradle.kotlin.dsl.listProperty -import org.gradle.kotlin.dsl.mapProperty import org.gradle.kotlin.dsl.newInstance -import org.gradle.kotlin.dsl.property import org.gradle.process.CommandLineArgumentProvider @CacheableTask @Incubating -abstract class MpsGenerate : JavaExec() { +abstract class MpsGenerate : JavaExec(), MpsProjectTask { - @get:Input - val logLevel: Property = objectFactory.property().convention(project.gradle.startParameter.logLevel) - - @get:Internal("covered by mpsVersion, classpath") - val mpsHome: DirectoryProperty = objectFactory.directoryProperty() + @Input + abstract override fun getLogLevel(): Property - @get:Input - @get:Optional - val mpsVersion: Property = objectFactory.property() - .convention(MpsVersionDetection.fromMpsHome(project.layout, providerFactory, mpsHome.asFile)) + @Internal("covered by mpsVersion and classpath") + abstract override fun getMpsHome(): DirectoryProperty - @get:Internal("only modules and models matter, covered by #sources") - val projectLocation: DirectoryProperty = - objectFactory.directoryProperty().convention(project.layout.projectDirectory) + @Input + @Optional + abstract override fun getMpsVersion(): Property - @get:Classpath - val pluginRoots: ConfigurableFileCollection = objectFactory.fileCollection() + @Internal("covered by sources") + abstract override fun getProjectLocation(): DirectoryProperty - @get:Internal("Folder macros are ignored for the purposes of up-to-date checks and caching") - val folderMacros: MapProperty = objectFactory.mapProperty() + @Classpath + abstract override fun getPluginRoots(): ConfigurableFileCollection - @get:Input - val environmentKind: Property = objectFactory.property() - .convention(EnvironmentKind.MPS) + @Internal("Folder macros are ignored for the purposes of up-to-date checks and caching") + abstract override fun getFolderMacros(): MapProperty @get:Input - val varMacros: MapProperty = objectFactory.mapProperty() + abstract val environmentKind: Property @get:Input - val models: ListProperty = objectFactory.listProperty() + abstract val models: ListProperty @get:Input - val modules: ListProperty = objectFactory.listProperty() + abstract val modules: ListProperty @get:Input - val excludeModels: ListProperty = objectFactory.listProperty() + abstract val excludeModels: ListProperty @get:Input - val excludeModules: ListProperty = objectFactory.listProperty() + abstract val excludeModules: ListProperty @get:Input - val strictMode: Property = objectFactory.property().convention(true) + abstract val strictMode: Property @get:Input - val parallelGenerationThreads: Property = objectFactory.property().convention(0) + abstract val parallelGenerationThreads: Property @get:Internal("covered by classpath") - val additionalGenerateBackendClasspath: ConfigurableFileCollection = objectFactory.fileCollection() + abstract val additionalGenerateBackendClasspath: ConfigurableFileCollection + + init { + mpsVersion.convention(MpsVersionDetection.fromMpsHome(project.layout, providerFactory, mpsHome.asFile)) + projectLocation.convention(project.layout.projectDirectory) + environmentKind.convention(EnvironmentKind.MPS) + strictMode.convention(true) + parallelGenerationThreads.convention(0) + } @Suppress("unused") @get:InputFiles @@ -105,7 +104,6 @@ abstract class MpsGenerate : JavaExec() { addPluginRoots(result, pluginRoots) addFolderMacros(result, folderMacros) - addVarMacros(result, varMacros) result.add("--environment=${environmentKind.get()}") diff --git a/src/main/kotlin/de/itemis/mps/gradle/tasks/MpsMigrate.kt b/mps-gradle-plugin/src/main/kotlin/de/itemis/mps/gradle/tasks/MpsMigrate.kt similarity index 77% rename from src/main/kotlin/de/itemis/mps/gradle/tasks/MpsMigrate.kt rename to mps-gradle-plugin/src/main/kotlin/de/itemis/mps/gradle/tasks/MpsMigrate.kt index 1019fe3e..fb75e658 100644 --- a/src/main/kotlin/de/itemis/mps/gradle/tasks/MpsMigrate.kt +++ b/mps-gradle-plugin/src/main/kotlin/de/itemis/mps/gradle/tasks/MpsMigrate.kt @@ -4,10 +4,11 @@ import de.itemis.mps.gradle.TaskGroups import de.itemis.mps.gradle.launcher.MpsVersionDetection import groovy.xml.MarkupBuilder import org.gradle.api.DefaultTask +import org.gradle.api.GradleException import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.Directory import org.gradle.api.file.DirectoryProperty -import org.gradle.api.file.RegularFileProperty +import org.gradle.api.file.FileCollection import org.gradle.api.logging.LogLevel import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.ListProperty @@ -16,30 +17,23 @@ import org.gradle.api.provider.Property import org.gradle.api.provider.ProviderFactory import org.gradle.api.tasks.* import org.gradle.jvm.toolchain.JavaLauncher -import org.gradle.kotlin.dsl.listProperty -import org.gradle.kotlin.dsl.mapProperty -import org.gradle.kotlin.dsl.property import org.gradle.kotlin.dsl.withGroovyBuilder import org.gradle.process.ExecOperations import java.io.File import javax.inject.Inject @UntrackedTask(because = "Operates 'in place'") -abstract class MpsMigrate @Inject constructor( - objectFactory: ObjectFactory, - providerFactory: ProviderFactory -) : DefaultTask() { +abstract class MpsMigrate : DefaultTask(), MpsProjectTask { - @get:Input - val logLevel: Property = objectFactory.property().convention(project.gradle.startParameter.logLevel) + @Input + abstract override fun getLogLevel(): Property - @get:Internal - val mpsHome: DirectoryProperty = objectFactory.directoryProperty() + @Internal("covered by mpsVersion and classpath") + abstract override fun getMpsHome(): DirectoryProperty - @get:Input - @get:Optional - val mpsVersion: Property = objectFactory.property() - .convention(MpsVersionDetection.fromMpsHome(project.layout, providerFactory, mpsHome.asFile)) + @Input + @Optional + abstract override fun getMpsVersion(): Property /** * (Since MPS 2021.1) Whether to halt if a pre-check has failed. Note that to ignore the check for migrated @@ -47,68 +41,92 @@ abstract class MpsMigrate @Inject constructor( */ @get:Input @get:Optional - val haltOnPrecheckFailure: Property = objectFactory.property() + abstract val haltOnPrecheckFailure: Property /** * (Since MPS 2021.3.4) Whether to halt when a non-migrated dependency is discovered. */ @get:Input @get:Optional - val haltOnDependencyError: Property = objectFactory.property() + abstract val haltOnDependencyError: Property + + @Internal("covered by allProjectFiles") + abstract override fun getProjectLocation(): DirectoryProperty @get:Internal("covered by allProjectFiles") - val projectDirectories: ConfigurableFileCollection = objectFactory.fileCollection() + abstract val projectLocations: ConfigurableFileCollection @get:InputFiles @get:IgnoreEmptyDirectories @get:SkipWhenEmpty - protected val allProjectFiles = providerFactory.provider { projectDirectories.flatMap { objectFactory.fileTree().from(it) } } - - @get:Internal - val javaExecutable: RegularFileProperty = objectFactory.fileProperty() + @get:PathSensitive(PathSensitivity.RELATIVE) + protected val allProjectFiles = providerFactory.provider { + effectiveProjectLocations().flatMap { objectFactory.fileTree().from(it) } + } - @get:Nested - @get:Optional - val javaLauncher: Property = objectFactory.property() + @Nested + @Optional + abstract override fun getJavaLauncher(): Property @get:Input - val jvmArgs: ListProperty = objectFactory.listProperty() + abstract val jvmArgs: ListProperty @get:Input - val antJvmArgs: ListProperty = objectFactory.listProperty() + abstract val antJvmArgs: ListProperty @get:Input @get:Optional - val maxHeapSize: Property = objectFactory.property() + abstract val maxHeapSize: Property - @get:Internal("Folder macros are ignored for the purposes of up-to-date checks and caching") - val folderMacros: MapProperty = objectFactory.mapProperty() + @Internal("Folder macros are ignored for the purposes of up-to-date checks and caching") + abstract override fun getFolderMacros(): MapProperty - @get:Classpath - val pluginRoots: ConfigurableFileCollection = objectFactory.fileCollection() + @Classpath + abstract override fun getPluginRoots(): ConfigurableFileCollection @get:Inject protected abstract val execOperations: ExecOperations + @get:Inject + protected abstract val objectFactory: ObjectFactory + + @get:Inject + protected abstract val providerFactory: ProviderFactory + init { + mpsVersion.convention(MpsVersionDetection.fromMpsHome(project.layout, providerFactory, mpsHome.asFile)) group = TaskGroups.MIGRATION } + private fun effectiveProjectLocations(): FileCollection { + val hasMultiple = !projectLocations.isEmpty + val hasSingle = projectLocation.isPresent + if (hasMultiple && hasSingle) { + throw GradleException("Cannot set both projectLocation and projectLocations. Use one or the other.") + } + if (!hasMultiple && !hasSingle) { + throw GradleException("Must set either projectLocation or projectLocations.") + } + return if (hasMultiple) projectLocations else objectFactory.fileCollection().from(projectLocation) + } + @TaskAction fun execute() { // When MPS detects that files have changed externally then instead of updating the VFS cache it complains. // Cleaning temporary directory helps avoid this. cleanTemporaryDir() + val effectiveLocations = effectiveProjectLocations() + val buildFile = temporaryDir.resolve("build.xml") - writeBuildFile(buildFile) + writeBuildFile(buildFile, effectiveLocations) val mpsAntClasspath = mpsHome.asFileTree.matching { include("lib/ant/lib/*.jar") include("lib/*.jar") } - for(dir in projectDirectories) { + for (dir in effectiveLocations) { checkProjectLocation(dir) } @@ -116,7 +134,7 @@ abstract class MpsMigrate @Inject constructor( mainClass.set("org.apache.tools.ant.launch.Launcher") workingDir = temporaryDir classpath = mpsAntClasspath - val executableCandidate = javaExecutable.orElse(javaLauncher.map { it.executablePath }).orNull?.toString() + val executableCandidate = javaLauncher.orNull?.executablePath?.asFile?.toString() if (executableCandidate != null) { executable = executableCandidate } @@ -128,7 +146,7 @@ abstract class MpsMigrate @Inject constructor( temporaryDir.listFiles()?.forEach { it.deleteRecursively() } } - private fun writeBuildFile(buildFile: File) { + private fun writeBuildFile(buildFile: File, effectiveLocations: FileCollection) { buildFile.printWriter().use { writer -> MarkupBuilder(writer).withGroovyBuilder { "project" { @@ -157,12 +175,12 @@ abstract class MpsMigrate @Inject constructor( } val folderMacrosValue = folderMacros.get() - val allLibraries = projectDirectories + val allLibraries = effectiveLocations .flatMap { readLibraries(it, folderMacrosValue::get) } .toSortedSet() "migrate"(*argsToMigrate) { - projectDirectories.forEach { + effectiveLocations.forEach { "project"("path" to it) } diff --git a/src/main/kotlin/de/itemis/mps/gradle/tasks/PluginIds.kt b/mps-gradle-plugin/src/main/kotlin/de/itemis/mps/gradle/tasks/PluginIds.kt similarity index 98% rename from src/main/kotlin/de/itemis/mps/gradle/tasks/PluginIds.kt rename to mps-gradle-plugin/src/main/kotlin/de/itemis/mps/gradle/tasks/PluginIds.kt index 4b08b1ba..d5334dcb 100644 --- a/src/main/kotlin/de/itemis/mps/gradle/tasks/PluginIds.kt +++ b/mps-gradle-plugin/src/main/kotlin/de/itemis/mps/gradle/tasks/PluginIds.kt @@ -1,6 +1,5 @@ package de.itemis.mps.gradle.tasks -import de.itemis.mps.gradle.Plugin import org.slf4j.LoggerFactory import org.w3c.dom.Document import java.io.File @@ -15,6 +14,8 @@ import java.util.jar.JarFile import javax.xml.parsers.DocumentBuilder import javax.xml.parsers.DocumentBuilderFactory +internal data class Plugin(var id: String, var path: String) + internal val logger = LoggerFactory.getLogger("de.itemis.mps.gradle.tasks.PluginIds")!! internal fun findPluginsRecursively(root: File): List = mutableListOf().apply { diff --git a/src/main/kotlin/de/itemis/mps/gradle/tasks/Remigrate.kt b/mps-gradle-plugin/src/main/kotlin/de/itemis/mps/gradle/tasks/Remigrate.kt similarity index 57% rename from src/main/kotlin/de/itemis/mps/gradle/tasks/Remigrate.kt rename to mps-gradle-plugin/src/main/kotlin/de/itemis/mps/gradle/tasks/Remigrate.kt index bb349d92..f7d8eddc 100644 --- a/src/main/kotlin/de/itemis/mps/gradle/tasks/Remigrate.kt +++ b/mps-gradle-plugin/src/main/kotlin/de/itemis/mps/gradle/tasks/Remigrate.kt @@ -4,68 +4,68 @@ 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.GradleException 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.file.FileCollection import org.gradle.api.logging.LogLevel -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.provider.SetProperty 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.kotlin.dsl.setProperty 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() { +abstract class Remigrate : JavaExec(), MpsProjectTask { - @get:Input - val logLevel: Property = objectFactory.property().convention(project.gradle.startParameter.logLevel) + @Input + abstract override fun getLogLevel(): Property - @get:Internal - val mpsHome: DirectoryProperty = objectFactory.directoryProperty() + @Internal("covered by mpsVersion and classpath") + abstract override fun getMpsHome(): DirectoryProperty - @get:Input - @get:Optional - val mpsVersion: Property = objectFactory.property() - .convention(MpsVersionDetection.fromMpsHome(project.layout, providerFactory, mpsHome.asFile)) + @Input + @Optional + abstract override fun getMpsVersion(): Property + + @Internal("covered by allProjectFiles") + abstract override fun getProjectLocation(): DirectoryProperty @get:Internal("covered by allProjectFiles") - val projectDirectories: ConfigurableFileCollection = objectFactory.fileCollection() + abstract val projectLocations: ConfigurableFileCollection @get:InputFiles @get:SkipWhenEmpty @get:IgnoreEmptyDirectories - protected val allProjectFiles = providerFactory.provider { projectDirectories.flatMap { objectFactory.fileTree().from(it) } } + @get:PathSensitive(PathSensitivity.RELATIVE) + protected val allProjectFiles = providerFactory.provider { + effectiveProjectLocations().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() + @Internal("Folder macros are ignored for the purposes of up-to-date checks and caching") + abstract override fun getFolderMacros(): MapProperty - @get:Classpath - val pluginRoots: ConfigurableFileCollection = objectFactory.fileCollection() + @Classpath + abstract override fun getPluginRoots(): ConfigurableFileCollection @get:Internal - val additionalClasspath: ConfigurableFileCollection = - objectFactory.fileCollection().from(initialBackendClasspath()) + abstract val additionalClasspath: ConfigurableFileCollection @get:Input - val excludedModuleMigrations: SetProperty = objectFactory.setProperty() + abstract val excludedModuleMigrations: SetProperty fun excludeModuleMigration(language: String, version: Int) { excludedModuleMigrations.add(ExcludedModuleMigration(language, version)) } init { + mpsVersion.convention(MpsVersionDetection.fromMpsHome(project.layout, providerFactory, mpsHome.asFile)) + additionalClasspath.from(mpsHome.asFileTree.matching { include("lib/**/*.jar") }) + val backendBuilder: MpsBackendBuilder = project.objects.newInstance(MpsBackendBuilder::class) backendBuilder.withMpsHomeDirectory(mpsHome).withMpsVersion(mpsVersion).configure(this) @@ -75,7 +75,7 @@ open class Remigrate @Inject constructor( argumentProviders.add(CommandLineArgumentProvider { val result = mutableListOf() - for (dir in projectDirectories) { + for (dir in effectiveProjectLocations()) { result.add("--project=$dir") } @@ -111,13 +111,21 @@ open class Remigrate @Inject constructor( @TaskAction override fun exec() { - for (dir in projectDirectories) { + for (dir in effectiveProjectLocations()) { checkProjectLocation(dir) } super.exec() } - private fun initialBackendClasspath() = mpsHome.asFileTree.matching { - include("lib/**/*.jar") + private fun effectiveProjectLocations(): FileCollection { + val hasMultiple = !projectLocations.isEmpty + val hasSingle = projectLocation.isPresent + if (hasMultiple && hasSingle) { + throw GradleException("Cannot set both projectLocation and projectLocations. Use one or the other.") + } + if (!hasMultiple && !hasSingle) { + throw GradleException("Must set either projectLocation or projectLocations.") + } + return if (hasMultiple) projectLocations else objectFactory.fileCollection().from(projectLocation) } } diff --git a/src/main/kotlin/de/itemis/mps/gradle/tasks/backend_arguments.kt b/mps-gradle-plugin/src/main/kotlin/de/itemis/mps/gradle/tasks/backend_arguments.kt similarity index 69% rename from src/main/kotlin/de/itemis/mps/gradle/tasks/backend_arguments.kt rename to mps-gradle-plugin/src/main/kotlin/de/itemis/mps/gradle/tasks/backend_arguments.kt index 47cf84c4..e78e182a 100644 --- a/src/main/kotlin/de/itemis/mps/gradle/tasks/backend_arguments.kt +++ b/mps-gradle-plugin/src/main/kotlin/de/itemis/mps/gradle/tasks/backend_arguments.kt @@ -4,11 +4,10 @@ import de.itemis.mps.gradle.ErrorMessages import org.gradle.api.GradleException import org.gradle.api.file.Directory import org.gradle.api.file.FileCollection -import org.gradle.api.file.FileSystemLocation import org.gradle.api.provider.Provider import java.io.File -internal fun checkProjectLocation(projectLocation: Provider) = +internal fun checkProjectLocation(projectLocation: Provider) = checkProjectLocation(projectLocation.get().asFile) internal fun checkProjectLocation(projectLocation: File) { @@ -21,14 +20,6 @@ internal fun addPluginRoots(result: MutableCollection, pluginRoots: File pluginRoots.mapTo(result) { "--plugin-root=$it" } } -internal fun addPluginRoots(result: MutableCollection, pluginRoots: Iterable) { - pluginRoots.mapTo(result) { "--plugin-root=$it" } -} - internal fun addFolderMacros(result: MutableCollection, folderMacros: Provider>) { folderMacros.get().mapTo(result) { "--macro=${it.key}::${it.value.asFile}" } } - -internal fun addVarMacros(result: MutableCollection, varMacros: Provider>) { - varMacros.get().mapTo(result) { "--macro=${it.key}::${it.value}" } -} diff --git a/src/test/kotlin/support/ProjectHelper.kt b/mps-gradle-plugin/src/test/kotlin/support/ProjectHelper.kt similarity index 100% rename from src/test/kotlin/support/ProjectHelper.kt rename to mps-gradle-plugin/src/test/kotlin/support/ProjectHelper.kt diff --git a/mps-gradle-plugin/src/test/kotlin/support/TestVersions.kt b/mps-gradle-plugin/src/test/kotlin/support/TestVersions.kt new file mode 100644 index 00000000..4049780c --- /dev/null +++ b/mps-gradle-plugin/src/test/kotlin/support/TestVersions.kt @@ -0,0 +1,18 @@ +package support + +/** + * MPS version used by integration tests that spin up an MPS instance (model check, generate, execute, migrate, ...). + * Kept in a single place so Renovate can bump it. + */ +const val MPS_VERSION = "2025.1.2" + +/** + * The Java version to be used for [MPS_VERSION]. Updated manually. + */ +const val JAVA_VERSION_FOR_MPS = 21 + +/** + * Version of the foojay-resolver-convention plugin applied in the settings scripts that tests generate. + * Kept in a single place so Renovate can bump it. + */ +const val FOOJAY_RESOLVER_CONVENTION_VERSION = "1.0.0" diff --git a/mps-gradle-plugin/src/test/kotlin/test/MpsTaskInterfaceTest.kt b/mps-gradle-plugin/src/test/kotlin/test/MpsTaskInterfaceTest.kt new file mode 100644 index 00000000..596070cc --- /dev/null +++ b/mps-gradle-plugin/src/test/kotlin/test/MpsTaskInterfaceTest.kt @@ -0,0 +1,115 @@ +package test + +import org.gradle.testkit.runner.GradleRunner +import org.hamcrest.CoreMatchers.containsString +import org.hamcrest.MatcherAssert.assertThat +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder +import support.FOOJAY_RESOLVER_CONVENTION_VERSION + +class MpsTaskInterfaceTest { + @Rule + @JvmField + val testProjectDir: TemporaryFolder = TemporaryFolder() + + @Test + fun `withType MpsProjectTask configures all project tasks`() { + testProjectDir.newFile("settings.gradle.kts").writeText( + """ + plugins { + id("org.gradle.toolchains.foojay-resolver-convention") version ("$FOOJAY_RESOLVER_CONVENTION_VERSION") + } + """.trimIndent() + ) + + testProjectDir.newFile("build.gradle.kts").writeText( + """ + import de.itemis.mps.gradle.tasks.* + + plugins { + id("de.itemis.mps.gradle.common") + } + + val generate by tasks.registering(MpsGenerate::class) + val check by tasks.registering(MpsCheck::class) { + junitFile = layout.buildDirectory.file("output.xml") + } + val execute by tasks.registering(MpsExecute::class) { + module = "test" + className = "test.Class" + method = "run" + } + + tasks.withType().configureEach { + mpsHome = layout.projectDirectory.dir("test-mps-home") + projectLocation = layout.projectDirectory.dir("test-project") + } + + tasks.register("printConfig") { + doLast { + tasks.withType(MpsProjectTask::class.java).forEach { + println("TASK:${'$'}{it.name}:mpsHome=${'$'}{it.mpsHome.get()}:project=${'$'}{it.projectLocation.get()}") + } + } + } + """.trimIndent() + ) + + val result = GradleRunner.create() + .withProjectDir(testProjectDir.root) + .withArguments("printConfig") + .withPluginClasspath() + .build() + + assertThat(result.output, containsString("TASK:generate:mpsHome=")) + assertThat(result.output, containsString("TASK:check:mpsHome=")) + assertThat(result.output, containsString("TASK:execute:mpsHome=")) + assertThat(result.output, containsString("test-mps-home")) + assertThat(result.output, containsString("test-project")) + } + + @Test + fun `withType MpsTask configures RunAntScript tasks`() { + testProjectDir.newFile("settings.gradle.kts") + + testProjectDir.newFile("build.gradle.kts").writeText( + """ + import de.itemis.mps.gradle.BuildLanguages + import de.itemis.mps.gradle.tasks.MpsTask + import de.itemis.mps.gradle.tasks.MpsGenerate + + plugins { + id("de.itemis.mps.gradle.common") + } + + val generate by tasks.registering(MpsGenerate::class) + val buildLangs by tasks.registering(BuildLanguages::class) { + script = layout.projectDirectory.file("build.xml") + } + + tasks.withType().configureEach { + mpsHome = layout.projectDirectory.dir("shared-mps") + } + + tasks.register("printConfig") { + doLast { + tasks.withType(MpsTask::class.java).forEach { + println("TASK:${'$'}{it.name}:mpsHome=${'$'}{it.mpsHome.get()}") + } + } + } + """.trimIndent() + ) + + val result = GradleRunner.create() + .withProjectDir(testProjectDir.root) + .withArguments("printConfig") + .withPluginClasspath() + .build() + + assertThat(result.output, containsString("TASK:generate:mpsHome=")) + assertThat(result.output, containsString("TASK:buildLangs:mpsHome=")) + assertThat(result.output, containsString("shared-mps")) + } +} diff --git a/src/test/kotlin/test/codeexecution/MpsExecuteTaskTest.kt b/mps-gradle-plugin/src/test/kotlin/test/codeexecution/MpsExecuteTaskTest.kt similarity index 75% rename from src/test/kotlin/test/codeexecution/MpsExecuteTaskTest.kt rename to mps-gradle-plugin/src/test/kotlin/test/codeexecution/MpsExecuteTaskTest.kt index af0e136e..975f0010 100644 --- a/src/test/kotlin/test/codeexecution/MpsExecuteTaskTest.kt +++ b/mps-gradle-plugin/src/test/kotlin/test/codeexecution/MpsExecuteTaskTest.kt @@ -7,6 +7,8 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder +import support.FOOJAY_RESOLVER_CONVENTION_VERSION +import support.MPS_VERSION import support.extractTestProject import java.io.File @@ -31,11 +33,11 @@ class MpsExecuteTaskTest { private fun settingsScriptBoilerplate() = """ plugins { - id("org.gradle.toolchains.foojay-resolver-convention") version ("0.7.0") + id("org.gradle.toolchains.foojay-resolver-convention") version ("$FOOJAY_RESOLVER_CONVENTION_VERSION") } """.trimIndent() - private fun buildScriptBoilerplate(mpsVersion: String) = """ + private fun buildScriptBoilerplate() = """ import de.itemis.mps.gradle.tasks.MpsExecute import de.itemis.mps.gradle.tasks.MpsGenerate @@ -51,7 +53,7 @@ class MpsExecuteTaskTest { val mps = configurations.create("mps") dependencies { - mps("com.jetbrains:mps:$mpsVersion") + mps("com.jetbrains:mps:$MPS_VERSION") } val resolveMps by tasks.registering(Sync::class) { @@ -62,18 +64,18 @@ class MpsExecuteTaskTest { val resolvedMpsHome = resolveMps.map { it.destinationDir } val generate by tasks.registering(MpsGenerate::class) { - mpsHome.set(layout.dir(resolvedMpsHome)) - projectLocation.set(file("${mpsTestProjectPath.canonicalPath}")) + mpsHome = layout.dir(resolvedMpsHome) + projectLocation = file("${mpsTestProjectPath.canonicalPath}") doFirst { println(resolvedMpsHome.get().listFiles()?.toList()) } } - + val execute by tasks.registering(MpsExecute::class) { dependsOn(generate) - mpsHome.set(layout.dir(resolvedMpsHome)) - projectLocation.set(file("${mpsTestProjectPath.canonicalPath}")) + mpsHome = layout.dir(resolvedMpsHome) + projectLocation = file("${mpsTestProjectPath.canonicalPath}") doFirst { println(resolvedMpsHome.get()) @@ -83,11 +85,11 @@ class MpsExecuteTaskTest { @Test fun `execute with Project`() { - buildFile.writeText(buildScriptBoilerplate("2021.3.4") + """ + buildFile.writeText(buildScriptBoilerplate() + """ execute { - module.set("NewSolution") - className.set("NewSolution.myModel.MyClass") - method.set("onlyProject") + module = "NewSolution" + className = "NewSolution.myModel.MyClass" + method = "onlyProject" } """.trimIndent()) @@ -98,13 +100,13 @@ class MpsExecuteTaskTest { @Test fun `execute with Project and args`() { - buildFile.writeText(buildScriptBoilerplate("2021.3.4") + """ + buildFile.writeText(buildScriptBoilerplate() + """ execute { - module.set("NewSolution") - className.set("NewSolution.myModel.MyClass") - method.set("projectAndArgs") + module = "NewSolution" + className = "NewSolution.myModel.MyClass" + method = "projectAndArgs" - methodArguments.set(listOf("arg1", "arg2")) + methodArguments = listOf("arg1", "arg2") } """.trimIndent()) diff --git a/src/test/kotlin/test/migration/RemigrateTest.kt b/mps-gradle-plugin/src/test/kotlin/test/migration/RemigrateTest.kt similarity index 84% rename from src/test/kotlin/test/migration/RemigrateTest.kt rename to mps-gradle-plugin/src/test/kotlin/test/migration/RemigrateTest.kt index 2aea8a60..8c13b907 100644 --- a/src/test/kotlin/test/migration/RemigrateTest.kt +++ b/mps-gradle-plugin/src/test/kotlin/test/migration/RemigrateTest.kt @@ -7,6 +7,8 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder +import support.FOOJAY_RESOLVER_CONVENTION_VERSION +import support.MPS_VERSION import support.extractTestProject import java.io.File @@ -23,6 +25,9 @@ class RemigrateTest { settingsFile = testProjectDir.newFile("settings.gradle.kts") settingsFile.writeText( """ + plugins { + id("org.gradle.toolchains.foojay-resolver-convention") version ("$FOOJAY_RESOLVER_CONVENTION_VERSION") + } rootProject.name = "hello-world" """.trimIndent() ) @@ -51,7 +56,7 @@ class RemigrateTest { val mps = configurations.create("mps") dependencies { - mps("com.jetbrains:mps:2021.3.2") + mps("com.jetbrains:mps:$MPS_VERSION") } val resolveMps by tasks.registering(Sync::class) { @@ -60,8 +65,8 @@ class RemigrateTest { } val remigrate by tasks.registering(Remigrate::class) { - projectDirectories.from("$mpsTestPrjLocation") - mpsHome.set(layout.dir(resolveMps.map { it.destinationDir })) + projectLocations.from("$mpsTestPrjLocation") + mpsHome = layout.dir(resolveMps.map { it.destinationDir }) excludedModuleMigrations.add(ExcludedModuleMigration("foo", 0)) excludeModuleMigration("bar", 1) diff --git a/mps-gradle-plugin/src/test/kotlin/test/migration/RunMigrationsTest.kt b/mps-gradle-plugin/src/test/kotlin/test/migration/RunMigrationsTest.kt new file mode 100644 index 00000000..f1b98601 --- /dev/null +++ b/mps-gradle-plugin/src/test/kotlin/test/migration/RunMigrationsTest.kt @@ -0,0 +1,85 @@ +package test.migration + +import org.gradle.testkit.runner.GradleRunner +import org.gradle.testkit.runner.TaskOutcome +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder +import support.FOOJAY_RESOLVER_CONVENTION_VERSION +import support.JAVA_VERSION_FOR_MPS +import support.MPS_VERSION +import support.extractTestProject +import java.io.File + +class RunMigrationsTest { + @Rule + @JvmField + val testProjectDir: TemporaryFolder = TemporaryFolder() + private lateinit var settingsFile: File + private lateinit var buildFile: File + private lateinit var mpsTestPrjLocation: File + + @Before + fun setup() { + settingsFile = testProjectDir.newFile("settings.gradle.kts") + settingsFile.writeText( + """ + plugins { + id("org.gradle.toolchains.foojay-resolver-convention") version ("$FOOJAY_RESOLVER_CONVENTION_VERSION") + } + rootProject.name = "hello-world" + """.trimIndent() + ) + buildFile = testProjectDir.newFile("build.gradle.kts") + mpsTestPrjLocation = testProjectDir.newFolder("mps-prj") + extractTestProject("test-project", mpsTestPrjLocation) + } + + @Test + fun `MpsMigrate task works`() { + buildFile.writeText( + """ + import de.itemis.mps.gradle.tasks.MpsMigrate + + plugins { + id("de.itemis.mps.gradle.common") + `jvm-toolchains` + } + + repositories { + mavenCentral() + maven("https://artifacts.itemis.cloud/repository/maven-mps") + } + + val mps = configurations.create("mps") + dependencies { + mps("com.jetbrains:mps:$MPS_VERSION") + } + + val resolveMps by tasks.registering(Sync::class) { + from(Callable { zipTree(mps.singleFile) }) + into(layout.buildDirectory.dir("mps")) + } + + val migrate by tasks.registering(MpsMigrate::class) { + projectLocations.from("$mpsTestPrjLocation") + mpsHome = layout.dir(resolveMps.map { it.destinationDir }) + javaLauncher = javaToolchains.launcherFor { + languageVersion = JavaLanguageVersion.of(${JAVA_VERSION_FOR_MPS}) + vendor = JvmVendorSpec.JETBRAINS + } + } + """.trimIndent() + ) + + val result = gradleRunner().withArguments("migrate").build() + + assertEquals(TaskOutcome.SUCCESS, result.task(":migrate")?.outcome) + } + + private fun gradleRunner(): GradleRunner = GradleRunner.create() + .withProjectDir(testProjectDir.root) + .withPluginClasspath() +} diff --git a/src/test/kotlin/test/modelchecking/MpsCheckTaskTest.kt b/mps-gradle-plugin/src/test/kotlin/test/modelchecking/MpsCheckTaskTest.kt similarity index 80% rename from src/test/kotlin/test/modelchecking/MpsCheckTaskTest.kt rename to mps-gradle-plugin/src/test/kotlin/test/modelchecking/MpsCheckTaskTest.kt index 0fe95d00..13856e0e 100644 --- a/src/test/kotlin/test/modelchecking/MpsCheckTaskTest.kt +++ b/mps-gradle-plugin/src/test/kotlin/test/modelchecking/MpsCheckTaskTest.kt @@ -9,6 +9,8 @@ import org.junit.Assert import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder +import support.FOOJAY_RESOLVER_CONVENTION_VERSION +import support.MPS_VERSION import support.extractTestProject class MpsCheckTaskTest { @@ -18,11 +20,11 @@ class MpsCheckTaskTest { private fun settingsScriptBoilerplate() = """ plugins { - id("org.gradle.toolchains.foojay-resolver-convention") version ("0.7.0") + id("org.gradle.toolchains.foojay-resolver-convention") version ("$FOOJAY_RESOLVER_CONVENTION_VERSION") } """.trimIndent() - private fun buildScriptBoilerplate(mpsVersion: String) = """ + private fun buildScriptBoilerplate() = """ import de.itemis.mps.gradle.tasks.MpsCheck plugins { @@ -37,7 +39,7 @@ class MpsCheckTaskTest { val mps = configurations.create("mps") dependencies { - mps("com.jetbrains:mps:$mpsVersion") + mps("com.jetbrains:mps:$MPS_VERSION") } val resolveMps by tasks.registering(Sync::class) { @@ -55,10 +57,10 @@ class MpsCheckTaskTest { extractTestProject("test-project", mpsTestPrjLocation) settingsFile.writeText(settingsScriptBoilerplate()) - buildFile.writeText(buildScriptBoilerplate("2021.3.3") + """ + buildFile.writeText(buildScriptBoilerplate() + """ val checkProject by tasks.registering(MpsCheck::class) { - mpsHome.set(layout.dir(resolveMps.map { it.destinationDir })) - junitFile.set(layout.buildDirectory.file("output.xml")) + mpsHome = layout.dir(resolveMps.map { it.destinationDir }) + junitFile = layout.buildDirectory.file("output.xml") } """.trimIndent()) @@ -77,12 +79,12 @@ class MpsCheckTaskTest { extractTestProject("test-project", mpsTestPrjLocation) settingsFile.writeText(settingsScriptBoilerplate()) - buildFile.writeText(buildScriptBoilerplate("2021.3.3") + """ + buildFile.writeText(buildScriptBoilerplate() + """ val checkProject by tasks.registering(MpsCheck::class) { - mpsHome.set(layout.dir(resolveMps.map { it.destinationDir })) - projectLocation.set(file("mps-prj")) - junitFile.set(layout.buildDirectory.file("output.xml")) - pluginRoots.add(layout.dir(resolveMps.map { File(it.destinationDir, "plugins/mps-console") })) + mpsHome = layout.dir(resolveMps.map { it.destinationDir }) + projectLocation = file("mps-prj") + junitFile = layout.buildDirectory.file("output.xml") + pluginRoots.from(resolveMps.map { File(it.destinationDir, "plugins/mps-console") }) } """.trimIndent()) @@ -102,10 +104,10 @@ class MpsCheckTaskTest { extractTestProject("test-project", mpsTestPrjLocation) settingsFile.writeText(settingsScriptBoilerplate()) - buildFile.writeText(buildScriptBoilerplate("2021.3.3") + """ + buildFile.writeText(buildScriptBoilerplate() + """ val checkProject by tasks.registering(MpsCheck::class) { - mpsHome.set(layout.dir(resolveMps.map { it.destinationDir })) - projectLocation.set(file("mps-prj")) + mpsHome = layout.dir(resolveMps.map { it.destinationDir }) + projectLocation = file("mps-prj") } """.trimIndent()) diff --git a/src/test/kotlin/test/modelgeneration/MpsGenerateTaskTest.kt b/mps-gradle-plugin/src/test/kotlin/test/modelgeneration/MpsGenerateTaskTest.kt similarity index 85% rename from src/test/kotlin/test/modelgeneration/MpsGenerateTaskTest.kt rename to mps-gradle-plugin/src/test/kotlin/test/modelgeneration/MpsGenerateTaskTest.kt index df461362..9e05d032 100644 --- a/src/test/kotlin/test/modelgeneration/MpsGenerateTaskTest.kt +++ b/mps-gradle-plugin/src/test/kotlin/test/modelgeneration/MpsGenerateTaskTest.kt @@ -9,6 +9,8 @@ import org.junit.Assert import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder +import support.FOOJAY_RESOLVER_CONVENTION_VERSION +import support.MPS_VERSION import support.extractTestProject class MpsGenerateTaskTest { @@ -18,11 +20,11 @@ class MpsGenerateTaskTest { private fun settingsScriptBoilerplate() = """ plugins { - id("org.gradle.toolchains.foojay-resolver-convention") version ("0.7.0") + id("org.gradle.toolchains.foojay-resolver-convention") version ("$FOOJAY_RESOLVER_CONVENTION_VERSION") } """.trimIndent() - private fun buildScriptBoilerplate(mpsVersion: String) = """ + private fun buildScriptBoilerplate() = """ import de.itemis.mps.gradle.tasks.MpsGenerate plugins { @@ -37,7 +39,7 @@ class MpsGenerateTaskTest { val mps = configurations.create("mps") dependencies { - mps("com.jetbrains:mps:$mpsVersion") + mps("com.jetbrains:mps:$MPS_VERSION") } val resolveMps by tasks.registering(Sync::class) { @@ -55,9 +57,9 @@ class MpsGenerateTaskTest { extractTestProject("test-project", mpsTestPrjLocation) settingsFile.writeText(settingsScriptBoilerplate()) - buildFile.writeText(buildScriptBoilerplate("2021.3.3") + """ + buildFile.writeText(buildScriptBoilerplate() + """ val generateProject by tasks.registering(MpsGenerate::class) { - mpsHome.set(layout.dir(resolveMps.map { it.destinationDir })) + mpsHome = layout.dir(resolveMps.map { it.destinationDir }) } """.trimIndent()) @@ -76,10 +78,10 @@ class MpsGenerateTaskTest { extractTestProject("test-project", mpsTestPrjLocation) settingsFile.writeText(settingsScriptBoilerplate()) - buildFile.writeText(buildScriptBoilerplate("2021.3.3") + """ + buildFile.writeText(buildScriptBoilerplate() + """ val generateProject by tasks.registering(MpsGenerate::class) { - mpsHome.set(layout.dir(resolveMps.map { it.destinationDir })) - projectLocation.set(file("mps-prj")) + mpsHome = layout.dir(resolveMps.map { it.destinationDir }) + projectLocation = file("mps-prj") } """.trimIndent()) diff --git a/src/test/kotlin/test/others/GenerateProjectLibrariesXmlTest.kt b/mps-gradle-plugin/src/test/kotlin/test/others/GenerateProjectLibrariesXmlTest.kt similarity index 100% rename from src/test/kotlin/test/others/GenerateProjectLibrariesXmlTest.kt rename to mps-gradle-plugin/src/test/kotlin/test/others/GenerateProjectLibrariesXmlTest.kt diff --git a/src/test/kotlin/test/others/PluginIdsTest.kt b/mps-gradle-plugin/src/test/kotlin/test/others/PluginIdsTest.kt similarity index 100% rename from src/test/kotlin/test/others/PluginIdsTest.kt rename to mps-gradle-plugin/src/test/kotlin/test/others/PluginIdsTest.kt diff --git a/src/test/resources/test-project-with-errors/.mps/.name b/mps-gradle-plugin/src/test/resources/test-project-with-errors/.mps/.name similarity index 100% rename from src/test/resources/test-project-with-errors/.mps/.name rename to mps-gradle-plugin/src/test/resources/test-project-with-errors/.mps/.name diff --git a/src/test/resources/test-project-with-errors/.mps/encodings.xml b/mps-gradle-plugin/src/test/resources/test-project-with-errors/.mps/encodings.xml similarity index 100% rename from src/test/resources/test-project-with-errors/.mps/encodings.xml rename to mps-gradle-plugin/src/test/resources/test-project-with-errors/.mps/encodings.xml diff --git a/src/test/resources/test-project-with-errors/.mps/migration.xml b/mps-gradle-plugin/src/test/resources/test-project-with-errors/.mps/migration.xml similarity index 100% rename from src/test/resources/test-project-with-errors/.mps/migration.xml rename to mps-gradle-plugin/src/test/resources/test-project-with-errors/.mps/migration.xml diff --git a/src/test/resources/test-project-with-errors/.mps/misc.xml b/mps-gradle-plugin/src/test/resources/test-project-with-errors/.mps/misc.xml similarity index 100% rename from src/test/resources/test-project-with-errors/.mps/misc.xml rename to mps-gradle-plugin/src/test/resources/test-project-with-errors/.mps/misc.xml diff --git a/src/test/resources/test-project-with-errors/.mps/modules.xml b/mps-gradle-plugin/src/test/resources/test-project-with-errors/.mps/modules.xml similarity index 100% rename from src/test/resources/test-project-with-errors/.mps/modules.xml rename to mps-gradle-plugin/src/test/resources/test-project-with-errors/.mps/modules.xml diff --git a/src/test/resources/test-project-with-errors/.mps/vcs.xml b/mps-gradle-plugin/src/test/resources/test-project-with-errors/.mps/vcs.xml similarity index 100% rename from src/test/resources/test-project-with-errors/.mps/vcs.xml rename to mps-gradle-plugin/src/test/resources/test-project-with-errors/.mps/vcs.xml diff --git a/src/test/resources/test-project-with-errors/solutions/my.solution.with.errors/models/java.mps b/mps-gradle-plugin/src/test/resources/test-project-with-errors/solutions/my.solution.with.errors/models/java.mps similarity index 100% rename from src/test/resources/test-project-with-errors/solutions/my.solution.with.errors/models/java.mps rename to mps-gradle-plugin/src/test/resources/test-project-with-errors/solutions/my.solution.with.errors/models/java.mps diff --git a/src/test/resources/test-project-with-errors/solutions/my.solution.with.errors/my.solution.with.errors.msd b/mps-gradle-plugin/src/test/resources/test-project-with-errors/solutions/my.solution.with.errors/my.solution.with.errors.msd similarity index 100% rename from src/test/resources/test-project-with-errors/solutions/my.solution.with.errors/my.solution.with.errors.msd rename to mps-gradle-plugin/src/test/resources/test-project-with-errors/solutions/my.solution.with.errors/my.solution.with.errors.msd diff --git a/src/test/resources/test-project-with-errors/solutions/my.solution/models/java.mps b/mps-gradle-plugin/src/test/resources/test-project-with-errors/solutions/my.solution/models/java.mps similarity index 100% rename from src/test/resources/test-project-with-errors/solutions/my.solution/models/java.mps rename to mps-gradle-plugin/src/test/resources/test-project-with-errors/solutions/my.solution/models/java.mps diff --git a/src/test/resources/test-project-with-errors/solutions/my.solution/my.solution.msd b/mps-gradle-plugin/src/test/resources/test-project-with-errors/solutions/my.solution/my.solution.msd similarity index 100% rename from src/test/resources/test-project-with-errors/solutions/my.solution/my.solution.msd rename to mps-gradle-plugin/src/test/resources/test-project-with-errors/solutions/my.solution/my.solution.msd diff --git a/src/test/resources/test-project/.mps/.gitignore b/mps-gradle-plugin/src/test/resources/test-project/.mps/.gitignore similarity index 100% rename from src/test/resources/test-project/.mps/.gitignore rename to mps-gradle-plugin/src/test/resources/test-project/.mps/.gitignore diff --git a/mps-gradle-plugin/src/test/resources/test-project/.mps/migration.xml b/mps-gradle-plugin/src/test/resources/test-project/.mps/migration.xml new file mode 100644 index 00000000..31a52fb7 --- /dev/null +++ b/mps-gradle-plugin/src/test/resources/test-project/.mps/migration.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/test-project/.mps/misc.xml b/mps-gradle-plugin/src/test/resources/test-project/.mps/misc.xml similarity index 100% rename from src/test/resources/test-project/.mps/misc.xml rename to mps-gradle-plugin/src/test/resources/test-project/.mps/misc.xml diff --git a/src/test/resources/test-project/.mps/modules.xml b/mps-gradle-plugin/src/test/resources/test-project/.mps/modules.xml similarity index 100% rename from src/test/resources/test-project/.mps/modules.xml rename to mps-gradle-plugin/src/test/resources/test-project/.mps/modules.xml diff --git a/src/test/resources/test-project/.mps/vcs.xml b/mps-gradle-plugin/src/test/resources/test-project/.mps/vcs.xml similarity index 74% rename from src/test/resources/test-project/.mps/vcs.xml rename to mps-gradle-plugin/src/test/resources/test-project/.mps/vcs.xml index 4fce1d86..c68f248e 100644 --- a/src/test/resources/test-project/.mps/vcs.xml +++ b/mps-gradle-plugin/src/test/resources/test-project/.mps/vcs.xml @@ -1,6 +1,7 @@ + \ No newline at end of file diff --git a/src/test/resources/test-project/solutions/NewSolution/NewSolution.msd b/mps-gradle-plugin/src/test/resources/test-project/solutions/NewSolution/NewSolution.msd similarity index 91% rename from src/test/resources/test-project/solutions/NewSolution/NewSolution.msd rename to mps-gradle-plugin/src/test/resources/test-project/solutions/NewSolution/NewSolution.msd index 188f59c1..78012f19 100644 --- a/src/test/resources/test-project/solutions/NewSolution/NewSolution.msd +++ b/mps-gradle-plugin/src/test/resources/test-project/solutions/NewSolution/NewSolution.msd @@ -1,31 +1,30 @@ - + - + - 6354ebe7-c22a-4a0f-ac54-50b52ab9b065(JDK) 6ed54515-acc8-4d1e-a16c-9fd6cfe951ea(MPS.Core) - + - + - + diff --git a/src/test/resources/test-project/solutions/NewSolution/models/NewSolution.myModel.mps b/mps-gradle-plugin/src/test/resources/test-project/solutions/NewSolution/models/NewSolution.myModel.mps similarity index 91% rename from src/test/resources/test-project/solutions/NewSolution/models/NewSolution.myModel.mps rename to mps-gradle-plugin/src/test/resources/test-project/solutions/NewSolution/models/NewSolution.myModel.mps index 6d4064b9..b3d1980d 100644 --- a/src/test/resources/test-project/solutions/NewSolution/models/NewSolution.myModel.mps +++ b/mps-gradle-plugin/src/test/resources/test-project/solutions/NewSolution/models/NewSolution.myModel.mps @@ -51,7 +51,7 @@ - + @@ -61,7 +61,7 @@ - + @@ -73,7 +73,7 @@ - + @@ -123,8 +123,13 @@ - - + + + + + + + @@ -143,8 +148,13 @@ - - + + + + + + + diff --git a/renovate.json b/renovate.json new file mode 100644 index 00000000..12c95d57 --- /dev/null +++ b/renovate.json @@ -0,0 +1,37 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended" + ], + "forkProcessing": "enabled", + "ignorePaths": [], + "customManagers": [ + { + "customType": "regex", + "description": "Update the FOOJAY_RESOLVER_CONVENTION_VERSION constant used by tests.", + "managerFilePatterns": [ + "/^mps-gradle-plugin/src/test/kotlin/support/TestVersions\\.kt$/" + ], + "matchStrings": [ + "const val FOOJAY_RESOLVER_CONVENTION_VERSION\\s*=\\s*\"(?[^\"]+)\"" + ], + "depNameTemplate": "org.gradle.toolchains.foojay-resolver-convention", + "packageNameTemplate": "org.gradle.toolchains.foojay-resolver-convention:org.gradle.toolchains.foojay-resolver-convention.gradle.plugin", + "datasourceTemplate": "maven", + "registryUrlTemplate": "https://plugins.gradle.org/m2/" + }, + { + "customType": "regex", + "description": "Update the MPS_VERSION constant used by tests to spin up an MPS instance.", + "managerFilePatterns": [ + "/^mps-gradle-plugin/src/test/kotlin/support/TestVersions\\.kt$/" + ], + "matchStrings": [ + "const val MPS_VERSION\\s*=\\s*\"(?[^\"]+)\"" + ], + "depNameTemplate": "com.jetbrains:mps", + "datasourceTemplate": "maven", + "registryUrlTemplate": "https://artifacts.itemis.cloud/repository/maven-mps" + } + ] +} diff --git a/settings.gradle.kts b/settings.gradle.kts index a400c38f..360ec4be 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -3,9 +3,11 @@ pluginManagement { } plugins { - id("org.gradle.toolchains.foojay-resolver-convention") version ("0.8.0") + id("org.gradle.toolchains.foojay-resolver-convention") version ("1.0.0") } rootProject.name = "mps-gradle-plugin" includeBuild("git-based-versioning") +includeBuild("mps-gradle-plugin-api") +includeBuild("mps-gradle-plugin") diff --git a/src/main/groovy/de/itemis/mps/gradle/BundleMacosJdk.groovy b/src/main/groovy/de/itemis/mps/gradle/BundleMacosJdk.groovy deleted file mode 100644 index 1553c55a..00000000 --- a/src/main/groovy/de/itemis/mps/gradle/BundleMacosJdk.groovy +++ /dev/null @@ -1,76 +0,0 @@ -package de.itemis.mps.gradle - -import org.gradle.api.DefaultTask -import org.gradle.api.GradleException -import org.gradle.api.artifacts.Dependency -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.InputFile -import org.gradle.api.tasks.Optional -import org.gradle.api.tasks.OutputFile -import org.gradle.api.tasks.TaskAction - -class BundleMacosJdk extends DefaultTask { - @InputFile - File rcpArtifact - - @Optional @Input - String jdkDirname = "jre" - - @InputFile - File jdk - - @OutputFile - File outputFile - - def setRcpArtifact(Object file) { - this.rcpArtifact = project.file(file) - } - - def setJdkDirname(String dirname) { - this.jdkDirname = dirname - } - - def setJdk(Object file) { - this.jdk = project.file(file) - } - - /** - * Sets the {@link #jdk} property from a dependency, given as either a {@link Dependency} object or in dependency - * notation. - */ - def setJdkDependency(Object jdkDependency) { - Dependency dep = project.dependencies.create(jdkDependency) - def files = project.configurations.detachedConfiguration(dep).resolve() - if (files.size() != 1) { - throw new GradleException( - "Expected a single file for jdkDependency '$jdkDependency', got ${files.size()} files") - } - this.jdk = files.first() - } - - def setOutputFile(Object file) { - this.outputFile = project.file(file) - } - - @TaskAction - def build() { - project.logger.lifecycle("The jdkDirname: ${jdkDirname}") - File scriptsDir = File.createTempDir() - File tmpDir = File.createTempDir() - try { - String scriptName = 'bundle_macos_jdk.sh' - BundledScripts.extractScriptsToDir(scriptsDir, scriptName) - project.exec { - executable new File(scriptsDir, scriptName) - args rcpArtifact, tmpDir, jdkDirname, jdk, outputFile - workingDir scriptsDir - } - } finally { - // Do not use File.deleteDir() because it follows symlinks! - // (e.g. the symlink to /Applications inside tmpDir) - project.exec { - commandLine 'rm', '-rf', scriptsDir, tmpDir - } - } - } -} diff --git a/src/main/groovy/de/itemis/mps/gradle/BundledScripts.groovy b/src/main/groovy/de/itemis/mps/gradle/BundledScripts.groovy deleted file mode 100644 index 6ca62f1e..00000000 --- a/src/main/groovy/de/itemis/mps/gradle/BundledScripts.groovy +++ /dev/null @@ -1,26 +0,0 @@ -package de.itemis.mps.gradle - -import org.gradle.api.GradleException - -import java.nio.file.Files -import java.nio.file.attribute.PosixFilePermissions - -class BundledScripts { - static void extractScriptsToDir(File dir, String... scriptNames) { - def rwxPermissions = PosixFilePermissions.fromString("rwx------") - - for (name in scriptNames) { - File file = new File(dir, name) - if (!file.parentFile.isDirectory() && ! file.parentFile.mkdirs()) { - throw new GradleException("Could not create directory " + file.parentFile) - } - InputStream resourceStream = BundledScripts.class.getResourceAsStream(name) - if (resourceStream == null) { - throw new IllegalArgumentException("Resource ${name} was not found") - } - - resourceStream.withStream { is -> file.newOutputStream().withStream { os -> os << is } } - Files.setPosixFilePermissions(file.toPath(), rwxPermissions) - } - } -} diff --git a/src/main/groovy/de/itemis/mps/gradle/CreateDmg.groovy b/src/main/groovy/de/itemis/mps/gradle/CreateDmg.groovy deleted file mode 100644 index b0dc1957..00000000 --- a/src/main/groovy/de/itemis/mps/gradle/CreateDmg.groovy +++ /dev/null @@ -1,106 +0,0 @@ -package de.itemis.mps.gradle - -import org.gradle.api.DefaultTask -import org.gradle.api.GradleException -import org.gradle.api.artifacts.Dependency -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.InputFile -import org.gradle.api.tasks.OutputFile -import org.gradle.api.tasks.TaskAction -import org.gradle.api.tasks.Optional - -class CreateDmg extends DefaultTask { - @InputFile - File rcpArtifact - - @InputFile - File backgroundImage - - @InputFile - File jdk - - @OutputFile - File dmgFile - - @Optional @Input - String signKeyChainPassword - - @Optional @Input - String signIdentity - - @InputFile @Optional - File signKeyChain - - def setSignKeyChain(Object file) { - this.signKeyChain = project.file(file) - } - - def setRcpArtifact(Object file) { - this.rcpArtifact = project.file(file) - } - - def setBackgroundImage(Object file) { - this.backgroundImage = project.file(file) - } - - def setJdk(Object file) { - this.jdk = project.file(file) - } - - /** - * Sets the {@link #jdk} property from a dependency, given as either a {@link Dependency} object or in dependency - * notation. - */ - def setJdkDependency(Object jdkDependency) { - Dependency dep = project.dependencies.create(jdkDependency) - def files = project.configurations.detachedConfiguration(dep).resolve() - if (files.size() != 1) { - throw new GradleException( - "Expected a single file for jdkDependency '$jdkDependency', got ${files.size()} files") - } - this.jdk = files.first() - } - - def setDmgFile(Object file) { - this.dmgFile = project.file(file) - if (dmgFile != null && !dmgFile.name.endsWith(".dmg")) { - throw new GradleException("Value of dmgFile must end with .dmg but was $dmgFile") - } - } - - @TaskAction - def build() { - String[] scripts = ['mpssign.sh', 'mpsdmg.sh', 'mpsdmg.pl', - 'Mac/Finder/DSStore/BuddyAllocator.pm', 'Mac/Finder/DSStore.pm'] - File scriptsDir = File.createTempDir() - File dmgDir = File.createTempDir() - def signingInfo = [signKeyChainPassword, signKeyChain, signIdentity] - try { - BundledScripts.extractScriptsToDir(scriptsDir, scripts) - project.exec { - executable new File(scriptsDir, 'mpssign.sh') - - if(signingInfo.every {it != null}) { - args '-r', rcpArtifact, '-o', dmgDir, '-j', jdk, '-p', signKeyChainPassword, '-k', signKeyChain, '-i', signIdentity - }else if (signingInfo.every {it == null}){ - args '-r', rcpArtifact, '-o', dmgDir, '-j', jdk - }else{ - throw new IllegalArgumentException("Not all signing paramters set. signKeyChain: ${getSigningInfo[1]}, signIdentity: ${getSigningInfo[2]} and signKeyChainPassword needs to be set. ") - } - workingDir scriptsDir - } - project.exec { - executable new File(scriptsDir, 'mpsdmg.sh') - args dmgDir, dmgFile, backgroundImage - workingDir scriptsDir - } - } finally { - // Do not use File.deleteDir() because it follows symlinks! - // (e.g. the symlink to /Applications inside dmgDir) - project.exec { - commandLine 'rm', '-rf', scriptsDir, dmgDir - } - } - } - -} diff --git a/src/main/groovy/de/itemis/mps/gradle/GetMpsInBrowser.groovy b/src/main/groovy/de/itemis/mps/gradle/GetMpsInBrowser.groovy deleted file mode 100644 index 252f91b9..00000000 --- a/src/main/groovy/de/itemis/mps/gradle/GetMpsInBrowser.groovy +++ /dev/null @@ -1,54 +0,0 @@ -package de.itemis.mps.gradle - -import org.gradle.api.DefaultTask -import org.gradle.api.GradleException -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.TaskAction -import org.apache.tools.ant.taskdefs.condition.Os - -import java.awt.Desktop - -class GetMpsInBrowser extends DefaultTask { - - @Input - String version - - def setVersion(String version) { - this.version = version - } - - private String getMajorPart() { - def split = version.split("\\.") - if (split.length == 2) { - return version - } - - return split.take(2).join(".") - } - - private URI getDownloadUrl() { - def major = getMajorPart() - - if (Os.isFamily(Os.FAMILY_WINDOWS)) { - return new URI("https://download.jetbrains.com/mps/${major}/MPS-${version}.exe") - } else if (Os.isFamily(Os.FAMILY_MAC)) { - return new URI("https://download.jetbrains.com/mps/${major}/MPS-${version}-macos-jdk-bundled.dmg") - } else if (Os.isFamily(Os.FAMILY_UNIX)) { - return new URI("https://download.jetbrains.com/mps/${major}/MPS-${version}.tar.gz") - } else { - print "Warning: could not determine OS downloading generic distribution" - return new URI("http://download.jetbrains.com/mps/${major}/MPS-${version}.zip") - } - - } - - @TaskAction - def build() { - - if (Desktop.isDesktopSupported()) { - Desktop.getDesktop().browse(getDownloadUrl()) - } else { - throw new GradleException("this task is not supported in headless mode") - } - } -} diff --git a/src/main/kotlin/de/itemis/mps/gradle/Common.kt b/src/main/kotlin/de/itemis/mps/gradle/Common.kt deleted file mode 100644 index 94783cea..00000000 --- a/src/main/kotlin/de/itemis/mps/gradle/Common.kt +++ /dev/null @@ -1,114 +0,0 @@ -package de.itemis.mps.gradle - -import org.apache.log4j.Logger -import org.gradle.api.GradleException -import org.gradle.api.JavaVersion -import org.gradle.api.artifacts.Configuration -import org.gradle.api.model.ObjectFactory -import org.gradle.api.provider.ListProperty -import org.gradle.kotlin.dsl.property -import org.gradle.process.CommandLineArgumentProvider -import java.io.File -import javax.inject.Inject - -private val logger = Logger.getLogger("de.itemis.mps.gradle.common") - -const val MPS_SUPPORT_MSG = ErrorMessages.MPS_VERSION_NOT_SUPPORTED - -const val MPS_BUILD_BACKENDS_VERSION = "[1.15,2.0)" // 1.15 required for --plugin-root support. - -data class Plugin( - var id: String, - var path: String -) - -data class Macro( - var name: String, - var value: String -) - -enum class EnvironmentKind { MPS, IDEA } - -open class BasePluginExtensions @Inject constructor(objectFactory: ObjectFactory) { - var mpsConfig: Configuration? = null - var mpsLocation: File? = null - var mpsVersion: String? = null - - /** - * The plugins to load. Backed by [pluginsProperty] which should be used instead of this property. - */ - @Deprecated("Use pluginsProperty") - var plugins: List - get() = pluginsProperty.get() - set(value) { pluginsProperty.value(value) } - - /** - * The plugins to load. - */ - val pluginsProperty: ListProperty = objectFactory.listProperty(Plugin::class.java) - - var pluginLocation: File? = null - var macros: List = emptyList() - var projectLocation: File? = null - var debug = false - var javaExec: File? = null - var backendConfig: Configuration? = null - - /** - * The environment to set up, IDEA or MPS. Default is IDEA for backwards compatibility reasons. - */ - val environmentKind = objectFactory.property(EnvironmentKind::class).convention(EnvironmentKind.IDEA) - - /** - * Maximum heap size, passed as the argument to the `-Xmx` JVM option. Example: `4G`, `512m`. - */ - var maxHeap: String? = null -} - -fun validateDefaultJvm(){ - if (JavaVersion.current() < JavaVersion.VERSION_11) logger.error("MPS requires at least Java 11 but current JVM uses ${JavaVersion.current()}, starting MPS will most probably fail!") -} - -fun argsFromBaseExtension(extensions: BasePluginExtensions): CommandLineArgumentProvider = - CommandLineArgumentProvider { - val result = mutableListOf() - - if (extensions.pluginLocation != null) { - result.add("--plugin-location=${extensions.pluginLocation!!.absolutePath}") - } - - val projectLocation = extensions.projectLocation ?: throw GradleException("No project path set") - result.add("--project=${projectLocation.absolutePath}") - - extensions.pluginsProperty.get().mapTo(result) { "--plugin=${it.id}::${it.path}" } - extensions.macros.mapTo(result) { "--macro=${it.name}::${it.value}" } - - // --environment is supported by backend 1.2 and above - result.add("--environment=${extensions.environmentKind.get().name}") - - result - } - -@Deprecated("Use getMPSVersion(extensionName)", replaceWith = ReplaceWith("getMPSVersion(this.javaClass.name)")) -fun BasePluginExtensions.getMPSVersion(): String = getMPSVersion(this.javaClass.name) - -/** - * [extensionName]: extension name, for diagnostics. - */ -fun BasePluginExtensions.getMPSVersion(extensionName: String): String { - // If the user supplies explicit mpsVersion, we use it. - if (mpsVersion != null) return mpsVersion!! - - val mpsConfig = mpsConfig - if (mpsConfig != null) { - // If the user supplies a configuration, we use it to detect MPS version. - return mpsConfig.resolvedConfiguration.firstLevelModuleDependencies - .find { it.moduleGroup == "com.jetbrains" && it.moduleName == "mps" } - ?.moduleVersion - ?: throw GradleException(ErrorMessages.couldNotDetermineMpsVersionFromConfiguration(mpsConfig) - ) - } - - // Otherwise, the version has to be provided explicitly. - throw GradleException(ErrorMessages.mustSetVersionWhenNoMpsConfiguration(extensionName)) -} diff --git a/src/main/kotlin/de/itemis/mps/gradle/ErrorMessages.kt b/src/main/kotlin/de/itemis/mps/gradle/ErrorMessages.kt deleted file mode 100644 index 15142fbf..00000000 --- a/src/main/kotlin/de/itemis/mps/gradle/ErrorMessages.kt +++ /dev/null @@ -1,18 +0,0 @@ -package de.itemis.mps.gradle - -import org.gradle.api.artifacts.Configuration -import java.io.File - -internal object ErrorMessages { - 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." - - internal fun noMpsProjectIn(dir: File): String = "Directory does not contain an MPS project: $dir" - - internal fun couldNotDetermineMpsVersionFromConfiguration(mpsConfig: Configuration) = - "Could not determine MPS version from configuration ${mpsConfig.name} (configuration must contain com.jetbrains:mps dependency)." - - internal fun mustSetConfigOrLocation(extensionName: String) = "Either mpsConfig or mpsLocation needs to specified for extension $extensionName." - internal fun mustSetVersionWhenNoMpsConfiguration(extensionName: String) = - "Could not determine MPS version because mpsConfiguration was not specified. Set mpsVersion of $extensionName" + - " extension explicitly." -} diff --git a/src/main/kotlin/de/itemis/mps/gradle/RunAntScript.kt b/src/main/kotlin/de/itemis/mps/gradle/RunAntScript.kt deleted file mode 100644 index 25815c3a..00000000 --- a/src/main/kotlin/de/itemis/mps/gradle/RunAntScript.kt +++ /dev/null @@ -1,118 +0,0 @@ -package de.itemis.mps.gradle; - -import org.gradle.api.DefaultTask -import org.gradle.api.Project -import org.gradle.api.file.FileCollection -import org.gradle.api.logging.LogLevel -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.InputFiles -import org.gradle.api.tasks.Optional -import org.gradle.api.tasks.TaskAction -import org.gradle.process.ExecOperations -import java.io.File -import javax.inject.Inject - -abstract class RunAntScript : DefaultTask() { - @Input - lateinit var script: Any - @Input - var targets: List = emptyList() - @Optional @InputFiles - var scriptClasspath: FileCollection? = null - @Input - var scriptArgs: List = emptyList() - @Input - var includeDefaultArgs = true - @Input - var includeDefaultClasspath = true - @Optional @Input - var executable: Any? = null - @get:Inject - protected abstract val execOperations: ExecOperations - - /** - * Whether to build incrementally. - * - * Possible values: - * * `true` - perform an incremental build. If the [targets] list includes `clean` target it will be removed, and - * `-Dmps.generator.skipUnmodifiedModels=true` will be passed to Ant. - * * `false` - The backwards compatible default. The [targets] list will not be modified and no properties will be - * passed to Ant. Any outside customizations made to targets and Ant arguments are left intact so the build may - * in fact be incremental. - */ - @Input - var incremental: Boolean = false - - fun targets(vararg targets: String) { - this.targets = targets.toList() - } - - fun executable(executable: Any?) { - this.executable = executable - } - - @TaskAction - fun build() { - val allArgs = scriptArgs.toMutableList() - if (includeDefaultArgs) { - val defaultArgs = project.findProperty("itemis.mps.gradle.ant.defaultScriptArgs") as Collection<*>? - if (defaultArgs != null) { - allArgs += defaultArgs.map { it as String } - } - } - - if(logging.level != null && logging.level != LogLevel.LIFECYCLE && !allArgs.any { it.startsWith("-Dmps.ant.log=") }) { - allArgs += "-Dmps.ant.log=${logging.level.toString().lowercase()}" - } - - if (incremental) { - allArgs += "-Dmps.generator.skipUnmodifiedModels=true" - } - - val targets = if (incremental) { targets - "clean" } else { targets } - - project.runAnt(execOperations, executable, project.rootDir, allArgs + "-buildfile" + project.file(script).toString() + targets, - includeDefaultClasspath, scriptClasspath) - } -} - -abstract class BuildLanguages : RunAntScript() { - init { - targets = listOf("clean", "generate", "assemble") - } -} - - abstract class TestLanguages : RunAntScript() { - init { - targets = listOf("clean", "generate", "assemble", "check") - } -} - -internal fun Project.runAnt(execOperations: ExecOperations, executable: Any?, workingDir: File, args: List, - includeDefaultClasspath: Boolean = true, - scriptClasspath: Any? = null -) { - val effectiveExecutable = executable ?: project.findProperty("itemis.mps.gradle.ant.defaultJavaExecutable") - - execOperations.javaexec { - if (effectiveExecutable != null) { - executable(effectiveExecutable) - } - - mainClass.set("org.apache.tools.ant.launch.Launcher") - this@javaexec.workingDir = workingDir - - if (includeDefaultClasspath) { - val defaultClasspath = project.findProperty("itemis.mps.gradle.ant.defaultScriptClasspath") - if (defaultClasspath != null) { - classpath(defaultClasspath) - } - } - - if (scriptClasspath != null) { - classpath(scriptClasspath) - } - - args(args) - } -} diff --git a/src/main/kotlin/de/itemis/mps/gradle/downloadJBR/Plugin.kt b/src/main/kotlin/de/itemis/mps/gradle/downloadJBR/Plugin.kt deleted file mode 100644 index 957c6b61..00000000 --- a/src/main/kotlin/de/itemis/mps/gradle/downloadJBR/Plugin.kt +++ /dev/null @@ -1,137 +0,0 @@ -package de.itemis.mps.gradle.downloadJBR - -import org.apache.tools.ant.taskdefs.condition.Os -import org.gradle.api.GradleException -import org.gradle.api.Plugin -import org.gradle.api.Project -import org.gradle.api.file.DirectoryProperty -import org.gradle.api.model.ObjectFactory -import org.gradle.process.ExecOperations -import java.io.File -import javax.inject.Inject - -open class DownloadJbrConfiguration @Inject constructor(objects: ObjectFactory) { - lateinit var jbrVersion: String - var distributionType : String? = null - internal val downloadDirProperty: DirectoryProperty = objects.directoryProperty() - - var downloadDir: File? - get() = downloadDirProperty.get().asFile - set(value) { - downloadDirProperty.set(value) - } -} - -abstract class DownloadJbrProjectPlugin : Plugin { - - @get:Inject - protected abstract val execOperations: ExecOperations - - override fun apply(project: Project) { - project.run { - - val extension = extensions.create("downloadJbr", DownloadJbrConfiguration::class.java) - extension.downloadDirProperty.convention(layout.buildDirectory.dir("jbrDownload")) - - val configuration = configurations.detachedConfiguration() - configuration.dependencies.addLater(provider { project.dependencies.create(dependencyString(extension)) }) - - val extractJbr = tasks.register("extractJbr") { - inputs.files(configuration).skipWhenEmpty() - outputs.dir(extension.downloadDirProperty) - - doLast { - if (Os.isFamily(Os.FAMILY_UNIX)) { - // Use Unix utilities to properly deal with symlinks - delete(extension.downloadDirProperty) - val downloadDir = mkdir(extension.downloadDirProperty) - - execOperations.exec { - commandLine("tar", "-xzf", configuration.singleFile.absolutePath) - workingDir = downloadDir - } - - if (downloadDir.listFiles { _, name -> name.startsWith("jbr_") || name.startsWith("jbr-") }!!.any()) { - execOperations.exec { - commandLine("sh", "-c", "mv jbr* jbr") - workingDir = downloadDir - } - } - - execOperations.exec { - commandLine("chmod", "-R", "u+w", ".") - workingDir = downloadDir - } - } else { - // On Windows we don't worry about symlinks nor file modes. - sync { - from({ tarTree(configuration.singleFile) }) - into(extension.downloadDirProperty) - includeEmptyDirs = false - eachFile { - permissions { user { write = true } } - } - filesMatching("jbr_*/**") { - path = path.replace("jbr_(.*?)/(.*)".toRegex(), "jbr/$2") - } - } - } - } - } - - tasks.register("downloadJbr", DownloadJbrForPlatform::class.java) { - dependsOn(extractJbr) - group = "Build" - description = "Downloads the JetBrains Runtime for the current platform and extracts it." - - jbrDirProperty.set(extension.downloadDirProperty.dir( - if (Os.isFamily(Os.FAMILY_MAC)) "jbr/Contents/Home" - else "jbr" - )) - javaExecutableProperty.set(jbrDirProperty.file(if (Os.isFamily(Os.FAMILY_WINDOWS)) "bin/java.exe" else "bin/java")) - } - } - } - - private fun dependencyString(extension: DownloadJbrConfiguration): String { - val version = extension.jbrVersion - // from version 10 on the jbr distribution type is replaced with jbr_jcef - // jbr_jcef is the distribution used to start a normal desktop ide and should include everything - // required for running tests. While a little bit larger than jbr_nomod it should cause the least - // surprises when using it as a default. - // see https://github.com/mbeddr/build.publish.jdk/commit/10bbf7d177336179ca189fc8bb4c1262029c69da - val distributionType = - if (extension.distributionType != null) { - extension.distributionType - } else if (Regex("""11_0_[0-9][^0-9]""").find(version) != null) { - "jbr" - } else { - "jbr_jcef" - } - - val cpuArch = when(System.getProperty ("os.arch")) { - "aarch64" -> "aarch64" - "amd64" -> "x64" - "x86_64" -> "x64" - else -> throw GradleException("Unsupported CPU Architecture: ${System.getProperty ("os.arch")}! Please open a bug at https://github.com/mbeddr/mps-gradle-plugin with details about your operating system and CPU.") - - } - - val dependencyString = when { - Os.isFamily(Os.FAMILY_MAC) -> { - "com.jetbrains.jdk:$distributionType:$version:osx-$cpuArch@tgz" - } - Os.isFamily(Os.FAMILY_WINDOWS) -> { - "com.jetbrains.jdk:$distributionType:$version:windows-$cpuArch@tgz" - } - Os.isFamily(Os.FAMILY_UNIX) -> { - "com.jetbrains.jdk:$distributionType:$version:linux-$cpuArch@tgz" - } - else -> { - throw GradleException("Unsupported platform! Please open a bug at https://github.com/mbeddr/mps-gradle-plugin with details about your operating system.") - } - } - - return dependencyString - } -} diff --git a/src/main/kotlin/de/itemis/mps/gradle/downloadJBR/Tasks.kt b/src/main/kotlin/de/itemis/mps/gradle/downloadJBR/Tasks.kt deleted file mode 100644 index 709ce2cc..00000000 --- a/src/main/kotlin/de/itemis/mps/gradle/downloadJBR/Tasks.kt +++ /dev/null @@ -1,56 +0,0 @@ -package de.itemis.mps.gradle.downloadJBR - -import org.gradle.api.DefaultTask -import org.gradle.api.file.DirectoryProperty -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.model.ObjectFactory -import org.gradle.api.provider.Provider -import org.gradle.api.tasks.* -import org.gradle.jvm.toolchain.JavaLauncher -import org.gradle.jvm.toolchain.JavaToolchainService -import org.gradle.jvm.toolchain.JavaToolchainSpec -import org.gradle.jvm.toolchain.internal.SpecificInstallationToolchainSpec -import java.io.File -import javax.inject.Inject - -open class DownloadJbrForPlatform @Inject constructor( - objects: ObjectFactory, - private val javaToolchainService: JavaToolchainService -) : DefaultTask() { - - private val toolchainSpecFactory = objects.newInstance(ToolchainSpecFactory::class.java) - - @get:Internal - internal val jbrDirProperty: DirectoryProperty = objects.directoryProperty() - - @get:Internal - var jbrDir : File - get() = jbrDirProperty.get().asFile - set(value) { - jbrDirProperty.set(value) - } - - @get:Internal - internal val javaExecutableProperty: RegularFileProperty = objects.fileProperty() - - @get:Internal - var javaExecutable: File - get() = javaExecutableProperty.get().asFile - set(value) { - javaExecutableProperty.set(value) - } - - /** - * A [JavaToolchainSpec] that can be passed to [JavaToolchainService] to obtain various tools (Java compiler, - * launcher, javadoc). - */ - @get:Internal - val toolchainSpec: Provider = - javaExecutableProperty.map { toolchainSpecFactory.fromJavaExecutable(it.asFile.path) } - - /** - * A [JavaLauncher] for the downloaded JBR that can be used with [JavaExec] task. - */ - @get:Internal - val javaLauncher: Provider = toolchainSpec.flatMap(javaToolchainService::launcherFor) -} diff --git a/src/main/kotlin/de/itemis/mps/gradle/downloadJBR/ToolchainSpecFactory.kt b/src/main/kotlin/de/itemis/mps/gradle/downloadJBR/ToolchainSpecFactory.kt deleted file mode 100644 index 2b4ad893..00000000 --- a/src/main/kotlin/de/itemis/mps/gradle/downloadJBR/ToolchainSpecFactory.kt +++ /dev/null @@ -1,37 +0,0 @@ -package de.itemis.mps.gradle.downloadJBR - -import org.gradle.api.internal.provider.PropertyFactory -import org.gradle.api.model.ObjectFactory -import org.gradle.jvm.toolchain.internal.SpecificInstallationToolchainSpec -import javax.inject.Inject - -internal abstract class ToolchainSpecFactory { - @get:Inject - abstract val propertyFactory: PropertyFactory - - val method813 = try { - SpecificInstallationToolchainSpec::class.java.getMethod("fromJavaExecutable", - PropertyFactory::class.java, String::class.java) - } catch (_: NoSuchMethodException) { - null - } - - @get:Inject - abstract val objectFactory: ObjectFactory - - val method812 = try { - SpecificInstallationToolchainSpec::class.java.getMethod("fromJavaExecutable", - ObjectFactory::class.java, String::class.java) - } catch (_: NoSuchMethodException) { - null - } - - fun fromJavaExecutable(javaExecutable: String): SpecificInstallationToolchainSpec = - if (method813 != null) { - method813.invoke(null, propertyFactory, javaExecutable) as SpecificInstallationToolchainSpec - } else if (method812 != null) { - method812.invoke(null, objectFactory, javaExecutable) as SpecificInstallationToolchainSpec - } else { - throw IllegalStateException("Unsupported Gradle version, cannot create a SpecificInstallationToolchainSpec") - } -} diff --git a/src/main/kotlin/de/itemis/mps/gradle/generate/Plugin.kt b/src/main/kotlin/de/itemis/mps/gradle/generate/Plugin.kt deleted file mode 100644 index 5b936cab..00000000 --- a/src/main/kotlin/de/itemis/mps/gradle/generate/Plugin.kt +++ /dev/null @@ -1,123 +0,0 @@ -package de.itemis.mps.gradle.generate - -import de.itemis.mps.gradle.* -import de.itemis.mps.gradle.launcher.MpsBackendBuilder -import de.itemis.mps.gradle.launcher.MpsBackendLauncher -import org.apache.tools.ant.taskdefs.Java -import org.gradle.api.GradleException -import org.gradle.api.Plugin -import org.gradle.api.Project -import org.gradle.api.artifacts.Configuration -import org.gradle.api.model.ObjectFactory -import org.gradle.api.provider.Provider -import org.gradle.api.tasks.Copy -import org.gradle.api.tasks.JavaExec -import org.gradle.jvm.toolchain.JavaLauncher -import org.gradle.kotlin.dsl.newInstance -import org.gradle.process.CommandLineArgumentProvider -import java.io.File - -open class GeneratePluginExtensions(objectFactory: ObjectFactory): BasePluginExtensions(objectFactory){ - var models: List = emptyList() - var modules: List = emptyList() - var excludeModels: List = emptyList() - var excludeModules: List = emptyList() - var parallelGenerationThreads: Int = 0 -} - -open class GenerateMpsProjectPlugin : Plugin { - - override fun apply(project: Project) { - project.run { - val extensionName = "generate" - val extension = extensions.create(extensionName, GeneratePluginExtensions::class.java) - val generate = tasks.register("generate", JavaExec::class.java) - val fake = tasks.register("fakeBuildNumber", FakeBuildNumberTask::class.java) - - afterEvaluate { - val mpsVersion = extension.getMPSVersion(extensionName) - - val genConfig = extension.backendConfig ?: createDetachedBackendConfig(project) - - if(mpsVersion.substring(0..3).toInt() < 2020) { - throw GradleException(MPS_SUPPORT_MSG) - } - - val mpsLocation = extension.mpsLocation ?: layout.buildDirectory.dir("mps").get().asFile - val resolveMps = if (extension.mpsConfig != null) { - tasks.register("resolveMpsForGeneration", Copy::class.java) { - from({ extension.mpsConfig!!.resolve().map(::zipTree) }) - into(mpsLocation) - } - } else if (extension.mpsLocation != null) { - tasks.register("resolveMpsForGeneration") - } else { - throw GradleException(ErrorMessages.mustSetConfigOrLocation(extensionName)) - } - - /* - * The problem here is is that for some reason the ApplicationInfo isn't initialised properly. - * That causes PluginManagerCore.BUILD_NUMBER to be null. - * In this case the PluginManagerCore resorts to BuildNumber.currentVersion() which finally - * calls into BuildNumber.fromFile(). - * - * This behaviour allows us to place a build.txt in the root of the home path (see PathManager.getHomePath()). - * The file is then used to load the build number. - * - * TODO: Since MPS 2018.2 a newer version of the platform allows to get a similar behaviour via setting idea.plugins.compatible.build property. - * - */ - fake.configure { - mpsDir.set(mpsLocation) - dependsOn(resolveMps) - } - - generate.configure { - val backendBuilder: MpsBackendBuilder = project.objects.newInstance(MpsBackendBuilder::class) - backendBuilder.withMpsHome(mpsLocation).withMpsVersion(mpsVersion).configure(this) - - dependsOn(fake) - - argumentProviders.add(argsFromBaseExtension(extension)) - argumentProviders.add(CommandLineArgumentProvider { - mutableListOf().apply { - addAll(extension.models.map { "--model=$it" }) - addAll(extension.modules.map { "--module=$it" }) - addAll(extension.excludeModels.map { "--exclude-model=$it" }) - addAll(extension.excludeModules.map { "--exclude-module=$it" }) - add("--parallel-generation-threads=${extension.parallelGenerationThreads}") - } - }) - - if (extension.javaExec != null) { - javaLauncher.set(null as JavaLauncher?) - executable(extension.javaExec!!) - } else { - validateDefaultJvm() - } - - group = "build" - description = "Generates models in the project" - classpath(fileTree(File(mpsLocation, "/lib")).include("**/*.jar")) - // add only minimal number of plugins jars that are required by the generate code - // (to avoid conflicts with plugin classloader if custom configured plugins are loaded) - // git4idea: has to be on classpath as bundled plugin to be loaded (since 2019.3) - classpath(fileTree(File(mpsLocation, "/plugins")).include("git4idea/**/*.jar")) - classpath(genConfig) - debug = extension.debug - mainClass.set("de.itemis.mps.gradle.generate.MainKt") - - if (extension.maxHeap != null) { - maxHeapSize = extension.maxHeap!! - } - } - } - } - } - - private fun createDetachedBackendConfig(project: Project): Configuration { - val dep = project.dependencies.create("de.itemis.mps.build-backends:execute-generators:${MPS_BUILD_BACKENDS_VERSION}") - val genConfig = project.configurations.detachedConfiguration(dep) - return genConfig - } -} diff --git a/src/main/kotlin/de/itemis/mps/gradle/generate/Tasks.kt b/src/main/kotlin/de/itemis/mps/gradle/generate/Tasks.kt deleted file mode 100644 index 81982bda..00000000 --- a/src/main/kotlin/de/itemis/mps/gradle/generate/Tasks.kt +++ /dev/null @@ -1,41 +0,0 @@ -package de.itemis.mps.gradle.generate - -import org.gradle.api.DefaultTask -import org.gradle.api.GradleException -import org.gradle.api.file.DirectoryProperty -import org.gradle.api.file.RegularFile -import org.gradle.api.provider.Provider -import org.gradle.api.tasks.InputDirectory -import org.gradle.api.tasks.Optional -import org.gradle.api.tasks.OutputFile -import org.gradle.api.tasks.TaskAction -import java.io.PrintWriter -import java.util.* - - -abstract class FakeBuildNumberTask : DefaultTask() { - - @get:InputDirectory - abstract val mpsDir: DirectoryProperty - - @TaskAction - fun fakeBuildNumber() { - val buildProperties = mpsDir.get().asFile.resolve("build.properties") - - if (!buildProperties.isFile) throw GradleException("can't locate build.properties file in MPS directory") - - val props = Properties() - props.load(buildProperties.inputStream()) - val buildNumber = props.getProperty("mps.build.number") - val buildTxt: Provider = this.getBuildTxt() - val writer = PrintWriter(buildTxt.get().asFile.outputStream()) - - writer.write(buildNumber) - writer.close() - } - - @OutputFile - fun getBuildTxt(): Provider { - return mpsDir.file("build.txt") - } -} diff --git a/src/main/kotlin/de/itemis/mps/gradle/modelcheck/Plugin.kt b/src/main/kotlin/de/itemis/mps/gradle/modelcheck/Plugin.kt deleted file mode 100644 index 1cedf057..00000000 --- a/src/main/kotlin/de/itemis/mps/gradle/modelcheck/Plugin.kt +++ /dev/null @@ -1,134 +0,0 @@ -package de.itemis.mps.gradle.modelcheck - -import de.itemis.mps.gradle.* -import de.itemis.mps.gradle.launcher.MpsBackendBuilder -import de.itemis.mps.gradle.launcher.MpsBackendLauncher -import org.gradle.api.GradleException -import org.gradle.api.Plugin -import org.gradle.api.Project -import org.gradle.api.artifacts.Configuration -import org.gradle.api.model.ObjectFactory -import org.gradle.api.tasks.Copy -import org.gradle.api.tasks.JavaExec -import org.gradle.kotlin.dsl.newInstance -import org.gradle.process.CommandLineArgumentProvider -import java.io.File -import javax.inject.Inject - -open class ModelCheckPluginExtensions(objectFactory: ObjectFactory) : BasePluginExtensions(objectFactory) { - var models: List = emptyList() - var modules: List = emptyList() - var excludeModels: List = emptyList() - var excludeModules: List = emptyList() - var warningAsError = false - var errorNoFail = false - var junitFile: File? = null - var junitFormat: String? = null - var parallel: Boolean = false -} - -open class ModelcheckMpsProjectPlugin : Plugin { - override fun apply(project: Project) { - project.run { - val extensionName = "modelcheck" - val extension = extensions.create(extensionName, ModelCheckPluginExtensions::class.java) - val checkmodels = tasks.register("checkmodels", JavaExec::class.java) - - afterEvaluate { - val mpsVersion = extension.getMPSVersion(extensionName) - - val genConfig = extension.backendConfig ?: createDetachedBackendConfig(project) - - if(mpsVersion.substring(0..3).toInt() < 2020) { - throw GradleException(MPS_SUPPORT_MSG) - } - - val mpsLocation = extension.mpsLocation ?: layout.buildDirectory.dir("mps").get().asFile - val resolveMps = if (extension.mpsConfig != null) { - tasks.register("resolveMpsForModelcheck", Copy::class.java) { - from({ extension.mpsConfig!!.resolve().map(::zipTree) }) - into(mpsLocation) - } - } else if (extension.mpsLocation != null) { - tasks.register("resolveMpsForModelcheck") - } else { - throw GradleException(ErrorMessages.mustSetConfigOrLocation(extensionName)) - } - - checkmodels.configure { - val backendBuilder: MpsBackendBuilder = project.objects.newInstance(MpsBackendBuilder::class) - backendBuilder.withMpsHome(mpsLocation).withMpsVersion(mpsVersion).configure(this) - - dependsOn(resolveMps) - - argumentProviders.add(argsFromBaseExtension(extension)) - argumentProviders.add(CommandLineArgumentProvider { - val args = mutableListOf() - args.addAll(extension.models.map { "--model=$it" }) - args.addAll(extension.modules.map { "--module=$it" }) - args.addAll(extension.excludeModels.map { "--exclude-model=$it" }) - args.addAll(extension.excludeModules.map { "--exclude-module=$it" }) - - if (extension.warningAsError) { - args.add("--warning-as-error") - } - - if (extension.errorNoFail) { - args.add("--error-no-fail") - } - - if (extension.junitFile != null) { - args.add("--result-file=${extension.junitFile!!.absolutePath}") - } - - if (extension.junitFormat != null) { - args.add("--result-format=${extension.junitFormat}") - } - - if (extension.parallel) { - args.add("--parallel") - } - args - }) - - if (extension.javaExec != null) { - javaLauncher.set(null) - executable(extension.javaExec!!) - } else { - validateDefaultJvm() - } - - group = "test" - description = "Check models in the project" - if (extension.maxHeap != null) { - maxHeapSize = extension.maxHeap!! - } - classpath(fileTree(File(mpsLocation, "/lib")).include("**/*.jar")) - // add only minimal number of plugins jars that are required by the modelcheck code - // (to avoid conflicts with plugin classloader if custom configured plugins are loaded) - // mps-httpsupport: we need it to print the node url to the console. - // mps-modelchecker: contains used UnresolvedReferencesChecker - // git4idea: has to be on classpath as bundled plugin to be loaded (since 2019.3) - classpath( - fileTree(File(mpsLocation, "/plugins")).include( - "mps-modelchecker/**/*.jar", - "mps-httpsupport/**/*.jar", - "git4idea/**/*.jar" - ) - ) - classpath(genConfig) - debug = extension.debug - mainClass.set("de.itemis.mps.gradle.modelcheck.MainKt") - } - } - - } - } - - private fun createDetachedBackendConfig(project: Project): Configuration { - val dep = project.dependencies.create("de.itemis.mps.build-backends:modelcheck:${MPS_BUILD_BACKENDS_VERSION}") - val genConfig = project.configurations.detachedConfiguration(dep) - return genConfig - } - -} diff --git a/src/main/kotlin/de/itemis/mps/gradle/runmigrations/Plugin.kt b/src/main/kotlin/de/itemis/mps/gradle/runmigrations/Plugin.kt deleted file mode 100644 index e5de4866..00000000 --- a/src/main/kotlin/de/itemis/mps/gradle/runmigrations/Plugin.kt +++ /dev/null @@ -1,221 +0,0 @@ -package de.itemis.mps.gradle.runmigrations - -import de.itemis.mps.gradle.BasePluginExtensions -import de.itemis.mps.gradle.ErrorMessages -import de.itemis.mps.gradle.getMPSVersion -import de.itemis.mps.gradle.runAnt -import groovy.xml.MarkupBuilder -import net.swiftzer.semver.SemVer -import org.gradle.api.GradleException -import org.gradle.api.Plugin -import org.gradle.api.Project -import org.gradle.api.model.ObjectFactory -import org.gradle.api.tasks.Copy -import org.gradle.kotlin.dsl.withGroovyBuilder -import org.gradle.process.ExecOperations -import java.io.File -import javax.inject.Inject - -open class MigrationExecutorPluginExtensions @Inject constructor(of: ObjectFactory) : BasePluginExtensions(of) { - /** - * (Since MPS 2021.1) Whether to halt if a pre-check has failed. Note that to ignore the check for migrated - * dependencies the [haltOnDependencyError] option must be set to `false` as well. - */ - var haltOnPrecheckFailure: Boolean? = null - - /** - * (Since MPS 2021.3.4) Whether to halt when a non-migrated dependency is discovered. - */ - var haltOnDependencyError: Boolean? = null - - /** - * (Since MPS 2021.3) Whether to force a migration even if the project directory contains `.allow-pending-migrations` file. - */ - var force: Boolean? = null -} - -@Suppress("unused") -abstract class RunMigrationsMpsProjectPlugin : Plugin { - - @get:Inject - protected abstract val execOperations: ExecOperations - - companion object { - val MIN_VERSION_FOR_HALT_ON_PRECHECK_FAILURE = SemVer(2021, 1) - val MIN_VERSION_FOR_HALT_ON_DEPENDENCY_ERROR = SemVer(2021, 3, 4) - val MIN_VERSION_FOR_FORCE = SemVer(2021, 3) - } - - override fun apply(project: Project) { - project.run { - val extensionName = "runMigrations" - val extension = extensions.create(extensionName, MigrationExecutorPluginExtensions::class.java) - - tasks.register("runMigrations") - - afterEvaluate { - val projectLocation = extension.projectLocation ?: throw GradleException("No project path set") - if (!file(projectLocation).exists()) { - throw GradleException("The path to the project doesn't exist: $projectLocation") - } - - val mpsVersion = extension.getMPSVersion(extensionName) - val parsedMPSVersion = SemVer.parse(mpsVersion) - - if (extension.force != null && parsedMPSVersion < MIN_VERSION_FOR_FORCE) { - throw GradleException("The force migration flag is only supported for MPS version $MIN_VERSION_FOR_FORCE and higher.") - } - - if (extension.haltOnPrecheckFailure != null && parsedMPSVersion < MIN_VERSION_FOR_HALT_ON_PRECHECK_FAILURE) { - throw GradleException("The 'do not halt on pre-check failure' option is only supported for MPS version $MIN_VERSION_FOR_HALT_ON_PRECHECK_FAILURE and higher.") - } - - if (extension.haltOnDependencyError != null && parsedMPSVersion < MIN_VERSION_FOR_HALT_ON_DEPENDENCY_ERROR) { - 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.") - } - - val mpsLocation = extension.mpsLocation ?: layout.buildDirectory.dir("mps").get().asFile - val resolveMps = if (extension.mpsConfig != null) { - tasks.register("resolveMpsForMigrations", Copy::class.java) { - from({ extension.mpsConfig!!.resolve().map(::zipTree) }) - into(mpsLocation) - } - } else if (extension.mpsLocation != null) { - tasks.register("resolveMpsForMigrations") - } else { - throw GradleException(ErrorMessages.mustSetConfigOrLocation(extensionName)) - } - - tasks.named("runMigrations") { - dependsOn(resolveMps) - doLast { - if (!mpsLocation.isDirectory) { - throw GradleException("Specified MPS location does not exist or is not a directory: $mpsLocation") - } - - // Clean temporary dir to help avoid strange errors - temporaryDir.listFiles()?.forEach { it.deleteRecursively() } - - val buildFile = temporaryDir.resolve("build.xml") - buildFile.printWriter().use { - MarkupBuilder(it).withGroovyBuilder { - "project" { - "path"("id" to "path.mps.ant.path") { - // The different MPS versions need different jars. Let's just keep it simple and include all jars. - "fileset"("dir" to "$mpsLocation/lib", "includes" to "**/*.jar") - } - "taskdef"( - "resource" to "jetbrains/mps/build/ant/antlib.xml", - "classpathref" to "path.mps.ant.path" - ) - - val argsToMigrate = mutableListOf>().run { - add("project" to projectLocation) - add("mpsHome" to mpsLocation) - - extension.force?.let { add("force" to it) } - extension.haltOnPrecheckFailure?.let { add("haltOnPrecheckFailure" to it) } - extension.haltOnDependencyError?.let { add("haltOnDependencyError" to it) } - - toTypedArray() - } - - "migrate"(*argsToMigrate) { - "macro"("name" to "mps_home", "path" to mpsLocation) - - extension.macros.forEach { - "macro"("name" to it.name, "path" to it.value) - } - - "jvmargs" { - "arg"("value" to "-Didea.log.config.file=log.xml") - "arg"("value" to "-ea") - - if (extension.maxHeap != null) { - "arg"("value" to "-Xmx${extension.maxHeap}") - } - - if (extension.debug) { - "arg"("value" to "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005") - } - - "arg"("value" to "--add-opens=java.base/java.io=ALL-UNNAMED") - "arg"("value" to "--add-opens=java.base/java.lang=ALL-UNNAMED") - "arg"("value" to "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED") - "arg"("value" to "--add-opens=java.base/java.net=ALL-UNNAMED") - "arg"("value" to "--add-opens=java.base/java.nio=ALL-UNNAMED") - "arg"("value" to "--add-opens=java.base/java.nio.charset=ALL-UNNAMED") - "arg"("value" to "--add-opens=java.base/java.text=ALL-UNNAMED") - "arg"("value" to "--add-opens=java.base/java.time=ALL-UNNAMED") - "arg"("value" to "--add-opens=java.base/java.util=ALL-UNNAMED") - "arg"("value" to "--add-opens=java.base/java.util.concurrent=ALL-UNNAMED") - "arg"("value" to "--add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED") - "arg"("value" to "--add-opens=java.base/jdk.internal.vm=ALL-UNNAMED") - "arg"("value" to "--add-opens=java.base/sun.nio.ch=ALL-UNNAMED") - "arg"("value" to "--add-opens=java.base/sun.security.ssl=ALL-UNNAMED") - "arg"("value" to "--add-opens=java.base/sun.security.util=ALL-UNNAMED") - "arg"("value" to "--add-opens=java.desktop/java.awt=ALL-UNNAMED") - "arg"("value" to "--add-opens=java.desktop/java.awt.dnd.peer=ALL-UNNAMED") - "arg"("value" to "--add-opens=java.desktop/java.awt.event=ALL-UNNAMED") - "arg"("value" to "--add-opens=java.desktop/java.awt.image=ALL-UNNAMED") - "arg"("value" to "--add-opens=java.desktop/java.awt.peer=ALL-UNNAMED") - "arg"("value" to "--add-opens=java.desktop/javax.swing=ALL-UNNAMED") - "arg"("value" to "--add-opens=java.desktop/javax.swing.plaf.basic=ALL-UNNAMED") - "arg"("value" to "--add-opens=java.desktop/javax.swing.text.html=ALL-UNNAMED") - "arg"("value" to "--add-opens=java.desktop/sun.awt.datatransfer=ALL-UNNAMED") - "arg"("value" to "--add-opens=java.desktop/sun.awt.image=ALL-UNNAMED") - "arg"("value" to "--add-opens=java.desktop/sun.awt=ALL-UNNAMED") - "arg"("value" to "--add-opens=java.desktop/sun.font=ALL-UNNAMED") - "arg"("value" to "--add-opens=java.desktop/sun.java2d=ALL-UNNAMED") - "arg"("value" to "--add-opens=java.desktop/sun.swing=ALL-UNNAMED") - "arg"("value" to "--add-opens=jdk.attach/sun.tools.attach=ALL-UNNAMED") - "arg"("value" to "--add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED") - "arg"("value" to "--add-opens=jdk.internal.jvmstat/sun.jvmstat.monitor=ALL-UNNAMED") - "arg"("value" to "--add-opens=jdk.jdi/com.sun.tools.jdi=ALL-UNNAMED") - "arg"("value" to "--add-opens=java.desktop/com.apple.laf=ALL-UNNAMED") - "arg"("value" to "--add-opens=java.desktop/com.apple.eawt=ALL-UNNAMED") - "arg"("value" to "--add-opens=java.desktop/com.apple.eawt.event=ALL-UNNAMED") - } - - extension.pluginsProperty.get().forEach { - // Same handling as in mps-build-backends - if (File(it.path).isAbsolute) { - "plugin"("path" to it.path, "id" to it.id) - } else if (extension.pluginLocation != null && File( - extension.pluginLocation, - it.path - ).exists() - ) { - "plugin"( - "path" to File(extension.pluginLocation, it.path), - "id" to it.id - ) - } else { - "plugin"( - "path" to mpsLocation.resolve("plugins").resolve(it.path), - "id" to it.id - ) - } - } - } - } - } - } - - val classpath = project.fileTree(mpsLocation.resolve("lib")) { - include("ant/lib/*.jar") - include("*.jar") - builtBy(resolveMps) - } - - runAnt( - execOperations, extension.javaExec, temporaryDir, args = listOf(), - includeDefaultClasspath = false, - scriptClasspath = classpath - ) - } - } - } - } - } -} diff --git a/src/main/resources/de/itemis/mps/gradle/Mac/Finder/DSStore.pm b/src/main/resources/de/itemis/mps/gradle/Mac/Finder/DSStore.pm deleted file mode 100644 index 66ae1ec5..00000000 --- a/src/main/resources/de/itemis/mps/gradle/Mac/Finder/DSStore.pm +++ /dev/null @@ -1,733 +0,0 @@ -package Mac::Finder::DSStore; - -=head1 NAME - -Mac::Finder::DSStore - Read and write Macintosh Finder DS_Store files - -=head1 DESCRIPTION - -C provides a handful of functions for reading and -writing the desktop database files created by the Macintosh Finder. - -=head1 FUNCTIONS - -Many functions take a C<$store> argument which is the opened file as -an instance of L, or a C<$block> -argument which is a specific block of the file as an instance of -L. - -=cut - -use strict; -use warnings; -use POSIX qw(ceil); -use Carp qw(croak); -use Fcntl; -require Exporter; - -our($VERSION) = '1.00'; -our(@ISA) = qw(Exporter); -our(@EXPORT_OK) = qw( getDSDBEntries putDSDBEntries writeDSDBEntries makeEntries ); - -our($testpoint); - -=head2 @records = &Mac::Finder::DSStore::getDSDBEntries($store[, $callback]) - -Retrieves the "superblock" pointed to by the C entry in the store's table -of contents, and traverses the B-tree it points to, returning a list of -the records in the tree. Alternately, you can supply a callback which will -be invoked for each record, and C will return an empty list. - -=cut - -sub getBTreeRootblock { - my($store) = @_; - return $store->blockByNumber($store->{toc}->{DSDB})->read(20, 'N*'); -} - -sub getDSDBEntries { - my($file, $callback) = @_; - - my(@retval); - - $callback = sub { push(@retval, $_[0]); } unless defined $callback; - - my($rootnode, $height, $nrec, $nnodes, $blksize) = &getBTreeRootblock($file); - - my($n) = &traverse_btree($file, $rootnode, $callback); - - warn "Header node count ($nrec) not equal to actual node count ($n)" - if $n != $nrec; - - @retval; -} - -=head2 &Mac::Finder::DSStore::putDSDBEntries($store, $arrayref) - -C<$arrayref> must contain a correctly ordered list of -C objects. They will be evenly -organized into a B-tree structure and written to the C<$store>. If there is -an existing tree of records in the file already, it will be deallocated. - -This function does not flush the allocator's information back to the file. - -=cut - -sub putDSDBEntries { - my($file, $recs) = @_; - - my($tocblock, $pagesize); - my($pagecount, $reccount, $height); - - # Delete the old btree (but keep its superblock), or allocate a superblock. - if(defined($file->{toc}->{DSDB})) { - $tocblock = $file->{toc}->{DSDB}; - my($old_rootblock); - ($old_rootblock, $pagesize) = (&getBTreeRootblock($file))[0, 4]; - &freeBTreeNode($file, $old_rootblock); - } else { - $tocblock = $file->allocate( 20 ); - $file->{toc}->{DSDB} = $tocblock; - $pagesize = 0x1000; - } - - $reccount = @$recs; - $pagecount = 0; - $height = 0; - - my(@children); - - # Partition the records into btree nodes, from the bottom of - # the tree working towards the root. - do { - my(@sizes); - - if (@children) { - # Interior node: child pointers interleaved with records - @sizes = map { 4 + $_->byteSize } @$recs; - } else { - # Leaf node: just a bunch of records - @sizes = map { $_->byteSize } @$recs; - } - - # In addition to @sizes, each page contains a record - # count and a flag/childnode field (4 bytes each) - my(@interleaf) = &partition_sizes($pagesize - 8, @sizes); - my(@nchildren); - - my($next) = 0; - foreach my $non (@interleaf, 1+$#$recs) { - my($blknr) = $file->allocate($pagesize); - push(@nchildren, $blknr); - my($blk) = $file->blockByNumber($blknr, 1); - if (@children) { - &writeBTreeNode($blk, - [ @$recs[ $next .. $non-1 ] ], - [ @children[ $next .. $non ] ] ); - } else { - &writeBTreeNode($blk, - [ @$recs[ $next .. $non-1 ] ]); - } - $blk->close(1); - $next = $non + 1; - $pagecount ++; - } - - $height ++; - $recs = [ map { $recs->[$_] } @interleaf ]; - @children = @nchildren; - die unless @children == 1+@$recs; - } while(@children > 1); - die unless 0 == @$recs; - - my($masterblock) = $file->blockByNumber($tocblock, 1); - $masterblock->write('NNNNN', - $children[0], - $height - 1, - $reccount, - $pagecount, - $pagesize); - $masterblock->close; - - 1; -} - -# Given a list of sizes, break them into groups so that -# each group sums to no more than $max, not including the items -# that separate them (returned in @ejecta). -sub partition_sizes { - my($max, @sizes) = @_; - my($sum) = 0; - $sum += $_ foreach @sizes; - - return () if $sum <= $max; - - my(@ejecta); - my($bcount) = ceil($sum / $max); - my($target) = $sum / $bcount; - - my($n) = 0; - for(;;) { - my($bsum) = 0; - while( $n < @sizes && $bsum < $target && ($bsum + $sizes[$n]) < $max ) { - $bsum += $sizes[$n]; - $n ++; - } - - last if $n >= @sizes; - - push(@ejecta, $n); - $n++; - } - - @ejecta; -} - -sub traverse_btree { - my($store, $nodenr, $callback) = @_; - my($count); - my($values, $pointers) = &readBTreeNode( $store->blockByNumber( $nodenr ) ); - - if ($testpoint) { - my($o) = Mac::Finder::DSStore::BuddyAllocator::StringBlock->new(); - { - # Temporarily disable the test point so writeBTreeNode doesn't - # recursively invoke it - local($testpoint) = undef; - &writeBTreeNode($o, $values, $pointers); - } - my($actual) = $store->blockByNumber( $nodenr )->copyback; - my($roundtrip) = $o->copyback; - $actual = substr($actual, 0, length($roundtrip)); - $testpoint->( $actual, $roundtrip ); - } - - $count = @$values; - - if (defined $pointers) { - die "Value count should be one less than pointer count" - unless ( @$values + 1 ) == ( @$pointers ) ; - $count += &traverse_btree($store, shift(@$pointers), $callback); - while(@$values) { - &{$callback}(shift @$values); - $count += &traverse_btree($store, shift(@$pointers), $callback); - } - } else { - &{$callback}($_) foreach @$values; - } - - $count; -} - -sub freeBTreeNode { - my($allocator, $nodeid) = @_; - my($block) = $allocator->blockByNumber( $nodeid ); - - if($block->read(4, 'N') != 0) { - $block->seek(0); - my(undef, $pointers) = &readBTreeNode($block); - &freeBTreeNode($allocator, $_) foreach @$pointers; - } - - $allocator->free($nodeid); -} - -sub readBTreeNode { - my($node) = @_; - - my($pointer) = $node->read(4, 'N'); - - my($count) = $node->read(4, 'N'); - if ($pointer > 0) { - my(@pointers, @values); - while($count) { - push(@pointers, $node->read(4, 'N')); - push(@values, Mac::Finder::DSStore::Entry->readEntry($node)); - $count --; - } - push(@pointers, $pointer); - return \@values, \@pointers; - } else { - my(@values); - while($count) { - push(@values, Mac::Finder::DSStore::Entry->readEntry($node)); - $count --; - } - return \@values, undef; - } -} - -sub writeBTreeNode { - my($into, $values, $pointers) = @_; - - if (!$pointers) { - # A leaf node: no pointers, just database entries. - $into->write('NN', 0, scalar(@$values)); - $_->write($into) foreach @$values; - } else { - # An internal node: interleaved pointers and values, - # with the final pointer moved to the front. - my(@vals) = @$values; - my(@ps) = @$pointers; - die "number of pointers must be one more than number of entries" - unless 1+@vals == @ps; - $into->write('NN', pop(@ps), scalar(@vals)); - while(@vals) { - $into->write('N', shift(@ps)); - ( shift(@vals) )->write($into); - } - } - - if($testpoint) { - my($x) = [ &readBTreeNode($into->copyback) ]; - $testpoint->( [ $values, $pointers], $x ); - } -} - -=head2 &Mac::Finder::DSStore::writeDSDBEntries($file, @entries) - -A convenience function which sorts a list of entries and writes them -to the specified file using C, then flushes the allocator's -data structures to disk. -C<$file> may be a filename or an open file handle. -The store object is returned, but you don't need to do anything else with it. - -=cut - -sub writeDSDBEntries { - my($store, $recs); - { - my($file, @entries) = @_; - - require IO::File; - require Mac::Finder::DSStore::BuddyAllocator; - - unless(ref $file) { - my($filename) = $file; - $file = IO::File->new( $filename, Fcntl::O_RDWR | Fcntl::O_CREAT ); - croak "$filename: $!, died" unless $file; - } - - if((stat($file))[7] > 32) { - $store = Mac::Finder::DSStore::BuddyAllocator->open($file); - } else { - $store = Mac::Finder::DSStore::BuddyAllocator->new($file); - } - - $recs = [ sort { $a->cmp($b) } @entries ]; - } - - putDSDBEntries($store, $recs); - $store->writeMetaData; - - $store; -} - -=head2 &Mac::Finder::DSStore::makeEntries($filename, [ what => value ... ]) - -C encapsulates some information about the format of individual -records in the DS_Store file. It returns a list of records constructed with the -given filename and with the information specified in the rest of its args. -Most args come in pairs, a name and a value, so C kind of looks -like it takes a hash. Some names take no value and some could take several. -Some produce more than one record as a result. - -See the output of the F script for an example of how -to use this, and check the source code for a list of the formats it accepts. - -This function might change in the future. - -=cut - -sub makeEntries { - my($filename, @info) = @_; - my(@results); - - while(@info) { - my($recordType) = shift @info; - - if ($recordType =~ /^....$/) { - my($record) = Mac::Finder::DSStore::Entry->new($filename, $recordType); - $record->value( shift @info ); - push(@results, $record); - } elsif ($recordType =~ /^(....)_hex$/) { - my($record) = Mac::Finder::DSStore::Entry->new($filename, $1); - $record->value( pack('H*', shift @info) ); - push(@results, $record); - } else { - my($mkr) = $Mac::Finder::DSStore::Entry::{'make_'.$recordType}; - croak "Don't know how to handle '$recordType'" unless $mkr; - push(@results, &{$mkr}($filename, $recordType, \@info)); - } - } - - @results; -} - -package Mac::Finder::DSStore::Entry; - -=head1 Mac::Finder::DSStore::Entry - -This class holds the individual records from the database. Each record -contains a filename (in some cases, "." to refer to the containing -directory), a 4-character record type, and a value. The value is -one of a few concrete types, according to the record type. - -=cut - -use strict; -use warnings; -use Encode (); -use Carp qw(croak); - -# -# Concrete types of known ids -# -our(%types) = ( - 'BKGD' => 'blob', - 'bwsp' => 'blob', - 'cmmt' => 'ustr', - 'dilc' => 'blob', - 'dscl' => 'bool', - 'extn' => 'ustr', - 'fwi0' => 'blob', - 'fwsw' => 'long', - 'fwvh' => 'shor', - 'GRP0' => 'ustr', - 'icgo' => 'blob', - 'icsp' => 'blob', - 'icvo' => 'blob', - 'ICVO' => 'bool', - 'icvp' => 'blob', - 'icvt' => 'shor', - 'Iloc' => 'blob', - 'info' => 'blob', - 'lg1S' => 'comp', - 'logS' => 'comp', - 'lssp' => 'blob', - 'lsvo' => 'blob', - 'LSVO' => 'bool', - 'lsvP' => 'blob', - 'lsvp' => 'blob', - 'lsvt' => 'shor', - 'moDD' => 'dutc', - 'modD' => 'dutc', - 'ph1S' => 'comp', - 'phyS' => 'comp', - 'pict' => 'blob', - 'vSrn' => 'long', - 'vstl' => 'type', - ); - -=head2 $entry = ...::Entry->new($filename, $typecode) - -Creates a new entry with no value. The concrete type is inferred from the -record type code. - -=head2 $entry->filename - -Gets the filename of an entry. - -=head2 $entry->strucId - -Gets the record type of this entry, as a four-character string, indicating -what aspect of the file the entry describes. - -=head2 $entry->value([$value]) - -Gets or sets the value of an entry. - -If the concrete type is C or C, the value is interpreted as a byte string; -if it is C, as a character string. -If the concrete type is C, C, C, C, or C, -then the value should be an integer. - -=cut - -sub new { - my($class, $filename, $strucId, @opts) = @_; - - croak "no opts supported yet, died" if @opts; - - bless([ $filename, $strucId, $types{$strucId}, undef ], - ref $class || $class); -} - -sub filename { - $_[0]->[0]; -} - -sub strucId { - $_[0]->[1]; -} - -sub value { - my($self, $value) = @_; - - return $self->[3] unless defined $value; - - croak "Can't set a value on an entry with no concrete type" - unless defined($self->[2]); - - my($t) = $self->[2]; - if($t eq 'blob' or $t eq 'ustr') { - $self->[3] = '' . $value; - } elsif ($t eq 'bool' or $t eq 'shor' or $t eq 'long') { - $self->[3] = 0 + $value; - } elsif ($t eq 'type') { - $value = '' . $value; - croak "'type' values must be exactly four bytes long" - unless length($value) == 4; - $self->[3] = $value; - } else { - die "Unknown concrete type $t, died"; - } - - $self->[3]; -} - -sub readEntry { - my($class, $block) = @_; - - my($filename, $strucId, $strucType, $value); - - $filename = &readFilename($block); - $strucId = $block->read(4); - $strucType = $block->read(4); - - if ($strucType eq 'bool') { - $value = $block->read(1, 'C'); - } elsif ($strucType eq 'long' or $strucType eq 'shor') { - $value = $block->read(4, 'N'); - } elsif ($strucType eq 'blob') { - my($bloblen) = $block->read(4, 'N'); - $value = $block->read($bloblen); - } elsif ($strucType eq 'ustr') { - my($strlen) = $block->read(4, 'N'); - $value = Encode::decode('UTF-16BE', $block->read(2 * $strlen)); - } elsif ($strucType eq 'type') { - $value = $block->read(4); - } elsif ($strucType eq 'comp' || $strucType eq 'dutc') { - $value = $block->read(8, 'Q>'); - } else { - die "Unknown struc type '$strucType', died"; - } - - return bless([ $filename, $strucId, $strucType, $value ], - ref($class) || $class); -} - -sub readFilename { - my($block) = @_; - - my($flen) = $block->read(4, 'N'); - my($utf16be) = $block->read(2 * $flen); - - return Encode::decode('UTF-16BE', $utf16be, Encode::FB_CROAK); -} - -sub byteSize { - my($filename, $strucId, $strucType, $value) = @{$_[0]}; - my($size); - - # TODO: We're assuming that the filename is completely normal - # basic-multilingual-plane characters, and doesn't need to be de/re- - # composed or anything. - $size = length($filename) * 2 + 12; - # 12 bytes: 4 each for filename length, struct id, and struct type - - if ($strucType eq 'long' or $strucType eq 'shor' or $strucType eq 'type') { - $size += 4; - } elsif ($strucType eq 'bool') { - $size += 1; - } elsif ($strucType eq 'blob') { - $size += 4 + length($value); - } elsif ($strucType eq 'ustr') { - $size += 4 + 2 * length($value); - } elsif ($strucType eq 'comp' or $strucType eq 'dutc') { - $size += 8; - } else { - die "Unknown struc type '$strucType', died"; - } - - $size; -} - -sub write { - my($self, $into) = @_; - - my($fname) = Encode::encode('UTF-16BE', $self->[0]); - - my($strucType) = $self->[2]; - - $into->write('N a* a4 a4', length($fname)/2, $fname, - $self->[1], $strucType); - - if ($strucType eq 'long' or $strucType eq 'shor') { - $into->write('N', $self->[3]); - } elsif ($strucType eq 'bool') { - $into->write('C', $self->[3]); - } elsif ($strucType eq 'blob') { - $into->write('N', length($self->[3])); - $into->write($self->[3]); - } elsif ($strucType eq 'ustr') { - $into->write('N', length($self->[3])); - $into->write(Encode::encode('UTF-16BE', $self->[3])); - } elsif ($strucType eq 'type') { - $into->write('a4', $self->[3]); - } elsif ($strucType eq 'comp' or $strucType eq 'dutc') { - $into->write('Q>', $self->[3]); - } else { - die "Unknown struc type '$strucType', died"; - } -} - -=head2 $entry->cmp($other) - -Returns -1, 0, or 1 depending on the relative ordering of the two entries, -according to (a guess at) the record ordering used by the store's B-tree. - -=cut - -sub cmp { - my($self, $other) = @_; - - # - # There's probably some wacky Mac-specific Unicode collation - # rule for these, but case-insensitive comparison is a good - # approximation - # - - # Ordering in the btree is Finder-filename-ordering on the files, - # and simple bytewise ordering on the structure IDs. - - ( lc($self->[0]) cmp lc($other->[0]) ) - || - ( $self->[1] cmp $other->[1] ); -} - -# -# The make_foo subs are used by Mac::Finder::DSStore::makeEntries. -# - -sub make_BKGD_default { - my($filename, undef, undef) = @_; - - my($rec) = Mac::Finder::DSStore::Entry->new($filename, 'BKGD'); - $rec->value( pack('A4 x8', 'DefB') ); - $rec; -} - -sub make_BKGD_color { - my($filename, $strucId, $argv) = @_; - my($color) = shift @$argv; - my($rgb); - - if ($color =~ /^\#([0-9a-f]+)$/i) { - if(length($1) == 3) { - ( $rgb = $1 ) =~ s/(.)(.)(.)/$1$1$1$1$2$2$2$2$3$3$3$3/; - } elsif (length($1) == 6) { - ( $rgb = $1 ) =~ s/(..)(..)(..)/$1$1$2$2$3$3/; - } elsif (length($1) == 12) { - $rgb = $1; - } - } - - croak "Can't parse color string '$color'" - unless $rgb; - - my($rec) = Mac::Finder::DSStore::Entry->new($filename, 'BKGD'); - $rec->value( pack('A4 H12 x2', 'ClrB', $rgb) ); - - $rec; -} - -sub make_BKGD_alias { - my($filename, $strucId, $argv) = @_; - - my($image) = shift @$argv; - - if(!ref $image) { - require Mac::Memory; - require Mac::Files; - $image = Mac::Files::NewAlias($image); - } - - my($isize) = $image->size; - my($bkgd, $pict); - - $bkgd = Mac::Finder::DSStore::Entry->new($filename, 'BKGD'); - $bkgd->value( pack('A4 N nn', 'PctB', $isize, 0, 0) ); - - $pict = Mac::Finder::DSStore::Entry->new($filename, 'pict'); - $pict->value( $image->get ); - - ( $bkgd, $pict ); -} - -sub _make_packed { - my($filename, $strucId, $fmt, @values) = @_; - my($record) = Mac::Finder::DSStore::Entry->new($filename, $strucId); - $record->value( pack($fmt, @values) ); - $record; -} - -sub _make_packed_arrayref { - my($filename, $strucId, $argv, $format, $reqcount, $dflt) = @_; - my($values) = shift @$argv; - - croak "$strucId argument must be an array ref" - unless ref $values; - - croak "$strucId argument must have at least $reqcount items" - unless $reqcount <= @$values; - - my($max) = $reqcount + @$dflt; - - croak "$strucId argument can't have more than $max items" - if $max < @$values; - - my(@fields) = @$values; - if ($max > @fields) { - push(@fields, @{$dflt}[ ( @fields - $max ) .. -1 ]); - } - - return &_make_packed($filename, substr($strucId, 0, 4), - $format, @fields); -} - -sub make_Iloc_xy { - my($filename, $strucId, $argv) = @_; - return &_make_packed_arrayref($filename, $strucId, $argv, - 'NN nnnn', 2, [65535, 65535, 65535, 0]); -} - -sub make_fwi0_flds { - my($filename, $strucId, $argv) = @_; - my($flds) = shift @$argv; - - croak "$strucId argument must have 7 values" - unless 7 == @$flds; - - return &_make_packed($filename, 'fwi0', 'n4 A4 n*', @$flds); -} - - -=head1 SEE ALSO - -See L for more detailed information on -the record types found in a DS_Store file. - -See L for the low-level organization -of the DS_Store file. - -=head1 AUTHOR - -Copyright 2008 by Wim Lewis Ewiml@hhhh.orgE. - -Some information is from Mark Mentovai via the Mozilla project. -Thanks also to Martin Baker for bug reports. - -=cut - -1; diff --git a/src/main/resources/de/itemis/mps/gradle/Mac/Finder/DSStore/BuddyAllocator.pm b/src/main/resources/de/itemis/mps/gradle/Mac/Finder/DSStore/BuddyAllocator.pm deleted file mode 100644 index 76647184..00000000 --- a/src/main/resources/de/itemis/mps/gradle/Mac/Finder/DSStore/BuddyAllocator.pm +++ /dev/null @@ -1,783 +0,0 @@ -package Mac::Finder::DSStore::BuddyAllocator; - -=head1 NAME - -Mac::Finder::DSStore::BuddyAllocator - Allocate space within a file - -=head1 DESCRIPTION - -C -implements a buddy-allocation scheme within a file. It's used by -C to read certain files created by the Macintosh -Finder. - -The allocation methods do not perform any actual file I/O. -The contents of allocated blocks are read and written by the caller using -methods on C. -If the C and C methods are used, -or if the C hash is modified, -C must be called for the changes to be reflected in the file. - -=head1 METHODS - -=cut - -use strict; -use warnings; -use Carp; - -our($VERSION) = '1.00'; - -# Debug logging. Uncomment these and all uses of them to activate. -# It might be nice to make this more easily switchable. -#our($loglevel) = 0; -#sub logf { -# print STDERR ( ' ' x $loglevel ) . sprintf($_[0], @_[1 .. $#_ ]) . "\n"; -#} - -=head2 $allocator = Mac::Finder::DSStore::BuddyAllocator->open($fh) - -C constructs a new buddy allocator -and initializes its state from the information in the file. -The file handle is retained by the allocator for future -operations. - -=cut - -sub open { - my($class, $fh) = @_; - - binmode($fh); - - # read the file header: 32 bytes, plus a mysterious extra - # four bytes at the front - my($fheader); - $fh->read($fheader, 4 + 0x20) == 0x24 - or die "Can't read file header: $!"; - my($magic1, $magic, $offset, $size, $offset2, $unk2) = unpack('N a4 NNN a16', $fheader); - die 'bad magic' unless $magic eq 'Bud1' and $magic1 == 1; - die 'inconsistency: two root addresses are different' - unless $offset == $offset2; - - my($self) = { - fh => $fh, - unk2 => $unk2, - fudge => 4, # add this to offsets for some unknown reason - }; - bless($self, ref($class) || $class); - - # retrieve the root/index block which contains the allocator's - # book-keeping data - my ($rootblock) = $self->getBlock($offset, $size); - - # parse out the offsets of all the allocated blocks - # these are in tagged offset format (27 bits offset, 5 bits size) - my($offsetcount, $unk3) = $rootblock->read(8, 'NN'); - # not sure what the word following the offset count is - $self->{'unk3'} = $unk3; - # For some reason, offsets are always stored in blocks of 256. - my(@offsets); - while($offsetcount > 0) { - push(@offsets, $rootblock->read(1024, 'N256')); - $offsetcount -= 256; - } - # 0 indicates an empty slot; don't need to keep those around - while($offsets[$#offsets] == 0) { pop(@offsets); } - grep { $_ = undef if $_ == 0 } @offsets; - - # Next, read N key/value pairs - my($toccount) = $rootblock->read(4, 'N'); - my($toc) = { - }; - while($toccount--) { - my($len) = $rootblock->read(1, 'C'); - my($name) = $rootblock->read($len); - my($value) = $rootblock->read(4, 'N'); - $toc->{$name} = $value; - } - - $self->{'offsets'} = \@offsets; - $self->{'toc'} = $toc; - - # Finally, read the free lists. - my($freelists) = { }; - for(my $width = 0; $width < 32; $width ++) { - my($blkcount) = $rootblock->read(4, 'N'); - $freelists->{$width} = [ $rootblock->read(4 * $blkcount, 'N*') ]; - } - $self->{'freelist'} = $freelists; - - return $self; -} - -=head2 $allocator = Mac::Finder::DSStore::BuddyAllocator->new($fh) - -Similar to C, but does not read anything from the file. This -can be used to create a new file from scratch. - -=cut - -sub new { - my($cls, $fh) = @_; - - binmode($fh) if defined($fh); - - my($self) = { - fh => $fh, - toc => { }, - offsets => [ ], - freelist => { }, - - # And the mystery meat goes here... - unk2 => pack('NNNN', 0x100C, 0x0087, 0x200B, 0 ), - unk3 => 0, - fudge => 4 - }; - bless($self, ref $cls || $cls); - - # All our freelists are empty... - foreach my $width (0 .. 30) { - $self->{freelist}->{$width} = [ ]; - } - # ... except for a single 2GB block starting at 0 - $self->{freelist}->{31} = [ 0 ]; - - # Allocate the header block, 2^5 bytes wide - my($hdr) = $self->_alloc(5); - # it had better be at offset zero - ( $hdr == 0 ) or die; - - $self; -} - -=head2 $allocator->close( ) - -Closes the underlying file handle. - -=cut - -sub close { - my($self) = @_; - my($fh) = $self->{fh}; - - delete $self->{fh}; - - $fh->close; -} - -=head2 $allocator->listBlocks($verbose) - -List all the blocks in order and see if there are any gaps or overlaps. -If C<$verbose> is true, then the blocks are listed to the current -output filehandle. Returns true if the allocated and free blocks -have no gaps or overlaps. - -=cut - -sub listBlocks { - my($self, $verbose) = @_; - my(%byaddr); - my($addr, $len); - - # We store all blocks (allocated and free) in %byaddr, - # then go through its keys in order - - # Store the implicit 32-byte block that holds the file header - push(@{$byaddr{0}}, "5 (file header)"); - - # Store all the numbered/allocated blocks from @offsets - for my $blnum (0 .. $#{$self->{'offsets'}}) { - my($addr_size) = $self->{'offsets'}->[$blnum]; - next unless defined $addr_size; - $addr = $addr_size & ~0x1F; - $len = $addr_size & 0x1F; - push(@{$byaddr{$addr}}, "$len (blkid $blnum)"); - } - - # Store all the blocks in the freelist(s) - for $len (keys %{$self->{'freelist'}}) { - for $addr (@{$self->{'freelist'}->{$len}}) { - push(@{$byaddr{$addr}}, "$len (free)"); - } - } - - my($gaps, $overlaps) = (0, 0); - - # Loop through the blocks in order of address - my(@addrs) = sort {$a <=> $b} keys %byaddr; - $addr = 0; - while(@addrs) { - my($next) = shift @addrs; - if ($next > $addr) { - print "... ", ($next - $addr), " bytes unaccounted for\n" - if $verbose; - $gaps ++; - } - my(@uses) = @{$byaddr{$next}}; - printf "%08x %s\n", $next, join(', ', @uses) - if $verbose; - $overlaps ++ if @uses > 1; - - # strip off the length (log_2(length) really) from the info str - ($len = $uses[0]) =~ s/ .*//; - $addr = $next + ( 1 << (0 + $len) ); - } - - ( $gaps == 0 && $overlaps == 0 ); -} - -=head2 $allocator->writeMetaData( ) - -Writes the allocator's metadata (header block and root block) -back to the file. - -=cut - -sub writeMetaData { - my($self) = @_; - - # Root block nr is hardcoded to 0. - # We don't actually care, but the DSStore btree does. - my($blocknr) = 0; - - # Before computing the size of the rootblock to allocate it, - # make sure it'll be large enough to hold its own (eventual) - # allocation information. - $self->{offsets}->[0] = undef unless exists $self->{offsets}->[0]; - - my($rbs) = $self->rootBlockSize(); - $self->allocate($rbs, $blocknr); - - $self->writeRootblock($self->blockByNumber($blocknr, 1)); - - my($blockOffset, $blockLength) = $self->blockOffset($blocknr); - - $self->{fh}->seek(0, 0); - $self->{fh}->write(pack('N', 1)); # magic1 - $self->_sought(0)->write(pack('a4 NNN a16', - 'Bud1', # magic - $blockOffset, $blockLength, $blockOffset, - $self->{unk2})); - - $self->{fh}->flush; -} - -sub rootBlockSize { - my($self) = @_; - my($size); - - $size = 8; # The offset count and the unknown field that follows it - - # The offset blocks, rounded up to a multiple of 256 entries - my($offsetcount) = scalar( @{$self->{'offsets'}} ); - my($tail) = $offsetcount % 256; - $offsetcount += 256 - $tail if ($tail); - $size += 4 * $offsetcount; - - # The table of contents - $size += 4; # count - $size += (5 + length($_)) foreach keys %{$self->{'toc'}}; - - # The freelists - foreach my $width (0 .. 31) { - $size += 4 + 4 * scalar( @{$self->{'freelist'}->{$width}} ); - } - - $size; -} - -sub writeRootblock { - my($self, $into) = @_; - - my(@offsets) = @{$self->{'offsets'}}; - - # Write the offset count & the unknown field that follows it - $into->write('NN', scalar(@offsets), $self->{'unk3'}); - - # Write the offsets (using 0 to indicate an unused slot) - $into->write('N*', map { (defined($_) && $_ > 0)? $_ : 0 } @offsets); - - # The offsets are always written in blocks of 256. - my($offsetcount) = scalar(@offsets) % 256; - if ($offsetcount > 0) { - # Fill out the last block - $into->write('N*', (0) x (256-$offsetcount)); - } - - # The DS_Store files only ever have one item in their - # table of contents, so I'm not sure if it needs to be sorted or what - my(@tockeys) = sort keys %{$self->{'toc'}}; - $into->write('N', scalar(@tockeys)); - foreach my $entry (@tockeys) { - $into->write('C a* N', length($entry), $entry, $self->{'toc'}->{$entry}); - } - - # And finally the freelists - for my $width ( 0 .. 31 ) { - my($blks) = $self->{'freelist'}->{$width}; - $into->write('N N*', scalar(@$blks), @$blks); - } -} - -=head2 $block = $allocator->blockByNumber(blocknumber[, write]) - -Retrieves a block by its block number (I block ID). - -If C is supplied and is true, then the returned block implements the -C method but not the C method. - -=head2 $block = $allocator->getBlock(offset, size) - -Retrieves a block (a BuddyAllocator::Block instance) by offset & length. -Normally you should use C instead of this method. - -=cut - -sub getBlock { - my($self, $offset, $size) = @_; - - return Mac::Finder::DSStore::BuddyAllocator::Block->new($self, $offset, $size); -} - -# Retrieve a block by its block number (small integer) -sub blockByNumber { - my($self, $id, $write) = @_; - my($addr) = $self->{offsets}->[$id]; - return undef unless $addr; - my($offset, $len); - $offset = $addr & ~0x1F; - $len = 1 << ( $addr & 0x1F ); -# print " node id $id is $len bytes at 0x".sprintf('%x', $offset)."\n"; - if (!defined($write) || !$write) { - return Mac::Finder::DSStore::BuddyAllocator::Block->new($self, $offset, $len); - } else { - return Mac::Finder::DSStore::BuddyAllocator::WriteBlock->new($self, $offset, $len); - } -} - -=head2 ( $offset, $size ) = $allocator->blockOffset(blockid) - -Retrieves the file offset and size in bytes of a given block. -The offset doesn't include the 4-byte fudge. -In scalar context, just returns the offset. - -=cut - -sub blockOffset { - my($self, $id) = @_; - my($addr) = $self->{offsets}->[$id]; - croak "Block $id is not allocated" unless $addr; - my($offset) = $addr & ~0x1F; - return $offset unless wantarray; - return ( $offset, 1 << ( $addr & 0x1F ) ); -} - -# Return freelist + index of a block's buddy in its freelist (or empty list) -sub _buddy { - my($self, $offset, $width) = @_; - my($freelist, $buddyaddr); - - $freelist = $self->{'freelist'}->{$width}; - $buddyaddr = $offset ^ ( 1 << $width ); - - return ($freelist, - grep { $freelist->[$_] == $buddyaddr } 0 .. $#$freelist ); -} - -# Free a block, coalescing ith buddies as needed. -sub _free { - my($self, $offset, $width) = @_; - - my($freelist, $buddyindex) = $self->_buddy($offset, $width); - - if(defined($buddyindex)) { - # our buddy is free. Coalesce, and add the coalesced block to flist. - my($buddyoffset) = splice(@$freelist, $buddyindex, 1); - #&logf("Combining %x with buddy %x", $offset, $buddyoffset); - $self->_free($offset & $buddyoffset, $width+1); - } else { - #&logf("Adding block %x to freelist %d", $offset, $width); - @$freelist = sort( @$freelist, $offset ); - } -} - -# Allocate a block of a specified width, splitting as needed. -sub _alloc { - my($self, $width) = @_; - - #&logf("Allocating a block of width %d", $width); - #$loglevel ++; - - my($flist) = $self->{'freelist'}->{$width}; - if (@$flist) { - # There is a block of the desired size; return it. - #&logf("Pulling %x from freelist", $flist->[0]); $loglevel --; - return shift @$flist; - } else { - # Allocate a block of the next larger size; split it. - my($offset) = $self->_alloc($width + 1); - # and put the other half on the free list. - my($buddy) = $offset ^ ( 1 << $width ); - #&logf("Splitting %x into %x and %x", $offset, $offset, $buddy); - #$loglevel ++; - $self->_free($buddy, $width); - #$loglevel -= 2; - return $offset; - } -} - -=head2 $blocknumber = $allocator->allocate($size, [$blocknumber]) - -Allocates or re-allocates a block to be at least C<$size> bytes long. -If C<$blocknumber> is given, the specified block will be grown or -shrunk if needed, otherwise a new block number will be chosen and -given to the allocated block. - -Unlike the libc C function, this may move a block even if the -block is not grown. - -=head2 $allocator->free($blocknumer) - -Releases the block number and the block associated with it back to the -block pool. - -=cut - -sub allocate { - my($self, $bytes, $blocknum) = @_; - my($offsets) = $self->{'offsets'}; - - #if(defined($blocknum)) { - # &logf("(Re)allocating %d bytes for blkid %d", $bytes, $blocknum); - #} - - if(!defined($blocknum)) { - $blocknum = 1; - # search for an empty slot, or extend the array - $blocknum++ while defined($offsets->[$blocknum]); - #&logf("Allocating %d bytes, assigning blkid %d", $bytes, $blocknum); - } - - #$loglevel ++; - - my($wantwidth) = 5; - # Minimum width, since that's how many low-order bits we steal for the tag - $wantwidth ++ while $bytes > 1 << $wantwidth; - - my($blkaddr, $blkwidth, $blkoffset); - - if(exists($offsets->[$blocknum]) && $offsets->[$blocknum]) { - $blkaddr = $offsets->[$blocknum]; - $blkwidth = $blkaddr & 0x1F; - $blkoffset = $blkaddr & ~0x1F; - if ($blkwidth == $wantwidth) { - #&logf("Block is already width %d, no change", $wantwidth); - #$loglevel --; - # The block is currently of the desired size. Leave it alone. - return $blocknum; - } else { - #&logf("Freeing wrong-sized block"); - #$loglevel ++; - # Free the current block, allocate a new one. - $self->_free($blkoffset, $blkwidth); - delete $offsets->[$blocknum]; - #$loglevel --; - } - } - - # Allocate a block, update the offsets table, and return the new offset - $blkoffset = $self->_alloc($wantwidth); - $blkaddr = $blkoffset | $wantwidth; - $offsets->[$blocknum] = $blkaddr; - #$loglevel --; - $blocknum; -} - -sub free { - my($self, $blknum) = @_; - my($blkaddr) = $self->{'offsets'}->[$blknum]; - - #&logf("Freeing block index %d", $blknum); - #$loglevel ++; - - if($blkaddr) { - my($blkoffset, $blkwidth); - $blkwidth = $blkaddr & 0x1F; - $blkoffset = $blkaddr & ~0x1F; - $self->_free($blkoffset, $blkwidth); - } - - delete $self->{'offsets'}->[$blknum]; - #$loglevel --; - undef; -} - -=head1 ATTRIBUTES - -=head2 $allocator->{toc} - -C holds a hashref whose keys are short strings and whose values -are integers. This table of contents is read and written as part of the -allocator's metadata but is not otherwise used by the allocator; -users of the allocator use it to find their data within the file. - -=head2 $allocator->{fh} - -The file handle passed in to C or C. If you find yourself needing -to use this, you should probably try to extend the class so that you don't. - -=cut - -# Used by ...::Block to get a positioned file handle. -sub _sought { - my($self, $offset) = @_; - - my($fh) = $self->{fh}; - $fh->seek($offset + $self->{fudge}, 0) - or croak; - $fh; -} - -package Mac::Finder::DSStore::BuddyAllocator::Block; - -=head1 BuddyAllocator::Block - -C instances are returned by the -C and C methods. They hold a pointer into -the file and provide a handful of useful methods. - -(There are also two other classes, C and C, -which might be returned instead. Think of this as an interface rather -than as a concrete class.) - -=head2 $block->read(length, [format]) - -Reads C bytes from the block (advancing the read pointer -correspondingly). If C is specified, the bytes read are -unpacked using the format; otherwise a byte string is returned. - -=head2 $block->length( ) - -Returns the length (or size) of this block. - -=head2 $block->seek(position[, whence]) - -Adjusts the read/write pointer within the block. - -=head2 $block->write(bytes) - -=head2 $block->write(format, items...) - -Writes data to the underlying file, at the position represented by this -block. If multiple arguments are given, the first is a format string -and the rest are the remaining arguments to C. - -=head2 $block->close([ zerofill ]) - -This is generally a no-op, but if called on a writable block with -C, then zeroes will be written from the current -location to the end of the allocated block. - -=head2 $block->copyback( ) - -Returns the block's contents as a string. For write blocks, this -reads from the file. This is just here for debugging purposes and -might change. - -=cut - -use strict; -use warnings; -use Carp; - -# -# Block objects are created by the buddy allocator; they're a -# reference to an array with the following components: -# -# [ $allocator, $value, $position] -# - -sub new { - my($class, $allocator, $offset, $size) = @_; - - my($value); - $allocator->_sought($offset)->read($value, $size) - > 0 or die; - # Previously, this died if we couldn't read the full block. - # Not sure if it's really an error not to read the full - # block if the next layer up doesn't need the full block. - # So now we're succeeding as long as we get something; if - # the reader overruns it'll die in substr(). - - bless([ $allocator, $value, 0 ], ref $class || $class); -} - -sub read { - my($self, $len, $unpack) = @_; - - my($pos) = $self->[2]; - die "out of range: pos=$pos len=$len max=".(length($self->[1])) if $pos + $len > length($self->[1]); - my($bytes) = substr($self->[1], $pos, $len); - $self->[2] = $pos + $len; - - $unpack? unpack($unpack, $bytes) : $bytes; -} - -sub length { - return CORE::length($_[0]->[1]); -} - -sub close { - 1; -} - -sub seek { - my($self, $pos, $whence) = @_; - $whence = 0 unless defined $whence; - if ($whence == 0) { - # pos = pos - } elsif ($whence == 1) { - $pos += $self->[2]; - } elsif ($whence == 2) { - $pos += $self->length(); - } else { - croak "seek: whence=$whence"; - } - $self->[2] = $pos; -} - -sub copyback { - return $_[0]->[1]; -} - -package Mac::Finder::DSStore::BuddyAllocator::WriteBlock; - -use strict; -use warnings; -use Carp; - -# -# Write blocks -# - -sub new { - my($class, $allocator, $offset, $size) = @_; - - croak "Missing arguments" - unless defined($offset) && defined($size); - croak "Bad offset" - if $offset <= 0; - - bless([ $allocator, undef, 0, $offset, $size ], ref $class || $class); -} - -sub read { - my($self) = @_; - - croak "This is a write-only block"; -} - -sub length { - return ($_[0]->[4]); -} - -sub seek { - my($self, $pos, $whence) = @_; - if ($whence == 0) { - $self->[2] = $pos; - } elsif ($whence == 1) { - $self->[2] += $pos; - } elsif ($whence == 2) { - $self->[2] = $self->length + $pos; - } else { - croak "seek: whence=$whence"; - } - undef $self->[1]; - $self; -} - -sub write { - my($self, $what, @args) = @_;; - - if (!defined($self->[1])) { - $self->[1] = $self->[0]->_sought($self->[2] + $self->[3]); - } - - if (@args) { - $what = pack($what, @args); - } - - my($wlen) = CORE::length($what); - - croak "Writing past end of block (writing $wlen at ".($self->[2]).", end is at ".($self->[4])."), died" - if $self->[2]+$wlen > $self->[4]; - - $self->[1]->write($what); - $self->[2] += $wlen; -} - -sub close { - my($self, $fill) = @_; - if (defined($fill) && $fill && $self->[2] < $self->[4]) { - $self->write("\0" x ($self->[4] - $self->[2])); - } - undef $self->[1]; - 1; -} - -# -# This is just here for debugging/testing purposes -# - -sub copyback { - my($self) = @_; - - my($r) = Mac::Finder::DSStore::BuddyAllocator::Block->new(@{$self}[0, 3, 2]); - - undef $self->[1]; # probably need to re-seek now - - return $r; -} - -package Mac::Finder::DSStore::BuddyAllocator::StringBlock; - -use strict; -use warnings; - -# -# This one's kind of handy, really, but is only used for debugging and -# test harnesses right now. -# - -sub new { - my($x) = ''; - bless(\$x, ref $_[0] || $_[0]); -} - -sub write { - my($self, $what, @args) = @_;; - - if (@args) { - $what = pack($what, @args); - } - - ${$self} .= $what; -} - -sub copyback { - ${$_[0]}; -} - -=head1 AUTHOR - -Written by Wim Lewis as part of the Mac::Finder::DSStore package. - -This file is copyright 2008 by Wim Lewis. -All rights reserved. -This program is free software; you can redistribute it and/or -modify it under the same terms as Perl itself. - - -=cut - -1; diff --git a/src/main/resources/de/itemis/mps/gradle/bundle_macos_jdk.sh b/src/main/resources/de/itemis/mps/gradle/bundle_macos_jdk.sh deleted file mode 100755 index cf5c247d..00000000 --- a/src/main/resources/de/itemis/mps/gradle/bundle_macos_jdk.sh +++ /dev/null @@ -1,80 +0,0 @@ -#!/bin/bash -if [[ $# -ne 5 ]]; then - cat < *.jnilib if any .jnilib files are present - (this used to be the case for earlier versions of MPS) -3. If JDK_FILE is given, extracts JDK_FILE under Contents/$JDK_DIRNAME/ -4. Sets executable permissions on Contents/MacOS/* and appropriate Contents/bin/ files -5. Compresses the result into OUT_FILE (tar/gzip) - -IMPORTANT: All arguments must use absolute paths because the script changes the current directory several times! -EOF - exit 1 -fi - -set -o errexit # Exit immediately on any error - -# Arguments -RCP_FILE="$1" -TMP_DIR="$2" -JDK_DIRNAME="$3" -JDK_FILE="$4" -OUT_FILE="$5" - -echo "Unzipping $RCP_FILE to $TMP_DIR..." -unzip -q -o "$RCP_FILE" -d "$TMP_DIR" -BUILD_NAME=$(ls "$TMP_DIR") -CONTENTS="$TMP_DIR/$BUILD_NAME/Contents" - -if ls "$CONTENTS/bin"/*.jnilib >& /dev/null; then - echo 'Creating symlinks from *.jnilib to *.dylib:' - for f in "$CONTENTS/bin"/*.jnilib; do - b="$(basename "$f" .jnilib)" - echo " $f -> $b.dylib" - ln -sf "$b.jnilib" "$(dirname "$f")/$b.dylib" - done - echo 'Done creating symlinks' -fi - -if [[ -n "$JDK_FILE" ]]; then - if [[ ! -f "$JDK_FILE" ]]; then - echo "$JDK_FILE is not a file" - exit 1 - fi - echo "Modifying Info.plist" - sed -i -e 's/1.6\*/1.6\+/' "$CONTENTS/Info.plist" - sed -i -e 's/NoJavaDistribution/custom-jdk-bundled/' "$CONTENTS/Info.plist" - - # TODO This command appears to be useless, it only inserts a blank line into the file. - sed -i -e '/public.app-category.developer-tools/G' "$CONTENTS/Info.plist" - - sed -i -e '/public.app-category.developer-tools/a\'$'\n''NSSupportsAutomaticGraphicsSwitching' "$CONTENTS/Info.plist" - - # sed -i -e seems to work with both GNU sed and OS X sed, with the latter leaving the original file with -e suffix - # as a backup. - rm -f "$CONTENTS/Info.plist-e" - echo "Info.plist has been modified" - - echo "Extracting JDK: $JDK_FILE to $CONTENTS/$JDK_DIRNAME" - mkdir -p "$CONTENTS/$JDK_DIRNAME" - pushd "$CONTENTS/$JDK_DIRNAME" - COPY_EXTENDED_ATTRIBUTES_DISABLE=true COPYFILE_DISABLE=true tar xvf "$JDK_FILE" --exclude='._jdk' || exit 1 - echo "JDK has been extracted" - popd -fi - -chmod a+x "$CONTENTS"/MacOS/* -if ls "$CONTENTS"/bin/*.py 1> /dev/null 2>&1; then - chmod a+x "$CONTENTS"/bin/*.py -fi -chmod a+x "$CONTENTS"/bin/fs* -chmod a+x "$CONTENTS"/bin/restarter - -cd "$TMP_DIR" -COPY_EXTENDED_ATTRIBUTES_DISABLE=true COPYFILE_DISABLE=true tar czvf "$OUT_FILE" "$BUILD_NAME" -echo "Bundle created in $OUT_FILE" diff --git a/src/main/resources/de/itemis/mps/gradle/mpsdmg.pl b/src/main/resources/de/itemis/mps/gradle/mpsdmg.pl deleted file mode 100644 index ec4ac550..00000000 --- a/src/main/resources/de/itemis/mps/gradle/mpsdmg.pl +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/perl -w - -use Mac::Finder::DSStore qw( writeDSDBEntries makeEntries ); -use Mac::Memory qw( ); -use Mac::Files qw( NewAliasMinimal ); - -$volumeName = $ARGV[0]; -$name = $ARGV[1]; -$bg_pic = $ARGV[2]; - -&writeDSDBEntries("/Volumes/$volumeName/.DS_Store", - &makeEntries(".background", Iloc_xy => [ 560, 170 ]), - &makeEntries(".DS_Store", Iloc_xy => [ 610, 170 ]), - &makeEntries(".fseventsd", Iloc_xy => [ 660, 170 ]), - &makeEntries(".Trashes", Iloc_xy => [ 710, 170 ]), - &makeEntries(" ", Iloc_xy => [ 335, 120 ]), - &makeEntries(".", - BKGD_alias => NewAliasMinimal("/Volumes/$volumeName/.background/$bg_pic"), - ICVO => 1, - fwi0_flds => [ 100, 400, 396, 855, "icnv", 0, 0 ], - fwsw => 170, - fwvh => 296, - icvo => pack('A4 n A4 A4 n*', "icv4", 100, "none", "botm", 0, 0, 0, 0, 0, 1, 0, 100, 1), - icvt => 12 - ), - &makeEntries("$name.app", Iloc_xy => [ 110, 120 ]) -); - diff --git a/src/main/resources/de/itemis/mps/gradle/mpsdmg.sh b/src/main/resources/de/itemis/mps/gradle/mpsdmg.sh deleted file mode 100755 index 12787aa3..00000000 --- a/src/main/resources/de/itemis/mps/gradle/mpsdmg.sh +++ /dev/null @@ -1,65 +0,0 @@ -#!/bin/bash -if [[ $# -ne 3 ]]; then - cat < -o [-j ] [-p -k -i ]" >&2 - exit 1;; - :) echo "option: -$OPTARG requires an argument" >&2 - exit 1;; - esac -done -shift $((OPTIND -1)) #remove options that have already been handled from $@ - -if [[ -z "$RCP_FILE" || -z "$OUTPUT_DIR" ]]; then - echo "$USAGE" - exit 1 -fi - -echo "Unzipping $RCP_FILE to $OUTPUT_DIR..." -unzip -q -o "$RCP_FILE" -d "$OUTPUT_DIR" -BUILD_NAME=$(ls "$OUTPUT_DIR") -CONTENTS="$OUTPUT_DIR/$BUILD_NAME/Contents" - -if ls "$CONTENTS/bin"/*.jnilib >& /dev/null; then - echo 'Creating symlinks from *.jnilib to *.dylib:' - for f in "$CONTENTS/bin"/*.jnilib; do - b="$(basename "$f" .jnilib)" - echo " $f -> $b.dylib" - ln -sf "$b.jnilib" "$(dirname "$f")/$b.dylib" - done - echo 'Done creating symlinks' -fi - -if [[ -n "$JDK_FILE" ]]; then - if [[ ! -f "$JDK_FILE" ]]; then - echo "$JDK_FILE is not a file" - exit 1 - fi - echo "Modifying Info.plist" - sed -i -e 's/1.6\*/1.6\+/' "$CONTENTS/Info.plist" - sed -i -e 's/NoJavaDistribution/custom-jdk-bundled/' "$CONTENTS/Info.plist" - - # TODO This command appears to be useless, it only inserts a blank line into the file. - sed -i -e '/public.app-category.developer-tools/G' "$CONTENTS/Info.plist" - - sed -i -e '/public.app-category.developer-tools/a\'$'\n''NSSupportsAutomaticGraphicsSwitching' "$CONTENTS/Info.plist" - - # sed -i -e seems to work with both GNU sed and OS X sed, with the latter leaving the original file with -e suffix - # as a backup. - rm -f "$CONTENTS/Info.plist-e" - echo "Info.plist has been modified" - - echo "Extracting JDK: $JDK_FILE to $CONTENTS/jre" - mkdir -p "$CONTENTS/jre" - pushd "$CONTENTS/jre" - COPY_EXTENDED_ATTRIBUTES_DISABLE=true COPYFILE_DISABLE=true tar xvf "$JDK_FILE" --exclude='._jdk' || exit 1 - echo "JDK has been extracted" - popd -fi - -HELP_FILE=$(ls "$CONTENTS/Resources/" | grep -i help) || HELP_FILE='' -HELP_DIR="$CONTENTS/Resources/$HELP_FILE/Contents/Resources/English.lproj/" - -if [[ -d "$HELP_DIR" ]]; then - echo "Building help indices for $HELP_DIR" - hiutil -Cagvf "$HELP_DIR/search.helpindex" "$HELP_DIR" -fi - -# Make sure your certificate is imported into local KeyChain -if [[ -n "$SIGN_PW" && -n "$SIGN_KEY_CHAIN" && -n "$SIGN_IDENTITY" ]]; then - echo "Signing application $BUILD_NAME" - echo "key chain: $SIGN_KEY_CHAIN" - echo "sign identity: $SIGN_IDENTITY" - security unlock-keychain -p $SIGN_PW $SIGN_KEY_CHAIN - codesign -v --deep -s "$SIGN_IDENTITY" "$OUTPUT_DIR/$BUILD_NAME" - echo "signing is done" - echo "check sign" - codesign -v "$OUTPUT_DIR/$BUILD_NAME" -vvvvv - echo "check sign done" -else - echo "for signing the application $BUILD_NAME: SIGN_PW, SIGN_KEY_CHAIN and SIGN_IDENTITY needs to be provided" -fi - -chmod a+x "$CONTENTS"/MacOS/* -chmod a+x "$CONTENTS"/bin/*.py -chmod a+x "$CONTENTS"/bin/fs* -chmod a+x "$CONTENTS"/bin/restarter \ No newline at end of file diff --git a/src/test/kotlin/test/migration/RunMigrationsTest.kt b/src/test/kotlin/test/migration/RunMigrationsTest.kt deleted file mode 100644 index cade9e10..00000000 --- a/src/test/kotlin/test/migration/RunMigrationsTest.kt +++ /dev/null @@ -1,217 +0,0 @@ -package test.migration - -import org.gradle.testkit.runner.GradleRunner -import org.gradle.testkit.runner.TaskOutcome -import org.hamcrest.CoreMatchers.containsString -import org.hamcrest.MatcherAssert.assertThat -import org.junit.Assert -import org.junit.Assert.assertEquals -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.rules.TemporaryFolder -import support.extractTestProject -import java.io.File - -class RunMigrationsTest { - @Rule - @JvmField - val testProjectDir: TemporaryFolder = TemporaryFolder() - private lateinit var settingsFile: File - private lateinit var buildFile: File - private lateinit var mpsTestPrjLocation: File - - private fun commonGradleScriptPart():String { - return """ - import java.net.URI - - plugins { - id("run-migrations") - } - - repositories { - mavenCentral() - maven { - url = URI("https://artifacts.itemis.cloud/repository/maven-mps") - } - } - - val mps = configurations.create("mps") - """.trimIndent() - } - - @Before - fun setup() { - settingsFile = testProjectDir.newFile("settings.gradle.kts") - settingsFile.writeText( - """ - rootProject.name = "hello-world" - """.trimIndent() - ) - buildFile = testProjectDir.newFile("build.gradle.kts") - mpsTestPrjLocation = testProjectDir.newFolder("mps-prj") - extractProject("test-project") - } - - private fun extractProject(name: String) = extractTestProject(name, mpsTestPrjLocation) - - @Test - fun `check run migrations works with MPS 2020_3`() { - buildFile.writeText( - """ - ${commonGradleScriptPart()} - - dependencies { - mps("com.jetbrains:mps:2020.3.4") - } - - runMigrations { - projectLocation = file("${mpsTestPrjLocation.toPath()}") - mpsConfig = mps - } - """.trimIndent() - ) - - val result = gradleRunner() - .withArguments("runMigrations") - .build() - Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":runMigrations")?.outcome) - } - - @Test - fun `check run migrations fails with MPS 2020_3 with force set to true`() { - buildFile.writeText( - """ - ${commonGradleScriptPart()} - - dependencies { - mps("com.jetbrains:mps:2020.3.4") - } - - runMigrations { - projectLocation = file("${mpsTestPrjLocation.toPath()}") - mpsConfig = mps - force = true - } - """.trimIndent() - ) - - val result = gradleRunner() - .withArguments("runMigrations") - .buildAndFail() - - assertThat(result.output, containsString("The force migration flag is only supported for MPS version 2021.3.0 and higher.")) - } - - - @Test - fun `check run migrations works with MPS 2021_3 with force flag`() { - buildFile.writeText( - """ - ${commonGradleScriptPart()} - - dependencies { - mps("com.jetbrains:mps:2021.3.2") - } - - runMigrations { - projectLocation = file("${mpsTestPrjLocation.toPath()}") - mpsConfig = mps - force = true - } - """.trimIndent() - ) - - val result = gradleRunner() - .withArguments("runMigrations") - .build() - - Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":runMigrations")?.outcome) - } - - @Test - fun `check run migrations fails with invalid project path`() { - buildFile.writeText( - """ - ${commonGradleScriptPart()} - - dependencies { - mps("com.jetbrains:mps:2021.3.2") - } - - runMigrations { - projectLocation = file("not_existing") - mpsConfig = mps - } - """.trimIndent() - ) - - val result = gradleRunner() - .withArguments("runMigrations") - .buildAndFail() - - assertThat(result.output, containsString("The path to the project doesn't exist:")) - } - - @Test - fun `check run migrations fails with invalid mps path`() { - buildFile.writeText( - """ - ${commonGradleScriptPart()} - - runMigrations { - projectLocation = file("${mpsTestPrjLocation.toPath()}") - mpsLocation = file("not_existing") - mpsVersion = "2021.3.2" - } - """.trimIndent() - ) - - val result = gradleRunner() - .withArguments("runMigrations") - .buildAndFail() - - assertThat(result.output, containsString("Specified MPS location does not exist or is not a directory:")) - } - - @Test - fun `MpsMigrate task works`() { - buildFile.writeText( - """ - import de.itemis.mps.gradle.tasks.MpsMigrate - - 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 migrate by tasks.registering(MpsMigrate::class) { - projectDirectories.from("$mpsTestPrjLocation") - mpsHome.set(layout.dir(resolveMps.map { it.destinationDir })) - } - """.trimIndent() - ) - - val result = gradleRunner().withArguments("migrate").build() - - assertEquals(TaskOutcome.SUCCESS, result.task(":migrate")?.outcome) - } - - private fun gradleRunner(): GradleRunner = GradleRunner.create() - .withProjectDir(testProjectDir.root) - .withPluginClasspath() -} diff --git a/src/test/kotlin/test/modelchecking/ModelCheckWithPluginTest.kt b/src/test/kotlin/test/modelchecking/ModelCheckWithPluginTest.kt deleted file mode 100644 index 01b69715..00000000 --- a/src/test/kotlin/test/modelchecking/ModelCheckWithPluginTest.kt +++ /dev/null @@ -1,312 +0,0 @@ -package test.modelchecking - -import de.itemis.mps.gradle.ErrorMessages -import org.gradle.testkit.runner.GradleRunner -import org.gradle.testkit.runner.TaskOutcome -import org.hamcrest.CoreMatchers -import org.hamcrest.MatcherAssert -import org.junit.Assert -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.rules.TemporaryFolder -import support.extractTestProject -import java.io.File - -class ModelCheckWithPluginTest { - @Rule - @JvmField - val testProjectDir: TemporaryFolder = TemporaryFolder() - private lateinit var settingsFile: File - private lateinit var buildFile: File - private lateinit var mpsTestPrjLocation: File - private lateinit var junitFile: File - - @Before - fun setup() { - settingsFile = testProjectDir.newFile("settings.gradle.kts") - buildFile = testProjectDir.newFile("build.gradle.kts") - mpsTestPrjLocation = testProjectDir.newFolder("mps-prj") - junitFile = File(mpsTestPrjLocation, "junit.xml") - } - - private fun extractProject(name: String) = extractTestProject(name, mpsTestPrjLocation) - - private fun settingsBoilerplate() = """ - rootProject.name = "hello-world" - """.trimIndent() - - private fun buildScriptBoilerplate(mpsVersion: String) = """ - plugins { - id("modelcheck") - } - - repositories { - mavenCentral() - maven("https://artifacts.itemis.cloud/repository/maven-mps") - } - - val mps = configurations.create("mps") - - dependencies { - mps("com.jetbrains:mps:$mpsVersion") - } - """.trimIndent() + "\n" - - @Test - fun `check model works with MPS 2022_2_2`() { - extractProject("test-project") - - settingsFile.writeText(settingsBoilerplate()) - - buildFile.writeText( - buildScriptBoilerplate("2022.2.2") + """ - modelcheck { - projectLocation = file("${mpsTestPrjLocation.toPath()}") - mpsConfig = mps - junitFile = file("${junitFile.absolutePath}") - } - """.trimIndent() - ) - - val result = GradleRunner.create() - .withProjectDir(testProjectDir.root) - .withArguments("checkmodels") - .withPluginClasspath() - .build() - Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":checkmodels")?.outcome) - Assert.assertTrue(junitFile.exists()) - } - - @Test - fun `explicit javaExec`() { - buildFile.writeText( - """ - import de.itemis.mps.gradle.downloadJBR.DownloadJbrForPlatform - - plugins { - id("modelcheck") - id("download-jbr") - } - - downloadJbr { - jbrVersion = "17.0.6-b469.82" - } - - repositories { - mavenCentral() - maven("https://artifacts.itemis.cloud/repository/maven-mps") - } - - modelcheck { - projectLocation = projectDir - mpsLocation = file("build/mps") - mpsVersion = "2022.2.2" - javaExec = tasks.getByName("downloadJbr").javaExecutable - } - - tasks.register("verify") { - dependsOn("downloadJbr") - doLast { - val launcherMatches = - tasks.getByName("checkmodels").javaLauncher.get().executablePath.asFile == - tasks.getByName("downloadJbr").javaExecutable - println("javaLauncher matches: " + launcherMatches) - } - } - """.trimIndent() - ) - - val result = GradleRunner.create() - .withProjectDir(testProjectDir.root) - .withArguments("--stacktrace", "verify") - .withPluginClasspath() - .build() - - Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":verify")?.outcome) - - // When javaExec is explicitly set, the launcher should match the downloaded executable - Assert.assertTrue("javaLauncher should match explicitly set executable", - result.output.contains("javaLauncher matches: true")) - } - - @Test - fun `check model fails if errors are found`() { - extractProject("test-project-with-errors") - - settingsFile.writeText(settingsBoilerplate()) - - buildFile.writeText( - buildScriptBoilerplate("2022.2.2") + - """ - modelcheck { - projectLocation = file("${mpsTestPrjLocation.toPath()}") - mpsConfig = mps - junitFile = file("${junitFile.absolutePath}") - } - """.trimIndent() - ) - - val result = GradleRunner.create() - .withProjectDir(testProjectDir.root) - .withArguments("checkmodels") - .withPluginClasspath() - .buildAndFail() - Assert.assertEquals(TaskOutcome.FAILED, result.task(":checkmodels")?.outcome) - Assert.assertTrue(junitFile.exists()) - } - - @Test - fun `check model works with latest MPS and excluded models`() { - extractProject("test-project-with-errors") - - settingsFile.writeText(settingsBoilerplate()) - - buildFile.writeText( - buildScriptBoilerplate("2022.2.2") + - """ - modelcheck { - projectLocation = file("${mpsTestPrjLocation.toPath()}") - mpsConfig = mps - junitFile = file("${junitFile.absolutePath}") - excludeModels = listOf("my.solution.with.errors.java") - } - """.trimIndent() - ) - - val result = GradleRunner.create() - .withProjectDir(testProjectDir.root) - .withArguments("checkmodels") - .withPluginClasspath() - .build() - Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":checkmodels")?.outcome) - Assert.assertTrue(junitFile.exists()) - } - - @Test - fun `check model fails with unsupported MPS`() { - extractProject("test-project") - - settingsFile.writeText(settingsBoilerplate()) - - buildFile.writeText( - buildScriptBoilerplate("2019.3.7") + - """ - modelcheck { - projectLocation = file("${mpsTestPrjLocation.toPath()}") - mpsConfig = mps - junitFile = file("${junitFile.absolutePath}") - - } - """.trimIndent() - ) - - val result = GradleRunner.create() - .withProjectDir(testProjectDir.root) - .withArguments("checkmodels") - .withPluginClasspath() - .buildAndFail() - MatcherAssert.assertThat(result.output, CoreMatchers.containsString(ErrorMessages.MPS_VERSION_NOT_SUPPORTED)) - } - - @Test - fun `check model works with set MPS version and path`() { - extractProject("test-project") - - settingsFile.writeText(settingsBoilerplate()) - - buildFile.writeText( - buildScriptBoilerplate("2022.2.2") + - """ - modelcheck { - projectLocation = file("${mpsTestPrjLocation.toPath()}") - mpsVersion = "2020.2.2" - mpsLocation = file(".") - junitFile = file("${junitFile.absolutePath}") - } - """.trimIndent() - ) - - GradleRunner.create() - .withProjectDir(testProjectDir.root) - .withArguments() - .withPluginClasspath() - .build() - } - - @Test - fun `check model fails with set MPS invalid version and path`() { - extractProject("test-project") - - settingsFile.writeText(settingsBoilerplate()) - - buildFile.writeText( - buildScriptBoilerplate("2022.2.2") + - """ - modelcheck { - projectLocation = file("${mpsTestPrjLocation.toPath()}") - mpsVersion = "2019.2.2" - mpsLocation = file(".") - junitFile = file("${junitFile.absolutePath}") - } - """.trimIndent() - ) - - GradleRunner.create() - .withProjectDir(testProjectDir.root) - .withArguments() - .withPluginClasspath() - .buildAndFail() - } - - @Test - fun `check model fails with only MPS version set`() { - extractProject("test-project") - - settingsFile.writeText(settingsBoilerplate()) - - buildFile.writeText( - buildScriptBoilerplate("2022.2.2") + - """ - modelcheck { - projectLocation = file("${mpsTestPrjLocation.toPath()}") - mpsVersion = "2022.2.2" - junitFile = file("${junitFile.absolutePath}") - } - """.trimIndent() - ) - - val result = GradleRunner.create() - .withProjectDir(testProjectDir.root) - .withArguments() - .withPluginClasspath() - .buildAndFail() - - MatcherAssert.assertThat(result.output, CoreMatchers.containsString(ErrorMessages.mustSetConfigOrLocation("modelcheck"))) - } - - @Test - fun `check model fails with only MPS path set`() { - settingsFile.writeText(settingsBoilerplate()) - - buildFile.writeText( - buildScriptBoilerplate("2022.2.2") + - """ - modelcheck { - projectLocation = file("${mpsTestPrjLocation.toPath()}") - mpsLocation = file(".") - junitFile = file("${junitFile.absolutePath}") - } - """.trimIndent() - ) - - val result = GradleRunner.create() - .withProjectDir(testProjectDir.root) - .withArguments() - .withPluginClasspath() - .buildAndFail() - - MatcherAssert.assertThat(result.output, CoreMatchers.containsString( - ErrorMessages.mustSetVersionWhenNoMpsConfiguration("modelcheck"))) - } -} diff --git a/src/test/kotlin/test/modelgeneration/GenerateModelsTest.kt b/src/test/kotlin/test/modelgeneration/GenerateModelsTest.kt deleted file mode 100644 index b27284ad..00000000 --- a/src/test/kotlin/test/modelgeneration/GenerateModelsTest.kt +++ /dev/null @@ -1,363 +0,0 @@ -package test.modelgeneration - -import de.itemis.mps.gradle.ErrorMessages -import org.gradle.testkit.runner.GradleRunner -import org.gradle.testkit.runner.TaskOutcome -import org.hamcrest.CoreMatchers -import org.hamcrest.MatcherAssert -import org.junit.* -import org.junit.rules.TemporaryFolder -import support.extractTestProject -import java.io.File - -class GenerateModelsTest { - @Rule - @JvmField - val testProjectDir: TemporaryFolder = TemporaryFolder() - private lateinit var settingsFile: File - private lateinit var buildFile: File - private lateinit var cp: List - private lateinit var mpsTestPrjLocation: File - - - @Before - fun setup() { - settingsFile = testProjectDir.newFile("settings.gradle.kts") - buildFile = testProjectDir.newFile("build.gradle.kts") - mpsTestPrjLocation = testProjectDir.newFolder("mps-prj") - extractTestProject("test-project", mpsTestPrjLocation) - } - - @Test - fun `generate works with latest MPS in MPS environment`() { - settingsFile.writeText( - """ - rootProject.name = "hello-world" - """.trimIndent() - ) - - buildFile.writeText( - """ - import java.net.URI - import de.itemis.mps.gradle.EnvironmentKind - - plugins { - id("generate-models") - } - - repositories { - mavenCentral() - maven { - url = URI("https://artifacts.itemis.cloud/repository/maven-mps") - } - } - - val mps = configurations.create("mps") - - dependencies { - mps("com.jetbrains:mps:2021.3.1") - } - - generate { - projectLocation = file("${mpsTestPrjLocation.toPath()}") - mpsConfig = mps - environmentKind.set(EnvironmentKind.MPS) - } - """.trimIndent() - ) - - val result = GradleRunner.create() - .withProjectDir(testProjectDir.root) - .withArguments("generate") - .withPluginClasspath() - .build() - Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":generate")?.outcome) - } - @Test - fun `generate fails with unsupported MPS`() { - settingsFile.writeText( - """ - rootProject.name = "hello-world" - """.trimIndent() - ) - - buildFile.writeText( - """ - import java.net.URI - - plugins { - id("generate-models") - } - - repositories { - mavenCentral() - maven { - url = URI("https://artifacts.itemis.cloud/repository/maven-mps") - } - } - - val mps = configurations.create("mps") - - dependencies { - mps("com.jetbrains:mps:2019.3.7") - } - - generate { - projectLocation = file("${mpsTestPrjLocation.toPath()}") - mpsConfig = mps - } - """.trimIndent() - ) - - val result = GradleRunner.create() - .withProjectDir(testProjectDir.root) - .withArguments("generate") - .withPluginClasspath() - .buildAndFail() - - MatcherAssert.assertThat(result.output, CoreMatchers.containsString(ErrorMessages.MPS_VERSION_NOT_SUPPORTED)) - } - @Test - fun `generate works with set MPS version and path`() { - settingsFile.writeText( - """ - rootProject.name = "hello-world" - """.trimIndent() - ) - - val mpsFolder = testProjectDir.newFolder("mps") - - buildFile.writeText( - """ - import java.net.URI - - plugins { - id("generate-models") - } - - repositories { - mavenCentral() - maven { - url = URI("https://artifacts.itemis.cloud/repository/maven-mps") - } - } - - val mps = configurations.create("mps") - - dependencies { - mps("com.jetbrains:mps:2020.3.3") - } - - val resolveMps by tasks.registering(Sync::class) { - from({ project.zipTree(mps.singleFile) }) - into("$mpsFolder") - } - - tasks.named("fakeBuildNumber") { - dependsOn(resolveMps) - } - - generate { - projectLocation = file("${mpsTestPrjLocation.toPath()}") - mpsVersion = "2020.2.2" - mpsLocation = file("$mpsFolder") - } - """.trimIndent() - ) - - val result = GradleRunner.create() - .withProjectDir(testProjectDir.root) - .withArguments("resolveMps", "generate") - .withPluginClasspath() - .build() - - Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":generate")?.outcome) - } - - @Test - fun `generate fails with set MPS invalid version and path`() { - settingsFile.writeText( - """ - rootProject.name = "hello-world" - """.trimIndent() - ) - - buildFile.writeText( - """ - import java.net.URI - - plugins { - id("generate-models") - } - - repositories { - mavenCentral() - maven { - url = URI("https://artifacts.itemis.cloud/repository/maven-mps") - } - } - - val mps = configurations.create("mps") - - dependencies { - mps("com.jetbrains:mps:2020.3.3") - } - - generate { - projectLocation = file("${mpsTestPrjLocation.toPath()}") - mpsVersion = "2019.2.2" - mpsLocation = file(".") - } - """.trimIndent() - ) - - val result = GradleRunner.create() - .withProjectDir(testProjectDir.root) - .withArguments() - .withPluginClasspath() - .buildAndFail() - - MatcherAssert.assertThat(result.output, CoreMatchers.containsString(ErrorMessages.MPS_VERSION_NOT_SUPPORTED)) - - } - @Test - fun `generate fails with only MPS version set`() { - settingsFile.writeText( - """ - rootProject.name = "hello-world" - """.trimIndent() - ) - - buildFile.writeText( - """ - import java.net.URI - - plugins { - id("generate-models") - } - - repositories { - mavenCentral() - maven { - url = URI("https://artifacts.itemis.cloud/repository/maven-mps") - } - } - - val mps = configurations.create("mps") - - dependencies { - mps("com.jetbrains:mps:2020.3.3") - } - - generate { - projectLocation = file("${mpsTestPrjLocation.toPath()}") - mpsVersion = "2020.2.2" - } - """.trimIndent() - ) - - val result = GradleRunner.create() - .withProjectDir(testProjectDir.root) - .withArguments() - .withPluginClasspath() - .buildAndFail() - MatcherAssert.assertThat(result.output, CoreMatchers.containsString(ErrorMessages.mustSetConfigOrLocation("generate"))) - } - @Test - fun `generate fails with only MPS path set`() { - settingsFile.writeText( - """ - rootProject.name = "hello-world" - """.trimIndent() - ) - - buildFile.writeText( - """ - import java.net.URI - - plugins { - id("generate-models") - } - - repositories { - mavenCentral() - maven { - url = URI("https://artifacts.itemis.cloud/repository/maven-mps") - } - } - - val mps = configurations.create("mps") - - dependencies { - mps("com.jetbrains:mps:2020.3.3") - } - - generate { - projectLocation = file("${mpsTestPrjLocation.toPath()}") - mpsLocation = file(".") - } - """.trimIndent() - ) - - val result = GradleRunner.create() - .withProjectDir(testProjectDir.root) - .withArguments() - .withPluginClasspath() - .buildAndFail() - MatcherAssert.assertThat(result.output, CoreMatchers.containsString(ErrorMessages.mustSetVersionWhenNoMpsConfiguration("generate"))) - } - - @Test - fun `explicit javaExec`() { - buildFile.writeText( - """ - import de.itemis.mps.gradle.downloadJBR.DownloadJbrForPlatform - - plugins { - id("generate-models") - id("download-jbr") - } - - downloadJbr { - jbrVersion = "17.0.6-b469.82" - } - - repositories { - mavenCentral() - maven("https://artifacts.itemis.cloud/repository/maven-mps") - } - - generate { - projectLocation = projectDir - mpsLocation = file("build/mps") - mpsVersion = "2022.2.2" - javaExec = tasks.getByName("downloadJbr").javaExecutable - } - - tasks.register("verify") { - dependsOn("downloadJbr") - doLast { - val launcherMatches = - tasks.getByName("generate").javaLauncher.get().executablePath.asFile == - tasks.getByName("downloadJbr").javaExecutable - println("javaLauncher matches: " + launcherMatches) - } - } - """.trimIndent() - ) - - val result = GradleRunner.create() - .withProjectDir(testProjectDir.root) - .withArguments("--stacktrace", "verify") - .withPluginClasspath() - .build() - - Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":verify")?.outcome) - - // When javaExec is explicitly set, the launcher should match the downloaded executable - Assert.assertTrue("javaLauncher should match explicitly set executable", - result.output.contains("javaLauncher matches: true")) - } - - -} diff --git a/src/test/kotlin/test/others/JBRDownloadTest.kt b/src/test/kotlin/test/others/JBRDownloadTest.kt deleted file mode 100644 index af284742..00000000 --- a/src/test/kotlin/test/others/JBRDownloadTest.kt +++ /dev/null @@ -1,291 +0,0 @@ -package test.others - -import org.gradle.testkit.runner.GradleRunner -import org.gradle.testkit.runner.TaskOutcome -import org.hamcrest.CoreMatchers.containsString -import org.hamcrest.MatcherAssert.assertThat -import org.junit.Assert -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.rules.TemporaryFolder -import java.io.File - -class JBRDownloadTest { - - val JBR_VERSION = "11_0_10-b1341.41" - - @Rule - @JvmField - val testProjectDir: TemporaryFolder = TemporaryFolder() - private lateinit var settingsFile: File - private lateinit var buildFile: File - - @Before - fun setup() { - settingsFile = testProjectDir.newFile("settings.gradle.kts") - buildFile = testProjectDir.newFile("build.gradle.kts") - } - - @Test - fun `download with download dir`() { - settingsFile.writeText(""" - rootProject.name = "hello-world" - """.trimIndent()) - - buildFile.writeText(""" - import java.net.URI - - plugins { - id("download-jbr") - } - - repositories { - mavenCentral() - maven { - url = URI("https://artifacts.itemis.cloud/repository/maven-mps") - } - } - - downloadJbr { - jbrVersion = "$JBR_VERSION" - downloadDir = file("jbrdl") - } - """.trimIndent()) - - val result = GradleRunner.create() - .withProjectDir(testProjectDir.root) - .withArguments("downloadJbr") - .withPluginClasspath() - .build() - Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":downloadJbr")?.outcome) - Assert.assertTrue(File(testProjectDir.root, "jbrdl").exists()) - } - - @Test - fun `download without download dir`() { - settingsFile.writeText(""" - rootProject.name = "hello-world" - """.trimIndent()) - - buildFile.writeText(""" - import java.net.URI - - plugins { - id("download-jbr") - } - - repositories { - mavenCentral() - maven { - url = URI("https://artifacts.itemis.cloud/repository/maven-mps") - } - } - - downloadJbr { - jbrVersion = "$JBR_VERSION" - } - """.trimIndent()) - - val result = GradleRunner.create() - .withProjectDir(testProjectDir.root) - .withArguments("downloadJbr") - .withPluginClasspath() - .build() - Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":downloadJbr")?.outcome) - Assert.assertTrue(File(testProjectDir.root, "build/jbrDownload").exists()) - } - - @Test - fun `download new version without distribution type`() { - settingsFile.writeText(""" - rootProject.name = "hello-world" - """.trimIndent()) - - buildFile.writeText(""" - import java.net.URI - - plugins { - id("download-jbr") - } - - repositories { - mavenCentral() - maven { - url = URI("https://artifacts.itemis.cloud/repository/maven-mps") - } - } - - downloadJbr { - jbrVersion = "$JBR_VERSION" - } - """.trimIndent()) - - val result = GradleRunner.create() - .withProjectDir(testProjectDir.root) - .withArguments("downloadJbr") - .withPluginClasspath() - .build() - Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":downloadJbr")?.outcome) - Assert.assertTrue(File(testProjectDir.root, "build/jbrDownload").exists()) - } - - @Test - fun `download new version with distribution type`() { - settingsFile.writeText(""" - rootProject.name = "hello-world" - """.trimIndent()) - - buildFile.writeText(""" - import java.net.URI - - plugins { - id("download-jbr") - } - - repositories { - mavenCentral() - maven { - url = URI("https://artifacts.itemis.cloud/repository/maven-mps") - } - } - - downloadJbr { - jbrVersion = "$JBR_VERSION" - distributionType = "jbr_nomod" - } - """.trimIndent()) - - val result = GradleRunner.create() - .withProjectDir(testProjectDir.root) - .withArguments("downloadJbr") - .withPluginClasspath() - .build() - Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":downloadJbr")?.outcome) - Assert.assertTrue(File(testProjectDir.root, "build/jbrDownload").exists()) - } - - @Test - fun `executed downloaded java`() { - settingsFile.writeText(""" - rootProject.name = "hello-world" - """.trimIndent()) - - buildFile.writeText(""" - import java.net.URI - - plugins { - id("download-jbr") - } - - repositories { - mavenCentral() - maven { - url = URI("https://artifacts.itemis.cloud/repository/maven-mps") - } - } - - downloadJbr { - jbrVersion = "$JBR_VERSION" - } - tasks.register("exec") { - dependsOn(tasks.getByName("downloadJbr", de.itemis.mps.gradle.downloadJBR.DownloadJbrForPlatform::class)) - executable = tasks.getByName("downloadJbr", de.itemis.mps.gradle.downloadJBR.DownloadJbrForPlatform::class).javaExecutable.absolutePath - args("--version") - } - """.trimIndent()) - - val result = GradleRunner.create() - .withProjectDir(testProjectDir.root) - .withArguments("exec") - .withPluginClasspath() - .build() - Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":exec")?.outcome) - } - - @Test - fun `executed downloaded java using JavaLauncher`() { - settingsFile.writeText(""" - rootProject.name = "hello-world" - """.trimIndent()) - - buildFile.writeText(""" - import java.net.URI - import de.itemis.mps.gradle.downloadJBR.DownloadJbrForPlatform - - plugins { - id("download-jbr") - } - - repositories { - mavenCentral() - maven { - url = URI("https://artifacts.itemis.cloud/repository/maven-mps") - } - } - - downloadJbr { - jbrVersion = "$JBR_VERSION" - } - - val downloadJbrTask = tasks.named("downloadJbr", DownloadJbrForPlatform::class) - - tasks.register("exec") { - dependsOn(downloadJbrTask) - javaLauncher.set(downloadJbrTask.flatMap { it.javaLauncher }) - jvmArgs("--version") - - // Main class will be ignored due to --version but has to be provided - mainClass.set("ignored") - } - """.trimIndent()) - - val result = GradleRunner.create() - .withProjectDir(testProjectDir.root) - .withArguments("exec") - .withPluginClasspath() - .build() - Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":exec")?.outcome) - assertThat(result.output, containsString("OpenJDK Runtime Environment JBR")) - } - - @Test - fun `use JavaLauncher with Gradle 8_13`() { - buildFile.writeText(""" - import de.itemis.mps.gradle.downloadJBR.DownloadJbrForPlatform - - plugins { - id("download-jbr") - } - - repositories { - mavenCentral() - maven("https://artifacts.itemis.cloud/repository/maven-mps") - } - - downloadJbr { - jbrVersion = "$JBR_VERSION" - } - - val downloadJbrTask = tasks.named("downloadJbr", DownloadJbrForPlatform::class) - - tasks.register("exec") { - dependsOn(downloadJbrTask) - javaLauncher.set(downloadJbrTask.flatMap { it.javaLauncher }) - jvmArgs("--version") - - // Main class will be ignored due to --version but has to be provided - mainClass.set("ignored") - } - """.trimIndent()) - - val result = GradleRunner.create() - .withProjectDir(testProjectDir.root) - .withArguments("exec") - .withGradleVersion("8.13") - .withPluginClasspath() - .build() - Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":exec")?.outcome) - assertThat(result.output, containsString("OpenJDK Runtime Environment JBR")) - } -} diff --git a/src/test/resources/test-project/.mps/migration.xml b/src/test/resources/test-project/.mps/migration.xml deleted file mode 100644 index e9a4b4b7..00000000 --- a/src/test/resources/test-project/.mps/migration.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file