From 5a34899098d6cb0558b46573e504f5f8104fb434 Mon Sep 17 00:00:00 2001 From: ComFreek Date: Wed, 13 Mar 2019 10:21:30 +0100 Subject: [PATCH] Make SBT build relative to SBT's baseDirectory (#449) Make SBT build relative to SBT's baseDirectory to allow other SBT projects to reference MMT as a subproject. See https://stackoverflow.com/a/55107829 Special thanks go to the author of that answer: thirstycrow --- src/build.sbt | 92 ++++++++----- src/project/Utils.scala | 298 +++++++++++++++++++++------------------- src/travis.sbt | 195 +++++++++++++------------- 3 files changed, 315 insertions(+), 270 deletions(-) diff --git a/src/build.sbt b/src/build.sbt index 1004a62358..3b97de5bed 100644 --- a/src/build.sbt +++ b/src/build.sbt @@ -1,10 +1,21 @@ -import scala.io.Source +import Utils.utils import sbt.Keys._ +import scala.io.Source + +utils in ThisBuild := Utils((baseDirectory in src).value) + +// If in doubt, always use utils.root or the other File properties on utils to construct +// paths! +// This ensures other SBT projects being able to use MMT as a subproject in a multiproject SBT +// build, see https://github.com/UniFormal/MMT/pull/449. + // ================================= // META-DATA and Versioning // ================================= -version in ThisBuild := {Source.fromFile("mmt-api/resources/versioning/system.txt").getLines.mkString.trim} +version in ThisBuild := { + Source.fromFile(baseDirectory.value / "mmt-api/resources/versioning/system.txt").getLines.mkString.trim +} val now = { import java.text.SimpleDateFormat @@ -32,7 +43,7 @@ lazy val mmtMainClass = "info.kwarc.mmt.api.frontend.Run" // ================================= scalaVersion in Global := "2.12.3" scalacOptions in Global := Seq( - "-feature", "-language:postfixOps", "-language:implicitConversions", "-deprecation", + "-feature", "-language:postfixOps", "-language:implicitConversions", "-deprecation", "-Xmax-classfile-name", "128", // fix long classnames on weird filesystems "-sourcepath", baseDirectory.value.getAbsolutePath // make sure that all scaladoc source paths are relative ) @@ -60,7 +71,7 @@ scalacOptions in(ScalaUnidoc, unidoc) ++= Opts.doc.title("MMT") ++: Opts.doc.sourceUrl({ val repo = System.getenv("TRAVIS_REPO_SLUG") - s"https://github.com/${if(repo != null) repo else "UniFormal/MMT"}/blob/master/src€{FILE_PATH}.scala" + s"https://github.com/${if (repo != null) repo else "UniFormal/MMT"}/blob/master/src€{FILE_PATH}.scala" }) target in(ScalaUnidoc, unidoc) := file("../apidoc") @@ -104,9 +115,9 @@ def mmtProjectsSettings(nameStr: String) = commonSettings(nameStr) ++ Seq( scalaSource in Test := baseDirectory.value / "test" / "scala", resourceDirectory in Compile := baseDirectory.value / "resources", - unmanagedBase := baseDirectory.value / "lib", + unmanagedBase := baseDirectory.value / "lib", - publishTo := Some(Resolver.file("file", Utils.deploy.toJava / " main")), + publishTo := Some(Resolver.file("file", utils.value.deploy.toJava / " main")), install := {}, deploy := Utils.deployPackage("main/" + nameStr + ".jar").value @@ -116,6 +127,7 @@ def mmtProjectsSettings(nameStr: String) = commonSettings(nameStr) ++ Seq( // EXCLUDED PROJECTS // ================================= import VersionSpecificProject._ + lazy val excludedProjects = { Exclusions() .java7(repl, odk) @@ -132,13 +144,13 @@ lazy val src = (project in file(".")). enablePlugins(ScalaUnidocPlugin). exclusions(excludedProjects). aggregatesAndDepends( - mmt, api, - lf, concepts, tptp, owl, mizar, frameit, mathscheme, pvs, metamath, tps, imps, isabelle, odk, specware, stex, mathhub, planetary, interviews, latex, openmath, oeis, repl, got, coq, - tiscaf, lfcatalog, - jedit, intellij + mmt, api, + lf, concepts, tptp, owl, mizar, frameit, mathscheme, pvs, metamath, tps, imps, isabelle, odk, specware, stex, mathhub, planetary, interviews, latex, openmath, oeis, repl, got, coq, + tiscaf, lfcatalog, + jedit, intellij ). settings( - unidocProjectFilter in (ScalaUnidoc, unidoc) := excludedProjects.toFilter, + unidocProjectFilter in(ScalaUnidoc, unidoc) := excludedProjects.toFilter, // add the test folder to the test sources // but don't actually run any of them scalaSource in Test := baseDirectory.value / "test", @@ -148,13 +160,17 @@ lazy val src = (project in file(".")). // This is the main project. 'mmt/deploy' compiles all relevants subprojects, builds a self-contained jar file, and puts into the deploy folder, from where it can be run. lazy val mmt = (project in file("mmt")). exclusions(excludedProjects). - dependsOn(tptp, stex, pvs, specware, oeis, odk, jedit, latex, openmath, imps, isabelle, repl, concepts, interviews, mathhub, python, intellij,coq). + dependsOn(tptp, stex, pvs, specware, oeis, odk, jedit, latex, openmath, imps, isabelle, repl, concepts, interviews, mathhub, python, intellij, coq). settings(mmtProjectsSettings("mmt"): _*). settings( exportJars := false, publish := {}, - deploy := { - assembly in Compile map Utils.deployTo(Utils.deploy / "mmt.jar") + deploy := Def.taskDyn { + val jar = (assembly in Compile).value + val u = utils.value + Def.task { + Utils.deployTo(u.deploy / "mmt.jar")(jar) + } }.value, assemblyExcludedJars in assembly := { val cp = (fullClasspath in assembly).value @@ -172,13 +188,13 @@ lazy val mmt = (project in file("mmt")). // MMT is split into multiple subprojects to that are managed independently. -val apiJars = Seq( +def apiJars(u: Utils) = Seq( "scala-compiler.jar", "scala-reflect.jar", "scala-parser-combinators.jar", "scala-xml.jar", "xz.jar", -).map(Utils.lib.toJava / _ ) +).map(u.lib.toJava / _) // The kernel upon which everything else depends. Maintainer: Florian lazy val api = (project in file("mmt-api")). @@ -188,8 +204,8 @@ lazy val api = (project in file("mmt-api")). settings( scalacOptions in Compile ++= Seq("-language:existentials"), scalaSource in Compile := baseDirectory.value / "src" / "main", - unmanagedJars in Compile ++= apiJars, - unmanagedJars in Test ++= apiJars, + unmanagedJars in Compile ++= apiJars(utils.value), + unmanagedJars in Test ++= apiJars(utils.value), ) @@ -200,7 +216,7 @@ lazy val lf = (project in file("mmt-lf")). dependsOn(lfcatalog). settings(mmtProjectsSettings("mmt-lf"): _*). settings( -// libraryDependencies += "org.scala-lang" % "scala-parser-combinators" % "2.12.3" % "test", + // libraryDependencies += "org.scala-lang" % "scala-parser-combinators" % "2.12.3" % "test", ) // ================================= @@ -226,7 +242,7 @@ lazy val jedit = (project in file("jEdit-mmt")). resourceDirectory in Compile := baseDirectory.value / "src/resources", unmanagedJars in Compile ++= jeditJars map (baseDirectory.value / "lib" / _), deploy := Utils.deployPackage("main/MMTPlugin.jar").value, - install := Utils.installJEditJars + install := utils.value.installJEditJars ) // MMT IntelliJ-Plugin. Maintainer: Dennis @@ -237,7 +253,7 @@ lazy val intellij = (project in file("intellij-mmt")). lazy val coq = (project in file("mmt-coq")). dependsOn(api, lf). settings(mmtProjectsSettings("mmt-coq"): _*) - + // using MMT as a part of LaTeX. Maintainer: Florian lazy val latex = (project in file("latex-mmt")). dependsOn(stex). @@ -271,10 +287,10 @@ lazy val interviews = (project in file("mmt-interviews")). // using MMT from Python via Py4J, maintainer: Florian lazy val python = (project in file("python-mmt")). - dependsOn(api,odk). + dependsOn(api, odk). settings(mmtProjectsSettings("python-mmt"): _*). settings(unmanagedJars in Compile += baseDirectory.value / "lib" / "py4j0.10.7.jar") - + // graph optimization. Maintainer: Michael Banken lazy val got = (project in file("mmt-got")). dependsOn(api). @@ -304,8 +320,8 @@ lazy val concepts = (project in file("concept-browser")). libraryDependencies ++= Seq( "org.ccil.cowan.tagsoup" % "tagsoup" % "1.2" ), - unmanagedJars in Compile += Utils.lib.toJava / "scala-xml.jar" - ) + unmanagedJars in Compile += utils.value.lib.toJava / "scala-xml.jar" + ) // ================================= // MMT Projects: plugins for working with other languages in MMT @@ -355,14 +371,14 @@ lazy val metamath = (project in file("mmt-metamath")). // plugin for reading isabelle. Author: Makarius Wenzel // This only works if an Isabelle environment is present. If not, we use an empty dummy project. lazy val isabelle_root = - System.getenv().getOrDefault("ISABELLE_ROOT", System.getProperty("isabelle.root", "")) +System.getenv().getOrDefault("ISABELLE_ROOT", System.getProperty("isabelle.root", "")) lazy val isabelle_jars = if (isabelle_root == "") Nil else List(file(isabelle_root) / "lib" / "classes" / "Pure.jar") lazy val isabelle = (project in file(if (isabelle_root == "") "mmt-isabelle/dummy" else "mmt-isabelle")). - dependsOn(api, lf). - settings(mmtProjectsSettings("mmt-isabelle"): _*). - settings(unmanagedJars in Compile ++= isabelle_jars) + dependsOn(api, lf). + settings(mmtProjectsSettings("mmt-isabelle"): _*). + settings(unmanagedJars in Compile ++= isabelle_jars) // plugin for reading TPS lazy val tps = (project in file("mmt-tps")). @@ -394,9 +410,9 @@ lazy val oeis = (project in file("mmt-oeis")). dependsOn(planetary). settings(mmtProjectsSettings("mmt-oeis"): _*). settings( - unmanagedJars in Compile += Utils.lib.toJava / "scala-parser-combinators.jar" + unmanagedJars in Compile += utils.value.lib.toJava / "scala-parser-combinators.jar" ) - + // ================================= // DEPENDENT PROJECTS (projects that do not use mmt-api) // ================================= @@ -408,7 +424,7 @@ lazy val tiscaf = (project in file("tiscaf")). scalacOptions in Compile ++= Seq("-language:reflectiveCalls"), scalaSource in Compile := baseDirectory.value / "src/main/scala", libraryDependencies ++= Seq( -// "net.databinder.dispatch" %% "dispatch-core" % "0.11.3" % "test", + // "net.databinder.dispatch" %% "dispatch-core" % "0.11.3" % "test", "org.slf4j" % "slf4j-simple" % "1.7.12" % "test", "org.scala-lang" % "scala-compiler" % scalaVersion.value ), @@ -421,11 +437,15 @@ lazy val lfcatalog = (project in file("lfcatalog")). settings(commonSettings("lfcatalog")). settings( scalaSource in Compile := baseDirectory.value / "src", - publishTo := Some(Resolver.file("file", Utils.deploy.toJava / " main")), - deployLFCatalog := { - assembly in Compile map Utils.deployTo(Utils.deploy / "lfcatalog" / "lfcatalog.jar") + publishTo := Some(Resolver.file("file", utils.value.deploy.toJava / " main")), + deployLFCatalog := Def.taskDyn { + val jar = (assembly in Compile).value + val u = utils.value + Def.task { + Utils.deployTo(u.deploy / "lfcatalog" / "lfcatalog.jar")(jar) + } }.value, - unmanagedJars in Compile += Utils.lib.toJava / "scala-xml.jar" + unmanagedJars in Compile += utils.value.lib.toJava / "scala-xml.jar" ) // ================================= diff --git a/src/project/Utils.scala b/src/project/Utils.scala index 2f9b94fd47..f9bd398ee2 100644 --- a/src/project/Utils.scala +++ b/src/project/Utils.scala @@ -1,138 +1,160 @@ -import java.nio.file.Files -import java.nio.file.StandardCopyOption._ - -object Utils { - /** MMT root directory */ - val root = File("..").canonical - /** source folder */ - val src = root / "src" - /** MMT deploy directory */ - val deploy = root / "deploy" - /** MMT deploy lib directory */ - val lib = deploy / "lib" - - /** - * settings syntax is: 1 instance of 'key: value' per line - */ - val settingsFile = src / "mmt-sbt-settings" - import collection.mutable.Map - def settings: Map[String,String] = if (settingsFile.exists) File.readProperties(settingsFile) else Map[String,String]() - - /** executes a shell command (in the src folder) */ - def runscript(command: String) = sys.process.Process(Seq(command), src.getAbsoluteFile).!! - - def error(s: String) = throw new Exception(s) - - // ************************************************** deploy-specific code (see also the TaskKey's deploy and deployFull) - - /** - * packages the compiled binaries and copies to deploy - */ - import sbt.Keys.packageBin - import sbt._ - def deployPackage(name: String): Def.Initialize[Task[Unit]] = - packageBin in Compile map {jar => deployTo(Utils.deploy / name)(jar)} - - /** - * packages the compiled binaries and copies to deploy - */ - def deployMathHub(target: File): Def.Initialize[Task[Unit]] = - packageBin in Compile map {jar => deployTo(target)(jar)} - - /* - * copies files to deploy folder - */ - def deployTo(target: File)(jar: sbt.File): Unit = { - Files.copy(jar.toPath, target.toPath, REPLACE_EXISTING) - println("copied file: " + jar) - println("to file: " + target) - } - - - // ************************************************** MathHub-specific code - - private val mathhub = "mathhub-folder" - lazy val mathhubFolder: File = { - settings.get(mathhub) match { - case None => - println(s"no key $mathhub in $settingsFile") - throw new Exception - case Some(s) => - File(s) - } - } - - // ************************************************** jEdit-specific code - - // keys for build settings - val jeditSettingsFolder = "jedit-settings-folder" - val startJEDit = "start-jedit" - val killJEdit = "kill-jedit" - - - /** MMT jEditPlugin release jars directory */ - val jEditPluginRelease = deploy/"jedit-plugin"/"plugin"/"jars" - - /** These methods are used by the target jedit/install to copy files to the local jEdit installation */ - /** copy MMT jar to jEdit settings directory */ - def installJEditJars { - settings.get(killJEdit).foreach {x => runscript(x)} - Thread.sleep(500) - val fname = settings.get(jeditSettingsFolder).getOrElse { - error(s"cannot copy jars because there is no setting '$jeditSettingsFolder' in $settingsFile") - return - } - val jsf = File(fname) / "jars" - copyJEditJars(jsf) - settings.get(startJEDit).foreach {x => runscript(x)} - } - /** copy all jars to jEditPluginRelease */ - def releaseJEditJars { - copyJEditJars(jEditPluginRelease) - } - /** copy all jEdit jars to a directory */ - def copyJEditJars(to: File) { - copy(deploy/"mmt.jar", to/"MMTPlugin.jar") - // all other jars are bundled with the above - // val jEditDeps = List("scala-library.jar","scala-parser-combinators.jar","scala-reflect.jar","scala-xml.jar","tiscaf.jar") - // jEditDeps.foreach {f => copy(deploy/"lib"/f, to/f)} - // copy(deploy/"lfcatalog"/"lfcatalog.jar", to/"lfcatalog.jar") - } - - - // ************************************************** file system utilities - - /** copy a file */ - def copy(from: File, to: File) { - println(s"copying $from to $to") - if (!from.exists) { - error(s"error: file $from not found (when trying to copy it to $to)") - } else if (!to.exists || from.lastModified > to.lastModified) { - Files.copy(from.toPath, to.toPath, REPLACE_EXISTING) - } else { - println("skipped (up-to-date)") - } - println("\n") - } - - /** - * Recursively deletes a given folder - * @param log - * @param path - */ - def delRecursive(log: Logger, path: File): Unit = { - def delRecursive(path: File): Unit = { - path.listFiles foreach { f => - if (f.isDirectory) delRecursive(f) - else { - f.delete() - log.debug("deleted file: " + path) - } - } - path.delete() - log.debug("deleted directory: " + path) - } - if (path.exists && path.isDirectory) delRecursive(path) - else log.warn("ignoring missing directory: " + path) - } -} +import java.nio.file.Files +import java.nio.file.StandardCopyOption._ + +import sbt.Keys.packageBin +import sbt._ + +object Utils { + + // + val utils = settingKey[Utils]("Utils") + + def apply(base: java.io.File) = new Utils(File(base)) + + def error(s: String) = throw new Exception(s) + + // ************************************************** deploy-specific code (see also the TaskKey's deploy and deployFull) + + /** + * packages the compiled binaries and copies to deploy + */ + def deployMathHub(target: File): Def.Initialize[Task[Unit]] = + packageBin in Compile map { jar => Utils.deployTo(target)(jar) } + + /* + * copies files to deploy folder + */ + def deployTo(target: File)(jar: sbt.File): Unit = { + Files.copy(jar.toPath, target.toPath, REPLACE_EXISTING) + println("copied file: " + jar) + println("to file: " + target) + } + + /** + * packages the compiled binaries and copies to deploy + */ + def deployPackage(name: String): Def.Initialize[Task[Unit]] = Def.taskDyn { + val jarFile = (packageBin in Compile).value + Def.task { + Utils.deployTo(utils.value.deploy / name)(jarFile) + } + } + + // ************************************************** file system utilities + + /** copy a file */ + def copy(from: File, to: File) { + println(s"copying $from to $to") + if (!from.exists) { + Utils.error(s"error: file $from not found (when trying to copy it to $to)") + } else if (!to.exists || from.lastModified > to.lastModified) { + Files.copy(from.toPath, to.toPath, REPLACE_EXISTING) + } else { + println("skipped (up-to-date)") + } + println("\n") + } + + /** + * Recursively deletes a given folder + * + * @param log + * @param path + */ + def delRecursive(log: Logger, path: File): Unit = { + def delRecursive(path: File): Unit = { + path.listFiles foreach { f => + if (f.isDirectory) delRecursive(f) + else { + f.delete() + log.debug("deleted file: " + path) + } + } + path.delete() + log.debug("deleted directory: " + path) + } + + if (path.exists && path.isDirectory) delRecursive(path) + else log.warn("ignoring missing directory: " + path) + } +} + +/** + * @param base File path to the "/src" directory. + */ +class Utils(base: File) { + /** MMT root directory */ + val root: File = (base / "..").canonical + /** source folder */ + val src: File = root / "src" + + /** MMT deploy directory */ + val deploy: File = root / "deploy" + /** MMT deploy lib directory */ + val lib: File = deploy / "lib" + + /** + * settings syntax is: 1 instance of 'key: value' per line + */ + val settingsFile: File = src / "mmt-sbt-settings" + + import collection.mutable.Map + + def settings: Map[String, String] = if (settingsFile.exists) File.readProperties(settingsFile) else Map[String, String]() + + /** executes a shell command (in the src folder) */ + def runscript(command: String): String = + sys.process.Process(Seq(command), src.getAbsoluteFile).!! + + // ************************************************** jEdit-specific code + + // keys for build settings + val jeditSettingsFolder = "jedit-settings-folder" + val startJEDit = "start-jedit" + val killJEdit = "kill-jedit" + + + /** MMT jEditPlugin release jars directory */ + val jEditPluginRelease: File = deploy / "jedit-plugin" / "plugin" / "jars" + + /** These methods are used by the target jedit/install to copy files to the local jEdit installation */ + /** copy MMT jar to jEdit settings directory */ + def installJEditJars { + settings.get(killJEdit).foreach { x => runscript(x) } + Thread.sleep(500) + val fname = settings.get(jeditSettingsFolder).getOrElse { + Utils.error(s"cannot copy jars because there is no setting '$jeditSettingsFolder' in $settingsFile") + return + } + val jsf = File(fname) / "jars" + copyJEditJars(jsf) + settings.get(startJEDit).foreach { x => runscript(x) } + } + + /** copy all jars to jEditPluginRelease */ + def releaseJEditJars { + copyJEditJars(jEditPluginRelease) + } + + /** copy all jEdit jars to a directory */ + def copyJEditJars(to: File) { + Utils.copy(deploy / "mmt.jar", to / "MMTPlugin.jar") + // all other jars are bundled with the above + // val jEditDeps = List("scala-library.jar","scala-parser-combinators.jar","scala-reflect.jar","scala-xml.jar","tiscaf.jar") + // jEditDeps.foreach {f => copy(deploy/"lib"/f, to/f)} + // copy(deploy/"lfcatalog"/"lfcatalog.jar", to/"lfcatalog.jar") + } + + // ************************************************** MathHub-specific code + + private val mathhub = "mathhub-folder" + lazy val mathhubFolder: File = { + settings.get(mathhub) match { + case None => + println(s"no key $mathhub in $settingsFile") + throw new Exception + case Some(s) => + File(s) + } + } +} \ No newline at end of file diff --git a/src/travis.sbt b/src/travis.sbt index a27aa17902..3361634b97 100644 --- a/src/travis.sbt +++ b/src/travis.sbt @@ -1,96 +1,99 @@ -import sbt._ -import sbt.Keys._ -import travis.Matrix._ -import travis.Config._ - -import scala.io.Source - -val travisConfig = taskKey[TravisConfig]("Generate travis.yml configuration") -travisConfig := { - val ourScalaVersion: String = scalaVersion.value - - // convenience wrapper to run an sbt task and an optional check - def sbt(task: String, check: Option[String] = None) : List[String] = List( - s"cd src && (cat /dev/null | sbt ++$ourScalaVersion $task) && cd .." - ) ::: check.toList - - // convenience wrapper to tun a specific test class - def runMainClass(cls: String*) : List[String] = cls.map("java -cp deploy/mmt.jar " + _).toList - - // convenience functions for checks - def file(name: String) : Option[String] = Some("[[ -f \"" + name + "\" ]]") - def identical(name: String) : Option[String] = Some("(git diff --quiet --exit-code \"" + name + "\")") - def dir(name: String) : Option[String] = Some("[[ -d \"" + name + "\" ]]") - - val LinuxTesting = MatrixSet( - Trusty, Language("scala"), Env(Map(("SBT_VERSION_CMD", "\"^validate\""))), - OracleJDK8, OpenJDK11 - ) - - // in principle we would test OS X as follows - // but this does not properly work, because Travis - // does not fully support OS X (yet?) - val OSXTesting = MatrixSet( - OSX, Language("java"), Env(Map(("SBT_VERSION_CMD", "\"^validate ^validateUniversal\""))), - XCode83 - ) - - TravisConfig( - Map( - // before installation, we need to make sure that we have sbt - // on Mac OS X, this means we need to install it via 'brew install' - // hopefully this will be provided by Travis CI in the future - "before_install" -> List("if [[ \"$TRAVIS_OS_NAME\" = \"osx\" ]]; then brew update; brew install sbt; fi"), - - // on the install step, we run 'sbt update' to install all the dependencies - // if this fails, we will get an errored test, instead of a failed one - "install" -> sbt("update"), - - // setup the test environment, so that the lmh versioning is ignored on devel - "before_script" -> List( - "if [ \"$TRAVIS_BRANCH\" == \"devel\" ]; then export TEST_USE_DEVEL=1; fi; echo $TEST_USE_DEVEL;" - ) - ), - - MatrixSet( - Scala(ourScalaVersion) - ) && LinuxTesting, /* (LinuxTesting && OSXTesting) if OS X testing is ever fixed )*/ - - - TravisStage("SelfCheck", "check that 'sbt genTravisYML' has been run")( - TravisJob("Check that `sbt genTravisYML` has been run", sbt("genTravisYML", identical(".travis.yml")), MatrixSet(OpenJDK8), expansion = FirstExpansion) - ), - - TravisStage("CompileAndCheck", "Check that our tests run and the code compiles")( - TravisJob("Check mmt.jar generation and integration tests", - sbt("deploy", file("deploy/mmt.jar")) ::: runMainClass( - "info.kwarc.mmt.test.APITest", - "info.kwarc.mmt.test.LFTest", - "info.kwarc.mmt.odk.ODKTest", "info.kwarc.mmt.odk.MitMTest" - )), - TravisJob("Check that unit tests run", sbt("test")), - ), - - TravisStage("DeployCheck", "check that the 'apidoc' and 'deployLFCatalog' targets work")( - TravisJob("Check lfcatalog.jar generation using `sbt deployLFCatalog`", sbt("deployLFCatalog", file("deploy/lfcatalog/lfcatalog.jar"))), - TravisJob("Check that apidoc generation works", sbt("apidoc", dir("apidoc"))) - ), - - TravisStage("deploy", "deploy the api documentation", Some("branch = master"))( - TravisJob("Auto-deploy API documentation", List("bash scripts/travis/deploy_doc.sh"), MatrixSet(OpenJDK8), expansion = FirstExpansion) - ) - ) -} - - -val genTravisYML = taskKey[Unit]("Print out travis.yml configuration") -genTravisYML := { - // read the prefix and the config - val prefix = Source.fromFile(Utils.src / "project" / "prefix.travis.yml").getLines.filter(!_.startsWith("##")).mkString("\n") - val config = travisConfig.value.serialize - - // and write it into .travis.yml - val outFile = Utils.root / ".travis.yml" - IO.write(outFile, prefix + "\n" + config) - streams.value.log.info(s"Wrote $outFile") -} +import Utils.utils +import sbt.Keys._ +import sbt._ +import travis.Config._ +import travis.Matrix._ + +import scala.io.Source + +val travisConfig = taskKey[TravisConfig]("Generate travis.yml configuration") +travisConfig := { + val ourScalaVersion: String = scalaVersion.value + + // convenience wrapper to run an sbt task and an optional check + def sbt(task: String, check: Option[String] = None): List[String] = List( + s"cd src && (cat /dev/null | sbt ++$ourScalaVersion $task) && cd .." + ) ::: check.toList + + // convenience wrapper to tun a specific test class + def runMainClass(cls: String*): List[String] = cls.map("java -cp deploy/mmt.jar " + _).toList + + // convenience functions for checks + def file(name: String): Option[String] = Some("[[ -f \"" + name + "\" ]]") + + def identical(name: String): Option[String] = Some("(git diff --quiet --exit-code \"" + name + "\")") + + def dir(name: String): Option[String] = Some("[[ -d \"" + name + "\" ]]") + + val LinuxTesting = MatrixSet( + Trusty, Language("scala"), Env(Map(("SBT_VERSION_CMD", "\"^validate\""))), + OracleJDK8, OpenJDK11 + ) + + // in principle we would test OS X as follows + // but this does not properly work, because Travis + // does not fully support OS X (yet?) + val OSXTesting = MatrixSet( + OSX, Language("java"), Env(Map(("SBT_VERSION_CMD", "\"^validate ^validateUniversal\""))), + XCode83 + ) + + TravisConfig( + Map( + // before installation, we need to make sure that we have sbt + // on Mac OS X, this means we need to install it via 'brew install' + // hopefully this will be provided by Travis CI in the future + "before_install" -> List("if [[ \"$TRAVIS_OS_NAME\" = \"osx\" ]]; then brew update; brew install sbt; fi"), + + // on the install step, we run 'sbt update' to install all the dependencies + // if this fails, we will get an errored test, instead of a failed one + "install" -> sbt("update"), + + // setup the test environment, so that the lmh versioning is ignored on devel + "before_script" -> List( + "if [ \"$TRAVIS_BRANCH\" == \"devel\" ]; then export TEST_USE_DEVEL=1; fi; echo $TEST_USE_DEVEL;" + ) + ), + + MatrixSet( + Scala(ourScalaVersion) + ) && LinuxTesting, /* (LinuxTesting && OSXTesting) if OS X testing is ever fixed )*/ + + + TravisStage("SelfCheck", "check that 'sbt genTravisYML' has been run")( + TravisJob("Check that `sbt genTravisYML` has been run", sbt("genTravisYML", identical(".travis.yml")), MatrixSet(OpenJDK8), expansion = FirstExpansion) + ), + + TravisStage("CompileAndCheck", "Check that our tests run and the code compiles")( + TravisJob("Check mmt.jar generation and integration tests", + sbt("deploy", file("deploy/mmt.jar")) ::: runMainClass( + "info.kwarc.mmt.test.APITest", + "info.kwarc.mmt.test.LFTest", + "info.kwarc.mmt.odk.ODKTest", "info.kwarc.mmt.odk.MitMTest" + )), + TravisJob("Check that unit tests run", sbt("test")), + ), + + TravisStage("DeployCheck", "check that the 'apidoc' and 'deployLFCatalog' targets work")( + TravisJob("Check lfcatalog.jar generation using `sbt deployLFCatalog`", sbt("deployLFCatalog", file("deploy/lfcatalog/lfcatalog.jar"))), + TravisJob("Check that apidoc generation works", sbt("apidoc", dir("apidoc"))) + ), + + TravisStage("deploy", "deploy the api documentation", Some("branch = master"))( + TravisJob("Auto-deploy API documentation", List("bash scripts/travis/deploy_doc.sh"), MatrixSet(OpenJDK8), expansion = FirstExpansion) + ) + ) +} + + +val genTravisYML = taskKey[Unit]("Print out travis.yml configuration") +genTravisYML := { + // read the prefix and the config + val prefix = Source.fromFile(utils.value.src / "project" / "prefix.travis.yml").getLines.filter(!_.startsWith("##")).mkString("\n") + val config = travisConfig.value.serialize + + // and write it into .travis.yml + val outFile = utils.value.root / ".travis.yml" + IO.write(outFile, prefix + "\n" + config) + streams.value.log.info(s"Wrote $outFile") +}