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

RFC: Infrastructure to execute code with a project #25

Merged
merged 15 commits into from
Nov 22, 2018
Merged
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
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,8 @@
bin/
.project
build/
mps-prj/solutions/my.build/build.xml
.gradle/
test/generate-build-solution/mps-prj/solutions/my.build/build.xml
.DS_Store
workspace.xml
29 changes: 15 additions & 14 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,31 +1,30 @@
import java.net.URI

plugins {
kotlin("jvm") version "1.2.50"
`maven-publish`
groovy
`java-gradle-plugin`
`kotlin-dsl`
`maven-publish`
}

val versionMajor = 1
val versionMinor = 0
val versionMinor = 1

group = "de.itemis.mps"
version = "1.0.0"




val nexusUsername: String? by project
val nexusPassword: String? by project

val kotlinArgParserVersion by extra { "2.0.7" }
val mpsVersion by extra { "2018.2.4" }


if (project.hasProperty("forceCI") || project.hasProperty("teamcity")) {
version = de.itemis.mps.gradle.GitBasedVersioning.getVersion(versionMajor, versionMinor)
version = if (!project.hasProperty("useSnapshot") &&
(project.hasProperty("forceCI") || project.hasProperty("teamcity"))) {
de.itemis.mps.gradle.GitBasedVersioning.getVersion(versionMajor, versionMinor)
} else {
version = "$versionMajor.$versionMinor-SNAPSHOT"
"$versionMajor.$versionMinor-SNAPSHOT"
}


Expand All @@ -47,21 +46,21 @@ dependencies {
}

gradlePlugin {
(plugins) {
"generate-models" {
plugins {
register("generate-models") {
id = "generate-models"
implementationClass = "de.itemis.mps.gradle.generate.GenerateMpsProjectPlugin"
}
}
}

tasks {
"wrapper"(Wrapper::class) {
gradleVersion = "4.8"
register ("wrapper", Wrapper::class) {
gradleVersion = "4.10.2"
distributionType = Wrapper.DistributionType.ALL
}

"setTeamCityBuildNumber" {
register("setTeamCityBuildNumber") {
doLast {
println("##teamcity[buildNumber '$version']")
}
Expand All @@ -71,6 +70,7 @@ tasks {
publishing {
repositories {
maven {
name = "itemis"
url = uri("https://projects.itemis.de/nexus/content/repositories/mbeddr")
credentials {
username = nexusUsername
Expand All @@ -81,3 +81,4 @@ publishing {
}



27 changes: 7 additions & 20 deletions execute-generators/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,10 @@ repositories {
val nexusUsername: String? by project
val nexusPassword: String? by project

val kotlinArgParserVersion: String by project
val mpsVersion: String by project

//define directories
val artifactsDir = File(buildDir, "artifacts")
val mpsDir = File(artifactsDir, "mps")
val kotlin_argparser_version = "2.0.7"
val pluginVersion = "1"
val mpsVersion = "2017.3.5"

val pluginVersion = "2"

version = if (project.hasProperty("forceCI") || project.hasProperty("teamcity")) {
de.itemis.mps.gradle.GitBasedVersioning.getVersion(mpsVersion, pluginVersion)
Expand All @@ -38,20 +34,11 @@ version = if (project.hasProperty("forceCI") || project.hasProperty("teamcity"))
val mpsConfiguration = configurations.create("mps")

dependencies {
compile(kotlin("stdlib"))
compile("com.xenomachina:kotlin-argparser:$kotlin_argparser_version")
compileOnly(fileTree(mpsDir).include("**/*.jar"))
implementation(kotlin("stdlib-jdk8"))
implementation("com.xenomachina:kotlin-argparser:$kotlinArgParserVersion")
mpsConfiguration("com.jetbrains:mps:$mpsVersion")
}


tasks {
val resolveMps by creating(Copy::class) {
from(mpsConfiguration.resolve().map { zipTree(it) })
into(mpsDir)
}

getByName("compileKotlin").dependsOn(resolveMps)
compileOnly(mpsConfiguration.resolve().map { zipTree(it) }.first().matching { include("lib/*.jar")})
implementation(project(":project-loader"))
}

tasks.withType<KotlinCompile> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,27 +72,36 @@ private fun createScript(proj: Project, models: List<org.jetbrains.mps.openapi.m
return scb.withFacetNames(DEFAULT_FACETS).withFinalTarget(ITarget.Name("jetbrains.mps.make.facets.Make.make")).toScript()
}

private fun makeModels(proj: Project, models: List<org.jetbrains.mps.openapi.model.SModel>) {
private fun makeModels(proj: Project, models: List<org.jetbrains.mps.openapi.model.SModel>) : Boolean {
val session = MakeSession(proj, MsgHandler(), true)
val res = ModelsToResources(models).resources().toList()
val makeService = BuildMakeService()

if (res.isEmpty()) {
logger.warn("nothing to generate")
return
return false
}
logger.info("starting generation")
val future = makeService.make(session, res, createScript(proj, models))
try {
future.get()
val result= future.get()
logger.info("generation finished")
return if (result.isSucessful) {
logger.info("generation result: successful")
true
} else {
logger.error("generation result: failed")
logger.error(result)
false
}
} catch (ex: Exception) {
logger.error("failed to generate", ex)
}
return false
}


fun generateProject(parsed: Args, project: Project) {
fun generateProject(parsed: GenerateArgs, project: Project): Boolean {
val ftr = AsyncResult<List<SModel>>()

project.modelAccess.runReadAction {
Expand All @@ -105,7 +114,7 @@ fun generateProject(parsed: Args, project: Project) {

val modelsToGenerate = ftr.resultSync

makeModels(project, modelsToGenerate)
return makeModels(project, modelsToGenerate)
}


Expand Down
Original file line number Diff line number Diff line change
@@ -1,99 +1,34 @@
package de.itemis.mps.gradle.generate

import com.intellij.application.options.PathMacrosImpl
import com.xenomachina.argparser.ArgParser
import com.xenomachina.argparser.default
import com.xenomachina.argparser.SystemExitException
import com.xenomachina.argparser.mainBody
import jetbrains.mps.tool.environment.Environment
import jetbrains.mps.tool.environment.EnvironmentConfig
import jetbrains.mps.tool.environment.IdeaEnvironment
import de.itemis.mps.gradle.project.loader.Args
import de.itemis.mps.gradle.project.loader.executeWithProject
import org.apache.log4j.Logger
import java.io.File

private val logger = Logger.getLogger("de.itemis.mps.gradle.generate")
const val PROPERTY_PLUGINS_PATH = "idea.plugins.path"

fun basicEnvironmentConfig(): EnvironmentConfig {

// This is a somewhat "save" set of default plugins. It should work with most of the projects we have encountered
// mbeddr projects won't build build with this set of plugins for unknown reasons, most probably the runtime
// dependencies in the mbeddr plugins are so messed up that they simply broken beyond repair.

val config = EnvironmentConfig
.emptyConfig()
.withDefaultPlugins()
.withDevkitPlugin()
.withBuildPlugin()
.withBootstrapLibraries()
.withWorkbenchPath()
.withGit4IdeaPlugin()
.withJavaPlugin()
.addPlugin("http-support", "jetbrains.mps.ide.httpsupport")
return config
}

private fun splitAtColumn(s: String): Pair<String, String> {
val split = s.split(":")
if (split.size < 2) {
throw RuntimeException("string if not of the right format. Expected <key>:<value>")
}
return Pair(split[0], split[1])
}

class Args(parser: ArgParser) {

class GenerateArgs(parser: ArgParser) : Args(parser) {
val models by parser.adding("--model",
help = "list of models to generate")
val plugins by parser.adding("--plugin",
help = "plugin to to load. The format is --plugin=<name>:<id>")
{ splitAtColumn(this) }
val macros by parser.adding("--macros",
help = "macro to define. The format is --macro=<name>:<value>")
{ splitAtColumn(this) }

val pluginLocation by parser.storing("--plugin-location",
help = "location to load additional plugins from").default<String?>(null)

val project by parser.positional("PROJECT",
help = "project to generate from")
}


fun main(args: Array<String>) = mainBody {

val parsed = ArgParser(args).parseInto(::Args)
val parsed = ArgParser(args).parseInto(::GenerateArgs)
var result = false

if (!parsed.pluginLocation.isNullOrEmpty()) {
System.setProperty(PROPERTY_PLUGINS_PATH, parsed.pluginLocation)
}

val cfg = basicEnvironmentConfig()

parsed.plugins.forEach { cfg.addPlugin(it.first, it.second) }

var env: Environment? = null
try {
logger.info("Creating Env")

env = IdeaEnvironment.getOrCreate(cfg)
env.flushAllEvents()

val pathMacrosImpl = PathMacrosImpl.getInstanceEx()
parsed.macros.forEach { pathMacrosImpl.setMacro(it.first, it.second) }

val project = env.openProject(File(parsed.project))

env.flushAllEvents()

generateProject(parsed, project)

env.flushAllEvents()
result = executeWithProject(parsed) { project -> generateProject(parsed, project) }
} catch (ex: java.lang.Exception) {
logger.fatal("error generating", ex)
} catch (t: Throwable) {
logger.fatal("error generating", t)
} finally {
if (env != null) env.dispose()
}
if(!result) {
throw SystemExitException("generation failed", -1)
}

System.exit(0)
Expand Down
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.8-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
40 changes: 40 additions & 0 deletions project-loader/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
kotlin("jvm")
`maven-publish`
`java-gradle-plugin`
}

group = "de.itemis.mps"

val mpsVersion: String by project
val kotlinArgParserVersion: String by project

val pluginVersion = "1"

version = if (project.hasProperty("forceCI") || project.hasProperty("teamcity")) {
de.itemis.mps.gradle.GitBasedVersioning.getVersion(mpsVersion, pluginVersion)
} else {
"$mpsVersion.$pluginVersion-SNAPSHOT"
}

repositories {
mavenCentral()
maven {
url = uri("https://projects.itemis.de/nexus/content/repositories/mbeddr")
}
}

val mpsConfiguration = configurations.create("mps")

dependencies {
implementation(kotlin("stdlib-jdk8"))
mpsConfiguration("com.jetbrains:mps:$mpsVersion")
implementation("com.xenomachina:kotlin-argparser:$kotlinArgParserVersion")
compileOnly(mpsConfiguration.resolve().map { zipTree(it) }.first().matching { include("lib/*.jar")})
}

tasks.withType<KotlinCompile> {
kotlinOptions.jvmTarget = "1.8"
}
41 changes: 41 additions & 0 deletions project-loader/src/main/kotlin/Args.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package de.itemis.mps.gradle.project.loader

import com.xenomachina.argparser.ArgParser
import com.xenomachina.argparser.default
import java.io.File

private fun <T> splitAndCreate(str: String, creator: (String, String) -> T): T {
val split = str.split(":")
if (split.size < 2) {
throw RuntimeException("string if not of the right format. Expected <key>:<value>")
}
return creator(split[0], split[1])
}

private fun toMacro(str: String) = splitAndCreate(str, ::Macro)
private fun toPlugin(str: String) = splitAndCreate(str, ::Plugin)

/**
* Default set of arguments required to start a "headless" MPS. This class should be used by other users of the
* project-loader in order to establish a somewhat standardised command line interface. Passing instances of this or
* subclasses to [executeWithProject] is directly supported.
*/
open class Args(parser: ArgParser) {

val plugins by parser.adding("--plugin",
help = "plugin to to load. The format is --plugin=<path>:<id>")
{ toPlugin(this) }

val macros by parser.adding("--macro",
help = "macro to define. The format is --macro=<name>:<value>")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so is this --macros or --macro?

{ toMacro(this) }

val pluginLocation by parser.storing("--plugin-location",
help = "location to load additional plugins from") { File(this) }.default<File?>(null)

val buildNumber by parser.storing("--build-number",
help = "build number used to determine if the plugins are compatible").default<String?>(null)

val project by parser.storing("--project",
help = "project to generate from") { File(this) }
}
Loading