Skip to content

Commit

Permalink
port build to 0.10, fixes #30
Browse files Browse the repository at this point in the history
use 'publish-all' from the root to publish all projects to local
use 'proguard' to package the launcher
  • Loading branch information
harrah committed Jun 10, 2011
1 parent 79ab6ad commit d928047
Show file tree
Hide file tree
Showing 18 changed files with 477 additions and 694 deletions.
137 changes: 137 additions & 0 deletions project/Proguard.scala
@@ -0,0 +1,137 @@
import sbt._
import Keys._
import Scope.{GlobalScope, ThisScope}

object LaunchProguard
{
lazy val Proguard = config("proguard") hide ;

lazy val configurationFile = SettingKey[File]("configuration-file")
lazy val proguard = TaskKey[File]("proguard", "Produces the final compacted jar that contains only the classes needed using proguard.")
lazy val proguardConfiguration = TaskKey[File]("proguard-configuration", "Creates the configuration file to use with proguard.")
lazy val options = TaskKey[Seq[String]]("options")
lazy val optimizePasses = SettingKey[Int]("optimize-passes")
lazy val keepFullClasses = SettingKey[Seq[String]]("keep-full-classes")
lazy val keepClasses = SettingKey[Seq[String]]("keep-classes")
lazy val inputJars = TaskKey[Seq[File]]("input-jars")

lazy val settings: Seq[Setting[_]] =
inScope(GlobalScope)(inConfig(Proguard)(globalSettings)) ++
inConfig(Proguard)( baseSettings ) :+
(libraryDependencies += "net.sf.proguard" % "proguard" % "4.4" % Proguard.name)

def globalSettings = Seq(
optimizePasses := 2,
keepFullClasses := Nil,
keepClasses := Nil,
options := basicOptions
)
def baseSettings = Seq(
options <++= optimizePasses map { passes => if(passes <= 0) Seq("-dontoptimize") else Seq( "-optimizationpasses " + passes.toString, "-optimizations !code/allocation/variable") },
options <++= keepFullClasses map { _ map ("-keep public class " + _ + " {\n\tpublic protected * ;\n}") },
options <++= keepClasses map { _ map ("-keep class " + _ + " {}") },
configurationFile <<= target / "proguard.pro",
proguardConfiguration <<= writeProguardConfiguration,
proguard <<= proguardTask
)

def specific(launchSub: Reference): Seq[Setting[_]] = inConfig(Proguard)(Seq(
keepFullClasses ++= "xsbti.**" :: "jline.**" :: Nil,
keepClasses ++= "org.apache.ivy.plugins.resolver.URLResolver" :: "org.apache.ivy.plugins.resolver.IBiblioResolver" :: Nil,
artifactPath <<= (target, version) { (out,v) => out / ("sbt-launch-" + v + ".jar") },
options <++= (dependencyClasspath in (launchSub, Compile), compile in (launchSub,Compile), streams) map { (cp, analysis, s) => mapJars(cp.files, analysis.relations.allBinaryDeps.toSeq, s.log) },
options <+= packageBin map { f => "-injars " + mkpath(f) },
packageBin <<= (packageBin in (launchSub, Compile)).identity,
options <++= mainClass in (launchSub, Compile) map { _.toList map(s => keepMain.format(s)) },
options <+= artifactPath map { p => "-outjars " + mkpath(p) },
fullClasspath <<= (configuration, classpathTypes, update) map Classpaths.managedJars
))

def mapJars(in: Seq[File], all: Seq[File], log: Logger): Seq[String] =
mapInJars(in, log) ++ mapLibraryJars(all filterNot in.toSet)
def writeProguardConfiguration = (options, configurationFile, streams) map { (opts, conf, s) =>
val content = opts.mkString("\n")
if(!conf.exists || IO.read(conf) != content) {
s.log.info("Proguard configuration written to " + conf)
IO.write(conf, content)
}
conf
}

def basicOptions =
Seq(
"-keep,allowoptimization,allowshrinking class * { *; }",
"-keepattributes SourceFile,LineNumberTable",
"-dontnote",
"-dontwarn",
"-ignorewarnings")

private val keepMain =
"""-keep public class %s {
| public static void main(java.lang.String[]);
|}""".stripMargin

def mapLibraryJars(libraryJars: Seq[File]): Seq[String] = libraryJars.map(f => "-libraryjars " + mkpath(f))
def mapOutJar(outJar: File) = "-outjars " + mkpath(outJar)

def mkpath(f: File) : String = mkpath(f.getAbsolutePath, '\"')
def mkpath(path: String, delimiter : Char) : String = delimiter + path + delimiter

def proguardTask = (cacheDirectory, artifactPath, proguardConfiguration, fullClasspath, packageBin, streams) map { (cache, outputJar, configFile, cp, inJar, s) =>
val f = FileFunction.cached(cache / "proguard", FilesInfo.hash) { _ =>
runProguard(outputJar, configFile, cp.files, s.log)
Set(outputJar)
}
f(Set(inJar, configFile)) // make the assumption that if the classpath changed, the outputJar would change
outputJar
}
def runProguard(outputJar: File, configFile: File, cp: Seq[File], log: Logger)
{
IO.delete(outputJar)
val fileString = mkpath(configFile.getAbsolutePath, '\'')
val exitValue = Process("java", List("-Xmx256M", "-cp", Path.makeString(cp), "proguard.ProGuard", "-include " + fileString)) ! log
if(exitValue != 0) error("Proguard failed with nonzero exit code (" + exitValue + ")")
}

def mapInJars(inJars: Seq[File], log: Logger): Seq[String] =
{
val (jlineJars, notJLine) = inJars partition isJarX("jline")
val (ivyJars, notIvy) = notJLine partition isJarX("ivy")
val (libraryJar, remaining) = notIvy partition isJarX("scala-library")
val (compilerJar, otherJars) = remaining partition isJarX("scala-compiler")

log.debug("proguard configuration:")
log.debug("\tJLline jar location: " + jlineJars.mkString(", "))
log.debug("\tIvy jar location: " + ivyJars.mkString(", "))
log.debug("\tOther jars:\n\t" + otherJars.mkString("\n\t"))

((withJar(ivyJars.toSeq, "Ivy") + excludeString(excludeIvyResources)) ::
(withJar(jlineJars, "JLine") + jlineFilter ) ::
(withJar(libraryJar, "Scala library") + libraryFilter) ::
otherJars.map(jar => mkpath(jar) + generalFilter).toList) map { "-injars " + _ }
}

private def excludeString(s: List[String]) = s.map("!" + _).mkString("(",",",")")
private def excludeIvyResources =
"META-INF/**" ::
"fr/**" ::
"**/antlib.xml" ::
"**/*.png" ::
"org/apache/ivy/core/settings/ivyconf*.xml" ::
"org/apache/ivy/core/settings/ivysettings-*.xml" ::
"org/apache/ivy/plugins/resolver/packager/*" ::
"**/ivy_vfs.xml" ::
"org/apache/ivy/plugins/report/ivy-report-*" ::
Nil

private def libraryFilter = "(!META-INF/**,!*.properties,!scala/actors/**.!scala/util/parsing/*.class,!scala/xml/**.class,!scala/package$.class,**.class)"
private def jlineFilter = "(!META-INF/**)"
private def generalFilter = "(!META-INF/**,!*.properties)"

private def withJar[T](files: Seq[File], name: String) = mkpath(files.firstOption getOrElse error(name + " not present") )
private def isJarX(x: String)(file: File) =
{
val name = file.getName
name.startsWith(x) && name.endsWith(".jar")
}
}
211 changes: 211 additions & 0 deletions project/Sbt.scala
@@ -0,0 +1,211 @@
// TODO(high): proper incremental xsbt.version.properties generation
// TODO(low): proper generated API sources caching: doesn't detect output directory change
// TODO: fix discovery/test

import sbt._
import Keys._
import Project.Initialize
import Util._
import Common._
import Licensed._
import Scope.ThisScope
import LaunchProguard.{proguard, Proguard}

object Sbt extends Build
{
override lazy val settings = super.settings ++ Seq(
organization := "org.scala-tools.sbt",
version := "0.10.1-SNAPSHOT",
publishArtifact in packageDoc := false,
scalaVersion := "2.8.1",
publishMavenStyle := false,
componentID := None,
publishTo := Some( Resolver.url("typesafe-ivy-releases", url("http://repo.typesafe.com/typesafe/ivy-releases/"))(Resolver.ivyStylePatterns) ),
credentials += Credentials(Path.userHome / ".ivy2" / ".typesafe-credentials")
)

lazy val myProvided = config("provided") intransitive;
override def projects = super.projects.map(p => p.copy(configurations = (p.configurations.filter(_ != Provided)) :+ myProvided))
lazy val root: Project = Project("xsbt", file("."), aggregate = nonRoots ) settings( rootSettings : _*) configs( Sxr.sxrConf, Proguard )
lazy val nonRoots = projects.filter(_ != root).map(p => LocalProject(p.id))

/*** Subproject declarations ***/

// defines the Java interfaces through which the launcher and the launched application communicate
lazy val launchInterfaceSub = project(launchPath / "interface", "Launcher Interface") settings(javaOnly : _*)
// the launcher. Retrieves, loads, and runs applications based on a configuration file.
lazy val launchSub = testedBaseProject(launchPath, "Launcher") dependsOn(ioSub % "test->test", interfaceSub % "test", launchInterfaceSub) settings(launchSettings : _*)
def launchSettings = Seq(jline, ivy, crossPaths := false,
compile in Test <<= compile in Test dependsOn(publishLocal in interfaceSub, publishLocal in testSamples, publishLocal in launchInterfaceSub)
// mappings in (Compile, packageBin) <++= (mappings in (launchInterfaceSub, Compile, packageBin) ).identity
)

// used to test the retrieving and loading of an application: sample app is packaged and published to the local repository
lazy val testSamples = noPublish( baseProject(launchPath / "test-sample", "Launch Test") ) dependsOn(interfaceSub, launchInterfaceSub) settings(scalaCompiler, crossPaths := false)

// defines Java structures used across Scala versions, such as the API structures and relationships extracted by
// the analysis compiler phases and passed back to sbt. The API structures are defined in a simple
// format from which Java sources are generated by the datatype generator subproject
lazy val interfaceSub = project(file("interface"), "Interface") settings(interfaceSettings : _*)

// defines operations on the API of a source, including determining whether it has changed and converting it to a string
lazy val apiSub = baseProject(compilePath / "api", "API") dependsOn(interfaceSub)

/***** Utilities *****/

lazy val controlSub = baseProject(utilPath / "control", "Control")
lazy val collectionSub = testedBaseProject(utilPath / "collection", "Collections")
// The API for forking, combining, and doing I/O with system processes
lazy val processSub = baseProject(utilPath / "process", "Process") dependsOn(ioSub % "test->test")
// Path, IO (formerly FileUtilities), NameFilter and other I/O utility classes
lazy val ioSub = testedBaseProject(utilPath / "io", "IO") dependsOn(controlSub)
// Utilities related to reflection, managing Scala versions, and custom class loaders
lazy val classpathSub = baseProject(utilPath / "classpath", "Classpath") dependsOn(launchInterfaceSub, ioSub) settings(scalaCompiler)
// Command line-related utilities.
lazy val completeSub = testedBaseProject(utilPath / "complete", "Completion") dependsOn(collectionSub, controlSub, ioSub) settings(jline)
// logging
lazy val logSub = testedBaseProject(utilPath / "log", "Logging") dependsOn(interfaceSub, processSub) settings(libraryDependencies += jlineDep % "optional")
// class file reader and analyzer
lazy val classfileSub = testedBaseProject(utilPath / "classfile", "Classfile") dependsOn(ioSub, interfaceSub, logSub)
// generates immutable or mutable Java data types according to a simple input format
lazy val datatypeSub = baseProject(utilPath /"datatype", "Datatype Generator") dependsOn(ioSub)

/***** Intermediate-level Modules *****/

// Apache Ivy integration
lazy val ivySub = baseProject(file("ivy"), "Ivy") dependsOn(interfaceSub, launchInterfaceSub, logSub % "compile;test->test", ioSub % "compile;test->test", launchSub % "test->test") settings(ivy, jsch)
// Runner for uniform test interface
lazy val testingSub = baseProject(file("testing"), "Testing") dependsOn(ioSub, classpathSub, logSub) settings(libraryDependencies += "org.scala-tools.testing" % "test-interface" % "0.5")

// Basic task engine
lazy val taskSub = testedBaseProject(tasksPath, "Tasks") dependsOn(controlSub, collectionSub)
// Standard task system. This provides map, flatMap, join, and more on top of the basic task model.
lazy val stdTaskSub = testedBaseProject(tasksPath / "standard", "Task System") dependsOn(taskSub % "compile;test->test", collectionSub, logSub, ioSub, processSub)
// Persisted caching based on SBinary
lazy val cacheSub = baseProject(cachePath, "Cache") dependsOn(ioSub, collectionSub) settings(sbinary)
// Builds on cache to provide caching for filesystem-related operations
lazy val trackingSub = baseProject(cachePath / "tracking", "Tracking") dependsOn(cacheSub, ioSub)
// Embedded Scala code runner
lazy val runSub = baseProject(file("run"), "Run") dependsOn(ioSub, logSub, classpathSub, processSub)

// Compiler-side interface to compiler that is compiled against the compiler being used either in advance or on the fly.
// Includes API and Analyzer phases that extract source API and relationships.
lazy val compileInterfaceSub = baseProject(compilePath / "interface", "Compiler Interface") dependsOn(interfaceSub, ioSub % "test->test", logSub % "test->test", launchSub % "test->test") settings( compileInterfaceSettings : _*)
lazy val precompiled29 = precompiled("2.9.0-1")
// lazy val precompiled27 = precompiled("2.7.7")

// Implements the core functionality of detecting and propagating changes incrementally.
// Defines the data structures for representing file fingerprints and relationships and the overall source analysis
lazy val compileIncrementalSub = testedBaseProject(compilePath / "inc", "Incremental Compiler") dependsOn(collectionSub, apiSub, ioSub, logSub)
// Persists the incremental data structures using SBinary
lazy val compilePersistSub = baseProject(compilePath / "persist", "Persist") dependsOn(compileIncrementalSub, apiSub) settings(sbinary)
// sbt-side interface to compiler. Calls compiler-side interface reflectively
lazy val compilerSub = testedBaseProject(compilePath, "Compile") dependsOn(launchInterfaceSub, interfaceSub % "compile;test->test", ivySub, ioSub, classpathSub,
logSub % "test->test", launchSub % "test->test", apiSub % "test->test") settings( compilerSettings : _*)

// Searches the source API data structures, currently looks for subclasses and annotations
lazy val discoverySub = testedBaseProject(compilePath / "discover", "Discovery") dependsOn(compileIncrementalSub, apiSub, compilerSub % "test->test")

lazy val scriptedBaseSub = baseProject(scriptedPath / "base", "Scripted Framework") dependsOn(ioSub, processSub)
lazy val scriptedSbtSub = baseProject(scriptedPath / "sbt", "Scripted sbt") dependsOn(ioSub, logSub, processSub, scriptedBaseSub, launchInterfaceSub % "provided")
lazy val scriptedPluginSub = baseProject(scriptedPath / "plugin", "Scripted Plugin") dependsOn(sbtSub, classpathSub)


// Implementation and support code for defining actions.
lazy val actionsSub = baseProject(mainPath / "actions", "Actions") dependsOn(
classfileSub, classpathSub, compileIncrementalSub, compilePersistSub, compilerSub, completeSub, discoverySub,
interfaceSub, ioSub, ivySub, logSub, processSub, runSub, stdTaskSub, taskSub, trackingSub, testingSub)

// The main integration project for sbt. It brings all of the subsystems together, configures them, and provides for overriding conventions.
lazy val mainSub = testedBaseProject(mainPath, "Main") dependsOn(actionsSub, interfaceSub, ioSub, ivySub, launchInterfaceSub, logSub, processSub, runSub)
// Strictly for bringing implicits and aliases from subsystems into the top-level sbt namespace through a single package object
// technically, we need a dependency on all of mainSub's dependencies, but we don't do that since this is strictly an integration project
// with the sole purpose of providing certain identifiers without qualification (with a package object)
lazy val sbtSub = baseProject(sbtPath, "Simple Build Tool") dependsOn(mainSub, scriptedSbtSub % "test->test") settings(sbtSettings : _*)

/* Nested subproject paths */
def sbtPath = file("sbt")
def cachePath = file("cache")
def tasksPath = file("tasks")
def launchPath = file("launch")
def utilPath = file("util")
def compilePath = file("compile")
def mainPath = file("main")
def scriptedPath = file("scripted")

def sbtSettings = Seq(
normalizedName := "sbt"
)

def scriptedTask: Initialize[InputTask[Unit]] = inputTask { result =>
(proguard in Proguard, fullClasspath in scriptedSbtSub in Test, scalaInstance in scriptedSbtSub, publishAll, version, scalaVersion, scriptedScalaVersion, scriptedSource, result) map {
(launcher, scriptedSbtClasspath, scriptedSbtInstance, _, v, sv, ssv, sourcePath, args) =>
val loader = classpath.ClasspathUtilities.toLoader(scriptedSbtClasspath.files, scriptedSbtInstance.loader)
val m = ModuleUtilities.getObject("sbt.test.ScriptedTests", loader)
val r = m.getClass.getMethod("run", classOf[File], classOf[Boolean], classOf[String], classOf[String], classOf[String], classOf[Array[String]], classOf[File])
r.invoke(m, sourcePath, true: java.lang.Boolean, v, sv, ssv, args.toArray[String], launcher)
}
}

lazy val scriptedScalaVersion = SettingKey[String]("scripted-scala-version")
lazy val scripted = InputKey[Unit]("scripted")
lazy val scriptedSource = SettingKey[File]("scripted-source")
lazy val publishAll = TaskKey[Unit]("publish-all")

def deepTasks[T](scoped: ScopedTask[Seq[T]]): Initialize[Task[Seq[T]]] = deep(scoped.task).map { _.flatMap(_.join.map(_.flatten)) }
def deep[T](scoped: ScopedSetting[T]): Initialize[Task[Seq[T]]] =
state map { s =>
val sxrProjects = projects filterNot Set(root, sbtSub, scriptedBaseSub, scriptedSbtSub, scriptedPluginSub) map { p => LocalProject(p.id) }
Defaults.inAllProjects(sxrProjects, scoped, Project.extract(s).structure.data)
}

import Sxr.sxr
def rootSettings = LaunchProguard.settings ++ LaunchProguard.specific(launchSub) ++ Sxr.settings ++ docSetting ++ Seq(
scriptedScalaVersion := "2.8.1",
scripted <<= scriptedTask,
scriptedSource <<= (sourceDirectory in sbtSub) / "sbt-test",
sources in sxr <<= deepTasks(sources in Compile),
Sxr.sourceDirectories <<= deep(sourceDirectories in Compile).map(_.map(_.flatten)),
fullClasspath in sxr <<= (externalDependencyClasspath in Compile in sbtSub).identity,
compileInputs in (Compile,sxr) <<= (sources in sxr, compileInputs in sbtSub in Compile, fullClasspath in sxr) map { (srcs, in, cp) =>
in.copy(config = in.config.copy(sources = srcs, classpath = cp.files))
},
publishAll <<= state flatMap { s => nop dependsOn( Defaults.inAllProjects(nonRoots, publishLocal.task, Project.extract(s).structure.data) : _*) },
TaskKey[Unit]("build-all") <<= (publishAll, sxr, doc) map { (_,_,_) => () }
)
def docSetting = inConfig(Compile)(inTask(sxr)(doc in ThisScope.copy(task = Global, config = Global) <<= Defaults.docTask))

def interfaceSettings = javaOnly ++ Seq(
crossPaths := false,
projectComponent,
exportJars := true,
componentID := Some("xsbti"),
watchSources <++= apiDefinitions.identity,
resourceGenerators in Compile <+= (version, resourceManaged, streams) map generateVersionFile,
apiDefinitions <<= baseDirectory map { base => (base / "definition") :: (base / "other") :: (base / "type") :: Nil },
sourceGenerators in Compile <+= (cacheDirectory, apiDefinitions, fullClasspath in Compile in datatypeSub, sourceManaged in Compile, mainClass in datatypeSub in Compile, runner, streams) map generateAPICached
)

def precompiledSettings = Seq(
artifact in packageBin <<= scalaInstance in Compile apply { si =>
val bincID = binID + "_" + si.actualVersion
Artifact(binID) extra("e:component" -> bincID)
},
target <<= (target, scalaVersion) { (base, sv) => base / ("precompiled_" + sv) },
scalacOptions := Nil,
crossPaths := false,
exportedProducts in Compile := Nil,
exportedProducts in Test := Nil,
libraryDependencies <+= scalaVersion( "org.scala-lang" % "scala-compiler" % _ % "provided"),
libraryDependencies += jlineDep artifacts(Artifact("jline", Map("e:component" -> srcID)))
)
def compileInterfaceSettings: Seq[Setting[_]] = precompiledSettings ++ Seq(
exportJars := true,
artifact in packageSrc := Artifact(srcID) extra("e:component" -> srcID)
)
def compilerSettings = Seq(
libraryDependencies <+= scalaVersion( "org.scala-lang" % "scala-compiler" % _ % "test"),
unmanagedJars in Test <<= (packageSrc in compileInterfaceSub in Compile).map(x => Seq(x).classpath)
)
def precompiled(scalav: String): Project = baseProject(compilePath / "interface", "Precompiled " + scalav.replace('.', '_')) dependsOn(interfaceSub) settings(scalaVersion := scalav) settings(precompiledSettings : _*)
}

0 comments on commit d928047

Please sign in to comment.