Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add language plugins as dependencies #84

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 55 additions & 2 deletions src/main/kotlin/de/itemis/mps/gradle/Common.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,17 @@ package de.itemis.mps.gradle
import org.apache.log4j.Logger
import org.gradle.api.GradleException
import org.gradle.api.JavaVersion
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.tasks.Copy
import org.w3c.dom.Document
import org.w3c.dom.Node
import java.io.File
import java.util.zip.ZipFile
import javax.xml.parsers.DocumentBuilderFactory
import javax.xml.xpath.XPath
import javax.xml.xpath.XPathConstants
import javax.xml.xpath.XPathFactory

private val logger = Logger.getLogger("de.itemis.mps.gradle.common")

Expand All @@ -20,6 +29,7 @@ data class Macro(

open class BasePluginExtensions {
lateinit var mpsConfig: Configuration
var mpsPluginConfig: Configuration? = null
var mpsLocation: File? = null
var plugins: List<Plugin> = emptyList()
var pluginLocation: File? = null
Expand All @@ -29,7 +39,7 @@ open class BasePluginExtensions {
var javaExec: File? = null
}

fun validateDefaultJvm(){
fun validateDefaultJvm() {
if (JavaVersion.current() != JavaVersion.VERSION_11) logger.error("MPS requires Java 11 but current JVM uses ${JavaVersion.current()}, starting MPS will most probably fail!")
}

Expand All @@ -48,4 +58,47 @@ fun argsFromBaseExtension(extensions: BasePluginExtensions): MutableList<String>
extensions.plugins.map { "--plugin=${it.id}::${it.path}" }.asSequence(),
extensions.macros.map { "--macro=${it.name}::${it.value}" }.asSequence(),
prj).flatten().toMutableList()
}
}

fun Project.initializeMpsPluginDependencies(extension: BasePluginExtensions) {
val mpsLocation = extension.mpsLocation ?: File(buildDir, "mps")
extension.pluginLocation = extension.pluginLocation ?: File(mpsLocation, "plugins")
val pluginDeps = (extension.mpsPluginConfig?.dependencies?.asSequence() ?: emptySequence())
.flatMap { extension.mpsPluginConfig!!.files(it).asSequence() }
.map { toPlugin(it) }
.asSequence()
extension.plugins = sequenceOf(
extension.plugins.asSequence(),
pluginDeps
).flatten().toList()


}

fun Project.addCopyMpsPluginDepsTask(extension: BasePluginExtensions, dependencies: Any, pluginTail: String) =
tasks.create("copyMpsPluginsFor$pluginTail", Copy::class.java) {
dependsOn(dependencies)
into(extension.pluginLocation!!)
extension.mpsPluginConfig?.asFileTree?.forEach {
from(zipTree(it))
}
}

private fun toPlugin(file: File): de.itemis.mps.gradle.Plugin {
return ZipFile(file).use { zipFile ->
zipFile.entries().asSequence()
.first { it.name.endsWith("META-INF/plugin.xml") }
.let { entry ->
val name = entry.name.substring(0, entry.name.indexOf('/'))
val xmlDoc: Document = DocumentBuilderFactory.newInstance()
.newDocumentBuilder()
.parse(zipFile.getInputStream(entry))
xmlDoc.documentElement.normalize()
val xPath: XPath = XPathFactory.newInstance().newXPath()
val evaluate: Node =
xPath.compile("/idea-plugin/id")
.evaluate(xmlDoc, XPathConstants.NODE) as Node
de.itemis.mps.gradle.Plugin(evaluate.textContent, name)
}
}
}
11 changes: 8 additions & 3 deletions src/main/kotlin/de/itemis/mps/gradle/generate/Plugin.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package de.itemis.mps.gradle.generate

import de.itemis.mps.gradle.BasePluginExtensions
import de.itemis.mps.gradle.addCopyMpsPluginDepsTask
import de.itemis.mps.gradle.argsFromBaseExtension
import de.itemis.mps.gradle.initializeMpsPluginDependencies
import de.itemis.mps.gradle.validateDefaultJvm
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.tasks.Copy
import org.gradle.api.tasks.JavaExec
import org.gradle.kotlin.dsl.support.zipTo
Expand All @@ -28,6 +29,8 @@ open class GenerateMpsProjectPlugin : Plugin<Project> {
val mpsLocation = extension.mpsLocation ?: File(project.buildDir, "mps")

afterEvaluate {
initializeMpsPluginDependencies(extension)

val mpsVersion = extension
.mpsConfig
.resolvedConfiguration
Expand Down Expand Up @@ -89,8 +92,10 @@ open class GenerateMpsProjectPlugin : Plugin<Project> {
dependsOn(resolveMps)
}

val copyPlugins = addCopyMpsPluginDepsTask(extension, fake, "Generation")

tasks.create("generate", JavaExec::class.java) {
dependsOn(fake)
dependsOn(copyPlugins)
args(args)
if (extension.javaExec != null)
executable(extension.javaExec!!)
Expand All @@ -110,4 +115,4 @@ open class GenerateMpsProjectPlugin : Plugin<Project> {
}
}
}
}
}
10 changes: 6 additions & 4 deletions src/main/kotlin/de/itemis/mps/gradle/modelcheck/Plugin.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package de.itemis.mps.gradle.modelcheck

import de.itemis.mps.gradle.BasePluginExtensions
import de.itemis.mps.gradle.argsFromBaseExtension
import de.itemis.mps.gradle.validateDefaultJvm
import de.itemis.mps.gradle.*
import org.gradle.api.GradleException
import org.gradle.api.Plugin
import org.gradle.api.Project
Expand Down Expand Up @@ -30,6 +28,7 @@ open class ModelcheckMpsProjectPlugin : Plugin<Project> {

afterEvaluate {
val mpsLocation = extension.mpsLocation ?: File(project.buildDir, "mps")
initializeMpsPluginDependencies(extension)

val mpsVersion = extension
.mpsConfig
Expand Down Expand Up @@ -94,8 +93,11 @@ open class ModelcheckMpsProjectPlugin : Plugin<Project> {
}
into(mpsLocation)
}

val copyPlugins = addCopyMpsPluginDepsTask(extension, resolveMps, "Generation")

tasks.create("checkmodels", JavaExec::class.java) {
dependsOn(resolveMps)
dependsOn(copyPlugins)
args(args)
if (extension.javaExec != null)
executable(extension.javaExec!!)
Expand Down
123 changes: 123 additions & 0 deletions src/test/kotlin/GenerateModel.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import de.itemis.mps.gradle.generate.GenerateMpsProjectPlugin
import org.gradle.testkit.runner.GradleRunner
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 GenerateModelTest {

@Rule
@JvmField
val testProjectDir: TemporaryFolder = TemporaryFolder()

private lateinit var settingsFile: File
private lateinit var buildFile: File
private lateinit var cp: List<File>
private lateinit var file: File

@Before
fun setup() {
val pkgCount = GenerateMpsProjectPlugin::class.java.packageName.count { it == '.' }
file = File(GenerateMpsProjectPlugin::class.java.getResource("").toURI())
for (i in 0..pkgCount) {
file = file.parentFile
}

settingsFile = testProjectDir.newFile("settings.gradle")
buildFile = testProjectDir.newFile("build.gradle")
cp = javaClass.classLoader.getResource(
"plugin-classpath.txt")!!.readText().lines().map { File(it) }
testProjectDir.newFolder("buildSrc")
val buildSrcGradle = testProjectDir.newFile("buildSrc/build.gradle")
buildSrcGradle.writeText("""
plugins {
id 'java-gradle-plugin'
}
dependencies {
implementation files('${file.absolutePath}')
}
gradlePlugin {
plugins {
myPlugins {
id = 'generate-models'
implementationClass = 'de.itemis.mps.gradle.generate.GenerateMpsProjectPlugin'
}
}
}
""".trimIndent())
}

@Test
fun `test generate model`() {
val testProjFolder = File(javaClass.classLoader.getResource("test-project")!!.toURI())
val langPlugin = File(javaClass.classLoader.getResource("zargari-lang.zip")!!.toURI())
testProjFolder.copyRecursively(testProjectDir.root)
settingsFile.writeText("""
rootProject.name = "test-project"
""".trimIndent())
buildFile.writeText("""
ext.mpsMajor = "2020.1"
ext.mpsMinor = "6"

buildscript {
dependencies {
classpath files('${file.absolutePath}')
classpath files(${cp.map { """"${it.invariantSeparatorsPath}"""" }.joinToString()})
}
}

repositories {
maven { url 'https://projects.itemis.de/nexus/content/repositories/mbeddr' }
jcenter()
ivy {
url "https://download.jetbrains.com/mps/${'$'}mpsMajor/"
layout 'pattern', {
artifact "[module]-[revision].[ext]"
}
metadataSources { // skip downloading ivy.xml
artifact()
}
}
}

apply plugin: 'generate-models'

configurations {
mps
mpsPlugin
}

ext.mpsVersion = '2020.1.6'

generate {
projectLocation = new File("${testProjectDir.root.absolutePath}")
mpsConfig = configurations.mps
mpsPluginConfig = configurations.mpsPlugin
}

dependencies {
mps "com.jetbrains:mps:${'$'}mpsVersion"
mpsPlugin files('${langPlugin.absolutePath}')
}
""".trimIndent())

GradleRunner.create()
.withProjectDir(testProjectDir.root)
.withArguments("generate")
.withPluginClasspath(cp)
.build()
val generatedSrcDir = testProjectDir.root
.resolve("solutions")
.resolve("ZargariSolution")
.resolve("source_gen")
.resolve("ZargariSolution")
.resolve("java")
Assert.assertTrue(generatedSrcDir.exists())
Assert.assertTrue(generatedSrcDir.resolve("MyClass.java").exists())
}
}

8 changes: 8 additions & 0 deletions src/test/resources/test-project/.mps/modules.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MPSProject">
<projectModules>
<modulePath path="$PROJECT_DIR$/solutions/ZargariSolution/ZargariSolution.msd" folder="" />
</projectModules>
</component>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<solution name="ZargariSolution" uuid="2657ebd1-5697-4513-a68d-7cdfae0b16be" moduleVersion="0" compileInMPS="true">
<models>
<modelRoot contentPath="${module}" type="default">
<sourceRoot location="models" />
</modelRoot>
</models>
<facets>
<facet type="java">
<classes generated="true" path="${module}/classes_gen" />
</facet>
</facets>
<sourcePath />
<languageVersions />
<dependencyVersions>
<module reference="2657ebd1-5697-4513-a68d-7cdfae0b16be(ZargariSolution)" version="0" />
</dependencyVersions>
</solution>

Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<model ref="r:dbb077d6-8129-4184-ae04-3d9954305ec1(ZargariSolution.java)">
<persistence version="9" />
<languages>
<use id="27bd808c-5d4d-421d-beae-695731389bb2" name="ir.amv.laas.samples.zargari.lang" version="0" />
</languages>
<imports />
<registry>
<language id="ceab5195-25ea-4f22-9b92-103b95ca8c0c" name="jetbrains.mps.lang.core">
<concept id="1169194658468" name="jetbrains.mps.lang.core.structure.INamedConcept" flags="ng" index="TrEIO">
<property id="1169194664001" name="name" index="TrG5h" />
</concept>
</language>
<language id="27bd808c-5d4d-421d-beae-695731389bb2" name="ir.amv.laas.samples.zargari.lang">
<concept id="685495001935116272" name="ir.amv.laas.samples.zargari.lang.structure.Kezelazeseze" flags="ng" index="2c9EBy" />
</language>
</registry>
<node concept="2c9EBy" id="3g5K6Hd6fgn">
<property role="TrG5h" value="MyClass" />
</node>
</model>

Binary file added src/test/resources/zargari-lang.zip
Binary file not shown.