aboutsummaryrefslogtreecommitdiff
path: root/stage2
diff options
context:
space:
mode:
Diffstat (limited to 'stage2')
-rw-r--r--stage2/AdminStage2.scala13
-rw-r--r--stage2/AdminTasks.scala109
-rw-r--r--stage2/BasicBuild.scala52
-rw-r--r--stage2/BuildBuild.scala8
-rw-r--r--stage2/BuildDependency.scala2
-rw-r--r--stage2/GitDependency.scala6
-rw-r--r--stage2/Lib.scala234
-rw-r--r--stage2/NameTransformer.scala161
-rw-r--r--stage2/PackageBuild.scala22
-rw-r--r--stage2/Scaffold.scala7
-rw-r--r--stage2/Stage2.scala31
-rw-r--r--stage2/mixins.scala5
12 files changed, 451 insertions, 199 deletions
diff --git a/stage2/AdminStage2.scala b/stage2/AdminStage2.scala
index d923b22..883b5ed 100644
--- a/stage2/AdminStage2.scala
+++ b/stage2/AdminStage2.scala
@@ -1,13 +1,12 @@
package cbt
import java.io._
-object AdminStage2{
- def main(_args: Array[String]) = {
- val args = _args.drop(1).dropWhile(Seq("admin","direct") contains _)
- val init = new Init(args)
- val lib = new Lib(init.logger)
- val adminTasks = new AdminTasks(lib, args, new File(_args(0)))
+object AdminStage2 extends Stage2Base{
+ def run( _args: Stage2Args ): Unit = {
+ val args = _args.args.dropWhile(Seq("admin","direct") contains _)
+ val lib = new Lib(_args.logger)
+ val adminTasks = new AdminTasks(lib, args, _args.cwd)
new lib.ReflectObject(adminTasks){
- def usage: String = "Available methods: " ++ lib.taskNames(subclassType).mkString(" ")
+ def usage: String = "Available methods: " ++ lib.taskNames(adminTasks.getClass).mkString(" ")
}.callNullary(args.lift(0))
}
}
diff --git a/stage2/AdminTasks.scala b/stage2/AdminTasks.scala
index e7fc78b..069b712 100644
--- a/stage2/AdminTasks.scala
+++ b/stage2/AdminTasks.scala
@@ -1,35 +1,132 @@
package cbt
import scala.collection.immutable.Seq
-import java.io._
-class AdminTasks(lib: Lib, args: Array[String], cwd: File){
+import java.io.{Console=>_,_}
+import java.nio.file._
+class AdminTasks(lib: Lib, args: Seq[String], cwd: File){
implicit val logger: Logger = lib.logger
def resolve = {
ClassPath.flatten(
args(1).split(",").toVector.map{
d =>
val v = d.split(":")
- new JavaDependency(v(0),v(1),v(2))(lib.logger).classpath
+ new JavaDependency(v(0),v(1),v(2)).classpath
}
)
}
+ def dependencyTree = {
+ args(1).split(",").toVector.map{
+ d =>
+ val v = d.split(":")
+ new JavaDependency(v(0),v(1),v(2)).dependencyTree
+ }.mkString("\n\n")
+ }
def amm = ammonite
def ammonite = {
val version = args.lift(1).getOrElse(constants.scalaVersion)
val scalac = new ScalaCompilerDependency( version )
val d = JavaDependency(
- "com.lihaoyi","ammonite-repl_2.11.7",args.lift(1).getOrElse("0.5.6")
+ "com.lihaoyi","ammonite-repl_2.11.7",args.lift(1).getOrElse("0.5.7")
)
// FIXME: this does not work quite yet, throws NoSuchFileException: /ammonite/repl/frontend/ReplBridge$.class
lib.runMain(
- "ammonite.repl.Main", Seq(), d.classLoader
+ "ammonite.repl.Main", Seq(), d.classLoader(new ClassLoaderCache(logger))
)
}
def scala = {
val version = args.lift(1).getOrElse(constants.scalaVersion)
val scalac = new ScalaCompilerDependency( version )
lib.runMain(
- "scala.tools.nsc.MainGenericRunner", Seq("-cp", scalac.classpath.string), scalac.classLoader
+ "scala.tools.nsc.MainGenericRunner", Seq("-cp", scalac.classpath.string), scalac.classLoader(new ClassLoaderCache(logger))
)
}
def scaffoldBasicBuild: Unit = lib.scaffoldBasicBuild( cwd )
+ def cbtEarlyDependencies = {
+ val scalaVersion = args.lift(1).getOrElse(constants.scalaVersion)
+ val scalaMajorVersion = scalaVersion.split("\\.").take(2).mkString(".")
+ val scalaXmlVersion = args.lift(2).getOrElse(constants.scalaXmlVersion)
+ val zincVersion = args.lift(3).getOrElse(constants.zincVersion)
+ /*
+ def tree(d: JavaDependency, indent: Int): String ={
+ val dependencies = {
+ if( d.dependencies.nonEmpty ){
+ d.dependencies.map{
+ case d: JavaDependency => tree(d,indent + 1)
+ }.mkString(",\n" ++ ( " " * indent ),",\n" ++ ( " " * indent ), "")
+ } else ""
+ }
+ (
+ s"""new EarlyDependency( "${d.groupId}", "${d.artifactId}", "${d.version}", "${d.jarSha1}"$dependencies)"""
+ )
+ }*/
+ val scalaDeps = Seq(
+ JavaDependency("org.scala-lang","scala-reflect",scalaVersion),
+ JavaDependency("org.scala-lang","scala-compiler",scalaVersion)
+ )
+
+ val scalaXml = Dependencies(
+ JavaDependency("org.scala-lang.modules","scala-xml_"+scalaMajorVersion,scalaXmlVersion),
+ JavaDependency("org.scala-lang","scala-library",scalaVersion)
+ )
+
+ val zinc = JavaDependency("com.typesafe.zinc","zinc",zincVersion)
+ println(zinc.dependencyTree)
+
+ def valName(dep: JavaDependency) = {
+ val words = dep.artifactId.split("_").head.split("-")
+ words(0) ++ words.drop(1).map(s => s(0).toString.toUpperCase ++ s.drop(1)).mkString ++ "_" ++ dep.version.replace(".","_") ++ "_"
+ }
+
+ def vals(d: JavaDependency) = s""" """
+
+ def jarVal(dep: JavaDependency) = "_" + valName(dep) +"Jar"
+ def transitive(dep: Dependency) = (dep +: dep.transitiveDependencies.reverse).collect{case d: JavaDependency => d}
+ def codeEach(dep: Dependency) = {
+ transitive(dep).tails.map(_.reverse).toVector.reverse.drop(1).map{
+ deps =>
+ val d = deps.last
+ val parents = deps.dropRight(1)
+ val parentString = if(parents.isEmpty) "" else ( ", " ++ valName(parents.last) )
+ val n = valName(d)
+ s"""
+ // ${d.groupId}:${d.artifactId}:${d.version}
+ download(new URL(MAVEN_URL + "${d.basePath}.jar"), Paths.get(${n}File), "${d.jarSha1}");
+ ClassLoader $n = cachePut(
+ classLoader( ${n}File$parentString ),
+ ${deps.sortBy(_.jar).map(valName(_)+"File").mkString(", ")}
+ );"""
+ }
+ }
+ val assignments = codeEach(zinc) ++ codeEach(scalaXml)
+ //{ case (name, dep) => s"$name =\n ${tree(dep, 4)};" }.mkString("\n\n ")
+ val code = s"""// This file was auto-generated using `cbt admin cbtEarlyDependencies`
+package cbt;
+import java.io.*;
+import java.nio.file.*;
+import java.net.*;
+import java.security.*;
+import static cbt.NailgunLauncher.*;
+
+class EarlyDependencies{
+
+ /** ClassLoader for stage1 */
+ ClassLoader stage1;
+ /** ClassLoader for zinc */
+ ClassLoader zinc;
+
+${(scalaDeps ++ transitive(scalaXml) ++ transitive(zinc)).map(d => s""" String ${valName(d)}File = MAVEN_CACHE + "${d.basePath}.jar";""").mkString("\n")}
+
+ public EarlyDependencies() throws MalformedURLException, IOException, NoSuchAlgorithmException{
+${scalaDeps.map(d => s""" download(new URL(MAVEN_URL + "${d.basePath}.jar"), Paths.get(${valName(d)}File), "${d.jarSha1}");""").mkString("\n")}
+${assignments.mkString("\n")}
+
+ stage1 = scalaXml_${scalaXmlVersion.replace(".","_")}_;
+
+ zinc = zinc_${zincVersion.replace(".","_")}_;
+ }
+}
+"""
+ val file = paths.nailgun ++ ("/" ++ "EarlyDependencies.java")
+ Files.write( file.toPath, code.getBytes )
+ println( Console.GREEN ++ "Wrote " ++ file.string ++ Console.RESET )
+ }
}
diff --git a/stage2/BasicBuild.scala b/stage2/BasicBuild.scala
index 2f90197..9ed8c26 100644
--- a/stage2/BasicBuild.scala
+++ b/stage2/BasicBuild.scala
@@ -2,7 +2,6 @@ package cbt
import cbt.paths._
import java.io._
-import java.lang.reflect.InvocationTargetException
import java.net._
import java.nio.file.{Path =>_,_}
import java.nio.file.Files.readAllBytes
@@ -10,15 +9,13 @@ import java.security.MessageDigest
import java.util.jar._
import scala.collection.immutable.Seq
-import scala.reflect.runtime.{universe => ru}
import scala.util._
-import ammonite.ops.{cwd => _,_}
-
class BasicBuild( context: Context ) extends Build( context )
class Build(val context: Context) extends Dependency with TriggerLoop{
// library available to builds
implicit final val logger: Logger = context.logger
+ implicit final val classLoaderCache: ClassLoaderCache = context.classLoaderCache
override final protected val lib: Lib = new Lib(logger)
// ========== general stuff ==========
@@ -26,7 +23,7 @@ class Build(val context: Context) extends Dependency with TriggerLoop{
def enableConcurrency = false
final def projectDirectory: File = lib.realpath(context.cwd)
assert( projectDirectory.exists, "projectDirectory does not exist: " ++ projectDirectory.string )
- final def usage: Unit = new lib.ReflectBuild(this).usage
+ final def usage: String = lib.usage(this.getClass, context)
// ========== meta data ==========
@@ -51,6 +48,12 @@ class Build(val context: Context) extends Dependency with TriggerLoop{
def apiTarget: File = scalaTarget ++ "/api"
/** directory where the class files should be put (in package directories) */
def compileTarget: File = scalaTarget ++ "/classes"
+ /**
+ File which cbt uses to determine if it needs to trigger an incremental re-compile.
+ Last modified date is the time when the last successful compilation started.
+ Contents is the cbt version git hash.
+ */
+ def compileStatusFile: File = compileTarget ++ ".last-success"
/** Source directories and files. Defaults to .scala and .java files in src/ and top-level. */
def sources: Seq[File] = Seq(defaultSourceDirectory) ++ projectDirectory.listFiles.toVector.filter(sourceFileFilter)
@@ -113,7 +116,7 @@ class Build(val context: Context) extends Dependency with TriggerLoop{
override def dependencyClasspath : ClassPath = ClassPath(localJars) ++ super.dependencyClasspath
override def dependencyJars : Seq[File] = localJars ++ super.dependencyJars
- def exportedClasspath : ClassPath = ClassPath(Seq(compile))
+ def exportedClasspath : ClassPath = ClassPath(compile.toSeq:_*)
def targetClasspath = ClassPath(Seq(compileTarget))
def exportedJars: Seq[File] = Seq()
// ========== compile, run, test ==========
@@ -121,38 +124,25 @@ class Build(val context: Context) extends Dependency with TriggerLoop{
/** scalac options used for zinc and scaladoc */
def scalacOptions: Seq[String] = Seq( "-feature", "-deprecation", "-unchecked" )
- val updated: Boolean = {
- val existingClassFiles = lib.listFilesRecursive(compileTarget)
- val sourcesChanged = existingClassFiles.nonEmpty && {
- val oldestClassFile = existingClassFiles.sortBy(_.lastModified).head
- val oldestClassFileAge = oldestClassFile.lastModified
- val changedSourceFiles = sourceFiles.filter(_.lastModified > oldestClassFileAge)
- if(changedSourceFiles.nonEmpty){
- /*
- println(changedSourceFiles)
- println(changedSourceFiles.map(_.lastModified))
- println(changedSourceFiles.map(_.lastModified > oldestClassFileAge))
- println(oldestClassFile)
- println(oldestClassFileAge)
- println("-"*80)
- */
- }
- changedSourceFiles.nonEmpty
- }
- sourcesChanged || transitiveDependencies.map(_.updated).fold(false)(_ || _)
+ private object needsUpdateCache extends Cache[Boolean]
+ def needsUpdate: Boolean = {
+ needsUpdateCache(
+ lib.needsUpdate( sourceFiles, compileStatusFile )
+ || transitiveDependencies.exists(_.needsUpdate)
+ )
}
- private object compileCache extends Cache[File]
- def compile: File = compileCache{
+ private object compileCache extends Cache[Option[File]]
+ def compile: Option[File] = compileCache{
lib.compile(
- updated,
- sourceFiles, compileTarget, dependencyClasspath, scalacOptions,
- zincVersion = zincVersion, scalaVersion = scalaVersion
+ needsUpdate,
+ sourceFiles, compileTarget, compileStatusFile, dependencyClasspath, scalacOptions,
+ context.classLoaderCache, zincVersion = zincVersion, scalaVersion = scalaVersion
)
}
def runClass: String = "Main"
- def run: ExitCode = lib.runMainIfFound( runClass, context.args, classLoader )
+ def run: ExitCode = lib.runMainIfFound( runClass, context.args, classLoader(context.classLoaderCache) )
def test: ExitCode = lib.test(context)
diff --git a/stage2/BuildBuild.scala b/stage2/BuildBuild.scala
index 5e0f5d3..9746d8c 100644
--- a/stage2/BuildBuild.scala
+++ b/stage2/BuildBuild.scala
@@ -8,10 +8,14 @@ class BuildBuild(context: Context) extends Build(context){
val managedBuild = {
val managedContext = context.copy( cwd = managedBuildDirectory )
val cl = new cbt.URLClassLoader(
- classpath,
+ exportedClasspath,
classOf[BuildBuild].getClassLoader // FIXME: this looks wrong. Should be ClassLoader.getSystemClassLoader but that crashes
)
- lib.create( lib.buildClassName )( managedContext )( cl ).asInstanceOf[Build]
+ cl
+ .loadClass(lib.buildClassName)
+ .getConstructor(classOf[Context])
+ .newInstance(managedContext)
+ .asInstanceOf[Build]
}
override def triggerLoopFiles = super.triggerLoopFiles ++ managedBuild.triggerLoopFiles
override def finalBuild = managedBuild.finalBuild
diff --git a/stage2/BuildDependency.scala b/stage2/BuildDependency.scala
index 84a0100..e3a01c7 100644
--- a/stage2/BuildDependency.scala
+++ b/stage2/BuildDependency.scala
@@ -25,7 +25,7 @@ case class BuildDependency(context: Context) extends TriggerLoop{
def exportedJars = Seq()
def dependencies = Seq(build)
def triggerLoopFiles = root.triggerLoopFiles
- final val updated = build.updated
+ override final val needsUpdate = build.needsUpdate
def targetClasspath = ClassPath(Seq())
}
/*
diff --git a/stage2/GitDependency.scala b/stage2/GitDependency.scala
index c3e38b6..59de98a 100644
--- a/stage2/GitDependency.scala
+++ b/stage2/GitDependency.scala
@@ -7,7 +7,7 @@ import org.eclipse.jgit.lib.Ref
case class GitDependency(
url: String, ref: String // example: git://github.com/cvogt/cbt.git#<some-hash>
-)(implicit val logger: Logger) extends Dependency{
+)(implicit val logger: Logger, classLoaderCache: ClassLoaderCache ) extends Dependency{
override def lib = new Lib(logger)
// TODO: add support for authentication via ssh and/or https
@@ -37,7 +37,7 @@ case class GitDependency(
}
val managedBuild = lib.loadDynamic(
- Context( cwd = checkoutDirectory, args = Seq(), logger )
+ Context( cwd = checkoutDirectory, args = Seq(), logger, classLoaderCache )
)
Seq( managedBuild )
}
@@ -45,5 +45,5 @@ case class GitDependency(
def exportedClasspath = ClassPath(Seq())
def exportedJars = Seq()
private[cbt] def targetClasspath = exportedClasspath
- def updated: Boolean = false
+ def needsUpdate: Boolean = false
}
diff --git a/stage2/Lib.scala b/stage2/Lib.scala
index 60e7dd4..dd4a12f 100644
--- a/stage2/Lib.scala
+++ b/stage2/Lib.scala
@@ -8,13 +8,11 @@ import java.nio.file.{Path =>_,_}
import java.nio.file.Files.readAllBytes
import java.security.MessageDigest
import java.util.jar._
+import java.lang.reflect.Method
import scala.collection.immutable.Seq
-import scala.reflect.runtime.{universe => ru}
import scala.util._
-import ammonite.ops.{cwd => _,_}
-
// pom model
case class Developer(id: String, name: String, timezone: String, url: URL)
case class License(name: String, url: URL)
@@ -55,28 +53,18 @@ final class Lib(logger: Logger) extends Stage1Lib(logger) with Scaffold{
}
}
- def compile(
- updated: Boolean,
- sourceFiles: Seq[File], compileTarget: File, dependenyClasspath: ClassPath,
- compileArgs: Seq[String], zincVersion: String, scalaVersion: String
- ): File = {
- if(sourceFiles.nonEmpty)
- lib.zinc(
- updated, sourceFiles, compileTarget, dependenyClasspath, compileArgs
- )( zincVersion = zincVersion, scalaVersion = scalaVersion )
- compileTarget
- }
-
- def srcJar(sources: Seq[File], artifactId: String, version: String, jarTarget: File): File = {
- val file = jarTarget ++ ("/"++artifactId++"-"++version++"-sources.jar")
- lib.jarFile(file, sources)
- file
+ def srcJar(sourceFiles: Seq[File], artifactId: String, version: String, jarTarget: File): Option[File] = {
+ lib.jarFile(
+ jarTarget ++ ("/"++artifactId++"-"++version++"-sources.jar"),
+ sourceFiles
+ )
}
- def jar(artifactId: String, version: String, compileTarget: File, jarTarget: File): File = {
- val file = jarTarget ++ ("/"++artifactId++"-"++version++".jar")
- lib.jarFile(file, Seq(compileTarget))
- file
+ def jar(artifactId: String, version: String, compileTarget: File, jarTarget: File): Option[File] = {
+ lib.jarFile(
+ jarTarget ++ ("/"++artifactId++"-"++version++".jar"),
+ Seq(compileTarget)
+ )
}
def docJar(
@@ -87,29 +75,31 @@ final class Lib(logger: Logger) extends Stage1Lib(logger) with Scaffold{
jarTarget: File,
artifactId: String,
version: String,
- compileArgs: Seq[String]
- ): File = {
- mkdir(Path(apiTarget))
- if(sourceFiles.nonEmpty){
+ compileArgs: Seq[String],
+ classLoaderCache: ClassLoaderCache
+ ): Option[File] = {
+ if(sourceFiles.isEmpty){
+ None
+ } else {
+ apiTarget.mkdirs
val args = Seq(
// FIXME: can we use compiler dependency here?
"-cp", dependencyClasspath.string, // FIXME: does this break for builds that don't have scalac dependencies?
"-d", apiTarget.toString
) ++ compileArgs ++ sourceFiles.map(_.toString)
logger.lib("creating docs for source files "+args.mkString(", "))
- trapExitCode{
- redirectOutToErr{
- runMain(
- "scala.tools.nsc.ScalaDoc",
- args,
- ScalaDependencies(scalaVersion)(logger).classLoader
- )
- }
+ redirectOutToErr{
+ runMain(
+ "scala.tools.nsc.ScalaDoc",
+ args,
+ ScalaDependencies(scalaVersion)(logger).classLoader(classLoaderCache)
+ )
}
+ lib.jarFile(
+ jarTarget ++ ("/"++artifactId++"-"++version++"-javadoc.jar"),
+ Vector(apiTarget)
+ )
}
- val docJar = jarTarget ++ ("/"++artifactId++"-"++version++"-javadoc.jar")
- lib.jarFile(docJar, Vector(apiTarget))
- docJar
}
def test( context: Context ): ExitCode = {
@@ -128,60 +118,66 @@ final class Lib(logger: Logger) extends Stage1Lib(logger) with Scaffold{
}
// task reflection helpers
- import ru._
- private lazy val anyRefMembers: Set[String] = ru.typeOf[AnyRef].members.toSet.map(taskName)
- def taskNames(tpe: Type): Seq[String] = tpe.members.toVector.flatMap(lib.toTask).map(taskName).sorted
- private def taskName(method: Symbol): String = method.name.decodedName.toString
- def toTask(symbol: Symbol): Option[MethodSymbol] = {
- Option(symbol)
- .filter(_.isPublic)
- .filter(_.isMethod)
- .map(_.asMethod)
- .filter(_.paramLists.flatten.size == 0)
- .filterNot(taskName(_) contains "$")
- .filterNot(t => anyRefMembers contains taskName(t))
- }
+ def tasks(cls:Class[_]): Map[String, Method] =
+ Stream
+ .iterate(cls.asInstanceOf[Class[Any]])(_.getSuperclass)
+ .takeWhile(_ != null)
+ .toVector
+ .dropRight(1) // drop Object
+ .reverse
+ .flatMap(
+ c =>
+ c
+ .getDeclaredMethods
+ .filterNot( _.getName contains "$" )
+ .filter{ m =>
+ java.lang.reflect.Modifier.isPublic(m.getModifiers)
+ }
+ .filter( _.getParameterCount == 0 )
+ .map(m => NameTransformer.decode(m.getName) -> m)
+ ).toMap
- class ReflectBuild(val build: Build) extends ReflectObject(build){
- def usage: String = {
- val baseTasks = lib.taskNames(ru.typeOf[Build])
- val thisTasks = lib.taskNames(subclassType) diff baseTasks
+ def taskNames(cls: Class[_]): Seq[String] = tasks(cls).keys.toVector.sorted
+
+ def usage(buildClass: Class[_], context: Context): String = {
+ val baseTasks = lib.taskNames(classOf[Build])
+ val thisTasks = lib.taskNames(buildClass) diff baseTasks
+ (
(
- (
- if( thisTasks.nonEmpty ){
- s"""Methods provided by Build ${build.context.cwd}
+ if( thisTasks.nonEmpty ){
+ s"""Methods provided by Build ${context}
${thisTasks.mkString(" ")}
"""
- } else ""
- ) ++ s"""Methods provided by CBT (but possibly overwritten)
+ } else ""
+ ) ++ s"""Methods provided by CBT (but possibly overwritten)
${baseTasks.mkString(" ")}"""
) ++ "\n"
- }
}
+ class ReflectBuild[T:scala.reflect.ClassTag](build: Build) extends ReflectObject(build){
+ def usage = lib.usage(build.getClass, build.context)
+ }
abstract class ReflectObject[T:scala.reflect.ClassTag](obj: T){
- lazy val mirror = ru.runtimeMirror(obj.getClass.getClassLoader)
- lazy val subclassType = mirror.classSymbol(obj.getClass).toType
def usage: String
def callNullary( taskName: Option[String] ): Unit = {
- taskName
- .map{ n => subclassType.member(ru.TermName(n).encodedName) }
- .filter(_ != ru.NoSymbol)
- .flatMap(toTask _)
- .map{ methodSymbol =>
- val result = mirror.reflect(obj).reflectMethod(methodSymbol)()
-
+ val ts = tasks(obj.getClass)
+ taskName.map( NameTransformer.encode ).flatMap(ts.get).map{ method =>
+ val result: Option[Any] = Option(method.invoke(obj)) // null in case of Unit
+ result.flatMap{
+ case v: Option[_] => v
+ case other => Some(other)
+ }.map{
+ value =>
// Try to render console representation. Probably not the best way to do this.
- scala.util.Try( result.getClass.getDeclaredMethod("toConsole") ) match {
- case scala.util.Success(m) =>
- println(m.invoke(result))
+ scala.util.Try( value.getClass.getDeclaredMethod("toConsole") ) match {
+ case scala.util.Success(toConsole) =>
+ println(toConsole.invoke(value))
- case scala.util.Failure(e) if e.getMessage contains "toConsole" =>
- result match {
- case () => ""
+ case scala.util.Failure(e) if Option(e.getMessage).getOrElse("") contains "toConsole" =>
+ value match {
case ExitCode(code) => System.exit(code)
case other => println( other.toString ) // no method .toConsole, using to String
}
@@ -189,16 +185,17 @@ final class Lib(logger: Logger) extends Stage1Lib(logger) with Scaffold{
case scala.util.Failure(e) =>
throw e
}
- }.getOrElse{
- taskName.foreach{ n =>
- System.err.println(s"Method not found: $n")
- System.err.println("")
- }
- System.err.println(usage)
- taskName.foreach{ _ =>
- ExitCode.Failure
- }
+ }.getOrElse("")
+ }.getOrElse{
+ taskName.foreach{ n =>
+ System.err.println(s"Method not found: $n")
+ System.err.println("")
}
+ System.err.println(usage)
+ taskName.foreach{ _ =>
+ ExitCode.Failure
+ }
+ }
}
}
@@ -207,35 +204,41 @@ final class Lib(logger: Logger) extends Stage1Lib(logger) with Scaffold{
def dirname(path: File): File = new File(realpath(path).string.stripSuffix("/").split("/").dropRight(1).mkString("/"))
def nameAndContents(file: File) = basename(file) -> readAllBytes(Paths.get(file.toString))
- def jarFile( jarFile: File, files: Seq[File] ): Unit = {
- logger.lib("Start packaging "++jarFile.string)
- val manifest = new Manifest
- manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0")
- val jar = new JarOutputStream(new FileOutputStream(jarFile.toString), manifest)
-
- val names = for {
- base <- files.filter(_.exists).map(realpath)
- file <- listFilesRecursive(base) if file.isFile
- } yield {
- val name = if(base.isDirectory){
- file.toString stripPrefix base.toString
- } else file.toString
- val entry = new JarEntry( name )
- entry.setTime(file.lastModified)
- jar.putNextEntry(entry)
- jar.write( readAllBytes( Paths.get(file.toString) ) )
- jar.closeEntry
- name
- }
+ def jarFile( jarFile: File, files: Seq[File] ): Option[File] = {
+ if( files.isEmpty ){
+ None
+ } else {
+ logger.lib("Start packaging "++jarFile.string)
+ val manifest = new Manifest
+ manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0")
+ val jar = new JarOutputStream(new FileOutputStream(jarFile.toString), manifest)
+
+ val names = for {
+ base <- files.filter(_.exists).map(realpath)
+ file <- listFilesRecursive(base) if file.isFile
+ } yield {
+ val name = if(base.isDirectory){
+ file.toString stripPrefix base.toString
+ } else file.toString
+ val entry = new JarEntry( name )
+ entry.setTime(file.lastModified)
+ jar.putNextEntry(entry)
+ jar.write( readAllBytes( Paths.get(file.toString) ) )
+ jar.closeEntry
+ name
+ }
- val duplicateFiles = (names diff names.distinct).distinct
- assert(
- duplicateFiles.isEmpty,
- s"Conflicting file names when trying to create $jarFile: "++duplicateFiles.mkString(", ")
- )
+ val duplicateFiles = (names diff names.distinct).distinct
+ assert(
+ duplicateFiles.isEmpty,
+ s"Conflicting file names when trying to create $jarFile: "++duplicateFiles.mkString(", ")
+ )
- jar.close
- logger.lib("Done packaging " ++ jarFile.toString)
+ jar.close
+ logger.lib("Done packaging " ++ jarFile.toString)
+
+ Some(jarFile)
+ }
}
lazy val passphrase =
@@ -321,8 +324,9 @@ final class Lib(logger: Logger) extends Stage1Lib(logger) with Scaffold{
</dependencies>
</project>
val path = jarTarget.toString ++ ( "/" ++ artifactId ++ "-" ++ version ++ ".pom" )
- write.over(Path(path), "<?xml version='1.0' encoding='UTF-8'?>\n" ++ xml.toString)
- new File(path)
+ val file = new File(path)
+ Files.write(file.toPath, ("<?xml version='1.0' encoding='UTF-8'?>\n" ++ xml.toString).getBytes)
+ file
}
def concurrently[T,R]( concurrencyEnabled: Boolean )( items: Seq[T] )( projection: T => R ): Seq[R] = {
@@ -364,7 +368,7 @@ final class Lib(logger: Logger) extends Stage1Lib(logger) with Scaffold{
val httpCon = url.openConnection.asInstanceOf[HttpURLConnection]
httpCon.setDoOutput(true)
httpCon.setRequestMethod("PUT")
- val userPassword = read(Path(sonatypeLogin)).trim
+ val userPassword = new String(readAllBytes(sonatypeLogin.toPath)).trim
val encoding = new sun.misc.BASE64Encoder().encode(userPassword.getBytes)
httpCon.setRequestProperty("Authorization", "Basic " ++ encoding)
httpCon.setRequestProperty("Content-Type", "application/binary")
diff --git a/stage2/NameTransformer.scala b/stage2/NameTransformer.scala
new file mode 100644
index 0000000..33489ca
--- /dev/null
+++ b/stage2/NameTransformer.scala
@@ -0,0 +1,161 @@
+// Adapted from https://github.com/scala/scala/blob/5cb3d4ec14488ce2fc5a1cc8ebdd12845859c57d/src/library/scala/reflect/NameTransformer.scala
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package cbt
+
+/** Provides functions to encode and decode Scala symbolic names.
+ * Also provides some constants.
+ */
+object NameTransformer {
+ // XXX Short term: providing a way to alter these without having to recompile
+ // the compiler before recompiling the compiler.
+ val MODULE_SUFFIX_STRING = sys.props.getOrElse("SCALA_MODULE_SUFFIX_STRING", "$")
+ val NAME_JOIN_STRING = sys.props.getOrElse("SCALA_NAME_JOIN_STRING", "$")
+ val MODULE_INSTANCE_NAME = "MODULE$"
+ val LOCAL_SUFFIX_STRING = " "
+ val SETTER_SUFFIX_STRING = "_$eq"
+ val TRAIT_SETTER_SEPARATOR_STRING = "$_setter_$"
+
+ private val nops = 128
+ private val ncodes = 26 * 26
+
+ private class OpCodes(val op: Char, val code: String, val next: OpCodes)
+
+ private val op2code = new Array[String](nops)
+ private val code2op = new Array[OpCodes](ncodes)
+ private def enterOp(op: Char, code: String) = {
+ op2code(op.toInt) = code
+ val c = (code.charAt(1) - 'a') * 26 + code.charAt(2) - 'a'
+ code2op(c.toInt) = new OpCodes(op, code, code2op(c))
+ }
+
+ /* Note: decoding assumes opcodes are only ever lowercase. */
+ enterOp('~', "$tilde")
+ enterOp('=', "$eq")
+ enterOp('<', "$less")
+ enterOp('>', "$greater")
+ enterOp('!', "$bang")
+ enterOp('#', "$hash")
+ enterOp('%', "$percent")
+ enterOp('^', "$up")
+ enterOp('&', "$amp")
+ enterOp('|', "$bar")
+ enterOp('*', "$times")
+ enterOp('/', "$div")
+ enterOp('+', "$plus")
+ enterOp('-', "$minus")
+ enterOp(':', "$colon")
+ enterOp('\\', "$bslash")
+ enterOp('?', "$qmark")
+ enterOp('@', "$at")
+
+ /** Replace operator symbols by corresponding `\$opname`.
+ *
+ * @param name the string to encode
+ * @return the string with all recognized opchars replaced with their encoding
+ */
+ def encode(name: String): String = {
+ var buf: StringBuilder = null
+ val len = name.length()
+ var i = 0
+ while (i < len) {
+ val c = name charAt i
+ if (c < nops && (op2code(c.toInt) ne null)) {
+ if (buf eq null) {
+ buf = new StringBuilder()
+ buf.append(name.substring(0, i))
+ }
+ buf.append(op2code(c.toInt))
+ /* Handle glyphs that are not valid Java/JVM identifiers */
+ }
+ else if (!Character.isJavaIdentifierPart(c)) {
+ if (buf eq null) {
+ buf = new StringBuilder()
+ buf.append(name.substring(0, i))
+ }
+ buf.append("$u%04X".format(c.toInt))
+ }
+ else if (buf ne null) {
+ buf.append(c)
+ }
+ i += 1
+ }
+ if (buf eq null) name else buf.toString()
+ }
+
+ /** Replace `\$opname` by corresponding operator symbol.
+ *
+ * @param name0 the string to decode
+ * @return the string with all recognized operator symbol encodings replaced with their name
+ */
+ def decode(name0: String): String = {
+ //System.out.println("decode: " + name);//DEBUG
+ val name = if (name0.endsWith("<init>")) name0.stripSuffix("<init>") + "this"
+ else name0
+ var buf: StringBuilder = null
+ val len = name.length()
+ var i = 0
+ while (i < len) {
+ var ops: OpCodes = null
+ var unicode = false
+ val c = name charAt i
+ if (c == '$' && i + 2 < len) {
+ val ch1 = name.charAt(i+1)
+ if ('a' <= ch1 && ch1 <= 'z') {
+ val ch2 = name.charAt(i+2)
+ if ('a' <= ch2 && ch2 <= 'z') {
+ ops = code2op((ch1 - 'a') * 26 + ch2 - 'a')
+ while ((ops ne null) && !name.startsWith(ops.code, i)) ops = ops.next
+ if (ops ne null) {
+ if (buf eq null) {
+ buf = new StringBuilder()
+ buf.append(name.substring(0, i))
+ }
+ buf.append(ops.op)
+ i += ops.code.length()
+ }
+ /* Handle the decoding of Unicode glyphs that are
+ * not valid Java/JVM identifiers */
+ } else if ((len - i) >= 6 && // Check that there are enough characters left
+ ch1 == 'u' &&
+ ((Character.isDigit(ch2)) ||
+ ('A' <= ch2 && ch2 <= 'F'))) {
+ /* Skip past "$u", next four should be hexadecimal */
+ val hex = name.substring(i+2, i+6)
+ try {
+ val str = Integer.parseInt(hex, 16).toChar
+ if (buf eq null) {
+ buf = new StringBuilder()
+ buf.append(name.substring(0, i))
+ }
+ buf.append(str)
+ /* 2 for "$u", 4 for hexadecimal number */
+ i += 6
+ unicode = true
+ } catch {
+ case _:NumberFormatException =>
+ /* `hex` did not decode to a hexadecimal number, so
+ * do nothing. */
+ }
+ }
+ }
+ }
+ /* If we didn't see an opcode or encoded Unicode glyph, and the
+ buffer is non-empty, write the current character and advance
+ one */
+ if ((ops eq null) && !unicode) {
+ if (buf ne null)
+ buf.append(c)
+ i += 1
+ }
+ }
+ //System.out.println("= " + (if (buf == null) name else buf.toString()));//DEBUG
+ if (buf eq null) name else buf.toString()
+ }
+}
diff --git a/stage2/PackageBuild.scala b/stage2/PackageBuild.scala
index 2866b7c..79e54a7 100644
--- a/stage2/PackageBuild.scala
+++ b/stage2/PackageBuild.scala
@@ -4,23 +4,23 @@ import scala.collection.immutable.Seq
abstract class PackageBuild(context: Context) extends BasicBuild(context) with ArtifactInfo{
def `package`: Seq[File] = lib.concurrently( enableConcurrency )(
Seq(() => jar, () => docJar, () => srcJar)
- )( _() )
+ )( _() ).flatten
- private object cacheJarBasicBuild extends Cache[File]
- def jar: File = cacheJarBasicBuild{
- lib.jar( artifactId, version, compile, jarTarget )
+ private object cacheJarBasicBuild extends Cache[Option[File]]
+ def jar: Option[File] = cacheJarBasicBuild{
+ compile.flatMap( lib.jar( artifactId, version, _, jarTarget ) )
}
- private object cacheSrcJarBasicBuild extends Cache[File]
- def srcJar: File = cacheSrcJarBasicBuild{
+ private object cacheSrcJarBasicBuild extends Cache[Option[File]]
+ def srcJar: Option[File] = cacheSrcJarBasicBuild{
lib.srcJar( sourceFiles, artifactId, version, scalaTarget )
}
- private object cacheDocBasicBuild extends Cache[File]
- def docJar: File = cacheDocBasicBuild{
- lib.docJar( scalaVersion, sourceFiles, dependencyClasspath, apiTarget, jarTarget, artifactId, version, scalacOptions )
+ private object cacheDocBasicBuild extends Cache[Option[File]]
+ def docJar: Option[File] = cacheDocBasicBuild{
+ lib.docJar( scalaVersion, sourceFiles, dependencyClasspath, apiTarget, jarTarget, artifactId, version, scalacOptions, context.classLoaderCache )
}
- override def jars = jar +: dependencyJars
- override def exportedJars: Seq[File] = Seq(jar)
+ override def jars = jar.toVector ++ dependencyJars
+ override def exportedJars: Seq[File] = jar.toVector
}
diff --git a/stage2/Scaffold.scala b/stage2/Scaffold.scala
index e181ebf..3dcb9ae 100644
--- a/stage2/Scaffold.scala
+++ b/stage2/Scaffold.scala
@@ -1,13 +1,14 @@
package cbt
import java.io._
+import java.nio.file._
import java.net._
-import ammonite.ops.{cwd => _,_}
-
trait Scaffold{
def logger: Logger
private def createFile( projectDirectory: File, fileName: String, code: String ){
- write( Path( projectDirectory.string ++ "/" ++ fileName ), code )
+ val outputFile = projectDirectory ++ ("/" ++ fileName)
+ outputFile.getParentFile.mkdirs
+ Files.write( ( outputFile ).toPath, code.getBytes, StandardOpenOption.CREATE_NEW )
import scala.Console._
println( GREEN ++ "Created " ++ fileName ++ RESET )
}
diff --git a/stage2/Stage2.scala b/stage2/Stage2.scala
index 4145e55..e893a06 100644
--- a/stage2/Stage2.scala
+++ b/stage2/Stage2.scala
@@ -8,26 +8,24 @@ import scala.collection.immutable.Seq
import cbt.paths._
+object Stage2 extends Stage2Base{
+ def run( args: Stage2Args ): Unit = {
+ import args.logger
-object Stage2{
- def main(args: Array[String]): Unit = {
- val init = new Init(args)
- import init._
+ val lib = new Lib(args.logger)
- val lib = new Lib(init.logger)
-
- init.logger.stage2(s"[$now] Stage2 start")
- val loop = argsV.lift(1) == Some("loop")
- val direct = argsV.lift(1) == Some("direct")
+ logger.stage2(s"[$now] Stage2 start")
+ val loop = args.args.lift(0) == Some("loop")
+ val direct = args.args.lift(0) == Some("direct")
val taskIndex = if (loop || direct) {
- 2
- } else {
1
+ } else {
+ 0
}
- val task = argsV.lift( taskIndex )
+ val task = args.args.lift( taskIndex )
- val context = Context( new File(argsV(0)), argsV.drop( taskIndex + 1 ), logger )
+ val context = Context( args.cwd, args.args.drop( taskIndex ), logger, /*args.cbtHasChanged,*/ new ClassLoaderCache(logger) )
val first = lib.loadRoot( context )
val build = first.finalBuild
@@ -47,14 +45,15 @@ object Stage2{
scala.util.control.Breaks.break
case file if triggerFiles.exists(file.toString startsWith _.toString) =>
- val reflectBuild = new lib.ReflectBuild( lib.loadDynamic(context) )
- logger.loop(s"Re-running $task for " ++ reflectBuild.build.projectDirectory.toString)
+ val build = lib.loadDynamic(context)
+ val reflectBuild = new lib.ReflectBuild( build )
+ logger.loop(s"Re-running $task for " ++ build.projectDirectory.toString)
reflectBuild.callNullary(task)
}
} else {
new lib.ReflectBuild(build).callNullary(task)
}
- init.logger.stage2(s"[$now] Stage2 end")
+ logger.stage2(s"[$now] Stage2 end")
}
}
diff --git a/stage2/mixins.scala b/stage2/mixins.scala
index 2b38cdf..c3a57da 100644
--- a/stage2/mixins.scala
+++ b/stage2/mixins.scala
@@ -20,16 +20,13 @@ trait ScalaTest extends Build with Test{
"org.scalatest" %% "scalatest" % scalaTestVersion
) ++ super.dependencies
- // workaround probable ScalaTest bug throwing away the outer classloader. Not caching doesn't nest them.
- override def cacheDependencyClassLoader = false
-
override def run: ExitCode = {
val discoveryPath = compile.toString++"/"
context.logger.lib("discoveryPath: " ++ discoveryPath)
lib.runMain(
"org.scalatest.tools.Runner",
Seq("-R", discoveryPath, "-oF") ++ context.args.drop(1),
- classLoader
+ classLoader(context.classLoaderCache)
)
}
}