diff options
author | Josh Suereth <joshua.suereth@gmail.com> | 2012-05-10 09:48:03 -0400 |
---|---|---|
committer | Josh Suereth <joshua.suereth@gmail.com> | 2012-05-10 09:48:03 -0400 |
commit | d9ca44bd31532c91df124cdd2f6f2a417bb4a0e1 (patch) | |
tree | 5a7c36f38ba2f5af1080427cc9f37a45d5bed2ff | |
parent | 3012bd9205636f0a4614d6dec65ffe5f86b719f6 (diff) | |
parent | 31546d1b3d841a0fddc9c84af48361c74243ce8e (diff) | |
download | scala-d9ca44bd31532c91df124cdd2f6f2a417bb4a0e1.tar.gz scala-d9ca44bd31532c91df124cdd2f6f2a417bb4a0e1.tar.bz2 scala-d9ca44bd31532c91df124cdd2f6f2a417bb4a0e1.zip |
Merge remote-tracking branch 'jsuereth/sbt-build' into sbt-build-0.11.3
-rw-r--r-- | project/Build.scala | 6 | ||||
-rw-r--r-- | project/CheatingComponentCompiler.scala | 148 | ||||
-rw-r--r-- | project/Layers.scala | 6 | ||||
-rw-r--r-- | project/Partest.scala | 2 | ||||
-rw-r--r-- | project/Release.scala | 106 | ||||
-rw-r--r-- | project/ShaResolve.scala | 2 | ||||
-rw-r--r-- | project/Versions.scala | 142 |
7 files changed, 303 insertions, 109 deletions
diff --git a/project/Build.scala b/project/Build.scala index 25fb31cf5b..c753b4901f 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -23,7 +23,7 @@ object ScalaBuild extends Build with Layers { lazy val buildFixed = AttributeKey[Boolean]("build-uri-fixed") // Build wide settings: - override lazy val settings = super.settings ++ Seq( + override lazy val settings = super.settings ++ Versions.settings ++ Seq( autoScalaLibrary := false, resolvers += Resolver.url( "Typesafe nightlies", @@ -34,7 +34,7 @@ object ScalaBuild extends Build with Layers { ScalaToolsSnapshots ), organization := "org.scala-lang", - version := "2.10.0-SNAPSHOT", + version <<= Versions.mavenVersion, pomExtra := <xml:group> <inceptionYear>2002</inceptionYear> <licenses> @@ -170,7 +170,7 @@ object ScalaBuild extends Build with Layers { skip in Compile <<= lockFile.map(_ exists), lock <<= lockFile map { f => IO.touch(f) }, unlock <<= lockFile map IO.delete - ) + ) ++ CheatingCompilerSettings.settings // -------------------------------------------------------------- // Libraries used by Scalac that change infrequently diff --git a/project/CheatingComponentCompiler.scala b/project/CheatingComponentCompiler.scala new file mode 100644 index 0000000000..e23acfd946 --- /dev/null +++ b/project/CheatingComponentCompiler.scala @@ -0,0 +1,148 @@ +import sbt._ + +import xsbti.{Logger => _,_} +import compiler._ +import inc._ +import Locate.DefinesClass +import java.io.File + +import java.util.concurrent._ +import Keys._ +import Compiler.Compilers + + +object CheatingCompilerSettings { + val hackedmodules = TaskKey[Map[String,File]]("hacked-sbt-components") + + def settings: Seq[Setting[_]] = Seq( + hackedmodules := Map.empty, + + // Now look for org.scala-sbt compiler-interface-src! + hackedmodules <<= (hackedmodules, target, streams) map { (h, t, s) => Map(sbtCompilerInterface(t,s)) ++ h }, + + compilers <<= (hackedmodules, scalaInstance, appConfiguration, streams, classpathOptions, javaHome) map { + (hacks, si, app, s, co, jh) => CheatingCompilers.compilers(hacks, si, co, jh)(app, s.log) + } + ) + + + def sbtCompilerInterface(cacheDir: File, s: TaskStreams): (String, File) = + "compiler-interface-src" -> pullFile("compiler-interface-src", "http://typesafe.artifactoryonline.com/typesafe/ivy-releases/org.scala-sbt/compiler-interface/0.12.0-M2/jars/compiler-interface-src.jar", cacheDir, s) + + import dispatch.{url=>_,_} + def pullFile(name: String, url: String, cacheDir: File, s: TaskStreams): File = { + val cachedFile = cacheDir / (name + ".jar") + if (!cachedFile.exists) { + // Ensure the directory for the cache exists. + cachedFile.getParentFile.mkdirs() + val fous = new java.io.FileOutputStream(cachedFile) + try Http(dispatch.url(url) >>> fous) finally fous.close() + val sha = pullSha(url) + if(!isShaOk(cachedFile, sha)) sys.error("Downloaded sha does not match file: " + cachedFile.getAbsolutePath) + IO.write(file(cachedFile.getAbsolutePath + ".sha1"), sha) + } + cachedFile + } + + def isShaOk(f: File, sha: String): Boolean = + ShaResolve.calculateSha(f) == sha + + def isCached(f: File, url: String): Boolean = + if(f.exists) { + val savedsha = file(f.getAbsolutePath + ".sha1") + savedsha.exists && isShaOk(f, IO.read(savedsha)) + } else false + + def pullSha(url: String): String = + Http(dispatch.url(url + ".sha1") >- identity) +} + + +object CheatingCompilers { + + def compilers(hacks: Map[String,File], instance: ScalaInstance, cpOptions: ClasspathOptions, javaHome: Option[File])(implicit app: AppConfiguration, log: Logger): Compilers = { + val javac = Compiler.directOrFork(instance, cpOptions, javaHome) + val scalac = scalaCompiler(hacks, instance, cpOptions) + new Compilers(scalac, javac) + } + + def scalaCompiler(hacks: Map[String,File], instance: ScalaInstance, cpOptions: ClasspathOptions)(implicit app: AppConfiguration, log: Logger): AnalyzingCompiler = { + val launcher = app.provider.scalaProvider.launcher + val componentManager = new CheatingComponentManager(hacks, launcher.globalLock, app.provider.components, Option(launcher.ivyHome), log) + new AnalyzingCompiler(instance, componentManager, cpOptions, log) + } +} + + +class CheatingComponentManager(val hacks: Map[String,File], globalLock: xsbti.GlobalLock, provider: xsbti.ComponentProvider, ivyHome: Option[File], log: Logger) + extends ComponentManager(globalLock, provider, ivyHome, log) { + + private[this] val ivyCache = new IvyCache(ivyHome) + /** Get all of the files for component 'id', throwing an exception if no files exist for the component. */ + override def files(id: String)(ifMissing: IfMissing): Iterable[File] = { + def fromGlobal1 = + lockGlobalCache { + try { update(id); getOrElse1(createAndCache1) } + catch { case e: NotInCache => createAndCache1 } + } + def getOrElse1(orElse: => Iterable[File]): Iterable[File] = { + val existing = provider.component(hacked(id)) + if(existing.isEmpty) orElse else existing + } + def notFound1 = invalid("Could not find required component '" + id + "'") + def createAndCache1 = + ifMissing match { + case IfMissing.Fail => notFound1 + case d: IfMissing.Define => + d() + if(d.cache) cache(id) + getOrElse1(notFound1) + } + val result = lockLocalCache { getOrElse1(fromGlobal1) } + println("cheating-component-manager.files("+id+")") + result foreach println + result + } + private def isHacked(id: String) = hacks.keySet contains id + private def hacked(id: String) = if(isHacked(id)) "hacked-"+id else id + /** This is used to lock the local cache in project/boot/. By checking the local cache first, we can avoid grabbing a global lock. */ + private def lockLocalCache[T](action: => T): T = lock(provider.lockFile)( action ) + /** This is used to ensure atomic access to components in the global Ivy cache.*/ + private def lockGlobalCache[T](action: => T): T = lock(ivyCache.lockFile)( action ) + private def lock[T](file: File)(action: => T): T = globalLock(file, new Callable[T] { def call = action }) + private def invalid(msg: String) = throw new InvalidComponent(msg) + private def invalid(e: NotInCache) = throw new InvalidComponent(e.getMessage, e) + + override def define(id: String, files: Iterable[File]) = lockLocalCache { provider.defineComponent(hacked(id), files.toSeq.toArray) } + /** Retrieve the file for component 'id' from the local repository. */ + private def update(id: String): Unit = + if(isHacked(id)) { + // Grab jar magically + println("Grabbing hacked jar for ["+id+"] = " + hacks.get(id)) + define(hacked(id), Seq(hacks(id))) + } else ivyCache.withCachedJar(sbtModuleID(id), Some(globalLock), log) {jar => + define(id, Seq(jar)) + } + + + //---------------------!!!!!!!!!!!!!!!!---------------------------------------- + // YUK - Ugly hack to use newer compiler interface - YUK + //---------------------!!!!!!!!!!!!!!!!---------------------------------------- + private def sbtModuleID(id: String) = if(isHacked(id)) { + println("Making hackd module id!") + val version = "0.12.0-M2" // HACKED 20120314203952 + val timestamp = "20120315T001212" // Hacked "20120314203952" // + val stampedVersion = version + "_" + timestamp + val result = ModuleID("org.scala-sbt", id, stampedVersion) + println("Looking for hacked module id: " + result) + result + } else ModuleID("org.scala-tools.sbt", id, ComponentManager.stampedVersion) + //---------------------!!!!!!!!!!!!!!!!---------------------------------------- + // ENDYUK - Ugly hack to use newer compiler interface - ENDYUK + //---------------------!!!!!!!!!!!!!!!!---------------------------------------- + + /** Install the files for component 'id' to the local repository. This is usually used after writing files to the directory returned by 'location'. */ + override def cache(id: String): Unit = ivyCache.cacheJar(sbtModuleID(id), file(id)(IfMissing.Fail), Some(globalLock), log) + override def clearCache(id: String): Unit = lockGlobalCache { ivyCache.clearCachedJar(sbtModuleID(id), Some(globalLock), log) } +} + diff --git a/project/Layers.scala b/project/Layers.scala index d39e58014c..b15b327895 100644 --- a/project/Layers.scala +++ b/project/Layers.scala @@ -16,6 +16,8 @@ trait Layers extends Build { def fjbg: Project /** A setting that adds some external dependencies. */ def externalDeps: Setting[_] + /** The root project. */ + def aaa_root: Project /** Creates a reference Scala version that can be used to build other projects. This takes in the raw * library, compiler and fjbg libraries as well as a string representing the layer name (used for compiling the compile-interface). @@ -57,7 +59,7 @@ trait Layers extends Build { // TODO - Allow other scalac option settings. scalacOptions in Compile <++= (scalaSource in Compile) map (src => Seq("-sourcepath", src.getAbsolutePath)), classpathOptions := ClasspathOptions.manual, - resourceGenerators in Compile <+= (baseDirectory, version, resourceManaged, gitRunner, streams) map Release.generatePropertiesFile("library.properties"), + resourceGenerators in Compile <+= (resourceManaged, Versions.scalaVersions, streams) map Versions.generateVersionPropertiesFile("library.properties"), referenceScala ) @@ -70,7 +72,7 @@ trait Layers extends Build { defaultExcludes := ("tests"), javacOptions ++= Seq("-source", "1.4"), defaultExcludes in unmanagedResources := "*.scala", - resourceGenerators in Compile <+= (baseDirectory, version, resourceManaged, gitRunner, streams) map Release.generatePropertiesFile("compiler.properties"), + resourceGenerators in Compile <+= (resourceManaged, Versions.scalaVersions, streams) map Versions.generateVersionPropertiesFile("compiler.properties"), // Note, we might be able to use the default task, but for some reason ant was filtering files out. Not sure what's up, but we'll // stick with that for now. unmanagedResources in Compile <<= (baseDirectory) map { diff --git a/project/Partest.scala b/project/Partest.scala index ad8047fa00..bbc160a41d 100644 --- a/project/Partest.scala +++ b/project/Partest.scala @@ -115,7 +115,7 @@ object partest { } } - def partestRunnerTask(classpath: ScopedTask[Classpath], javacOptions: ScopedTask[Seq[String]]): Project.Initialize[Task[PartestRunner]] = + def partestRunnerTask(classpath: ScopedTask[Classpath], javacOptions: SettingKey[Seq[String]]): Project.Initialize[Task[PartestRunner]] = (classpath, javacOptions) map ((cp, opts) => new PartestRunner(Build.data(cp), opts mkString " ")) } diff --git a/project/Release.scala b/project/Release.scala index 5ed77548fc..1a17956c13 100644 --- a/project/Release.scala +++ b/project/Release.scala @@ -1,115 +1,15 @@ import sbt._ import Keys._ -import _root_.com.jsuereth.git.GitRunner object Release { - // TODO - move more of the dist project over here... + // TODO - Just make the STARR artifacts and dump the sha1 files. lazy val pushStarr = Command.command("push-starr") { (state: State) => - def f(s: Setting[_]): Setting[_] = s.key.key match { - case version.key => // TODO - use full version - s.asInstanceOf[Setting[String]].mapInit( (_,_) => timeFormat format (new java.util.Date)) - case organization.key => - s.asInstanceOf[Setting[String]].mapInit( (_,_) => "org.scala-lang.bootstrapp") - // TODO - Switch publish repo to be typesafe starr repo. - case publishTo.key => - s.asInstanceOf[Setting[Option[Resolver]]].mapInit((_,_) => Some("Starr Repo" at "http://typesafe.artifactoryonline.com/typesafe/starr-releases/")) - case _ => s - } - val extracted = Project.extract(state) - import extracted._ - // Swap version on projects - val transformed = session.mergeSettings map ( s => f(s) ) - val newStructure = Load.reapply(transformed, structure) - val newState = Project.setProject(session, newStructure, state) - // TODO - Run tasks. Specifically, push scala-compiler + scala-library. *Then* bump the STARR version locally. - // The final course of this command should be: - // publish-local - // Project.evaluateTask(publishLocal, newState) - // bump STARR version setting - // TODO - Define Task - // Rebuild quick + test to ensure it works - // Project.evaluateTask(test, newState) - // push STARR remotely - Project.evaluateTask(publish, newState) + // TODO do something // Revert to previous project state. - Project.setProject(session, structure, state) + state } - // TODO - Autocomplete - /*lazy val setStarrHome = Command.single("set-starr-home") { (state: State, homeDir: String) => - def f(s: Setting[_]): Setting[_] = - if(s.key.key == scalaInstance.key) { - s.asInstanceOf[Setting[ScalaInstance]] mapInit { (key, value) => - if(value.version == "starr") - scalaInstance <<= appConfiguration map { app => - val launcher = app.provider.scalaProvider.launcher - ScalaInstance("starr", new File(homeDir), launcher) - } - else value - } - } else s - val extracted = Project.extract(state) - import extracted._ - val transformed = session.mergeSettings map f - val newStructure = Load.reapply(transformed, structure) - Project.setProject(session, newStructure, state) - }*/ - - lazy val timeFormat = { - val formatter = new java.text.SimpleDateFormat("yyyyMMdd'T'HHmmss") - formatter.setTimeZone(java.util.TimeZone.getTimeZone("GMT")) - formatter - } - - /** This generates a properties file, if it does not already exist, with the maximum lastmodified timestamp - * of any source file. */ - def generatePropertiesFile(name: String)(baseDirectory: File, version: String, dir: File, git: GitRunner, s: TaskStreams): Seq[File] = { - // TODO - We can probably clean this up by moving caching bits elsewhere perhaps.... - val target = dir / name - // TODO - Regenerate on triggers, like recompilation or something... - val fullVersion = makeFullVersionString(baseDirectory, version, git, s) - def hasSameVersion: Boolean = { - val props = new java.util.Properties - val in = new java.io.FileInputStream(target) - try props.load(in) finally in.close() - def withoutDate(s: String): String = s.reverse.dropWhile (_ != '.').reverse - withoutDate(fullVersion) == withoutDate(props getProperty "version.number") - } - if (!target.exists || !hasSameVersion) { - makePropertiesFile(target, fullVersion) - } - target :: Nil - } - - // This creates the *.properties file used to determine the current version of scala at runtime. TODO - move these somewhere utility like. - def makePropertiesFile(f: File, version: String): Unit = - IO.write(f, "version.number = "+version+"\ncopyright.string = Copyright 2002-2011, LAMP/EPFL") - - def makeFullVersionString(baseDirectory: File, baseVersion: String, git: GitRunner, s: TaskStreams) = baseVersion+"."+getGitRevision(baseDirectory, git, currentDay, s) - - // TODO - do we want this in the build number? - def currentDay = (new java.text.SimpleDateFormat("yyyyMMdd'T'HHmmss")) format (new java.util.Date) - - - - def getGitRevision(baseDirectory: File, git: GitRunner, date: String, s: TaskStreams) = { - - val mergeBase = { - // TODO - Cache this value. - // git("merge-base","v2.8.2","v2.9.1","master")(baseDirectory, s.log) - "df13e31bbb" - } - // current commit sha - val sha = - git("rev-list", "-n", "1", "HEAD")(baseDirectory, s.log) - - val commits = - git("--no-pager", "log", "--pretty=oneline", mergeBase +"..HEAD")(baseDirectory, s.log) split "[\r\n]+" size - - "rdev-%d-%s-g%s" format (commits, date, sha.substring(0,7)) - } - } diff --git a/project/ShaResolve.scala b/project/ShaResolve.scala index f54e96c0c6..e6824ee464 100644 --- a/project/ShaResolve.scala +++ b/project/ShaResolve.scala @@ -40,6 +40,8 @@ object ShaResolve { throw t } + // TODO - Finish this publishing aspect. + def getShaFromShafile(file: File): String = parseShaFile(file)._2 // This should calculate the SHA sum of a file the same as the linux process. diff --git a/project/Versions.scala b/project/Versions.scala new file mode 100644 index 0000000000..5f1fe0cacc --- /dev/null +++ b/project/Versions.scala @@ -0,0 +1,142 @@ +import sbt._ +import Keys._ +import java.util.Properties +import scala.util.control.Exception.catching +import java.lang.{NumberFormatException => NFE} +import java.io.FileInputStream +import com.jsuereth.git.GitRunner +import com.jsuereth.git.GitKeys.gitRunner + +case class VersionInfo(canonical: String, + maven: String, + osgi: String) + +/** this file is responsible for setting up Scala versioning schemes and updating all the necessary bits. */ +object Versions { + val buildNumberFile = SettingKey[File]("scala-build-number-file") + // TODO - Make this a setting? + val buildNumberProps = SettingKey[BaseBuildNumber]("scala-build-number-props") + val buildRelease = SettingKey[Boolean]("scala-build-release", "This is set to true if we're building a release.") + val mavenSuffix = SettingKey[String]("scala-maven-suffix", "This is set to whatever maven suffix is required.") + + val gitSha = TaskKey[String]("scala-git-sha", "The sha of the current git commit.") + val gitDate = TaskKey[String]("scala-git-date", "The date of the current git commit.") + + val mavenVersion = SettingKey[String]("scala-maven-version", "The maven version number.") + val osgiVersion = TaskKey[String]("scala-osgi-version", "The OSGi version number.") + val canonicalVersion = TaskKey[String]("scala-canonical-version", "The canonical version number.") + + val scalaVersions = TaskKey[VersionInfo]("scala-version-info", "The scala versions used for this build.") + + + + def settings: Seq[Setting[_]] = Seq( + buildNumberFile <<= baseDirectory apply (_ / "build.number"), + buildNumberProps <<= buildNumberFile apply loadBuildNumberProps, + buildRelease := Option(System.getProperty("build.release")) map (!_.isEmpty) getOrElse false, + mavenSuffix <<= buildRelease apply pickMavenSuffix, + mavenVersion <<= (buildNumberProps, mavenSuffix) apply makeMavenVersion, + gitSha <<= (gitRunner, baseDirectory, streams) map getGitSha, + gitDate <<= (gitRunner, baseDirectory, streams) map getGitDate, + osgiVersion <<= (buildNumberProps, gitDate, gitSha) map makeOsgiVersion, + canonicalVersion <<= (buildRelease, mavenVersion, buildNumberProps, gitDate, gitSha) map makeCanonicalVersion, + scalaVersions <<= (canonicalVersion, mavenVersion, osgiVersion) map VersionInfo.apply + ) + + + /** This generates a properties file, if it does not already exist, with the maximum lastmodified timestamp + * of any source file. */ + def generateVersionPropertiesFile(name: String)(dir: File, versions: VersionInfo, s: TaskStreams): Seq[File] = { + // TODO - We can probably clean this up by moving caching bits elsewhere perhaps.... + val target = dir / name + // TODO - Regenerate on triggers, like recompilation or something... + def hasSameVersion: Boolean = { + val props = new java.util.Properties + val in = new java.io.FileInputStream(target) + try props.load(in) finally in.close() + versions.canonical == (props getProperty "version.number") + } + if (!target.exists || !hasSameVersion) { + makeVersionPropertiesFile(target, versions) + } + target :: Nil + } + + // This creates the *.properties file used to determine the current version of scala at runtime. TODO - move these somewhere utility like. + def makeVersionPropertiesFile(f: File, versions: VersionInfo): Unit = + IO.write(f, "version.number = "+versions.canonical+"\n"+ + "osgi.number = "+versions.osgi+"\n"+ + "maven.number = "+versions.maven+"\n"+ + "copyright.string = Copyright 2002-2011, LAMP/EPFL") + + def makeCanonicalVersion(isRelease: Boolean, mvnVersion: String, base: BaseBuildNumber, gitDate: String, gitSha: String): String = + if(isRelease) mvnVersion + else { + val suffix = if(base.bnum > 0) "-%d".format(base.bnum) else "" + "%s.%s.%s%s-%s-%s" format (base.major, base.minor, base.patch, suffix, gitDate, gitSha) + } + + def makeMavenVersion(base: BaseBuildNumber, suffix: String): String = { + val firstSuffix = if(base.bnum > 0) "-%d".format(base.bnum) else "" + "%d.%d.%d%s%s" format (base.major, base.minor, base.patch, firstSuffix, suffix) + } + + def makeOsgiVersion(base: BaseBuildNumber, gitDate: String, gitSha: String): String = { + val suffix = if(base.bnum > 0) "-%d".format(base.bnum) else "" + "%s.%s.%s.v%s%s-%s" format (base.major, base.minor, base.patch, gitDate, suffix, gitSha) + } + + /** Determines what the maven sufffix should be for this build. */ + def pickMavenSuffix(isRelease: Boolean): String = { + def default = if(isRelease) "" else "-SNAPSHOT" + Option(System.getProperty("maven.version.suffix")) getOrElse default + } + + /** Loads the build.number properties file into SBT. */ + def loadBuildNumberProps(file: File): BaseBuildNumber = { + val fin = new FileInputStream(file) + try { + val props = new Properties() + props.load(fin) + def getProp(name: String): Int = + (for { + v <- Option(props.getProperty(name)) + v2 <- catching(classOf[NFE]) opt v.toInt + } yield v2) getOrElse sys.error("Could not convert %s to integer!" format (name)) + + BaseBuildNumber( + major=getProp("version.major"), + minor=getProp("version.minor"), + patch=getProp("version.patch"), + bnum =getProp("version.bnum") + ) + } finally fin.close() + } + + + def getGitDate(git: GitRunner, baseDirectory: File, s: TaskStreams): String = { + val lines = getGitLines("log","-1","--format=\"%ci\"")(git,baseDirectory, s) + val line = if(lines.isEmpty) sys.error("Could not retreive git commit sha!") else lines.head + // Lines *always* start with " for some reason... + line drop 1 split "\\s+" match { + case Array(date, time, _*) => "%s-%s" format (date.replaceAll("\\-", ""), time.replaceAll(":","")) + case _ => sys.error("Could not parse git date: " + line) + } + } + + def getGitSha(git: GitRunner, baseDirectory: File, s: TaskStreams): String = { + val lines = getGitLines("log","-1","--format=\"%H\"", "HEAD")(git,baseDirectory, s) + val line = if(lines.isEmpty) sys.error("Could not retreive git commit sha!") else lines.head + val noquote = if(line startsWith "\"") line drop 1 else line + val nog = if(noquote startsWith "g") noquote drop 1 else noquote + nog take 10 + } + + def getGitLines(args: String*)(git: GitRunner, baseDirectory: File, s: TaskStreams): Seq[String] = + git(args: _*)(baseDirectory, s.log) split "[\r\n]+" +} + + +case class BaseBuildNumber(major: Int, minor: Int, patch: Int, bnum: Int) { + override def toString = "BaseBuildNumber(%d.%d.%d-%d)" format (major, minor, patch, bnum) +} |