From 5e080eb204dab36dd4ae1e42adc63737fe8a9e6d Mon Sep 17 00:00:00 2001 From: Stefan Zeiger Date: Tue, 27 Oct 2015 16:17:41 +0100 Subject: Fix the dist/mkBin target in the sbt build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Set the correct unmanagedResourceDirectories for the compiler and library projects. These projects already had includeFilter definitions to copy the correct resource files to the target dirs but they were meaningless without a source dir. - Set a target path for ‘dist’ to make stream files go to the proper .gitignored dir. - Set permissions ugo+rx on all generated script and batch files. --- build.sbt | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'build.sbt') diff --git a/build.sbt b/build.sbt index 8e79e1019f..5dc8cad01d 100644 --- a/build.sbt +++ b/build.sbt @@ -154,6 +154,7 @@ lazy val library = configureAsSubproject(project) val libraryAuxDir = (baseDirectory in ThisBuild).value / "src/library-aux" Seq("-doc-no-compile", libraryAuxDir.toString) }, + unmanagedResourceDirectories in Compile += (baseDirectory in ThisBuild).value / "src" / project.id, includeFilter in unmanagedResources in Compile := libIncludes) .dependsOn (forkjoin) @@ -184,6 +185,7 @@ lazy val compiler = configureAsSubproject(project) (mappings in Compile in packageBin in LocalProject("interactive")).value ++ (mappings in Compile in packageBin in LocalProject("scaladoc")).value ++ (mappings in Compile in packageBin in LocalProject("repl")).value, + unmanagedResourceDirectories in Compile += (baseDirectory in ThisBuild).value / "src" / project.id, includeFilter in unmanagedResources in Compile := compilerIncludes) .dependsOn(library, reflect) @@ -299,7 +301,8 @@ lazy val root = (project in file(".")). ) lazy val dist = (project in file("dist")).settings( - mkBin := mkBinImpl.value + mkBin := mkBinImpl.value, + target := (baseDirectory in ThisBuild).value / "target" / thisProject.value.id ) /** @@ -446,6 +449,7 @@ def clearSourceAndResourceDirectories = Seq(Compile, Test).flatMap(config => inC ))) lazy val mkBinImpl: Def.Initialize[Task[Seq[File]]] = Def.task { + import java.io.IOException def mkScalaTool(mainCls: String, classpath: Seq[Attributed[File]]): ScalaTool = ScalaTool(mainClass = mainCls, classpath = classpath.toList.map(_.data.getAbsolutePath), @@ -453,11 +457,18 @@ lazy val mkBinImpl: Def.Initialize[Task[Seq[File]]] = Def.task { javaOpts = "-Xmx256M -Xms32M", toolFlags = "") val rootDir = (classDirectory in Compile in compiler).value - def writeScripts(scalaTool: ScalaTool, file: String, outDir: File): Seq[File] = - Seq( + def writeScripts(scalaTool: ScalaTool, file: String, outDir: File): Seq[File] = { + val res = Seq( scalaTool.writeScript(file, "unix", rootDir, outDir), scalaTool.writeScript(file, "windows", rootDir, outDir) ) + res.foreach { f => + //TODO 2.12: Use Files.setPosixFilePermissions() (Java 7+) instead of calling out to chmod + if(Process(List("chmod", "ugo+rx", f.getAbsolutePath())).! > 0) + throw new IOException("chmod failed") + } + res + } def mkQuickBin(file: String, mainCls: String, classpath: Seq[Attributed[File]]): Seq[File] = { val scalaTool = mkScalaTool(mainCls, classpath) val outDir = buildDirectory.value / "quick/bin" -- cgit v1.2.3 From 9debc84dcd57c331c184a3cf58b627045db632e0 Mon Sep 17 00:00:00 2001 From: Stefan Zeiger Date: Wed, 28 Oct 2015 20:28:52 +0100 Subject: Create shaded JLine in sbt build Reusing parts of #4593, this commits adds two additional subprojects to the sbt build: - repl-jline, which is already used by the ant build, builds the part of the REPL that depends on JLine. The actual JLine depenency is moved to this project. - repl-jline-shaded uses JarJar to create a shaded version of repl-jline and jline.jar. Unlike the ant build, this setup does not use any circular dependencies. dist/mkBin puts all of quick/repl, quick/repl-jline and quick/repl-jline-shaded onto the classpath of build-sbt/quick/bin/scala. A future addition to the sbt build for building build-sbt/pack will have to put the generated classfiles into the correct JARs, mirroring the old structure. --- build.sbt | 53 +++++++++++++-- project/JarJar.scala | 83 ++++++++++++++++++++++++ project/plugins.sbt | 4 +- src/repl/scala/tools/nsc/interpreter/ILoop.scala | 2 +- 4 files changed, 133 insertions(+), 9 deletions(-) create mode 100644 project/JarJar.scala (limited to 'build.sbt') diff --git a/build.sbt b/build.sbt index 5dc8cad01d..1bb6962977 100644 --- a/build.sbt +++ b/build.sbt @@ -184,7 +184,7 @@ lazy val compiler = configureAsSubproject(project) streams.value.cacheDirectory) ++ (mappings in Compile in packageBin in LocalProject("interactive")).value ++ (mappings in Compile in packageBin in LocalProject("scaladoc")).value ++ - (mappings in Compile in packageBin in LocalProject("repl")).value, + (mappings in Compile in packageBin in LocalProject("repl-jline-shaded")).value, unmanagedResourceDirectories in Compile += (baseDirectory in ThisBuild).value / "src" / project.id, includeFilter in unmanagedResources in Compile := compilerIncludes) .dependsOn(library, reflect) @@ -193,10 +193,8 @@ lazy val interactive = configureAsSubproject(project) .settings(disableDocsAndPublishingTasks: _*) .dependsOn(compiler) -// TODO: SI-9339 embed shaded copy of jline & its interface (see #4563) lazy val repl = configureAsSubproject(project) .settings( - libraryDependencies += jlineDep, connectInput in run := true, outputStrategy in run := Some(StdoutOutput), run <<= (run in Compile).partialInput(" -usejavacp") // Automatically add this so that `repl/run` works without additional arguments. @@ -204,6 +202,47 @@ lazy val repl = configureAsSubproject(project) .settings(disableDocsAndPublishingTasks: _*) .dependsOn(compiler, interactive) +lazy val replJline = configureAsSubproject(Project("repl-jline", file(".") / "src" / "repl-jline")) + .settings( + libraryDependencies += jlineDep + ) + .settings(disableDocsAndPublishingTasks: _*) + .dependsOn(repl) + +lazy val replJlineShaded = Project("repl-jline-shaded", file(".") / "target" / "repl-jline-shaded-src-dummy") + .settings(scalaSubprojectSettings: _*) + .settings(disableDocsAndPublishingTasks: _*) + .settings( + // There is nothing to compile for this project. Instead we use the compile task to create + // shaded versions of repl-jline and jline.jar. dist/mkBin puts all of quick/repl, + // quick/repl-jline and quick/repl-jline-shaded on the classpath for quick/bin scripts. + // This is different from the ant build where all parts are combined into quick/repl, but + // it is cleaner because it avoids circular dependencies. + compile in Compile <<= (compile in Compile).dependsOn(Def.task { + import java.util.jar._ + import collection.JavaConverters._ + val inputs: Iterator[JarJar.Entry] = { + val repljlineClasses = (products in Compile in replJline).value.flatMap(base => Path.allSubpaths(base).map(x => (base, x._1))) + val jlineJAR = (dependencyClasspath in Compile).value.find(_.get(moduleID.key) == Some(jlineDep)).get.data + val jarFile = new JarFile(jlineJAR) + val jarEntries = jarFile.entries.asScala.filterNot(_.isDirectory).map(entry => JarJar.JarEntryInput(jarFile, entry)) + def compiledClasses = repljlineClasses.iterator.map { case (base, file) => JarJar.FileInput(base, file) } + (jarEntries ++ compiledClasses).filter(x => x.name.endsWith(".class") || x.name.endsWith(".properties") || x.name.startsWith("META-INF/native")) + } + //println(inputs.map(_.name).mkString("\n")) + import JarJar.JarJarConfig._ + val config: Seq[JarJar.JarJarConfig] = Seq( + Rule("org.fusesource.**", "scala.tools.fusesource_embedded.@1"), + Rule("jline.**", "scala.tools.jline_embedded.@1"), + Rule("scala.tools.nsc.interpreter.jline.**", "scala.tools.nsc.interpreter.jline_embedded.@1"), + Keep("scala.tools.**") + ) + val outdir = (classDirectory in Compile).value + JarJar(inputs, outdir, config) + }) + ) + .dependsOn(replJline) + lazy val scaladoc = configureAsSubproject(project) .settings( libraryDependencies ++= Seq(scalaXmlDep, scalaParserCombinatorsDep, partestDep) @@ -224,7 +263,7 @@ lazy val actors = configureAsSubproject(project) lazy val forkjoin = configureAsForkOfJavaProject(project) lazy val partestExtras = configureAsSubproject(Project("partest-extras", file(".") / "src" / "partest-extras")) - .dependsOn(repl) + .dependsOn(replJlineShaded) .settings(clearSourceAndResourceDirectories: _*) .settings( libraryDependencies += partestDep, @@ -261,7 +300,7 @@ lazy val partestJavaAgent = (project in file(".") / "src" / "partest-javaagent") ) lazy val test = project. - dependsOn(compiler, interactive, actors, repl, scalap, partestExtras, partestJavaAgent, scaladoc). + dependsOn(compiler, interactive, actors, replJlineShaded, scalap, partestExtras, partestJavaAgent, scaladoc). configs(IntegrationTest). settings(disableDocsAndPublishingTasks: _*). settings(commonSettings: _*). @@ -291,7 +330,7 @@ lazy val test = project. ) lazy val root = (project in file(".")). - aggregate(library, forkjoin, reflect, compiler, interactive, repl, + aggregate(library, forkjoin, reflect, compiler, interactive, repl, replJline, replJlineShaded, scaladoc, scalap, actors, partestExtras, junit).settings( sources in Compile := Seq.empty, onLoadMessage := """|*** Welcome to the sbt build definition for Scala! *** @@ -484,7 +523,7 @@ lazy val mkBinImpl: Def.Initialize[Task[Seq[File]]] = Def.task { def mkBin(file: String, mainCls: String, classpath: Seq[Attributed[File]]): Seq[File] = mkQuickBin(file, mainCls, classpath) ++ mkPackBin(file, mainCls) - mkBin("scala" , "scala.tools.nsc.MainGenericRunner", (fullClasspath in Compile in repl).value) ++ + mkBin("scala" , "scala.tools.nsc.MainGenericRunner", (fullClasspath in Compile in replJlineShaded).value) ++ mkBin("scalac" , "scala.tools.nsc.Main", (fullClasspath in Compile in compiler).value) ++ mkBin("fsc" , "scala.tools.nsc.CompileClient", (fullClasspath in Compile in compiler).value) ++ mkBin("scaladoc" , "scala.tools.nsc.ScalaDoc", (fullClasspath in Compile in scaladoc).value) ++ diff --git a/project/JarJar.scala b/project/JarJar.scala new file mode 100644 index 0000000000..64281f23c1 --- /dev/null +++ b/project/JarJar.scala @@ -0,0 +1,83 @@ +import org.pantsbuild.jarjar +import org.pantsbuild.jarjar._ +import org.pantsbuild.jarjar.util._ +import scala.collection.JavaConverters._ +import java.util.jar._ +import java.io._ +import sbt._ + +object JarJar { + sealed abstract class JarJarConfig { + def toPatternElement: PatternElement + } + object JarJarConfig { + case class Rule(pattern: String, result: String) extends JarJarConfig { + def toPatternElement: PatternElement = { + val rule = new jarjar.Rule + rule.setPattern(pattern) + rule.setResult(result) + rule + } + } + case class Keep(pattern: String) extends JarJarConfig { + def toPatternElement: PatternElement = { + val keep = new jarjar.Keep + keep.setPattern(pattern) + keep + } + } + } + + sealed abstract class Entry { + def name: String + def time: Long + def data: Array[Byte] + } + + case class JarEntryInput(jarFile: JarFile, entry: JarEntry) extends Entry { + def name = entry.getName + def time = entry.getTime + def data = sbt.IO.readBytes(jarFile.getInputStream(entry)) + } + case class FileInput(base: File, file: File) extends Entry { + def name = file.relativeTo(base).get.getPath + def time = file.lastModified + def data = sbt.IO.readBytes(file) + } + + private def newMainProcessor(patterns: java.util.List[PatternElement], verbose: Boolean, skipManifest: Boolean): JarProcessor = { + val cls = Class.forName("org.pantsbuild.jarjar.MainProcessor") + val constructor = cls.getConstructor(classOf[java.util.List[_]], java.lang.Boolean.TYPE, java.lang.Boolean.TYPE) + constructor.setAccessible(true) + constructor.newInstance(patterns, Boolean.box(verbose), Boolean.box(skipManifest)).asInstanceOf[JarProcessor] + } + + def apply(in: Iterator[Entry], outdir: File, + config: Seq[JarJarConfig], verbose: Boolean = false): Seq[File] = { + val patterns = config.map(_.toPatternElement).asJava + val processor: JarProcessor = newMainProcessor(patterns, verbose, false) + def process(e: Entry): Option[File] = { + val struct = new EntryStruct() + struct.name = e.name + struct.time = e.time + struct.data = e.data + if (processor.process(struct)) { + if (struct.name.endsWith("/")) None + else { + val f = outdir / struct.name + try { + f.getParentFile.mkdirs() + sbt.IO.write(f, struct.data) + } catch { + case ex: Exception => + throw new IOException(s"Failed to write ${e.name} / ${f.getParentFile} / ${f.getParentFile.exists}", ex) + } + Some(f) + } + } + else None + } + in.flatMap(entry => process(entry)).toList + + } +} diff --git a/project/plugins.sbt b/project/plugins.sbt index dc266a8db1..862887d57f 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1 +1,3 @@ -libraryDependencies += "org.apache.commons" % "commons-lang3" % "3.3.2" \ No newline at end of file +libraryDependencies += "org.apache.commons" % "commons-lang3" % "3.3.2" + +libraryDependencies += "org.pantsbuild" % "jarjar" % "1.6.0" diff --git a/src/repl/scala/tools/nsc/interpreter/ILoop.scala b/src/repl/scala/tools/nsc/interpreter/ILoop.scala index bf7c8551e5..adac438b37 100644 --- a/src/repl/scala/tools/nsc/interpreter/ILoop.scala +++ b/src/repl/scala/tools/nsc/interpreter/ILoop.scala @@ -876,7 +876,7 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) if (settings.debug) { val readerDiags = (readerClasses, readers).zipped map { - case (cls, Failure(e)) => s" - $cls --> " + e.getStackTrace.mkString(e.toString+"\n\t", "\n\t","\n") + case (cls, Failure(e)) => s" - $cls --> \n\t" + scala.tools.nsc.util.stackTraceString(e) + "\n" case (cls, Success(_)) => s" - $cls OK" } Console.println(s"All InteractiveReaders tried: ${readerDiags.mkString("\n","\n","\n")}") -- cgit v1.2.3 From f79ef27c30dc5438f026d59a905df63b579a0f15 Mon Sep 17 00:00:00 2001 From: Stefan Zeiger Date: Mon, 2 Nov 2015 16:09:30 +0100 Subject: Create usable “quick” and “pack” builds from sbt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add dependencies for scala-swing, scala-continuations-library and scala-continuations-plugin - Build all JARs for “pack” in dist/packageBin - Add “dist/mkQuick” task for building all required projects for “quick” and creating the launcher scripts - Add “dist/mkPack” task for packaging all required projects for “pack” and creating the launcher scripts - Include classes from “forkjoin” project in scala-library.jar --- build.sbt | 96 ++++++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 64 insertions(+), 32 deletions(-) (limited to 'build.sbt') diff --git a/build.sbt b/build.sbt index 1bb6962977..a4dc472dac 100644 --- a/build.sbt +++ b/build.sbt @@ -1,10 +1,11 @@ /* * The new, sbt-based build definition for Scala. * - * What you see below is very much work-in-progress. Basics like compiling and packaging jars - * (into right location) work. Everything else is missing: - * building docs, placing shell scripts in right locations (so you can run compiler easily), - * running partest test, compiling and running JUnit test, and many, many other things. + * What you see below is very much work-in-progress. The following features are implemented: + * - Compiling all classses for the compiler and library ("compile" in the respective subprojects) + * - Running JUnit tests ("test") and partest ("test/it:test") + * - Creating build-sbt/quick with all compiled classes and launcher scripts ("dist/mkQuick") + * - Creating build-sbt/pack with all JARs and launcher scripts ("dist/mkPack") * * You'll notice that this build definition is much more complicated than your typical sbt build. * The main reason is that we are not benefiting from sbt's conventions when it comes project @@ -55,7 +56,10 @@ val bootstrapScalaVersion = versionProps("starr.version") def withoutScalaLang(moduleId: ModuleID): ModuleID = moduleId exclude("org.scala-lang", "*") // exclusion of the scala-library transitive dependency avoids eviction warnings during `update`. +val scalaContinuationsLibraryDep = withoutScalaLang("org.scala-lang.plugins" %% "scala-continuations-library" % versionNumber("scala-continuations-library")) +val scalaContinuationsPluginDep = withoutScalaLang("org.scala-lang.plugins" % ("scala-continuations-plugin_" + versionProps("scala.full.version")) % versionNumber("scala-continuations-plugin")) val scalaParserCombinatorsDep = withoutScalaLang("org.scala-lang.modules" %% "scala-parser-combinators" % versionNumber("scala-parser-combinators")) +val scalaSwingDep = withoutScalaLang("org.scala-lang.modules" %% "scala-swing" % versionNumber("scala-swing")) val scalaXmlDep = withoutScalaLang("org.scala-lang.modules" %% "scala-xml" % versionNumber("scala-xml")) val partestDep = withoutScalaLang("org.scala-lang.modules" %% "scala-partest" % versionNumber("partest")) val partestInterfaceDep = withoutScalaLang("org.scala-lang.modules" %% "scala-partest-interface" % "0.5.0") @@ -155,7 +159,11 @@ lazy val library = configureAsSubproject(project) Seq("-doc-no-compile", libraryAuxDir.toString) }, unmanagedResourceDirectories in Compile += (baseDirectory in ThisBuild).value / "src" / project.id, - includeFilter in unmanagedResources in Compile := libIncludes) + includeFilter in unmanagedResources in Compile := libIncludes, + // Include forkjoin classes in scala-library.jar + mappings in Compile in packageBin ++= + (mappings in Compile in packageBin in LocalProject("forkjoin")).value + ) .dependsOn (forkjoin) lazy val reflect = configureAsSubproject(project) @@ -184,7 +192,7 @@ lazy val compiler = configureAsSubproject(project) streams.value.cacheDirectory) ++ (mappings in Compile in packageBin in LocalProject("interactive")).value ++ (mappings in Compile in packageBin in LocalProject("scaladoc")).value ++ - (mappings in Compile in packageBin in LocalProject("repl-jline-shaded")).value, + (mappings in Compile in packageBin in LocalProject("repl")).value, unmanagedResourceDirectories in Compile += (baseDirectory in ThisBuild).value / "src" / project.id, includeFilter in unmanagedResources in Compile := compilerIncludes) .dependsOn(library, reflect) @@ -194,25 +202,25 @@ lazy val interactive = configureAsSubproject(project) .dependsOn(compiler) lazy val repl = configureAsSubproject(project) + .settings(disableDocsAndPublishingTasks: _*) .settings( connectInput in run := true, outputStrategy in run := Some(StdoutOutput), run <<= (run in Compile).partialInput(" -usejavacp") // Automatically add this so that `repl/run` works without additional arguments. ) - .settings(disableDocsAndPublishingTasks: _*) .dependsOn(compiler, interactive) lazy val replJline = configureAsSubproject(Project("repl-jline", file(".") / "src" / "repl-jline")) .settings( - libraryDependencies += jlineDep + libraryDependencies += jlineDep, + name := "scala-repl-jline" ) - .settings(disableDocsAndPublishingTasks: _*) .dependsOn(repl) -lazy val replJlineShaded = Project("repl-jline-shaded", file(".") / "target" / "repl-jline-shaded-src-dummy") +lazy val replJlineEmbedded = Project("repl-jline-embedded", file(".") / "target" / "repl-jline-embedded-src-dummy") .settings(scalaSubprojectSettings: _*) - .settings(disableDocsAndPublishingTasks: _*) .settings( + name := "scala-repl-jline-embedded", // There is nothing to compile for this project. Instead we use the compile task to create // shaded versions of repl-jline and jline.jar. dist/mkBin puts all of quick/repl, // quick/repl-jline and quick/repl-jline-shaded on the classpath for quick/bin scripts. @@ -227,7 +235,9 @@ lazy val replJlineShaded = Project("repl-jline-shaded", file(".") / "target" / " val jarFile = new JarFile(jlineJAR) val jarEntries = jarFile.entries.asScala.filterNot(_.isDirectory).map(entry => JarJar.JarEntryInput(jarFile, entry)) def compiledClasses = repljlineClasses.iterator.map { case (base, file) => JarJar.FileInput(base, file) } - (jarEntries ++ compiledClasses).filter(x => x.name.endsWith(".class") || x.name.endsWith(".properties") || x.name.startsWith("META-INF/native")) + (jarEntries ++ compiledClasses).filter(x => + x.name.endsWith(".class") || x.name.endsWith(".properties") || x.name.startsWith("META-INF/native") || x.name.startsWith("META-INF/maven") + ) } //println(inputs.map(_.name).mkString("\n")) import JarJar.JarJarConfig._ @@ -263,9 +273,10 @@ lazy val actors = configureAsSubproject(project) lazy val forkjoin = configureAsForkOfJavaProject(project) lazy val partestExtras = configureAsSubproject(Project("partest-extras", file(".") / "src" / "partest-extras")) - .dependsOn(replJlineShaded) + .dependsOn(replJlineEmbedded) .settings(clearSourceAndResourceDirectories: _*) .settings( + name := "scala-partest-extras", libraryDependencies += partestDep, unmanagedSourceDirectories in Compile := List(baseDirectory.value) ) @@ -300,7 +311,7 @@ lazy val partestJavaAgent = (project in file(".") / "src" / "partest-javaagent") ) lazy val test = project. - dependsOn(compiler, interactive, actors, replJlineShaded, scalap, partestExtras, partestJavaAgent, scaladoc). + dependsOn(compiler, interactive, actors, replJlineEmbedded, scalap, partestExtras, partestJavaAgent, scaladoc). configs(IntegrationTest). settings(disableDocsAndPublishingTasks: _*). settings(commonSettings: _*). @@ -330,7 +341,7 @@ lazy val test = project. ) lazy val root = (project in file(".")). - aggregate(library, forkjoin, reflect, compiler, interactive, repl, replJline, replJlineShaded, + aggregate(library, forkjoin, reflect, compiler, interactive, repl, replJline, replJlineEmbedded, scaladoc, scalap, actors, partestExtras, junit).settings( sources in Compile := Seq.empty, onLoadMessage := """|*** Welcome to the sbt build definition for Scala! *** @@ -339,10 +350,35 @@ lazy val root = (project in file(".")). |the Ant build definition for now. Check README.md for more information.""".stripMargin ) -lazy val dist = (project in file("dist")).settings( - mkBin := mkBinImpl.value, - target := (baseDirectory in ThisBuild).value / "target" / thisProject.value.id -) +// The following subprojects' binaries are required for building "pack": +lazy val distDependencies = Seq(replJline, replJlineEmbedded, compiler, library, partestExtras, partestJavaAgent, reflect, scalap, actors, scaladoc) + +lazy val dist = (project in file("dist")) + .settings(commonSettings) + .settings( + libraryDependencies ++= Seq(scalaContinuationsLibraryDep, scalaContinuationsPluginDep, scalaSwingDep, jlineDep), + mkBin := mkBinImpl.value, + mkQuick <<= Def.task {} dependsOn ((distDependencies.map(compile in Compile in _) :+ mkBin): _*), + mkPack <<= Def.task {} dependsOn (packageBin in Compile, mkBin), + target := (baseDirectory in ThisBuild).value / "target" / thisProject.value.id, + packageBin in Compile := { + val extraDeps = Set(scalaContinuationsLibraryDep, scalaContinuationsPluginDep, scalaSwingDep, scalaParserCombinatorsDep, scalaXmlDep) + val targetDir = (buildDirectory in ThisBuild).value / "pack" / "lib" + def uniqueModule(m: ModuleID) = (m.organization, m.name.replaceFirst("_.*", "")) + val extraModules = extraDeps.map(uniqueModule) + val extraJars = (externalDependencyClasspath in Compile).value.map(a => (a.get(moduleID.key), a.data)).collect { + case (Some(m), f) if extraModules contains uniqueModule(m) => f + } + val jlineJAR = (dependencyClasspath in Compile).value.find(_.get(moduleID.key) == Some(jlineDep)).get.data + val mappings = extraJars.map(f => (f, targetDir / f.getName)) :+ (jlineJAR, targetDir / "jline.jar") + IO.copy(mappings, overwrite = true) + targetDir + }, + cleanFiles += (buildDirectory in ThisBuild).value / "quick", + cleanFiles += (buildDirectory in ThisBuild).value / "pack", + packageBin in Compile <<= (packageBin in Compile).dependsOn(distDependencies.map(packageBin in Compile in _): _*) + ) + .dependsOn(distDependencies.map(p => p: ClasspathDep[ProjectReference]): _*) /** * Configures passed project as a subproject (e.g. compiler or repl) @@ -386,6 +422,8 @@ lazy val buildDirectory = settingKey[File]("The directory where all build produc lazy val copyrightString = settingKey[String]("Copyright string.") lazy val generateVersionPropertiesFile = taskKey[File]("Generating version properties file.") lazy val mkBin = taskKey[Seq[File]]("Generate shell script (bash or Windows batch).") +lazy val mkQuick = taskKey[Unit]("Generate a full build, including scripts, in build-sbt/quick") +lazy val mkPack = taskKey[Unit]("Generate a full build, including scripts, in build-sbt/pack") lazy val generateVersionPropertiesFileImpl: Def.Initialize[Task[File]] = Def.task { val propFile = (resourceManaged in Compile).value / s"${thisProject.value.id}.properties" @@ -496,6 +534,8 @@ lazy val mkBinImpl: Def.Initialize[Task[Seq[File]]] = Def.task { javaOpts = "-Xmx256M -Xms32M", toolFlags = "") val rootDir = (classDirectory in Compile in compiler).value + val quickOutDir = buildDirectory.value / "quick/bin" + val packOutDir = buildDirectory.value / "pack/bin" def writeScripts(scalaTool: ScalaTool, file: String, outDir: File): Seq[File] = { val res = Seq( scalaTool.writeScript(file, "unix", rootDir, outDir), @@ -508,22 +548,14 @@ lazy val mkBinImpl: Def.Initialize[Task[Seq[File]]] = Def.task { } res } - def mkQuickBin(file: String, mainCls: String, classpath: Seq[Attributed[File]]): Seq[File] = { - val scalaTool = mkScalaTool(mainCls, classpath) - val outDir = buildDirectory.value / "quick/bin" - writeScripts(scalaTool, file, outDir) - } - - def mkPackBin(file: String, mainCls: String): Seq[File] = { - val scalaTool = mkScalaTool(mainCls, classpath = Nil) - val outDir = buildDirectory.value / "pack/bin" - writeScripts(scalaTool, file, outDir) - } def mkBin(file: String, mainCls: String, classpath: Seq[Attributed[File]]): Seq[File] = - mkQuickBin(file, mainCls, classpath) ++ mkPackBin(file, mainCls) + writeScripts(mkScalaTool(mainCls, classpath), file, quickOutDir) ++ + writeScripts(mkScalaTool(mainCls, Nil ), file, packOutDir) + + streams.value.log.info(s"Creating scripts in $quickOutDir and $packOutDir") - mkBin("scala" , "scala.tools.nsc.MainGenericRunner", (fullClasspath in Compile in replJlineShaded).value) ++ + mkBin("scala" , "scala.tools.nsc.MainGenericRunner", (fullClasspath in Compile in replJlineEmbedded).value) ++ mkBin("scalac" , "scala.tools.nsc.Main", (fullClasspath in Compile in compiler).value) ++ mkBin("fsc" , "scala.tools.nsc.CompileClient", (fullClasspath in Compile in compiler).value) ++ mkBin("scaladoc" , "scala.tools.nsc.ScalaDoc", (fullClasspath in Compile in scaladoc).value) ++ -- cgit v1.2.3 From 3cddb7fa3e044734ef6999b4b61f1246b0e37657 Mon Sep 17 00:00:00 2001 From: Stefan Zeiger Date: Tue, 3 Nov 2015 17:36:01 +0100 Subject: Build scaladoc sets from sbt - Docs for actors, compiler, library, reflect and scalap are built by default. Generated artifacts are removed when cleaning the respective subproject. - Fix some exclude patterns for scaladoc sources in the ANT build --- build.sbt | 49 ++++++++++++++++++++++++++++++++++--------------- build.xml | 11 +++-------- 2 files changed, 37 insertions(+), 23 deletions(-) (limited to 'build.sbt') diff --git a/build.sbt b/build.sbt index a4dc472dac..d1136b2eb2 100644 --- a/build.sbt +++ b/build.sbt @@ -6,6 +6,7 @@ * - Running JUnit tests ("test") and partest ("test/it:test") * - Creating build-sbt/quick with all compiled classes and launcher scripts ("dist/mkQuick") * - Creating build-sbt/pack with all JARs and launcher scripts ("dist/mkPack") + * - Building all scaladoc sets ("doc") * * You'll notice that this build definition is much more complicated than your typical sbt build. * The main reason is that we are not benefiting from sbt's conventions when it comes project @@ -101,11 +102,12 @@ lazy val commonSettings = clearSourceAndResourceDirectories ++ Seq[Setting[_]]( // each subproject has to ask specifically for files they want to include includeFilter in unmanagedResources in Compile := NothingFilter, target := (baseDirectory in ThisBuild).value / "target" / thisProject.value.id, - target in Compile in doc := buildDirectory.value / "scaladoc" / thisProject.value.id, classDirectory in Compile := buildDirectory.value / "quick/classes" / thisProject.value.id, - // given that classDirectory is overriden to be _outside_ of target directory, we have - // to make sure its being cleaned properly + target in Compile in doc := buildDirectory.value / "scaladoc" / thisProject.value.id, + // given that classDirectory and doc target are overriden to be _outside_ of target directory, we have + // to make sure they are being cleaned properly cleanFiles += (classDirectory in Compile).value, + cleanFiles += (target in Compile in doc).value, fork in run := true ) @@ -140,6 +142,24 @@ lazy val generatePropertiesFileSettings = Seq[Setting[_]]( generateVersionPropertiesFile := generateVersionPropertiesFileImpl.value ) +def filterDocSources(ff: FileFilter): Seq[Setting[_]] = Seq( + sources in (Compile, doc) ~= (_.filter(ff.accept _)), + // Excluded sources may still be referenced by the included sources, so we add the compiler + // output to the scaladoc classpath to resolve them. For the `library` project this is + // always required because otherwise the compiler cannot even initialize Definitions without + // binaries of the library on the classpath. Specifically, we get this error: + // (library/compile:doc) scala.reflect.internal.FatalError: package class scala does not have a member Int + // Ant build does the same thing always: it puts binaries for documented classes on the classpath + // sbt never does this by default (which seems like a good default) + dependencyClasspath in (Compile, doc) += (classDirectory in Compile).value, + doc in Compile <<= doc in Compile dependsOn (compile in Compile) +) + +def regexFileFilter(s: String): FileFilter = new FileFilter { + val pat = s.r.pattern + def accept(f: File) = pat.matcher(f.getAbsolutePath.replace('\\', '/')).matches() +} + val libIncludes: FileFilter = "*.tmpl" | "*.xml" | "*.js" | "*.css" | "rootdoc.txt" lazy val library = configureAsSubproject(project) @@ -147,13 +167,6 @@ lazy val library = configureAsSubproject(project) .settings( name := "scala-library", scalacOptions in Compile ++= Seq[String]("-sourcepath", (scalaSource in Compile).value.toString), - // Workaround for a bug in `scaladoc` that it seems to not respect the `-sourcepath` option - // as a result of this bug, the compiler cannot even initialize Definitions without - // binaries of the library on the classpath. Specifically, we get this error: - // (library/compile:doc) scala.reflect.internal.FatalError: package class scala does not have a member Int - // Ant build does the same thing always: it puts binaries for documented classes on the classpath - // sbt never does this by default (which seems like a good default) - dependencyClasspath in Compile in doc += (classDirectory in Compile).value, scalacOptions in Compile in doc ++= { val libraryAuxDir = (baseDirectory in ThisBuild).value / "src/library-aux" Seq("-doc-no-compile", libraryAuxDir.toString) @@ -164,7 +177,10 @@ lazy val library = configureAsSubproject(project) mappings in Compile in packageBin ++= (mappings in Compile in packageBin in LocalProject("forkjoin")).value ) - .dependsOn (forkjoin) + .settings(filterDocSources("*.scala" -- (regexFileFilter(".*/runtime/.*\\$\\.scala") || + regexFileFilter(".*/runtime/ScalaRunTime\\.scala") || + regexFileFilter(".*/runtime/StringAdd\\.scala"))): _*) + .dependsOn(forkjoin) lazy val reflect = configureAsSubproject(project) .settings(generatePropertiesFileSettings: _*) @@ -213,7 +229,8 @@ lazy val repl = configureAsSubproject(project) lazy val replJline = configureAsSubproject(Project("repl-jline", file(".") / "src" / "repl-jline")) .settings( libraryDependencies += jlineDep, - name := "scala-repl-jline" + name := "scala-repl-jline", + doc := file("!!! NO DOCS !!!") ) .dependsOn(repl) @@ -264,10 +281,10 @@ lazy val scalap = configureAsSubproject(project). dependsOn(compiler) // deprecated Scala Actors project -// TODO: it packages into actors.jar but it should be scala-actors.jar lazy val actors = configureAsSubproject(project) .settings(generatePropertiesFileSettings: _*) .settings(name := "scala-actors") + .settings(filterDocSources("*.scala"): _*) .dependsOn(library) lazy val forkjoin = configureAsForkOfJavaProject(project) @@ -278,7 +295,8 @@ lazy val partestExtras = configureAsSubproject(Project("partest-extras", file(". .settings( name := "scala-partest-extras", libraryDependencies += partestDep, - unmanagedSourceDirectories in Compile := List(baseDirectory.value) + unmanagedSourceDirectories in Compile := List(baseDirectory.value), + doc := file("!!! NO DOCS !!!") ) lazy val junit = project.in(file("test") / "junit") @@ -289,7 +307,8 @@ lazy val junit = project.in(file("test") / "junit") fork in Test := true, libraryDependencies ++= Seq(junitDep, junitIntefaceDep), testOptions += Tests.Argument(TestFrameworks.JUnit, "-a", "-v"), - unmanagedSourceDirectories in Test := List(baseDirectory.value) + unmanagedSourceDirectories in Test := List(baseDirectory.value), + doc := file("!!! NO DOCS !!!") ) lazy val partestJavaAgent = (project in file(".") / "src" / "partest-javaagent"). diff --git a/build.xml b/build.xml index 7f8c91b47b..8583292ea7 100644 --- a/build.xml +++ b/build.xml @@ -1684,20 +1684,15 @@ TODO: - - - + + + - - - - - -- cgit v1.2.3 From 7248894154180291f18def808ed992fb495b257e Mon Sep 17 00:00:00 2001 From: Stefan Zeiger Date: Thu, 5 Nov 2015 18:37:44 +0100 Subject: Improve version handling and scaladoc generation in sbt build - Move version-related code into a separate VersionUtil object - Fix the canonical version. The sbt build previously took the full version including the suffix (plus timestamp and hash) whereas the ant build uses the version without the suffix - Include the version number in the generated scaladocs - Add project descriptions and include them in the scaladocs (like the ant build does) - Add other missing scaladoc options to the sbt build - Copy resources in all subprojects when building dist/mkQuick and fix `includeFilter` settings to include all required files --- build.sbt | 155 +++++++++++++++++++--------------------------- project/VersionUtil.scala | 103 ++++++++++++++++++++++++++++++ 2 files changed, 167 insertions(+), 91 deletions(-) create mode 100644 project/VersionUtil.scala (limited to 'build.sbt') diff --git a/build.sbt b/build.sbt index d1136b2eb2..6540d0b9c0 100644 --- a/build.sbt +++ b/build.sbt @@ -52,6 +52,8 @@ * https://groups.google.com/d/topic/scala-internals/gp5JsM1E0Fo/discussion */ +import VersionUtil.{versionProps, versionNumber, generatePropertiesFileSettings, versionProperties, versionPropertiesSettings} + val bootstrapScalaVersion = versionProps("starr.version") def withoutScalaLang(moduleId: ModuleID): ModuleID = moduleId exclude("org.scala-lang", "*") @@ -71,8 +73,9 @@ val jlineDep = "jline" % "jline" % versionProps("jline.version") val antDep = "org.apache.ant" % "ant" % "1.9.4" val scalacheckDep = withoutScalaLang("org.scalacheck" %% "scalacheck" % versionNumber("scalacheck") % "it") -lazy val commonSettings = clearSourceAndResourceDirectories ++ Seq[Setting[_]]( +lazy val commonSettings = clearSourceAndResourceDirectories ++ versionPropertiesSettings ++ Seq[Setting[_]]( organization := "org.scala-lang", + // The ANT build uses the file "build.number" and the property "build.release" to compute the version version := "2.11.8-SNAPSHOT", scalaVersion := bootstrapScalaVersion, // we don't cross build Scala itself @@ -95,6 +98,7 @@ lazy val commonSettings = clearSourceAndResourceDirectories ++ Seq[Setting[_]]( unmanagedJars in Compile := Seq.empty, sourceDirectory in Compile := baseDirectory.value, unmanagedSourceDirectories in Compile := List(baseDirectory.value), + unmanagedResourceDirectories in Compile += (baseDirectory in ThisBuild).value / "src" / thisProject.value.id, scalaSource in Compile := (sourceDirectory in Compile).value, javaSource in Compile := (sourceDirectory in Compile).value, // resources are stored along source files in our current layout @@ -108,7 +112,17 @@ lazy val commonSettings = clearSourceAndResourceDirectories ++ Seq[Setting[_]]( // to make sure they are being cleaned properly cleanFiles += (classDirectory in Compile).value, cleanFiles += (target in Compile in doc).value, - fork in run := true + fork in run := true, + scalacOptions in Compile in doc ++= Seq( + "-doc-footer", "epfl", + "-diagrams", + "-implicits", + "-groups", + "-doc-version", versionProperties.value.canonicalVersion, + "-doc-title", description.value, + "-sourcepath", (baseDirectory in ThisBuild).value.toString, + "-doc-source-url", s"https://github.com/scala/scala/tree/${versionProperties.value.githubTree}€{FILE_PATH}.scala#L1" + ) ) // disable various tasks that are not needed for projects that are used @@ -134,13 +148,7 @@ lazy val setJarLocation: Setting[_] = val resolvedArtifactName = s"${resolvedArtifact.name}.${resolvedArtifact.extension}" buildDirectory.value / "pack/lib" / resolvedArtifactName } -lazy val scalaSubprojectSettings: Seq[Setting[_]] = commonSettings :+ setJarLocation - -lazy val generatePropertiesFileSettings = Seq[Setting[_]]( - copyrightString := "Copyright 2002-2015, LAMP/EPFL", - resourceGenerators in Compile += generateVersionPropertiesFile.map(file => Seq(file)).taskValue, - generateVersionPropertiesFile := generateVersionPropertiesFileImpl.value -) +lazy val scalaSubprojectSettings: Seq[Setting[_]] = commonSettings ++ generatePropertiesFileSettings :+ setJarLocation def filterDocSources(ff: FileFilter): Seq[Setting[_]] = Seq( sources in (Compile, doc) ~= (_.filter(ff.accept _)), @@ -160,19 +168,21 @@ def regexFileFilter(s: String): FileFilter = new FileFilter { def accept(f: File) = pat.matcher(f.getAbsolutePath.replace('\\', '/')).matches() } -val libIncludes: FileFilter = "*.tmpl" | "*.xml" | "*.js" | "*.css" | "rootdoc.txt" - lazy val library = configureAsSubproject(project) .settings(generatePropertiesFileSettings: _*) .settings( name := "scala-library", + description := "Scala Standard Library", scalacOptions in Compile ++= Seq[String]("-sourcepath", (scalaSource in Compile).value.toString), scalacOptions in Compile in doc ++= { val libraryAuxDir = (baseDirectory in ThisBuild).value / "src/library-aux" - Seq("-doc-no-compile", libraryAuxDir.toString) + Seq( + "-doc-no-compile", libraryAuxDir.toString, + "-skip-packages", "scala.concurrent.impl", + "-doc-root-content", (sourceDirectory in Compile).value + "/rootdoc.txt" + ) }, - unmanagedResourceDirectories in Compile += (baseDirectory in ThisBuild).value / "src" / project.id, - includeFilter in unmanagedResources in Compile := libIncludes, + includeFilter in unmanagedResources in Compile := "*.tmpl" | "*.xml" | "*.js" | "*.css" | "rootdoc.txt", // Include forkjoin classes in scala-library.jar mappings in Compile in packageBin ++= (mappings in Compile in packageBin in LocalProject("forkjoin")).value @@ -184,17 +194,20 @@ lazy val library = configureAsSubproject(project) lazy val reflect = configureAsSubproject(project) .settings(generatePropertiesFileSettings: _*) - .settings(name := "scala-reflect") + .settings( + name := "scala-reflect", + description := "Scala Reflection Library", + scalacOptions in Compile in doc ++= Seq( + "-skip-packages", "scala.reflect.macros.internal:scala.reflect.internal:scala.reflect.io" + ) + ) .dependsOn(library) -val compilerIncludes: FileFilter = - "*.tmpl" | "*.xml" | "*.js" | "*.css" | "*.html" | "*.properties" | "*.swf" | - "*.png" | "*.gif" | "*.gif" | "*.txt" - lazy val compiler = configureAsSubproject(project) .settings(generatePropertiesFileSettings: _*) .settings( name := "scala-compiler", + description := "Scala Compiler", libraryDependencies ++= Seq(antDep, asmDep), // this a way to make sure that classes from interactive and scaladoc projects // end up in compiler jar (that's what Ant build does) @@ -209,12 +222,21 @@ lazy val compiler = configureAsSubproject(project) (mappings in Compile in packageBin in LocalProject("interactive")).value ++ (mappings in Compile in packageBin in LocalProject("scaladoc")).value ++ (mappings in Compile in packageBin in LocalProject("repl")).value, - unmanagedResourceDirectories in Compile += (baseDirectory in ThisBuild).value / "src" / project.id, - includeFilter in unmanagedResources in Compile := compilerIncludes) + includeFilter in unmanagedResources in Compile := + "*.tmpl" | "*.xml" | "*.js" | "*.css" | "*.html" | "*.properties" | "*.swf" | + "*.png" | "*.gif" | "*.gif" | "*.txt", + scalacOptions in Compile in doc ++= Seq( + "-doc-root-content", (sourceDirectory in Compile).value + "/rootdoc.txt" + ) + ) .dependsOn(library, reflect) lazy val interactive = configureAsSubproject(project) .settings(disableDocsAndPublishingTasks: _*) + .settings( + name := "scala-compiler-interactive", + description := "Scala Interactive Compiler" + ) .dependsOn(compiler) lazy val repl = configureAsSubproject(project) @@ -272,18 +294,29 @@ lazy val replJlineEmbedded = Project("repl-jline-embedded", file(".") / "target" lazy val scaladoc = configureAsSubproject(project) .settings( - libraryDependencies ++= Seq(scalaXmlDep, scalaParserCombinatorsDep, partestDep) + name := "scala-compiler-doc", + description := "Scala Documentation Generator", + libraryDependencies ++= Seq(scalaXmlDep, scalaParserCombinatorsDep, partestDep), + includeFilter in unmanagedResources in Compile := "*.html" | "*.css" | "*.gif" | "*.png" | "*.js" | "*.txt" ) .settings(disableDocsAndPublishingTasks: _*) .dependsOn(compiler) lazy val scalap = configureAsSubproject(project). - dependsOn(compiler) + settings( + description := "Scala Bytecode Parser", + // Include decoder.properties + includeFilter in unmanagedResources in Compile := "*.properties" + ) + .dependsOn(compiler) // deprecated Scala Actors project lazy val actors = configureAsSubproject(project) .settings(generatePropertiesFileSettings: _*) - .settings(name := "scala-actors") + .settings( + name := "scala-actors", + description := "Scala Actors Library" + ) .settings(filterDocSources("*.scala"): _*) .dependsOn(library) @@ -294,6 +327,7 @@ lazy val partestExtras = configureAsSubproject(Project("partest-extras", file(". .settings(clearSourceAndResourceDirectories: _*) .settings( name := "scala-partest-extras", + description := "Scala Compiler Testing Tool (compiler-specific extras)", libraryDependencies += partestDep, unmanagedSourceDirectories in Compile := List(baseDirectory.value), doc := file("!!! NO DOCS !!!") @@ -311,15 +345,17 @@ lazy val junit = project.in(file("test") / "junit") doc := file("!!! NO DOCS !!!") ) -lazy val partestJavaAgent = (project in file(".") / "src" / "partest-javaagent"). - settings(commonSettings: _*). - settings( +lazy val partestJavaAgent = Project("partest-javaagent", file(".") / "src" / "partest-javaagent") + .settings(commonSettings: _*) + .settings(generatePropertiesFileSettings: _*) + .settings( libraryDependencies += asmDep, doc := file("!!! NO DOCS !!!"), publishLocal := {}, publish := {}, // Setting name to "scala-partest-javaagent" so that the jar file gets that name, which the Runner relies on name := "scala-partest-javaagent", + description := "Scala Compiler Testing Tool (compiler-specific java agent)", // writing jar file to $buildDirectory/pack/lib because that's where it's expected to be found setJarLocation, // add required manifest entry - previously included from file @@ -377,7 +413,7 @@ lazy val dist = (project in file("dist")) .settings( libraryDependencies ++= Seq(scalaContinuationsLibraryDep, scalaContinuationsPluginDep, scalaSwingDep, jlineDep), mkBin := mkBinImpl.value, - mkQuick <<= Def.task {} dependsOn ((distDependencies.map(compile in Compile in _) :+ mkBin): _*), + mkQuick <<= Def.task {} dependsOn ((distDependencies.map(products in Runtime in _) :+ mkBin): _*), mkPack <<= Def.task {} dependsOn (packageBin in Compile, mkBin), target := (baseDirectory in ThisBuild).value / "target" / thisProject.value.id, packageBin in Compile := { @@ -438,59 +474,10 @@ def configureAsForkOfJavaProject(project: Project): Project = { } lazy val buildDirectory = settingKey[File]("The directory where all build products go. By default ./build") -lazy val copyrightString = settingKey[String]("Copyright string.") -lazy val generateVersionPropertiesFile = taskKey[File]("Generating version properties file.") lazy val mkBin = taskKey[Seq[File]]("Generate shell script (bash or Windows batch).") lazy val mkQuick = taskKey[Unit]("Generate a full build, including scripts, in build-sbt/quick") lazy val mkPack = taskKey[Unit]("Generate a full build, including scripts, in build-sbt/pack") -lazy val generateVersionPropertiesFileImpl: Def.Initialize[Task[File]] = Def.task { - val propFile = (resourceManaged in Compile).value / s"${thisProject.value.id}.properties" - val props = new java.util.Properties - - /** - * Regexp that splits version number split into two parts: version and suffix. - * Examples of how the split is performed: - * - * "2.11.5": ("2.11.5", null) - * "2.11.5-acda7a": ("2.11.5", "-acda7a") - * "2.11.5-SNAPSHOT": ("2.11.5", "-SNAPSHOT") - * - */ - val versionSplitted = """([\w+\.]+)(-[\w+\.]+)??""".r - - val versionSplitted(ver, suffixOrNull) = version.value - val osgiSuffix = suffixOrNull match { - case null => "-VFINAL" - case "-SNAPSHOT" => "" - case suffixStr => suffixStr - } - - def executeTool(tool: String) = { - val cmd = - if (System.getProperty("os.name").toLowerCase.contains("windows")) - s"cmd.exe /c tools\\$tool.bat -p" - else s"tools/$tool" - Process(cmd).lines.head - } - - val commitDate = executeTool("get-scala-commit-date") - val commitSha = executeTool("get-scala-commit-sha") - - props.put("version.number", s"${version.value}-$commitDate-$commitSha") - props.put("maven.version.number", s"${version.value}") - props.put("osgi.version.number", s"$ver.v$commitDate$osgiSuffix-$commitSha") - props.put("copyright.string", copyrightString.value) - - // unfortunately, this will write properties in arbitrary order - // this makes it harder to test for stability of generated artifacts - // consider using https://github.com/etiennestuder/java-ordered-properties - // instead of java.util.Properties - IO.write(props, null, propFile) - - propFile -} - /** * Extract selected dependencies to the `cacheDirectory` and return a mapping for the content. * Heavily inspired by sbt-assembly (https://github.com/sbt/sbt-assembly/blob/0.13.0/src/main/scala/sbtassembly/Assembly.scala#L157) @@ -582,17 +569,3 @@ lazy val mkBinImpl: Def.Initialize[Task[Seq[File]]] = Def.task { } buildDirectory in ThisBuild := (baseDirectory in ThisBuild).value / "build-sbt" - -lazy val versionProps: Map[String, String] = { - import java.io.FileInputStream - import java.util.Properties - val props = new Properties() - val in = new FileInputStream(file("versions.properties")) - try props.load(in) - finally in.close() - import scala.collection.JavaConverters._ - props.asScala.toMap -} - -def versionNumber(name: String): String = - versionProps(s"$name.version.number") diff --git a/project/VersionUtil.scala b/project/VersionUtil.scala new file mode 100644 index 0000000000..71de772b08 --- /dev/null +++ b/project/VersionUtil.scala @@ -0,0 +1,103 @@ +import sbt._ +import Keys._ +import java.util.Properties +import java.io.FileInputStream +import scala.collection.JavaConverters._ + +object VersionUtil { + lazy val copyrightString = settingKey[String]("Copyright string.") + lazy val versionProperties = settingKey[Versions]("Version properties.") + lazy val generateVersionPropertiesFile = taskKey[File]("Generating version properties file.") + + lazy val versionPropertiesSettings = Seq[Setting[_]]( + versionProperties := versionPropertiesImpl.value + ) + + lazy val generatePropertiesFileSettings = Seq[Setting[_]]( + copyrightString := "Copyright 2002-2015, LAMP/EPFL", + resourceGenerators in Compile += generateVersionPropertiesFile.map(file => Seq(file)).taskValue, + versionProperties := versionPropertiesImpl.value, + generateVersionPropertiesFile := generateVersionPropertiesFileImpl.value + ) + + case class Versions(canonicalVersion: String, mavenVersion: String, osgiVersion: String, commitSha: String, commitDate: String, isRelease: Boolean) { + val githubTree = + if(isRelease) "v" + mavenVersion + else if(commitSha != "unknown") commitSha + else "master" + + override def toString = s"Canonical: $canonicalVersion, Maven: $mavenVersion, OSGi: $osgiVersion, github: $githubTree" + + def toProperties: Properties = { + val props = new Properties + props.put("version.number", canonicalVersion) + props.put("maven.version.number", mavenVersion) + props.put("osgi.version.number", osgiVersion) + props + } + } + + lazy val versionPropertiesImpl: Def.Initialize[Versions] = Def.setting { + /** Regexp that splits version number split into two parts: version and suffix. + * Examples of how the split is performed: + * + * "2.11.5": ("2.11.5", null) + * "2.11.5-acda7a": ("2.11.5", "-acda7a") + * "2.11.5-SNAPSHOT": ("2.11.5", "-SNAPSHOT") */ + val versionSplitted = """([\w+\.]+)(-[\w+\.]+)??""".r + + val versionSplitted(ver, suffixOrNull) = version.value + + val osgiSuffix = suffixOrNull match { + case null => "-VFINAL" + case "-SNAPSHOT" => "" + case suffixStr => suffixStr + } + + def executeTool(tool: String) = { + val cmd = + if (System.getProperty("os.name").toLowerCase.contains("windows")) + s"cmd.exe /c tools\\$tool.bat -p" + else s"tools/$tool" + Process(cmd).lines.head + } + + val commitDate = executeTool("get-scala-commit-date") + val commitSha = executeTool("get-scala-commit-sha") + + Versions( + canonicalVersion = s"$ver-$commitDate-$commitSha", + mavenVersion = s"${version.value}", + osgiVersion = s"$ver.v$commitDate$osgiSuffix-$commitSha", + commitSha = commitSha, + commitDate = commitDate, + isRelease = !osgiSuffix.isEmpty + ) + } + + lazy val generateVersionPropertiesFileImpl: Def.Initialize[Task[File]] = Def.task { + val props = versionProperties.value.toProperties + val propFile = (resourceManaged in Compile).value / s"${thisProject.value.id}.properties" + props.put("copyright.string", copyrightString.value) + + // unfortunately, this will write properties in arbitrary order + // this makes it harder to test for stability of generated artifacts + // consider using https://github.com/etiennestuder/java-ordered-properties + // instead of java.util.Properties + IO.write(props, null, propFile) + propFile + } + + /** The global versions.properties data */ + lazy val versionProps: Map[String, String] = { + val props = new Properties() + val in = new FileInputStream(file("versions.properties")) + try props.load(in) + finally in.close() + props.asScala.toMap + } + + /** Get a subproject version number from `versionProps` */ + def versionNumber(name: String): String = + versionProps(s"$name.version.number") +} -- cgit v1.2.3