aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO.txt2
-rwxr-xr-xcbt95
-rw-r--r--nailgun_launcher/NailgunLauncher.java16
-rw-r--r--stage1/ClassPath.scala30
-rw-r--r--stage1/Stage1.scala28
-rw-r--r--stage1/Stage1Lib.scala166
-rw-r--r--stage1/cbt.scala21
-rw-r--r--stage1/classloader.scala55
-rw-r--r--stage1/logger.scala4
-rw-r--r--stage1/paths.scala22
-rw-r--r--stage1/resolver.scala86
-rw-r--r--stage2/AdminStage2.scala2
-rw-r--r--stage2/BasicBuild.scala (renamed from stage2/DefaultBuild.scala)123
-rw-r--r--stage2/BuildBuild.scala3
-rw-r--r--stage2/BuildDependency.scala (renamed from stage2/dependencies.scala)2
-rw-r--r--stage2/Lib.scala111
-rw-r--r--stage2/PackageBuild.scala27
-rw-r--r--stage2/PublishBuild.scala41
-rw-r--r--stage2/Scaffold.scala8
-rw-r--r--stage2/Stage2.scala43
-rw-r--r--stage2/mixins.scala12
-rw-r--r--test/test.scala116
22 files changed, 567 insertions, 446 deletions
diff --git a/TODO.txt b/TODO.txt
index 7f29fd1..e5c99f1 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -7,6 +7,7 @@ TODO:
- condition guarding zinc is too eager, needs to invalid
- immediate features
+ - maybe rename context.cwd
- add another class that makes all pom required fields abstract
- fix main project main method being run during tests
- DI lib into depencies
@@ -15,6 +16,7 @@ TODO:
- investigate and solve multiple compilations of the same SourceDependency Build. Maybe introduce global Build map.
- cleanup
+ - move from java File to nio Path
- defs for all tasks and cached where needed
- unify work classpath
- unify argument order
diff --git a/cbt b/cbt
index ab01819..603ec23 100755
--- a/cbt
+++ b/cbt
@@ -13,11 +13,11 @@ log () {
msg=$1
enabled=1
while test $# -gt 0; do
- case "$1" in
- "-Dlog=time") enabled=0 ;;
- "-Dlog=all") enabled=0 ;;
- esac
- shift
+ case "$1" in
+ "-Dlog=time") enabled=0 ;;
+ "-Dlog=all") enabled=0 ;;
+ esac
+ shift
done
if [ $enabled -eq 0 ]; then
which gdate 2>&1 > /dev/null
@@ -85,9 +85,29 @@ export TARGET=target/scala-2.11/classes/
mkdir -p $NAILGUN$TARGET
mkdir -p $STAGE1$TARGET
+nailgun_out=$NAILGUN/target/nailgun.stdout.log
+nailgun_err=$NAILGUN/target/nailgun.strerr.log
+foo(){
+ while test $# -gt 0; do
+ case "$1" in
+ "-Dlog=nailgun")
+ nailgun_out=/dev/stderr
+ nailgun_err=/dev/stderr
+ ;;
+ "-Dlog=all")
+ nailgun_out=/dev/stderr
+ nailgun_err=/dev/stderr
+ ;;
+ esac
+ shift
+ done
+}
+
+foo $@
+
if [ "$1" = "kill" ]; then
echo "Stopping nailgun" 1>&2
- $NG ng-stop >> $NAILGUN/target/nailgun.stdout.log 2>> $NAILGUN/target/nailgun.stderr.log &
+ $NG ng-stop >> $nailgun_out 2>> $nailgun_err &
exit 1
fi
@@ -104,10 +124,15 @@ else
echo "(Note: nc not found. It will make slightly startup faster.)" 1>&2
fi
-if [ $nc_installed -eq 0 ] || [ ! $server_up -eq 0 ]; then
+use_nailgun=0
+if [ $nailgun_installed -eq 1 ] || [ "$1" = "publishSigned" ] || [ "$2" = "publishSigned" ] || [ "$1" = "direct" ] || [ "$2" = "direct" ]; then
+ use_nailgun=1
+fi
+
+if [ $use_nailgun -eq 0 ] && [ ! $server_up -eq 0 ]; then
log "Starting up nailgun server." $*
# try to start nailgun-server, just in case it's not up
- ng-server 127.0.0.1:$NAILGUN_PORT >> $NAILGUN/target/nailgun.stdout.log 2>> $NAILGUN/target/nailgun.stderr.log &
+ ng-server 127.0.0.1:$NAILGUN_PORT >> $nailgun_out 2>> $nailgun_err &
fi
log "Downloading Scala jars if necessary..." $*
@@ -122,42 +147,48 @@ SCALAC="java -Xmx256M -Xms32M\
-deprecation\
-feature"
-stage1 () {
+stage1 () {
log "Checking for changes in cbt/nailgun_launcher" $*
NAILGUN_INDICATOR=$NAILGUN$TARGET/cbt/NailgunLauncher.class
changed=0
- for file in `ls $NAILGUN/*.java`; do
+ for file in `ls $NAILGUN/*.java`; do
if [ $file -nt $NAILGUN_INDICATOR ]; then changed=1; fi
done
compiles=0
if [ $changed -eq 1 ]; then
- #rm $NAILGUN$TARGET/cbt/*.class 2>/dev/null
+ rm $NAILGUN$TARGET/cbt/*.class 2>/dev/null # defensive delete of potentially broken class files
echo "Compiling cbt/nailgun_launcher" 1>&2
javac -Xlint:deprecation -d $NAILGUN$TARGET `ls $NAILGUN*.java`
compiles=$?
- if [ $compiles -ne 0 ]; then exit 1; fi
- if [ $nailgun_installed -eq 0 ]; then
+ if [ $compiles -ne 0 ]; then
+ rm $NAILGUN$TARGET/cbt/*.class 2>/dev/null # triggers recompilation next time.
+ break
+ fi
+ if [ $use_nailgun -eq 0 ]; then
echo "Stopping nailgun" 1>&2
- $NG ng-stop >> $NAILGUN/target/nailgun.stdout.log 2>> $NAILGUN/target/nailgun.stderr.log &
+ $NG ng-stop >> $nailgun_out 2>> $nailgun_err &
echo "Restarting nailgun" 1>&2
- ng-server 127.0.0.1:$NAILGUN_PORT >> $NAILGUN/target/nailgun.stdout.log 2>> $NAILGUN/target/nailgun.stderr.log &
+ ng-server 127.0.0.1:$NAILGUN_PORT >> $nailgun_out 2>> $nailgun_err &
fi
fi
log "Checking for changes in cbt/stage1" $*
STAGE1_INDICATOR=$STAGE1$TARGET/cbt/Stage1.class
changed2=0
- for file in `ls $STAGE1*.scala`; do
+ for file in `ls $STAGE1*.scala`; do
if [ $file -nt $STAGE1_INDICATOR ]; then changed2=1; fi
done
compiles2=0
if [ $changed2 -eq 1 ]; then
+ rm $STAGE1$TARGET/cbt/*.class 2>/dev/null # defensive delete of potentially broken class files
echo "Compiling cbt/stage1" 1>&2
- rm $STAGE1$TARGET/cbt/*.class 2>/dev/null
$SCALAC -cp $NAILGUN$TARGET -d $STAGE1$TARGET `ls $STAGE1/*.scala`
compiles2=$?
- if [ $compiles2 -ne 0 ]; then exit 1; fi
+ if [ $compiles2 -ne 0 ]; then
+ rm $STAGE1$TARGET/cbt/*.class 2>/dev/null # triggers recompilation next time.
+ break
+ fi
fi
log "run CBT and loop if desired. This allows recompiling CBT itself as part of compile looping." $*
@@ -168,34 +199,37 @@ stage1 () {
fi
CP=$STAGE1$TARGET:$SCALA_CLASSPATH
- if [ $nailgun_installed -eq 1 ] || [ "$1" = "publishSigned" ] || [ "$2" = "publishSigned" ] || [ "$1" = "direct" ] || [ "$2" = "direct" ]
+ if [ $use_nailgun -eq 1 ]
then
log "Running JVM directly" $*
# -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=localhost:5005
java -cp $NAILGUN$TARGET cbt.NailgunLauncher $mainClass $CP "$CWD" $*
else
log "Running via nailgun." $*
- while true; do
+ for i in 0 1 2 3 4 5 6 7 8 9; do
log "Adding classpath." $*
- $NG ng-cp $NAILGUN$TARGET >> $NAILGUN/target/nailgun.stdout.log 2>> $NAILGUN/target/nailgun.stderr.log
+ $NG ng-cp $NAILGUN$TARGET >> $nailgun_out 2>> $nailgun_err
log "Checking if nailgun is up yet." $*
- $NG cbt.NailgunLauncher cbt.CheckAlive $CP "$CWD" $* >> $NAILGUN/target/nailgun.stdout.log 2>> $NAILGUN/target/nailgun.stderr.log
+ $NG cbt.NailgunLauncher cbt.CheckAlive $CP "$CWD" $* >> $nailgun_out 2>> $nailgun_err
alive=$?
- if [[ $alive -eq 131 ]]; then
+ if [ $alive -eq 131 ]; then
echo "Nailgun call failed. Try 'cbt kill' and check the error log cbt/nailgun_launcher/target/nailgun.stderr.log" 1>&2
- elif [[ $alive -eq 33 ]]; then
+ elif [ $alive -eq 33 ]; then
break
else
- log "Nope. Sleeping for 1 second" $*
- echo "Waiting for nailgun to start..." 1>&2
- sleep 1
+ log "Nope. Sleeping for 0.5 seconds" $*
+ if [ "$i" -gt "1" ]; then
+ echo "Waiting for nailgun to start... (For problems try -Dlog=nailgun or check logs in cbt/nailgun_launcher/target/*.log)" 1>&2
+ fi
+ sleep 0.5
fi
done
log "Running $mainClass via Nailgun." $*
$NG cbt.NailgunLauncher $mainClass $CP "$CWD" $*
fi
+ exitCode=$?
log "Done running $mainClass." $*
-}
+}
while true; do
stage1 $*
@@ -205,4 +239,9 @@ while true; do
echo "======= Restarting CBT =======" 1>&2
done
+if [ $compiles -ne 0 ] || [ $compiles2 -ne 0 ]; then
+ exitCode=1
+fi
+
log "Exiting CBT" $*
+exit $exitCode
diff --git a/nailgun_launcher/NailgunLauncher.java b/nailgun_launcher/NailgunLauncher.java
index 3765457..78da041 100644
--- a/nailgun_launcher/NailgunLauncher.java
+++ b/nailgun_launcher/NailgunLauncher.java
@@ -7,7 +7,7 @@ import java.nio.file.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
-/**
+/**
* This launcher allows to start the JVM without loading anything else permanently into its
* classpath except for the launcher itself. That's why it is written in Java without
* dependencies outside the JDK.
@@ -17,16 +17,16 @@ import java.util.concurrent.ConcurrentHashMap;
*/
public class NailgunLauncher{
- /**
+ /**
* Persistent cache for caching classloaders for the JVM life time. Can be used as needed by user
- * code to improve startup time.
+ * code to improve startup time.
*/
- public static ConcurrentHashMap<String,ClassLoader> classLoaderCache =
+ public static ConcurrentHashMap<String,ClassLoader> classLoaderCache =
new ConcurrentHashMap<String,ClassLoader>();
- public static void main(String[] args) throws ClassNotFoundException,
- NoSuchMethodException,
- IllegalAccessException,
+ public static void main(String[] args) throws ClassNotFoundException,
+ NoSuchMethodException,
+ IllegalAccessException,
InvocationTargetException,
MalformedURLException {
if (args.length < 3) {
@@ -41,7 +41,7 @@ public class NailgunLauncher{
}
String[] newArgs = new String[args.length-2];
- for(int i = 0; i < args.length-2; i++){
+ for(int i = 0; i < args.length-2; i++){
newArgs[i] = args[i+2];
}
diff --git a/stage1/ClassPath.scala b/stage1/ClassPath.scala
new file mode 100644
index 0000000..66a1b44
--- /dev/null
+++ b/stage1/ClassPath.scala
@@ -0,0 +1,30 @@
+package cbt
+import java.io._
+import java.net._
+import scala.collection.immutable.Seq
+
+object ClassPath{
+ def apply(files: File*): ClassPath = ClassPath(files.toVector)
+ def flatten( classPaths: Seq[ClassPath] ): ClassPath = ClassPath( classPaths.map(_.files).flatten )
+}
+case class ClassPath(files: Seq[File]){
+ private val duplicates = (files diff files.distinct).distinct
+ assert(
+ duplicates.isEmpty,
+ "Duplicate classpath entries found:\n" ++ duplicates.mkString("\n") ++ "\nin classpath:\n"++string
+ )
+ private val nonExisting = files.distinct.filterNot(_.exists)
+ assert(
+ duplicates.isEmpty,
+ "Classpath contains entires that don't exist on disk:\n" ++ nonExisting.mkString("\n") ++ "\nin classpath:\n"++string
+ )
+
+ def +:(file: File) = ClassPath(file +: files)
+ def :+(file: File) = ClassPath(files :+ file)
+ def ++(other: ClassPath) = ClassPath(files ++ other.files)
+ def string = strings.mkString( File.pathSeparator )
+ def strings = files.map{
+ f => f.string ++ ( if(f.isDirectory) "/" else "" )
+ }
+ def toConsole = string
+}
diff --git a/stage1/Stage1.scala b/stage1/Stage1.scala
index 5391e2d..1aa3f09 100644
--- a/stage1/Stage1.scala
+++ b/stage1/Stage1.scala
@@ -14,7 +14,7 @@ object CheckAlive{
}
}
-private[cbt] class Init(args: Array[String]) {
+class Init(args: Array[String]) {
/**
* Raw parameters including their `-D` flag.
**/
@@ -35,8 +35,6 @@ private[cbt] class Init(args: Array[String]) {
}).toMap ++ System.getProperties.asScala
val logger = new Logger(props.get("log"))
-
- val cwd = argsV(0)
}
object Stage1 extends Stage1Base{
@@ -57,25 +55,27 @@ abstract class Stage1Base{
def main(args: Array[String]): Unit = {
val init = new Init(args)
val lib = new Stage1Lib(init.logger)
+ import lib._
- lib.logger.stage1(s"[$now] Stage1 start")
- lib.logger.stage1("Stage1: after creating lib")
+ logger.stage1(s"[$now] Stage1 start")
+ logger.stage1("Stage1: after creating lib")
val cwd = args(0)
val src = stage2.listFiles.toVector.filter(_.isFile).filter(_.toString.endsWith(".scala"))
- val changeIndicator = new File(stage2Target+"/cbt/Build.class")
+ val changeIndicator = stage2Target ++ "/cbt/Build.class"
- lib.logger.stage1("before conditionally running zinc to recompile CBT")
+ logger.stage1("before conditionally running zinc to recompile CBT")
if( src.exists(newerThan(_, changeIndicator)) ) {
- val stage1Classpath = CbtDependency(init.logger).dependencyClasspath
- lib.logger.stage1("cbt.lib has changed. Recompiling with cp: "+stage1Classpath)
- lib.zinc( true, src, stage2Target, stage1Classpath )( zincVersion = "0.3.9", scalaVersion = constants.scalaVersion )
+ val stage1Classpath = CbtDependency(logger).dependencyClasspath
+ logger.stage1("cbt.lib has changed. Recompiling with cp: " ++ stage1Classpath.string)
+ zinc( true, src, stage2Target, stage1Classpath )( zincVersion = "0.3.9", scalaVersion = constants.scalaVersion )
}
- lib.logger.stage1(s"[$now] calling CbtDependency.classLoader")
+ logger.stage1(s"[$now] calling CbtDependency.classLoader")
- lib.logger.stage1(s"[$now] Run Stage2")
- lib.runMain( mainClass, cwd +: args.drop(1).toVector, CbtDependency(init.logger).classLoader )
- lib.logger.stage1(s"[$now] Stage1 end")
+ logger.stage1(s"[$now] Run Stage2")
+ val ExitCode(exitCode) = runMain( mainClass, cwd +: args.drop(1).toVector, CbtDependency(logger).classLoader )
+ logger.stage1(s"[$now] Stage1 end")
+ System.exit(exitCode)
}
}
diff --git a/stage1/Stage1Lib.scala b/stage1/Stage1Lib.scala
index e1b9d3d..1ad3030 100644
--- a/stage1/Stage1Lib.scala
+++ b/stage1/Stage1Lib.scala
@@ -3,6 +3,7 @@ package cbt
import cbt.paths._
import java.io._
+import java.lang.reflect.InvocationTargetException
import java.net._
import java.nio.file._
import javax.tools._
@@ -12,34 +13,30 @@ import javax.xml.bind.annotation.adapters.HexBinaryAdapter
import scala.collection.immutable.Seq
-case class Context( cwd: String, args: Seq[String], logger: Logger )
-
-case class ClassPath(files: Seq[File]){
- private val duplicates = (files diff files.distinct).distinct
- assert(
- duplicates.isEmpty,
- "Duplicate classpath entries found:\n" + duplicates.mkString("\n") + "\nin classpath:\n"+string
- )
- private val nonExisting = files.distinct.filterNot(_.exists)
- assert(
- duplicates.isEmpty,
- "Classpath contains entires that don't exist on disk:\n" + nonExisting.mkString("\n") + "\nin classpath:\n"+string
- )
-
- def +:(file: File) = ClassPath(file +: files)
- def :+(file: File) = ClassPath(files :+ file)
- def ++(other: ClassPath) = ClassPath(files ++ other.files)
- def string = strings.mkString( File.pathSeparator )
- def strings = files.map{
- f => f.toString + ( if(f.isDirectory) "/" else "" )
- }
- def toConsole = string
+// CLI interop
+case class ExitCode(code: Int)
+object ExitCode{
+ val Success = ExitCode(0)
+ val Failure = ExitCode(1)
+}
+
+class TrappedExitCode(private val exitCode: Int) extends Exception
+object TrappedExitCode{
+ def unapply(e: Throwable): Option[ExitCode] =
+ Option(e) flatMap {
+ case i: InvocationTargetException => unapply(i.getTargetException)
+ case e: TrappedExitCode => Some( ExitCode(e.exitCode) )
+ case _ => None
+ }
}
-object ClassPath{
- def flatten( classPaths: Seq[ClassPath] ): ClassPath = ClassPath( classPaths.map(_.files).flatten )
+
+case class Context( cwd: File, args: Seq[String], logger: Logger )
+
+class BaseLib{
+ def realpath(name: File) = new File(Paths.get(name.getAbsolutePath).normalize.toString)
}
-class Stage1Lib( val logger: Logger ){
+class Stage1Lib( val logger: Logger ) extends BaseLib{
lib =>
// ========== reflection ==========
@@ -57,24 +54,24 @@ class Stage1Lib( val logger: Logger ){
}
// ========== file system / net ==========
-
+
def array2hex(padTo: Int, array: Array[Byte]): String = {
val hex = new java.math.BigInteger(1, array).toString(16)
- ("0" * (padTo-hex.size)) + hex
+ ("0" * (padTo-hex.size)) ++ hex
}
def md5( bytes: Array[Byte] ): String = array2hex(32, MessageDigest.getInstance("MD5").digest(bytes))
def sha1( bytes: Array[Byte] ): String = array2hex(40, MessageDigest.getInstance("SHA-1").digest(bytes))
- def red(string: String) = scala.Console.RED+string+scala.Console.RESET
- def blue(string: String) = scala.Console.BLUE+string+scala.Console.RESET
- def green(string: String) = scala.Console.GREEN+string+scala.Console.RESET
+ def red(string: String) = scala.Console.RED++string++scala.Console.RESET
+ def blue(string: String) = scala.Console.BLUE++string++scala.Console.RESET
+ def green(string: String) = scala.Console.GREEN++string++scala.Console.RESET
- def download(urlString: URL, target: Path, sha1: Option[String]){
- val incomplete = Paths.get(target+".incomplete");
- if( !Files.exists(target) ){
- new File(target.toString).getParentFile.mkdirs
- logger.resolver(blue("downloading ")+urlString)
- logger.resolver(blue("to ")+target)
+ def download(urlString: URL, target: File, sha1: Option[String]){
+ val incomplete = Paths.get( target.string ++ ".incomplete" );
+ if( !target.exists ){
+ target.getParentFile.mkdirs
+ logger.resolver(blue("downloading ") ++ urlString.string)
+ logger.resolver(blue("to ") ++ target.string)
val stream = urlString.openStream
Files.copy(stream, incomplete, StandardCopyOption.REPLACE_EXISTING)
sha1.foreach{
@@ -82,10 +79,10 @@ class Stage1Lib( val logger: Logger ){
val expected = hash
val actual = this.sha1(Files.readAllBytes(incomplete))
assert( expected == actual, s"$expected == $actual" )
- logger.resolver(green("verified")+" checksum for "+target)
+ logger.resolver( green("verified") ++ " checksum for " ++ target.string)
}
stream.close
- Files.move(incomplete, target, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
+ Files.move(incomplete, Paths.get(target.string), StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
}
}
@@ -97,16 +94,20 @@ class Stage1Lib( val logger: Logger ){
// ========== compilation / execution ==========
- def runMainIfFound(cls: String, args: Seq[String], classLoader: ClassLoader ){
- if( classLoader.canLoad(cls) ) runMain(cls: String, args: Seq[String], classLoader: ClassLoader )
+ def runMainIfFound(cls: String, args: Seq[String], classLoader: ClassLoader ): ExitCode = {
+ if( classLoader.canLoad(cls) ){
+ runMain(cls, args, classLoader )
+ } else ExitCode.Success
}
- def runMain(cls: String, args: Seq[String], classLoader: ClassLoader ){
- logger.lib(s"Running $cls.main($args) with classLoader: "+classLoader)
- classLoader
- .loadClass(cls)
- .getMethod( "main", scala.reflect.classTag[Array[String]].runtimeClass )
- .invoke( null, args.toArray.asInstanceOf[AnyRef] );
+ def runMain(cls: String, args: Seq[String], classLoader: ClassLoader ): ExitCode = {
+ logger.lib(s"Running $cls.main($args) with classLoader: " ++ classLoader.toString)
+ trapExitCode{
+ classLoader
+ .loadClass(cls)
+ .getMethod( "main", scala.reflect.classTag[Array[String]].runtimeClass )
+ .invoke( null, args.toArray.asInstanceOf[AnyRef] )
+ }
}
implicit class ClassLoaderExtensions(classLoader: ClassLoader){
@@ -116,7 +117,7 @@ class Stage1Lib( val logger: Logger ){
true
} catch {
case e: ClassNotFoundException => false
- }
+ }
}
}
@@ -125,8 +126,8 @@ class Stage1Lib( val logger: Logger ){
)( zincVersion: String, scalaVersion: String ): Unit = {
val cp = classpath.string
- if(classpath.files.isEmpty) throw new Exception("Trying to compile with empty classpath. Source files: "+files)
- if(files.isEmpty) throw new Exception("Trying to compile no files. ClassPath: "+cp)
+ if(classpath.files.isEmpty) throw new Exception("Trying to compile with empty classpath. Source files: " ++ files.toString)
+ if(files.isEmpty) throw new Exception("Trying to compile no files. ClassPath: " ++ cp)
// only run zinc if files changed, for performance reasons
// FIXME: this is broken, need invalidate on changes in dependencies as well
@@ -152,25 +153,33 @@ class Stage1Lib( val logger: Logger ){
val scalaReflect = MavenDependency("org.scala-lang","scala-reflect",scalaVersion)(logger).jar
val scalaCompiler = MavenDependency("org.scala-lang","scala-compiler",scalaVersion)(logger).jar
- redirectOutToErr{
- lib.runMain(
- "com.typesafe.zinc.Main",
- Seq(
- "-scala-compiler", scalaCompiler.toString,
- "-scala-library", scalaLibrary.toString,
- "-sbt-interface", sbtInterface.toString,
- "-compiler-interface", compilerInterface.toString,
- "-scala-extra", scalaReflect.toString,
- "-cp", cp,
- "-d", compileTarget.toString
- ) ++ extraArgs.map("-S"+_) ++ files.map(_.toString),
- zinc.classLoader
- )
+ val code = redirectOutToErr{
+ trapExitCode{
+ lib.runMain(
+ "com.typesafe.zinc.Main",
+ Seq(
+ "-scala-compiler", scalaCompiler.toString,
+ "-scala-library", scalaLibrary.toString,
+ "-sbt-interface", sbtInterface.toString,
+ "-compiler-interface", compilerInterface.toString,
+ "-scala-extra", scalaReflect.toString,
+ "-cp", cp,
+ "-d", compileTarget.toString
+ ) ++ extraArgs.map("-S"++_) ++ files.map(_.toString),
+ zinc.classLoader
+ )
+ }
+ }
+ if(code != ExitCode.Success){
+ // FIXME: zinc currently always returns exit code 0
+ // hack that triggers recompilation next time. Nicer solution?
+ val now = System.currentTimeMillis()
+ files.foreach{_.setLastModified(now)}
}
}
}
- def redirectOutToErr[T](code: => T): Unit = {
+ def redirectOutToErr[T](code: => T): T = {
val oldOut = System.out
try{
System.setOut(System.err)
@@ -180,5 +189,34 @@ class Stage1Lib( val logger: Logger ){
}
}
+ def trapExitCode( code: => Unit ): ExitCode = {
+ val old: Option[SecurityManager] = Option(System.getSecurityManager())
+ try{
+ val securityManager = new SecurityManager{
+ override def checkPermission( permission: Permission ) = {
+ /*
+ NOTE: is it actually ok, to just make these empty?
+ Calling .super leads to ClassNotFound exteption for a lambda.
+ Calling to the previous SecurityManager leads to a stack overflow
+ */
+ }
+ override def checkPermission( permission: Permission, context: Any ) = {
+ /* Does this methods need to be overidden? */
+ }
+ override def checkExit( status: Int ) = {
+ super.checkExit(status)
+ logger.lib(s"checkExit($status)")
+ throw new TrappedExitCode(status)
+ }
+ }
+ System.setSecurityManager( securityManager )
+ code
+ ExitCode.Success
+ } catch {
+ case TrappedExitCode(exitCode) => exitCode
+ } finally {
+ System.setSecurityManager(old.getOrElse(null))
+ }
+ }
}
diff --git a/stage1/cbt.scala b/stage1/cbt.scala
new file mode 100644
index 0000000..01af0d5
--- /dev/null
+++ b/stage1/cbt.scala
@@ -0,0 +1,21 @@
+package cbt
+import java.io._
+import java.nio.file._
+import java.net._
+object `package`{
+ private val lib = new BaseLib
+ implicit class FileExtensionMethods( file: File ){
+ def ++( s: String ): File = {
+ if(s endsWith "/") throw new Exception(
+ """Trying to append a String that ends in "/" to a File would loose it. Use .stripSuffix("/") if you need to."""
+ )
+ new File( file.toString ++ s )
+ }
+ def parent = lib.realpath(file ++ "/..")
+ def string = file.toString
+ }
+ implicit class URLExtensionMethods( url: URL ){
+ def ++( s: String ): URL = new URL( url.toString ++ s )
+ def string = url.toString
+ }
+}
diff --git a/stage1/classloader.scala b/stage1/classloader.scala
index 6f2213b..3293dd1 100644
--- a/stage1/classloader.scala
+++ b/stage1/classloader.scala
@@ -8,25 +8,29 @@ import scala.collection.immutable.Seq
object ClassLoaderCache{
private val cache = NailgunLauncher.classLoaderCache
- def classLoader( path: String, parent: ClassLoader ): ClassLoader = {
- def realpath( name: String ) = Paths.get(new File(name).getAbsolutePath).normalize.toString
- val normalized = realpath(path)
- if( cache.containsKey(normalized) ){
- //println("FOUND: "+normalized)
- cache.get(normalized)
+ def classLoader( classpath: ClassPath, parent: ClassLoader )(implicit logger: Logger): ClassLoader
+ = cache.synchronized{
+ val lib = new Stage1Lib(logger)
+ val key = classpath.strings.sorted.mkString(":")
+ if( cache.containsKey(key) ){
+ logger.resolver("CACHE HIT: "++key)
+ cache.get(key)
} else {
- //println("PUTTING: "+normalized)
- //Try(???).recover{ case e=>e.printStackTrace}
- val cl = new cbt.URLClassLoader( ClassPath(Seq(new File(normalized))), parent )
- cache.put( normalized, cl )
+ logger.resolver("CACHE MISS: "++key)
+ val cl = new cbt.URLClassLoader( classpath, parent )
+ cache.put( key, cl )
cl
}
}
- def remove( path: String ) = cache.remove( path )
+ def remove( classpath: ClassPath ) = {
+ val key = classpath.strings.sorted.mkString(":")
+ cache.remove( key )
+ }
}
+/*
class MultiClassLoader(parents: Seq[ClassLoader]) extends ClassLoader {
override def loadClass(name: String) = {
- //System.err.println("LOADING CLASS "+name);
+ //System.err.println("LOADING CLASS "++name);
val c = parents.toStream.map{
parent =>
Try{
@@ -37,29 +41,24 @@ class MultiClassLoader(parents: Seq[ClassLoader]) extends ClassLoader {
}.find(_.isDefined).flatten
c.getOrElse( ClassLoader.getSystemClassLoader.loadClass(name) )
}
- override def toString = "MultiClassLoader(" + parents.mkString(",") + ")"
+ override def toString = "MultiClassLoader(" ++ parents.mkString(",") ++ ")"
}
+*/
case class URLClassLoader(classPath: ClassPath, parent: ClassLoader)
extends java.net.URLClassLoader(
classPath.strings.map(
- path => new URL("file:"+path)
+ path => new URL("file:"++path)
).toArray,
parent
){
override def toString = (
- scala.Console.BLUE + "cbt.URLClassLoader" + scala.Console.RESET + "(\n " + getURLs.map(_.toString).sorted.mkString(",\n ")
- + (if(getParent() != ClassLoader.getSystemClassLoader()) ",\n" + getParent().toString.split("\n").map(" "+_).mkString("\n") else "")
- + "\n)"
+ scala.Console.BLUE ++ "cbt.URLClassLoader" ++ scala.Console.RESET
+ ++ "(\n " ++ getURLs.map(_.toString).sorted.mkString(",\n ")
+ ++ (
+ if(getParent() != ClassLoader.getSystemClassLoader())
+ ",\n" ++ getParent().toString.split("\n").map(" "++_).mkString("\n")
+ else ""
+ )
+ ++ "\n)"
)
- import scala.language.existentials
- /*override def loadClass(name: String): Class[_] = {
- //System.err.println("LOADING CLASS "+name+" in "+this);
- try{
- super.loadClass(name)
- } catch {
- case e: ClassNotFoundException =>
- // FIXME: Shouldn't this happen automatically?
- parent.loadClass(name)
- }
- }*/
}
diff --git a/stage1/logger.scala b/stage1/logger.scala
index eaf64db..b1d80a3 100644
--- a/stage1/logger.scala
+++ b/stage1/logger.scala
@@ -19,7 +19,7 @@ case class Logger(enabledLoggers: Set[String]) {
System.err.println( s"[${" "*(6-timeTaken.size)}$timeTaken][$name] $msg" )
}
- def showInvocation(method: String, args: Any) = method + "( " + args + " )"
+ def showInvocation(method: String, args: Any) = method ++ "( " ++ args.toString ++ " )"
final def stage1(msg: => String) = logGuarded(names.stage1, msg)
final def stage2(msg: => String) = logGuarded(names.stage2, msg)
@@ -28,6 +28,7 @@ case class Logger(enabledLoggers: Set[String]) {
final def composition(msg: => String) = logGuarded(names.composition, msg)
final def resolver(msg: => String) = logGuarded(names.resolver, msg)
final def lib(msg: => String) = logGuarded(names.lib, msg)
+ final def test(msg: => String) = logGuarded(names.test, msg)
private object names{
val stage1 = "stage1"
@@ -37,6 +38,7 @@ case class Logger(enabledLoggers: Set[String]) {
val resolver = "resolver"
val composition = "composition"
val lib = "lib"
+ val test = "test"
}
private def logGuarded(name: String, msg: => String) = {
diff --git a/stage1/paths.scala b/stage1/paths.scala
index f76c2f7..d3856c8 100644
--- a/stage1/paths.scala
+++ b/stage1/paths.scala
@@ -1,15 +1,15 @@
package cbt
import java.io._
object paths{
- val cbtHome = new File(Option(System.getenv("CBT_HOME")).get)
- val mavenCache = new File(cbtHome+"/cache/maven/")
- val userHome = new File(Option(System.getProperty("user.home")).get)
- val stage1 = new File(Option(System.getenv("STAGE1")).get)
- val stage2 = new File(cbtHome + "/stage2/")
- val nailgun = new File(Option(System.getenv("NAILGUN")).get)
- private val target = Option(System.getenv("TARGET")).get
- val stage1Target = new File(stage1 + "/" + target)
- val stage2Target = new File(stage2 + "/" + target)
- val nailgunTarget = new File(nailgun + "/" + target)
- val sonatypeLogin = new File(cbtHome+"/sonatype.login")
+ val cbtHome: File = new File(Option(System.getenv("CBT_HOME")).get)
+ val mavenCache: File = cbtHome ++ "/cache/maven"
+ val userHome: File = new File(Option(System.getProperty("user.home")).get)
+ val stage1: File = new File(Option(System.getenv("STAGE1")).get)
+ val stage2: File = cbtHome ++ "/stage2"
+ val nailgun: File = new File(Option(System.getenv("NAILGUN")).get)
+ private val target = Option(System.getenv("TARGET")).get.stripSuffix("/")
+ val stage1Target: File = stage1 ++ ("/" ++ target)
+ val stage2Target: File = stage2 ++ ("/" ++ target)
+ val nailgunTarget: File = nailgun ++ ("/" ++ target)
+ val sonatypeLogin: File = cbtHome ++ "/sonatype.login"
}
diff --git a/stage1/resolver.scala b/stage1/resolver.scala
index 880289c..9b3276b 100644
--- a/stage1/resolver.scala
+++ b/stage1/resolver.scala
@@ -10,7 +10,7 @@ private final class Tree( val root: Dependency, computeChildren: => Seq[Tree] ){
lazy val children = computeChildren
def linearize: Seq[Dependency] = root +: children.flatMap(_.linearize)
def show(indent: Int = 0): Stream[Char] = {
- (" " * indent + root.show + "\n").toStream #::: children.map(_.show(indent+1)).foldLeft(Stream.empty[Char])(_ #::: _)
+ (" " * indent ++ root.show ++ "\n").toStream #::: children.map(_.show(indent+1)).foldLeft(Stream.empty[Char])(_ #::: _)
}
}
@@ -20,9 +20,11 @@ trait ArtifactInfo extends Dependency{
def version: String
protected def str = s"$groupId:$artifactId:$version"
- override def show = super.show + s"($str)"
+ override def show = super.show ++ s"($str)"
}
abstract class Dependency{
+ implicit def logger: Logger
+ protected def lib = new Stage1Lib(logger)
def updated: Boolean
//def cacheClassLoader: Boolean = false
@@ -49,11 +51,10 @@ abstract class Dependency{
).par.map(_.exportedClasspath).seq.sortBy(_.string)
)
if(cacheDependencyClassLoader){
- val mavenClassPathKey = mavenClassPath.strings.sorted.mkString(":")
new URLClassLoader(
exportedClasspath ++ buildClassPath,
ClassLoaderCache.classLoader(
- mavenClassPathKey, new URLClassLoader( mavenClassPath, ClassLoader.getSystemClassLoader )
+ mavenClassPath, new URLClassLoader( mavenClassPath, ClassLoader.getSystemClassLoader )
)
)
} else {
@@ -85,11 +86,13 @@ abstract class Dependency{
def show: String = this.getClass.getSimpleName
// ========== debug ==========
def dependencyTree: String = dependencyTreeRecursion()
- def logger: Logger
- protected def lib = new Stage1Lib(logger)
- private def dependencyTreeRecursion(indent: Int = 0): String = ( " " * indent ) + (if(updated) lib.red(show) else show) + dependencies.map(_.dependencyTreeRecursion(indent + 1)).map("\n"+_).mkString("")
-
- private object cacheDependencyClassLoaderBasicBuild extends Cache[ClassLoader]
+ private def dependencyTreeRecursion(indent: Int = 0): String = (
+ ( " " * indent )
+ ++ (if(updated) lib.red(show) else show)
+ ++ dependencies.map(
+ _.dependencyTreeRecursion(indent + 1)
+ ).map( "\n" ++ _.toString ).mkString("")
+ )
}
// TODO: all this hard codes the scala version, needs more flexibility
@@ -140,16 +143,18 @@ case class MavenDependency( groupId: String, artifactId: String, version: String
def updated = false
private val groupPath = groupId.split("\\.").mkString("/")
- def basePath = s"/$groupPath/$artifactId/$version/$artifactId-$version"+(if(sources) "-sources" else "")
+ def basePath = s"/$groupPath/$artifactId/$version/$artifactId-$version"++(if(sources) "-sources" else "")
- private def resolverUrl = if(version.endsWith("-SNAPSHOT")) "https://oss.sonatype.org/content/repositories/snapshots" else "https://repo1.maven.org/maven2"
- private def baseUrl = resolverUrl + basePath
- private def baseFile = mavenCache + basePath
- private def pomFile = baseFile+".pom"
- private def jarFile = baseFile+".jar"
- //private def coursierJarFile = userHome+"/.coursier/cache/v1/https/repo1.maven.org/maven2"+basePath+".jar"
- private def pomUrl = baseUrl+".pom"
- private def jarUrl = baseUrl+".jar"
+ private def resolverUrl:URL = new URL(
+ if(version.endsWith("-SNAPSHOT")) "https://oss.sonatype.org/content/repositories/snapshots" else "https://repo1.maven.org/maven2"
+ )
+ private def baseUrl: URL = resolverUrl ++ basePath
+ private def baseFile: File = mavenCache ++ basePath
+ private def pomFile: File = baseFile ++ ".pom"
+ private def jarFile: File = baseFile ++ ".jar"
+ //private def coursierJarFile = userHome++"/.coursier/cache/v1/https/repo1.maven.org/maven2"++basePath++".jar"
+ private def pomUrl: URL = baseUrl ++ ".pom"
+ private def jarUrl: URL = baseUrl ++ ".jar"
def exportedJars = Seq( jar )
def exportedClasspath = ClassPath( exportedJars )
@@ -157,33 +162,32 @@ case class MavenDependency( groupId: String, artifactId: String, version: String
import scala.collection.JavaConversions._
def jarSha1 = {
- val file = jarFile+".sha1"
- def url = jarUrl+".sha1"
+ val file = jarFile ++ ".sha1"
scala.util.Try{
- lib.download( new URL(url), Paths.get(file), None )
+ lib.download( jarUrl ++ ".sha1" , file, None )
// split(" ") here so checksum file contents in this format work: df7f15de037a1ee4d57d2ed779739089f560338c jna-3.2.2.pom
- Files.readAllLines(Paths.get(file)).mkString("\n").split(" ").head.trim
- }.toOption // FIXME: .toOption is a temporary solution to ignore if libs don't have one
+ Files.readAllLines(Paths.get(file.string)).mkString("\n").split(" ").head.trim
+ }.toOption // FIXME: .toOption is a temporary solution to ignore if libs don't have one (not sure that's even possible)
}
+
def pomSha1 = {
- val file = pomFile+".sha1"
- def url = pomUrl+".sha1"
+ val file = pomFile++".sha1"
scala.util.Try{
- lib.download( new URL(url), Paths.get(file), None )
+ lib.download( pomUrl++".sha1" , file, None )
// split(" ") here so checksum file contents in this format work: df7f15de037a1ee4d57d2ed779739089f560338c jna-3.2.2.pom
- Files.readAllLines(Paths.get(file)).mkString("\n").split(" ").head.trim
- }.toOption // FIXME: .toOption is a temporary solution to ignore if libs don't have one
+ Files.readAllLines(Paths.get(file.string)).mkString("\n").split(" ").head.trim
+ }.toOption // FIXME: .toOption is a temporary solution to ignore if libs don't have one (not sure that's even possible)
}
+
def jar = {
- lib.download( new URL(jarUrl), Paths.get(jarFile), jarSha1 )
- new File(jarFile)
- }
- def pomXml = {
- XML.loadFile(pom.toString)
+ lib.download( jarUrl, jarFile, jarSha1 )
+ jarFile
}
+ def pomXml = XML.loadFile(pom.toString)
+
def pom = {
- lib.download( new URL(pomUrl), Paths.get(pomFile), pomSha1 )
- new File(pomFile)
+ lib.download( pomUrl, pomFile, pomSha1 )
+ pomFile
}
// ========== pom traversal ==========
@@ -211,13 +215,13 @@ case class MavenDependency( groupId: String, artifactId: String, version: String
}.toVector
}
def lookup( xml: Node, accessor: Node => NodeSeq ): Option[String] = {
- //println("lookup in "+pomUrl)
- val Substitution = "\\$\\{([a-z0-9\\.]+)\\}".r
+ //println("lookup in "++pomUrl)
+ val Substitution = "\\$\\{([a-z0-9\\.]++)\\}".r
accessor(xml).headOption.flatMap{v =>
- //println("found: "+v.text)
+ //println("found: "++v.text)
v.text match {
case Substitution(path) =>
- //println("lookup "+path + ": "+(pomXml\path).text)
+ //println("lookup "++path ++ ": "++(pomXml\path).text)
lookup(pomXml, _ \ "properties" \ path)
case value => Option(value)
}
@@ -247,7 +251,7 @@ object MavenDependency{
def removeOutdated(
deps: Seq[ArtifactInfo],
versionLessThan: (String, String) => Boolean = semanticVersionLessThan
- ): Seq[ArtifactInfo] = {
+ )(implicit logger: Logger): Seq[ArtifactInfo] = {
val latest = deps
.groupBy( d => (d.groupId, d.artifactId) )
.mapValues(
@@ -257,7 +261,7 @@ object MavenDependency{
deps.flatMap{
d =>
val l = latest.get((d.groupId,d.artifactId))
- //if(d != l) println("EVICTED: "+d.show)
+ if(d != l) logger.resolver("outdated: "++d.show)
l
}.distinct
}
diff --git a/stage2/AdminStage2.scala b/stage2/AdminStage2.scala
index d3db3e7..4120b1c 100644
--- a/stage2/AdminStage2.scala
+++ b/stage2/AdminStage2.scala
@@ -5,7 +5,7 @@ object AdminStage2{
val lib = new Lib(init.logger)
val adminTasks = new AdminTasks(lib, args.drop(3))
new lib.ReflectObject(adminTasks){
- def usage = "Available methods: " + lib.taskNames(subclassType)
+ def usage: String = "Available methods: " ++ lib.taskNames(subclassType).mkString(" ")
}.callNullary(args.lift(2))
}
}
diff --git a/stage2/DefaultBuild.scala b/stage2/BasicBuild.scala
index c0072ff..6da31b7 100644
--- a/stage2/DefaultBuild.scala
+++ b/stage2/BasicBuild.scala
@@ -15,80 +15,17 @@ import scala.util._
import ammonite.ops.{cwd => _,_}
-
-
-
-abstract class PackageBuild(context: Context) extends Build(context) with ArtifactInfo{
- def `package`: Seq[File] = lib.concurrently( enableConcurrency )(
- Seq(() => jar, () => docJar, () => srcJar)
- )( _() )
-
- private object cacheJarBasicBuild extends Cache[File]
- def jar: File = cacheJarBasicBuild{
- lib.jar( artifactId, version, compile, jarTarget )
- }
-
- private object cacheSrcJarBasicBuild extends Cache[File]
- def srcJar: File = cacheSrcJarBasicBuild{
- lib.srcJar(sources, artifactId, version, scalaTarget)
- }
-
- private object cacheDocBasicBuild extends Cache[File]
- def docJar: File = cacheDocBasicBuild{
- lib.docJar( sources, dependencyClasspath, apiTarget, jarTarget, artifactId, version, scalacOptions )
- }
-
- override def jars = jar +: dependencyJars
- override def exportedJars: Seq[File] = Seq(jar)
-}
-abstract class PublishBuild(context: Context) extends PackageBuild(context){
- def name = artifactId
- def description: String
- def url: URL
- def developers: Seq[Developer]
- def licenses: Seq[License]
- def scmUrl: String
- def scmConnection: String
- def pomExtra: Seq[scala.xml.Node] = Seq()
-
- // ========== package ==========
-
- /** put additional xml that should go into the POM file in here */
- def pom: File = lib.pom(
- groupId = groupId,
- artifactId = artifactId,
- version = version,
- name = name,
- description = description,
- url = url,
- developers = developers,
- licenses = licenses,
- scmUrl = scmUrl,
- scmConnection = scmConnection,
- dependencies = dependencies,
- pomExtra = pomExtra,
- jarTarget = jarTarget
- )
-
- // ========== publish ==========
- final protected def releaseFolder = s"/${groupId.replace(".","/")}/$artifactId/$version/"
- def snapshotUrl = new URL("https://oss.sonatype.org/content/repositories/snapshots")
- def releaseUrl = new URL("https://oss.sonatype.org/service/local/staging/deploy/maven2")
- def publishSnapshot: Unit = lib.publishSnapshot(sourceFiles, pom +: `package`, new URL(snapshotUrl + releaseFolder) )
- def publishSigned: Unit = lib.publishSigned(sourceFiles, pom +: `package`, new URL(releaseUrl + releaseFolder) )
-}
-
-
class BasicBuild(context: Context) extends Build(context)
class Build(val context: Context) extends Dependency with TriggerLoop{
// library available to builds
final val logger = context.logger
override final protected val lib: Lib = new Lib(logger)
+
// ========== general stuff ==========
-
+
def enableConcurrency = false
- final def projectDirectory: File = new File(context.cwd)
- assert( projectDirectory.exists, "projectDirectory does not exist: "+projectDirectory )
+ final def projectDirectory: File = context.cwd
+ assert( projectDirectory.exists, "projectDirectory does not exist: " ++ projectDirectory.string )
final def usage: Unit = new lib.ReflectBuild(this).usage
/*
def scaffold: Unit = lib.generateBasicBuildFile(
@@ -104,25 +41,25 @@ class Build(val context: Context) extends Dependency with TriggerLoop{
def dependencies: Seq[Dependency] = Seq(
"org.scala-lang" % "scala-library" % scalaVersion
)
-
+
// ========== paths ==========
- final private val defaultSourceDirectory = new File(projectDirectory+"/src/")
+ final private val defaultSourceDirectory = projectDirectory ++ "/src"
/** base directory where stuff should be generated */
- def target = new File(projectDirectory+"/target")
+ def target: File = projectDirectory ++ "/target"
/** base directory where stuff should be generated for this scala version*/
- def scalaTarget = new File(target + s"/scala-$scalaMajorVersion")
+ def scalaTarget: File = target ++ s"/scala-$scalaMajorVersion"
/** directory where jars (and the pom file) should be put */
- def jarTarget = scalaTarget
+ def jarTarget: File = scalaTarget
/** directory where the scaladoc should be put */
- def apiTarget = new File(scalaTarget + "/api")
+ def apiTarget: File = scalaTarget ++ "/api"
/** directory where the class files should be put (in package directories) */
- def compileTarget = new File(scalaTarget + "/classes")
+ def compileTarget: File = scalaTarget ++ "/classes"
/** 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)
- /** Which file endings to consider being source files. */
+ /** Which file endings to consider being source files. */
def sourceFileFilter(file: File): Boolean = file.toString.endsWith(".scala") || file.toString.endsWith(".java")
/** Absolute path names for all individual files found in sources directly or contained in directories. */
@@ -130,7 +67,7 @@ class Build(val context: Context) extends Dependency with TriggerLoop{
base <- sources.filter(_.exists).map(lib.realpath)
file <- lib.listFilesRecursive(base) if file.isFile && sourceFileFilter(file)
} yield file
-
+
protected def assertSourceDirectories(): Unit = {
val nonExisting =
sources
@@ -138,36 +75,29 @@ class Build(val context: Context) extends Dependency with TriggerLoop{
.diff( Seq(defaultSourceDirectory) )
assert(
nonExisting.isEmpty,
- "Some sources do not exist: \n"+nonExisting.mkString("\n")
+ "Some sources do not exist: \n"++nonExisting.mkString("\n")
)
}
assertSourceDirectories()
-
-
/** SBT-like dependency builder DSL */
class GroupIdAndArtifactId( groupId: String, artifactId: String ){
def %(version: String) = new MavenDependency(groupId, artifactId, version)(lib.logger)
}
implicit class DependencyBuilder(groupId: String){
- def %%(artifactId: String) = new GroupIdAndArtifactId( groupId, artifactId+"_"+scalaMajorVersion )
+ def %%(artifactId: String) = new GroupIdAndArtifactId( groupId, artifactId++"_"++scalaMajorVersion )
def %(artifactId: String) = new GroupIdAndArtifactId( groupId, artifactId )
}
- final def BuildDependency(path: String) = cbt.BuildDependency(
- context.copy(
- cwd = path,
- args = Seq()
- )
+ final def BuildDependency(path: File) = cbt.BuildDependency(
+ context.copy( cwd = path, args = Seq() )
)
def triggerLoopFiles: Seq[File] = sources ++ transitiveDependencies.collect{ case b: TriggerLoop => b.triggerLoopFiles }.flatten
-
def localJars : Seq[File] =
- Seq(projectDirectory + "/lib/")
- .map(new File(_))
+ Seq(projectDirectory ++ "/lib")
.filter(_.exists)
.flatMap(_.listFiles)
.filter(_.toString.endsWith(".jar"))
@@ -215,19 +145,20 @@ class Build(val context: Context) extends Dependency with TriggerLoop{
}
def runClass: String = "Main"
- def run: Unit = lib.runMainIfFound( runClass, Seq(), classLoader )
+ def run: ExitCode = lib.runMainIfFound( runClass, context.args, classLoader )
- def test: Unit = lib.test(context)
+ def test: ExitCode = lib.test(context)
context.logger.composition(">"*80)
- context.logger.composition("class "+this.getClass)
- context.logger.composition("dir "+context.cwd)
- context.logger.composition("sources "+sources.toList.mkString(" "))
- context.logger.composition("target "+target)
- context.logger.composition("dependencyTree\n"+dependencyTree)
+ context.logger.composition("class " ++ this.getClass.toString)
+ context.logger.composition("dir " ++ context.cwd.string)
+ context.logger.composition("sources " ++ sources.toList.mkString(" "))
+ context.logger.composition("target " ++ target.string)
+ context.logger.composition("context " ++ context.toString)
+ context.logger.composition("dependencyTree\n" ++ dependencyTree)
context.logger.composition("<"*80)
// ========== cbt internals ==========
private[cbt] def finalBuild = this
- override def show = this.getClass.getSimpleName + "("+context.cwd+")"
+ override def show = this.getClass.getSimpleName ++ "(" ++ context.cwd.string ++ ")"
}
diff --git a/stage2/BuildBuild.scala b/stage2/BuildBuild.scala
index 41589db..9283cdf 100644
--- a/stage2/BuildBuild.scala
+++ b/stage2/BuildBuild.scala
@@ -1,9 +1,10 @@
package cbt
+import java.io.File
import scala.collection.immutable.Seq
class BuildBuild(context: Context) extends Build(context){
override def dependencies = Seq( CbtDependency(context.logger) ) ++ super.dependencies
- def managedBuildDirectory = lib.realpath(projectDirectory + "/../")
+ def managedBuildDirectory: File = lib.realpath( projectDirectory.parent )
val managedBuild = {
val managedContext = context.copy( cwd = managedBuildDirectory )
val cl = new cbt.URLClassLoader(
diff --git a/stage2/dependencies.scala b/stage2/BuildDependency.scala
index 8ed36eb..f7b6b78 100644
--- a/stage2/dependencies.scala
+++ b/stage2/BuildDependency.scala
@@ -16,7 +16,7 @@ trait TriggerLoop extends Dependency{
}
/** You likely want to use the factory method in the BasicBuild class instead of this. */
case class BuildDependency(context: Context) extends TriggerLoop{
- override def show = this.getClass.getSimpleName + "("+context.cwd+")"
+ override def show = this.getClass.getSimpleName ++ "(" ++ context.cwd.string ++ ")"
final override lazy val logger = context.logger
final override lazy val lib: Lib = new Lib(logger)
private val root = lib.loadRoot( context.copy(args=Seq()) )
diff --git a/stage2/Lib.scala b/stage2/Lib.scala
index b92e0b3..6bb9c0b 100644
--- a/stage2/Lib.scala
+++ b/stage2/Lib.scala
@@ -15,6 +15,7 @@ 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)
@@ -36,9 +37,9 @@ final class Lib(logger: Logger) extends Stage1Lib(logger) with Scaffold{
*/
def loadRoot(context: Context, default: Context => Build = new Build(_)): Build = {
context.logger.composition( context.logger.showInvocation("Build.loadRoot",context) )
- def findStartDir(cwd: String): String = {
- val buildDir = realpath(cwd+"/build")
- if(new File(buildDir).exists) findStartDir(buildDir) else cwd
+ def findStartDir(cwd: File): File = {
+ val buildDir = realpath( cwd ++ "/build" )
+ if(buildDir.exists) findStartDir(buildDir) else cwd
}
val start = findStartDir(context.cwd)
@@ -50,7 +51,7 @@ final class Lib(logger: Logger) extends Stage1Lib(logger) with Scaffold{
if(useBasicBuildBuild) default( context ) else new cbt.BuildBuild( context.copy( cwd = start ) )
} catch {
case e:ClassNotFoundException if e.getMessage == rootBuildClassName =>
- throw new Exception(s"no class $rootBuildClassName found in "+start)
+ throw new Exception(s"no class $rootBuildClassName found in " ++ start.string)
}
}
@@ -67,13 +68,13 @@ final class Lib(logger: Logger) extends Stage1Lib(logger) with Scaffold{
}
def srcJar(sources: Seq[File], artifactId: String, version: String, jarTarget: File): File = {
- val file = new File(jarTarget+"/"+artifactId+"-"+version+"-sources.jar")
+ val file = jarTarget ++ ("/"++artifactId++"-"++version++"-sources.jar")
lib.jarFile(file, sources)
file
}
def jar(artifactId: String, version: String, compileTarget: File, jarTarget: File): File = {
- val file = new File(jarTarget+"/"+artifactId+"-"+version+".jar")
+ val file = jarTarget ++ ("/"++artifactId++"-"++version++".jar")
lib.jarFile(file, Seq(compileTarget))
file
}
@@ -87,17 +88,6 @@ final class Lib(logger: Logger) extends Stage1Lib(logger) with Scaffold{
version: String,
compileArgs: Seq[String]
): File = {
- class DisableSystemExit extends Exception
- object DisableSystemExit{
- def apply(e: Throwable): Boolean = {
- e match {
- case i: InvocationTargetException => apply(i.getTargetException)
- case _: DisableSystemExit => true
- case _ => false
- }
- }
- }
-
// FIXME: get this dynamically somehow, or is this even needed?
val javacp = ClassPath(
"/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/ext/cldrdata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/ext/dnsns.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/ext/jaccess.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/ext/jfxrt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/ext/localedata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/ext/nashorn.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/ext/sunec.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home/jre/lib/ext/zipfs.jar:/System/Library/Java/Extensions/MRJToolkit.jar".split(":").toVector.map(new File(_))
@@ -106,20 +96,13 @@ final class Lib(logger: Logger) extends Stage1Lib(logger) with Scaffold{
mkdir(Path(apiTarget))
if(sourceFiles.nonEmpty){
System.err.println("creating docs")
- try{
- System.setSecurityManager(
- new SecurityManager{
- override def checkPermission( permission: java.security.Permission ) = {
- if( permission.getName.startsWith("exitVM") ) throw new DisableSystemExit
- }
- }
- )
+ trapExitCode{
redirectOutToErr{
runMain(
"scala.tools.nsc.ScalaDoc",
Seq(
// FIXME: can we use compiler dependency here?
- "-cp", /*javacp+":"+*/ScalaDependencies(logger).classpath.string + ":" + dependenyClasspath.string,
+ "-cp", /*javacp++":"++*/ScalaDependencies(logger).classpath.string ++ ":" ++ dependenyClasspath.string,
"-d", apiTarget.toString
) ++ compileArgs ++ sourceFiles.map(_.toString),
new URLClassLoader(
@@ -128,32 +111,33 @@ final class Lib(logger: Logger) extends Stage1Lib(logger) with Scaffold{
)
)
}
- } catch {
- case e:InvocationTargetException if DisableSystemExit(e) =>
- } finally {
- System.setSecurityManager(null)
}
}
- val docJar = new File(jarTarget+"/"+artifactId+"-"+version+"-javadoc.jar")
+ val docJar = jarTarget ++ ("/"++artifactId++"-"++version++"-javadoc.jar")
lib.jarFile(docJar, Vector(apiTarget))
docJar
}
- def test( context: Context ) = {
+ def test( context: Context ): ExitCode = {
+ val loggers = logger.enabledLoggers.mkString(",")
+ // FIXME: this is a hack to pass logger args on to the tests.
+ // should probably have a more structured way
+ val loggerArg = if(loggers != "") Some("-Dlog="++loggers) else None
+
logger.lib(s"invoke testDefault( $context )")
- loadDynamic(
- context.copy( cwd = context.cwd+"/test/" ),
+ val exitCode: ExitCode = loadDynamic(
+ context.copy( cwd = context.cwd ++ "/test", args = loggerArg.toVector ++ context.args ),
new Build(_) with mixins.Test
).run
- logger.lib(s"return testDefault( $context )")
-
+ logger.lib(s"return testDefault( $context )")
+ exitCode
}
// task reflection helpers
import ru._
- private lazy val anyRefMembers = ru.typeOf[AnyRef].members.toVector.map(taskName)
- def taskNames(tpe: Type) = tpe.members.toVector.flatMap(lib.toTask).map(taskName).sorted
- private def taskName(method: Symbol) = method.name.decodedName.toString
+ 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)
@@ -177,10 +161,10 @@ final class Lib(logger: Logger) extends Stage1Lib(logger) with Scaffold{
"""
} else ""
- ) + s"""Methods provided by CBT (but possibly overwritten)
+ ) ++ s"""Methods provided by CBT (but possibly overwritten)
${baseTasks.mkString(" ")}"""
- ) + "\n"
+ ) ++ "\n"
}
}
@@ -203,6 +187,7 @@ final class Lib(logger: Logger) extends Stage1Lib(logger) with Scaffold{
case e:NoSuchMethodException if e.getMessage contains "toConsole" =>
result match {
case () => ""
+ case ExitCode(code) => System.exit(code)
case other => println( other.toString ) // no method .toConsole, using to String
}
}
@@ -217,17 +202,13 @@ final class Lib(logger: Logger) extends Stage1Lib(logger) with Scaffold{
}
}
-
// file system helpers
- def basename(path: String) = path.stripSuffix("/").split("/").last
- def basename(path: File) = path.toString.stripSuffix("/").split("/").last
- def dirname(path: String) = realpath(path).stripSuffix("/").split("/").dropRight(1).mkString("/")
- def realpath(name: String) = Paths.get(new File(name).getAbsolutePath).normalize.toString
- def realpath(name: File) = new File(Paths.get(name.getAbsolutePath).normalize.toString)
- def nameAndContents(file: File) = basename(file.toString) -> readAllBytes(Paths.get(file.toString))
+ def basename(path: File): String = path.toString.stripSuffix("/").split("/").last
+ 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)
+ 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)
@@ -242,7 +223,7 @@ final class Lib(logger: Logger) extends Stage1Lib(logger) with Scaffold{
val entry = new JarEntry( name )
entry.setTime(file.lastModified)
jar.putNextEntry(entry)
- jar.write( Files.readAllBytes( Paths.get(file.toString) ) )
+ jar.write( readAllBytes( Paths.get(file.toString) ) )
jar.closeEntry
name
}
@@ -250,11 +231,11 @@ final class Lib(logger: Logger) extends Stage1Lib(logger) with Scaffold{
val duplicateFiles = (names diff names.distinct).distinct
assert(
duplicateFiles.isEmpty,
- s"Conflicting file names when trying to create $jarFile: "+duplicateFiles.mkString(", ")
+ s"Conflicting file names when trying to create $jarFile: "++duplicateFiles.mkString(", ")
)
jar.close
- logger.lib("Done packaging "+jarFile)
+ logger.lib("Done packaging " ++ jarFile.toString)
}
lazy val passphrase =
@@ -270,9 +251,9 @@ final class Lib(logger: Logger) extends Stage1Lib(logger) with Scaffold{
new ProcessBuilder( "gpg", "--batch", "--yes", "-a", "-b", "-s", "--passphrase", passphrase, file.toString )
.inheritIO.start.waitFor
- if( 0 != statusCode ) throw new Exception("gpg exited with status code "+statusCode)
+ if( 0 != statusCode ) throw new Exception("gpg exited with status code " ++ statusCode.toString)
- new File(file+".asc")
+ file ++ ".asc"
}
//def requiredForPom[T](name: String): T = throw new Exception(s"You need to override `def $name` in order to generate a valid pom.")
@@ -339,9 +320,9 @@ final class Lib(logger: Logger) extends Stage1Lib(logger) with Scaffold{
}
</dependencies>
</project>
- val path = new File(jarTarget+"/"+artifactId+"-"+version+".pom")
- write.over(Path(path), "<?xml version='1.0' encoding='UTF-8'?>\n" + xml.toString)
- path
+ val path = jarTarget.toString ++ ( "/" ++ artifactId ++ "-" ++ version ++ ".pom" )
+ write.over(Path(path), "<?xml version='1.0' encoding='UTF-8'?>\n" ++ xml.toString)
+ new File(path)
}
def concurrently[T,R]( concurrencyEnabled: Boolean )( items: Seq[T] )( projection: T => R ): Seq[R] = {
@@ -362,8 +343,8 @@ final class Lib(logger: Logger) extends Stage1Lib(logger) with Scaffold{
val files = (artifacts ++ artifacts.map(sign)).map(nameAndContents)
lazy val checksums = files.flatMap{
case (name, content) => Seq(
- name+".md5" -> md5(content).toArray.map(_.toByte),
- name+".sha1" -> sha1(content).toArray.map(_.toByte)
+ name++".md5" -> md5(content).toArray.map(_.toByte),
+ name++".sha1" -> sha1(content).toArray.map(_.toByte)
)
}
val all = (files ++ checksums)
@@ -378,16 +359,14 @@ final class Lib(logger: Logger) extends Stage1Lib(logger) with Scaffold{
def upload(fileName: String, fileContents: Array[Byte], baseUrl: URL): Unit = {
import java.net._
import java.io._
- logger.task("uploading "+fileName)
- val url = new URL(
- baseUrl + fileName
- )
+ logger.task("uploading "++fileName)
+ val url = baseUrl ++ fileName
val httpCon = url.openConnection.asInstanceOf[HttpURLConnection]
httpCon.setDoOutput(true)
httpCon.setRequestMethod("PUT")
val userPassword = read(Path(sonatypeLogin)).trim
val encoding = new sun.misc.BASE64Encoder().encode(userPassword.getBytes)
- httpCon.setRequestProperty("Authorization", "Basic " + encoding)
+ httpCon.setRequestProperty("Authorization", "Basic " ++ encoding)
httpCon.setRequestProperty("Content-Type", "application/binary")
httpCon.getOutputStream.write(
fileContents
@@ -404,7 +383,7 @@ final class Lib(logger: Logger) extends Stage1Lib(logger) with Scaffold{
files.map{
file =>
- if(file.isFile) new File( dirname(file.toString) )
+ if(file.isFile) dirname(file)
else file
}.distinct.map{ file =>
val watchableFile = new WatchableFile(file)
@@ -426,7 +405,7 @@ final class Lib(logger: Logger) extends Stage1Lib(logger) with Scaffold{
.filterNot(_.kind == StandardWatchEventKind.OVERFLOW)
.map(_.context.toString)
.map(new File(_))
- changedFiles.foreach( f => logger.loop("Changed: "+f) )
+ changedFiles.foreach( f => logger.loop( "Changed: " ++ f.toString ) )
changedFiles.collect(action)
key.reset
}
diff --git a/stage2/PackageBuild.scala b/stage2/PackageBuild.scala
new file mode 100644
index 0000000..96c7b6f
--- /dev/null
+++ b/stage2/PackageBuild.scala
@@ -0,0 +1,27 @@
+package cbt
+import java.io.File
+import java.net.URL
+import scala.collection.immutable.Seq
+abstract class PackageBuild(context: Context) extends Build(context) with ArtifactInfo{
+ def `package`: Seq[File] = lib.concurrently( enableConcurrency )(
+ Seq(() => jar, () => docJar, () => srcJar)
+ )( _() )
+
+ private object cacheJarBasicBuild extends Cache[File]
+ def jar: File = cacheJarBasicBuild{
+ lib.jar( artifactId, version, compile, jarTarget )
+ }
+
+ private object cacheSrcJarBasicBuild extends Cache[File]
+ def srcJar: File = cacheSrcJarBasicBuild{
+ lib.srcJar(sources, artifactId, version, scalaTarget)
+ }
+
+ private object cacheDocBasicBuild extends Cache[File]
+ def docJar: File = cacheDocBasicBuild{
+ lib.docJar( sources, dependencyClasspath, apiTarget, jarTarget, artifactId, version, scalacOptions )
+ }
+
+ override def jars = jar +: dependencyJars
+ override def exportedJars: Seq[File] = Seq(jar)
+}
diff --git a/stage2/PublishBuild.scala b/stage2/PublishBuild.scala
new file mode 100644
index 0000000..e4e8fd7
--- /dev/null
+++ b/stage2/PublishBuild.scala
@@ -0,0 +1,41 @@
+package cbt
+import java.io.File
+import java.net.URL
+import scala.collection.immutable.Seq
+
+abstract class PublishBuild(context: Context) extends PackageBuild(context){
+ def name = artifactId
+ def description: String
+ def url: URL
+ def developers: Seq[Developer]
+ def licenses: Seq[License]
+ def scmUrl: String
+ def scmConnection: String
+ def pomExtra: Seq[scala.xml.Node] = Seq()
+
+ // ========== package ==========
+
+ /** put additional xml that should go into the POM file in here */
+ def pom: File = lib.pom(
+ groupId = groupId,
+ artifactId = artifactId,
+ version = version,
+ name = name,
+ description = description,
+ url = url,
+ developers = developers,
+ licenses = licenses,
+ scmUrl = scmUrl,
+ scmConnection = scmConnection,
+ dependencies = dependencies,
+ pomExtra = pomExtra,
+ jarTarget = jarTarget
+ )
+
+ // ========== publish ==========
+ final protected def releaseFolder = s"/${groupId.replace(".","/")}/$artifactId/$version/"
+ def snapshotUrl = new URL("https://oss.sonatype.org/content/repositories/snapshots")
+ def releaseUrl = new URL("https://oss.sonatype.org/service/local/staging/deploy/maven2")
+ def publishSnapshot: Unit = lib.publishSnapshot(sourceFiles, pom +: `package`, snapshotUrl ++ releaseFolder )
+ def publishSigned: Unit = lib.publishSigned(sourceFiles, pom +: `package`, releaseUrl ++ releaseFolder )
+}
diff --git a/stage2/Scaffold.scala b/stage2/Scaffold.scala
index 00c8706..18b2cc3 100644
--- a/stage2/Scaffold.scala
+++ b/stage2/Scaffold.scala
@@ -49,7 +49,7 @@ class Build(context: Context) extends BuildBuild(context){
override def scalaVersion: String = "2.11.7"
override def dependencies = super.dependencies ++ Seq(
- BuildDependency( projectDirectory + "/../build-shared/")
+ BuildDependency( projectDirectory.parent ++ "/build-shared")
// , "com.lihaoyi" %% "ammonite-ops" % "0.5.5"
)
}
@@ -85,7 +85,7 @@ class Build(context: Context) extends BuildBuild(context){
override def scalaVersion: String = "2.11.7"
override def dependencies = super.dependencies ++ Seq(
- BuildDependency( projectDirectory + "/../../build-shared/")
+ BuildDependency( projectDirectory.parent.parent ++ "/build-shared")
// , "com.lihaoyi" %% "ammonite-ops" % "0.5.5"
)
}
@@ -132,9 +132,9 @@ trait BuildShared extends BasicBuild{
generatedFiles.map{
case ( fileName, code ) =>
scala.util.Try{
- write( Path(projectDirectory+"/"+fileName), code )
+ write( Path( projectDirectory.string ++ "/" ++ fileName ), code )
import scala.Console._
- println( GREEN + "Created " + fileName + RESET )
+ println( GREEN ++ "Created " ++ fileName ++ RESET )
}
}.foreach(
_.recover{
diff --git a/stage2/Stage2.scala b/stage2/Stage2.scala
index f3e833f..ed63cf1 100644
--- a/stage2/Stage2.scala
+++ b/stage2/Stage2.scala
@@ -10,7 +10,7 @@ import cbt.paths._
object Stage2{
- def main(args: Array[String]) = {
+ def main(args: Array[String]): Unit = {
val init = new Init(args)
import init._
@@ -27,32 +27,33 @@ object Stage2{
}
val task = argsV.lift( taskIndex )
- val context = Context( cwd, argsV.drop( taskIndex + 1 ), logger )
+ val context = Context( new File(argsV(0)), argsV.drop( taskIndex + 1 ), logger )
val first = lib.loadRoot( context )
val build = first.finalBuild
- val res = if (loop) {
- // TODO: this should allow looping over task specific files, like test files as well
- val triggerFiles = first.triggerLoopFiles.map(lib.realpath)
- val triggerCbtFiles = Seq( nailgun, stage1, stage2 ).map(lib.realpath _)
- val allTriggerFiles = triggerFiles ++ triggerCbtFiles
-
- logger.loop("Looping change detection over:\n - "+allTriggerFiles.mkString("\n - "))
-
- lib.watch(allTriggerFiles) {
- case file if triggerCbtFiles.exists(file.toString startsWith _.toString) =>
- logger.loop("Change is in CBT's own source code.")
- logger.loop("Restarting CBT.")
- scala.util.control.Breaks.break
-
- case file if triggerFiles.exists(file.toString startsWith _.toString) =>
- new lib.ReflectBuild( lib.loadDynamic(context) ).callNullary(task)
+ val res = lib.trapExitCode{
+ if (loop) {
+ // TODO: this should allow looping over task specific files, like test files as well
+ val triggerFiles = first.triggerLoopFiles.map(lib.realpath)
+ val triggerCbtFiles = Seq( nailgun, stage1, stage2 ).map(lib.realpath _)
+ val allTriggerFiles = triggerFiles ++ triggerCbtFiles
+
+ logger.loop("Looping change detection over:\n - "++allTriggerFiles.mkString("\n - "))
+
+ lib.watch(allTriggerFiles) {
+ case file if triggerCbtFiles.exists(file.toString startsWith _.toString) =>
+ logger.loop("Change is in CBT's own source code.")
+ logger.loop("Restarting CBT.")
+ scala.util.control.Breaks.break
+
+ case file if triggerFiles.exists(file.toString startsWith _.toString) =>
+ new lib.ReflectBuild( lib.loadDynamic(context) ).callNullary(task)
+ }
+ } else {
+ new lib.ReflectBuild(build).callNullary(task)
}
- } else {
- new lib.ReflectBuild(build).callNullary(task)
}
init.logger.stage2(s"[$now] Stage2 end")
- res
}
}
diff --git a/stage2/mixins.scala b/stage2/mixins.scala
index 5ed26d8..2b38cdf 100644
--- a/stage2/mixins.scala
+++ b/stage2/mixins.scala
@@ -3,15 +3,15 @@ package mixins
import scala.collection.immutable.Seq
import java.io._
trait Test extends Build{
- lazy val testedBuild = BuildDependency(projectDirectory+"/../")
+ lazy val testedBuild = BuildDependency( projectDirectory.parent )
override def dependencies = Seq( testedBuild ) ++ super.dependencies
override def scalaVersion = testedBuild.build.scalaVersion
}
trait Sbt extends Build{
- override def sources = Seq(new File(projectDirectory+"/src/main/scala/"))
+ override def sources = Seq( projectDirectory ++ "/src/main/scala" )
}
trait SbtTest extends Test{
- override def sources = Vector(new File(projectDirectory+"/../src/test/scala"))
+ override def sources = Vector( projectDirectory.parent ++ "/src/test/scala" )
}
trait ScalaTest extends Build with Test{
def scalaTestVersion: String
@@ -23,9 +23,9 @@ trait ScalaTest extends Build with Test{
// workaround probable ScalaTest bug throwing away the outer classloader. Not caching doesn't nest them.
override def cacheDependencyClassLoader = false
- override def run = {
- val discoveryPath = compile.toString+"/"
- context.logger.lib("discoveryPath: "+discoveryPath)
+ 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),
diff --git a/test/test.scala b/test/test.scala
index 9ab5b4e..feab89f 100644
--- a/test/test.scala
+++ b/test/test.scala
@@ -1,66 +1,72 @@
+import cbt._
import cbt.paths._
import scala.collection.immutable.Seq
+// micro framework
object Main{
- // micro framework
- var successes = 0
- var failures = 0
- def assert(condition: Boolean, msg: String = null) = {
- scala.util.Try{
- Predef.assert(condition, msg)
- }.map{ _ =>
- print(".")
- successes += 1
- }.recover{
- case e: AssertionError =>
- println("FAILED")
- e.printStackTrace
- failures += 1
- }.get
- }
+ def main(args: Array[String]): Unit = {
+ val init = new Init(args)
+ implicit val logger: Logger = init.logger
+
+ var successes = 0
+ var failures = 0
+ def assert(condition: Boolean, msg: String = "")(implicit logger: Logger) = {
+ scala.util.Try{
+ Predef.assert(condition, "["++msg++"]")
+ }.map{ _ =>
+ print(".")
+ successes += 1
+ }.recover{
+ case e: AssertionError =>
+ println("FAILED")
+ e.printStackTrace
+ failures += 1
+ }.get
+ }
- def runCbt(path: String, args: Seq[String]) = {
- import java.io._
- val allArgs = ((cbtHome + "/cbt") +: args)
- val pb = new ProcessBuilder( allArgs :_* )
- pb.directory(new File(cbtHome + "/test/" + path))
- val p = pb.start
- val berr = new BufferedReader(new InputStreamReader(p.getErrorStream));
- val bout = new BufferedReader(new InputStreamReader(p.getInputStream));
- p.waitFor
- import collection.JavaConversions._
- val err = Stream.continually(berr.readLine()).takeWhile(_ != null).mkString("\n")
- val out = Stream.continually(bout.readLine()).takeWhile(_ != null).mkString("\n")
- Result(out, err, p.exitValue == 0)
- }
- case class Result(out: String, err: String, exit0: Boolean)
- def assertSuccess(res: Result) = {
- assert(res.exit0,res.toString)
- }
+ def runCbt(path: String, args: Seq[String])(implicit logger: Logger): Result = {
+ import java.io._
+ val allArgs: Seq[String] = ((cbtHome.string ++ "/cbt") +: "direct" +: (args ++ init.propsRaw))
+ logger.test(allArgs.toString)
+ val pb = new ProcessBuilder( allArgs :_* )
+ pb.directory(cbtHome ++ ("/test/" ++ path))
+ val p = pb.inheritIO.start
+ p.waitFor
+ val berr = new BufferedReader(new InputStreamReader(p.getErrorStream));
+ val bout = new BufferedReader(new InputStreamReader(p.getInputStream));
+ p.waitFor
+ import collection.JavaConversions._
+ val err = Stream.continually(berr.readLine()).takeWhile(_ != null).mkString("\n")
+ val out = Stream.continually(bout.readLine()).takeWhile(_ != null).mkString("\n")
+ Result(out, err, p.exitValue == 0)
+ }
+ case class Result(out: String, err: String, exit0: Boolean)
+ def assertSuccess(res: Result)(implicit logger: Logger) = {
+ assert(res.exit0, res.toString)
+ }
- // tests
- def usage(path: String) = {
- val usageString = "Methods provided by CBT"
- val res = runCbt(path, Seq())
- assert(res.out == "", res.out)
- assert(res.err contains usageString, res.err)
- }
- def compile(path: String) = {
- val res = runCbt(path, Seq("compile"))
- assertSuccess(res)
- // assert(res.err == "", res.err) // FIXME: enable this
- }
- def main(args: Array[String]): Unit = {
- import cbt._
+ // tests
+ def usage(path: String)(implicit logger: Logger) = {
+ val usageString = "Methods provided by CBT"
+ val res = runCbt(path, Seq())
+ logger.test(res.toString)
+ assertSuccess(res)
+ assert(res.out == "", res.toString)
+ assert(res.err contains usageString, res.toString)
+ }
+ def compile(path: String)(implicit logger: Logger) = {
+ val res = runCbt(path, Seq("compile"))
+ assertSuccess(res)
+ // assert(res.err == "", res.err) // FIXME: enable this
+ }
+
+ logger.test( "Running tests " ++ args.toList.toString )
- println("Running tests ")
-
usage("nothing")
compile("nothing")
{
- val logger = new Logger(Set[String]())
- val noContext = Context(cbtHome + "/test/" + "nothing",Seq(),logger)
+ val noContext = Context(cbtHome ++ "/test/nothing", Seq(), logger)
val b = new Build(noContext){
override def dependencies = Seq(
MavenDependency("net.incongru.watchservice","barbary-watchservice","1.0")(logger),
@@ -68,11 +74,11 @@ object Main{
)
}
val cp = b.classpath
- assert(cp.strings.distinct == cp.strings, "duplicates in classpath: "+cp)
+ assert(cp.strings.distinct == cp.strings, "duplicates in classpath: " ++ cp.string)
}
- println(" DONE!")
- println(successes+" succeeded, "+ failures+" failed" )
+ System.err.println(" DONE!")
+ System.err.println( successes.toString ++ " succeeded, "++ failures.toString ++ " failed" )
if(failures > 0) System.exit(1) else System.exit(0)
}
}