mirror of https://github.com/sbt/sbt.git
129 lines
5.9 KiB
Scala
129 lines
5.9 KiB
Scala
import sbt._
|
|
|
|
import LoaderProject._
|
|
import java.io.File
|
|
|
|
// a project for the sbt launcher
|
|
// the main content of this project definition is setting up and running proguard
|
|
// to combine and compact all dependencies into a single jar
|
|
protected/* removes the ambiguity as to which project is the entry point by making this class non-public*/
|
|
class LoaderProject(info: ProjectInfo) extends DefaultProject(info)
|
|
{
|
|
val mainClassName = "sbt.boot.Boot"
|
|
val baseName = "sbt-launcher"
|
|
val proguardConfigurationPath: Path = outputPath / "proguard.pro"
|
|
lazy val outputJar: Path = rootProject.outputPath / (baseName + "-" + version + ".jar")
|
|
def rootProjectDirectory = rootProject.info.projectPath
|
|
|
|
override def mainClass = Some(mainClassName)
|
|
override def defaultJarBaseName = baseName + "-" + version.toString
|
|
|
|
/****** Resources *****/
|
|
def extraResources = descendents(info.projectPath / "licenses", "*") +++ "LICENSE" +++ "NOTICE"
|
|
override def mainResources = super.mainResources +++ extraResources
|
|
|
|
/****** Dependencies *******/
|
|
val defaultConfig = config("default")
|
|
val toolsConfig = config("tools")
|
|
val ivy = "org.apache.ivy" % "ivy" % "2.0.0"
|
|
val proguardJar = "net.sf.proguard" % "proguard" % "4.3" % "tools->default"
|
|
|
|
/******** Proguard *******/
|
|
lazy val proguard = proguardTask dependsOn(`package`, writeProguardConfiguration) describedAs(ProguardDescription)
|
|
lazy val writeProguardConfiguration = writeProguardConfigurationTask dependsOn `package` describedAs WriteProguardDescription
|
|
|
|
private def proguardTask =
|
|
task
|
|
{
|
|
FileUtilities.clean(outputJar :: Nil, log)
|
|
val proguardClasspath = managedClasspath(toolsConfig)
|
|
val proguardClasspathString = Path.makeString(proguardClasspath.get)
|
|
val configFile = proguardConfigurationPath.asFile.getAbsolutePath
|
|
val exitValue = Process("java", List("-Xmx128M", "-cp", proguardClasspathString, "proguard.ProGuard", "@" + configFile)) ! log
|
|
if(exitValue == 0) None else Some("Proguard failed with nonzero exit code (" + exitValue + ")")
|
|
}
|
|
private def writeProguardConfigurationTask =
|
|
task
|
|
{
|
|
// these are classes that need to be explicitly kept because they are loaded reflectively
|
|
val ivyKeepResolvers =
|
|
"org.apache.ivy.plugins.resolver.URLResolver" ::
|
|
"org.apache.ivy.plugins.resolver.IBiblioResolver" ::
|
|
Nil
|
|
// the template for the proguard configuration file
|
|
val outTemplate = """
|
|
|-dontoptimize
|
|
|-dontobfuscate
|
|
|-dontnote
|
|
|-dontwarn
|
|
|-libraryjars %s
|
|
|-injars %s(!META-INF/**,!fr/**,!**/antlib.xml,!**/*.png)
|
|
|-injars %s(!META-INF/**)
|
|
|%s
|
|
|-outjars %s
|
|
|-ignorewarnings
|
|
|%s
|
|
|%s
|
|
|-keep public class %s {
|
|
| public static void main(java.lang.String[]);
|
|
|}"""
|
|
|
|
val defaultJar = (outputPath / defaultJarName).asFile.getAbsolutePath
|
|
log.debug("proguard configuration using main jar " + defaultJar)
|
|
val ivyKeepOptions = ivyKeepResolvers.map("-keep public class " + _ + allPublic).mkString("\n")
|
|
val runtimeClasspath = runClasspath.get.map(_.asFile).toList
|
|
val jlineJars = runtimeClasspath.filter(isJLineJar)
|
|
val externalDependencies = (mainCompileConditional.analysis.allExternals).map(_.getAbsoluteFile).filter(_.getName.endsWith(".jar"))
|
|
log.debug("proguard configuration external dependencies: \n\t" + externalDependencies.mkString("\n\t"))
|
|
// partition jars from the external jar dependencies of this project by whether they are located in the project directory
|
|
// if they are, they are specified with -injars, otherwise they are specified with -libraryjars
|
|
val (externalJars, libraryJars) = externalDependencies.toList.partition(jar => Path.relativize(rootProjectDirectory, jar).isDefined)
|
|
log.debug("proguard configuration library jars locations: " + libraryJars.mkString(", "))
|
|
// pull out Ivy in order to exclude resources inside
|
|
val (ivyJars, externalJarsNoIvy) = externalJars.partition(_.getName.startsWith("ivy"))
|
|
log.debug("proguard configuration ivy jar location: " + ivyJars.mkString(", "))
|
|
// the loader uses JLine, so there is a dependency on the compiler (because JLine is distributed with the compiler,
|
|
// it finds the JLine classes from the compiler jar instead of the jline jar on the classpath), but we don't want to
|
|
// include the version of JLine from the compiler.
|
|
val includeExternalJars = externalJarsNoIvy.filter(jar => !isJarX(jar, "scala-compiler"))
|
|
// exclude properties files and manifests from scala-library jar
|
|
val inJars = (defaultJar :: includeExternalJars.map( _ + "(!META-INF/**,!*.properties)")).map("-injars " + _).mkString("\n")
|
|
|
|
withJar(ivyJars, "Ivy") { ivyJar =>
|
|
withJar(jlineJars, "JLine") { jlineJar =>
|
|
val proguardConfiguration =
|
|
outTemplate.stripMargin.format(libraryJars.mkString(File.pathSeparator),
|
|
ivyJar.getAbsolutePath, jlineJar.getAbsolutePath,
|
|
inJars, outputJar.absolutePath, ivyKeepOptions, keepJLine, mainClassName)
|
|
log.debug("Proguard configuration written to " + proguardConfigurationPath)
|
|
FileUtilities.write(proguardConfigurationPath.asFile, proguardConfiguration, log)
|
|
}
|
|
}
|
|
}
|
|
private def withJar(files: List[File], name: String)(f: File => Option[String]): Option[String] =
|
|
files match
|
|
{
|
|
case Nil => Some(name + " not present (try running update)")
|
|
case jar :: _ => f(jar)
|
|
}
|
|
private def isJLineJar(file: File) = isJarX(file, "jline")
|
|
private def isJarX(file: File, x: String) =
|
|
{
|
|
val name = file.getName
|
|
name.startsWith(x) && name.endsWith(".jar")
|
|
}
|
|
// class body declaration for proguard that keeps all public members
|
|
private val allPublic = " {\n public * ;\n}"
|
|
|
|
private val keepJLine =
|
|
"""
|
|
|-keep public class jline.** {
|
|
| public protected *;
|
|
|}
|
|
""".stripMargin
|
|
}
|
|
object LoaderProject
|
|
{
|
|
val ProguardDescription = "Produces the final compacted jar that contains only the minimum classes needed using proguard."
|
|
val WriteProguardDescription = "Creates the configuration file to use with proguard."
|
|
} |