aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--DEVELOPER_GUIDE.txt2
-rw-r--r--README.md8
-rw-r--r--build/build.scala1
-rwxr-xr-xcbt72
-rw-r--r--compatibility/BuildInterface.java5
-rw-r--r--compatibility/Context.java6
-rw-r--r--doc/cbt-developer/version-compatibility.md3
-rw-r--r--nailgun_launcher/NailgunLauncher.java43
-rw-r--r--stage1/ContextImplementation.scala2
-rw-r--r--stage1/Stage1.scala22
-rw-r--r--stage1/Stage1Lib.scala8
-rw-r--r--stage1/cbt.scala16
-rw-r--r--stage1/resolver.scala2
-rw-r--r--stage2/BasicBuild.scala29
-rw-r--r--stage2/BuildBuild.scala3
-rw-r--r--stage2/BuildDependency.scala5
-rw-r--r--stage2/GitDependency.scala3
-rw-r--r--stage2/Lib.scala49
-rw-r--r--stage2/Stage2.scala37
-rw-r--r--test/test.scala2
20 files changed, 139 insertions, 179 deletions
diff --git a/DEVELOPER_GUIDE.txt b/DEVELOPER_GUIDE.txt
index 268852e..1a88253 100644
--- a/DEVELOPER_GUIDE.txt
+++ b/DEVELOPER_GUIDE.txt
@@ -29,7 +29,7 @@ compatibility/ Java interfaces that all CBT versions are source compatible
nailgun_launcher/ Self-contained helper that allows using Nailgun with minimal permanent classpath. (Is this actually needed?)
realpath/ Self-contained realpath source code to correctly figure our CBTs home directory. (Open for replacement ideas.)
stage1/ CBT's code that only relies only on Scala/Java built-ins. Contains a Maven resolver to download libs for stage2.
-stage2/ CBT's code that requires additional libs, e.g. barbary watchservice.
+stage2/ CBT's code that requires additional libs, e.g. jgit
test/ Unit tests that can serve as example builds
sonatype.login Sonatype credentials for deployment. Not in git obviously.
diff --git a/README.md b/README.md
index 205cc6e..cad92c9 100644
--- a/README.md
+++ b/README.md
@@ -203,7 +203,8 @@ As you can see it prints `asdf`. Adding tasks is that easy.
### Triggering tasks on file-changes
-When you call a task, you can prefix it with `loop`.
+When you call a task, you can prefix it with `loop`. You need to
+have fswatch install (e.g. via `brew install fswatch`).
CBT then watches the source files, the build files and even CBT's own
source code and re-runs the task when anything changes. If necessary,
this forces CBT to re-build itself, the project's dependencies and the project itself.
@@ -220,6 +221,11 @@ you managed to change windows back from your editor to the shell.
Try changing the build file and see how CBT reacts to it as well.
+To also clear the screen on each run use:
+```
+$ cbt loop clear run
+```
+
### Adding tests
The simplest way to add tests is putting a few assertions into the previously
diff --git a/build/build.scala b/build/build.scala
index ee1acfe..8f094c3 100644
--- a/build/build.scala
+++ b/build/build.scala
@@ -10,7 +10,6 @@ class Build(val context: Context) extends Shared with PublishLocal{
// FIXME: somehow consolidate this with cbt's own boot-strapping from source.
override def dependencies = {
super.dependencies ++ Resolver(mavenCentral).bind(
- MavenDependency("net.incongru.watchservice","barbary-watchservice","1.0"),
MavenDependency("org.eclipse.jgit", "org.eclipse.jgit", "4.2.0.201601211800-r"),
ScalaDependency("org.scala-lang.modules","scala-xml","1.0.5")
)
diff --git a/cbt b/cbt
index 22c789e..8ce8bd4 100755
--- a/cbt
+++ b/cbt
@@ -170,6 +170,17 @@ if [ "$1" = "direct" ]; then
use_nailgun=1
shift
fi
+loop=1
+if [ "$1" == "loop" ]; then
+ loop=0
+ shift
+fi
+clearScreen=1
+if [ "$1" == "clear" ]; then
+ clearScreen=0
+ shift
+fi
+
if [ $nailgun_installed -eq 1 ] || [ "$1" = "publishSigned" ]; then
use_nailgun=1
fi
@@ -184,7 +195,8 @@ stage1 () {
log "Checking for changes in cbt/nailgun_launcher" "$@"
NAILGUN_INDICATOR=$NAILGUN$TARGET../classes.last-success
changed=1
- for file in "$NAILGUN"/*.java; do
+ NAILGUN_SOURCES=("$NAILGUN"*.java)
+ for file in "${NAILGUN_SOURCES[@]}"; do
if [ "$file" -nt "$NAILGUN_INDICATOR" ]; then changed=0; fi
done
exitCode=0
@@ -194,7 +206,8 @@ stage1 () {
#rm $NAILGUN$TARGET/cbt/*.class 2>/dev/null # defensive delete of potentially broken class files
echo "Compiling cbt/nailgun_launcher" 1>&2
COMPILE_TIME=$(date +%YY%mm%dd%HH%MM.%SS|sed "s/[YmdHMS]//g")
- javac -Xlint:deprecation -Xlint:unchecked -d "$NAILGUN$TARGET" "$NAILGUN"*.java
+ #echo javac -Xlint:deprecation -Xlint:unchecked -d "$NAILGUN$TARGET" "${NAILGUN_SOURCES[@]}"
+ javac -Xlint:deprecation -Xlint:unchecked -d "$NAILGUN$TARGET" "${NAILGUN_SOURCES[@]}"
exitCode=$?
if [ $exitCode -eq 0 ]; then
touch -t "$COMPILE_TIME" "$NAILGUN_INDICATOR"
@@ -214,7 +227,7 @@ stage1 () {
log "Running JVM directly" "$@"
options=($JAVA_OPTS)
# JVM options to improve startup time. See https://github.com/cvogt/cbt/pull/262
- java "${options[@]}" $DEBUG -Xmx6072m -Xss10M -XX:MaxJavaStackTraceDepth=-1 -XX:+TieredCompilation -XX:TieredStopAtLevel=1 -Xverify:none -cp "$NAILGUN$TARGET" cbt.NailgunLauncher "$(time_taken)" "$CWD" "$@"
+ java "${options[@]}" $DEBUG -Xmx6072m -Xss10M -XX:MaxJavaStackTraceDepth=-1 -XX:+TieredCompilation -XX:TieredStopAtLevel=1 -Xverify:none -cp "$NAILGUN$TARGET" cbt.NailgunLauncher "$(time_taken)" "$CWD" "$loop" "$@"
exitCode=$?
else
log "Running via background process (nailgun)" "$@"
@@ -238,7 +251,7 @@ stage1 () {
sleep 0.3
done
log "Running CBT via Nailgun." "$@"
- $NG cbt.NailgunLauncher "$(time_taken)" "$CWD" "$@"
+ $NG cbt.NailgunLauncher "$(time_taken)" "$CWD" "$loop" "$@"
exitCode=$?
fi
log "Done running CBT." "$@"
@@ -246,46 +259,39 @@ stage1 () {
}
-loop=1
-case "$1" in
- "loop") loop=0
-esac
-case "$2" in
- "loop") loop=0
-esac
-
-CBT_SIGNALS_LOOPING=253
USER_PRESSED_CTRL_C=130
CBT_LOOP_FILE="$CWD/target/.cbt-loop.tmp"
+if [ $loop -eq 0 ]; then
+ which fswatch >/dev/null 2>/dev/null
+ export fswatch_installed=$?
+ if [ ! $fswatch_installed -eq 0 ]; then
+ echo "please install fswatch to use cbt loop, e.g. via brew install fswatch"
+ exit 1
+ fi
+fi
while true; do
+ if [ $clearScreen -eq 0 ]; then
+ clear
+ fi
+ if [ -f "$CBT_LOOP_FILE" ]; then
+ rm "$CBT_LOOP_FILE"
+ fi
stage1 "$@"
if [ ! $loop -eq 0 ] || [ $exitCode -eq $USER_PRESSED_CTRL_C ]; then
log "not looping, exiting" "$@"
break
else
- if [ ! $exitCode -eq $CBT_SIGNALS_LOOPING ]; then
- log "exitCode $exitCode" "$@"
- which fswatch >/dev/null 2>/dev/null
- export fswatch_installed=$?
- if [ -f "$CBT_LOOP_FILE" ]; then
- if [ $fswatch_installed -eq 0 ]; then
- # fswatch allows looping over CBT's sources itself
- log "fswatch found. looping." "$@"
- files=($(cat "$CBT_LOOP_FILE"))
- fswatch --one-event "${files[@]}"
- else
- log "fswatch not installed, stopping cbt" "$@"
- break
- fi
- else
- log "no $CBT_LOOP_FILE file, stopping cbt" "$@"
- break
- fi
+ files=
+ if [ -f "$CBT_LOOP_FILE" ]; then
+ files=($(cat "$CBT_LOOP_FILE"))
+ #rm "$CBT_LOOP_FILE"
fi
+ echo ""
+ echo "Watching for file changes... (ctrl+c short press for loop, long press for abort)"
+ #echo fswatch --one-event "${NAILGUN_SOURCES[@]}" "${files[@]}"
+ fswatch --one-event "${NAILGUN_SOURCES[@]}" "${files[@]}"
fi
-
- echo "======= Restarting CBT =======" 1>&2
done
log "Exiting CBT" "$@"
diff --git a/compatibility/BuildInterface.java b/compatibility/BuildInterface.java
index eb60960..d6d11db 100644
--- a/compatibility/BuildInterface.java
+++ b/compatibility/BuildInterface.java
@@ -6,7 +6,10 @@ public interface BuildInterface extends Dependency{
public default BuildInterface finalBuild(File current){
return finalBuild(); // legacy forwarder
}
- public abstract File[] triggerLoopFilesArray(); // needed for watching files across composed builds
+ @Deprecated
+ public default File[] triggerLoopFilesArray(){
+ return new File[0];
+ };
// deprecated methods, which clients are still allowed to implement, but not required
public abstract BuildInterface finalBuild(); // needed to propagage through build builds. Maybe we can get rid of this.
diff --git a/compatibility/Context.java b/compatibility/Context.java
index 1657ef0..f0fa42c 100644
--- a/compatibility/Context.java
+++ b/compatibility/Context.java
@@ -21,6 +21,9 @@ public interface Context{
public default File workingDirectory(){
return projectDirectory();
};
+ public default boolean loop(){
+ return false;
+ };
// methods that exist for longer which every CBT version in use should have by now, no default values needed
public abstract File cwd(); // REPLACE by something that allows to run cbt on some other directly
@@ -32,9 +35,6 @@ public interface Context{
public abstract File cbtRootHome(); // REMOVE
public abstract File compatibilityTarget(); // maybe replace this with search in the classloader for it?
public abstract BuildInterface parentBuildOrNull();
- public default File[] triggerLoopFilesArray(){
- return new File[0]; // REMOVE default value on next compatibility breaking release
- }
// deprecated methods
@java.lang.Deprecated
diff --git a/doc/cbt-developer/version-compatibility.md b/doc/cbt-developer/version-compatibility.md
index 986bb37..f324c31 100644
--- a/doc/cbt-developer/version-compatibility.md
+++ b/doc/cbt-developer/version-compatibility.md
@@ -25,6 +25,9 @@ However there are more things that can break compatibility when changed:
- communication between versions via reflection in particular
- how the TrapSecurityManager of each CBT version talks to the
installed TrapSecurityManager via reflection
+- communication via the file system
+ - cache folder location any layout
+ - .cbt-loop.tmp file
## How to detect accidental breakages?
diff --git a/nailgun_launcher/NailgunLauncher.java b/nailgun_launcher/NailgunLauncher.java
index 958b052..fe9f27f 100644
--- a/nailgun_launcher/NailgunLauncher.java
+++ b/nailgun_launcher/NailgunLauncher.java
@@ -6,6 +6,8 @@ import java.security.*;
import java.util.*;
import static cbt.Stage0Lib.*;
import static java.io.File.pathSeparator;
+import java.nio.file.*;
+import static java.nio.file.Files.write;
/**
* This launcher allows to start the JVM without loading anything else permanently into its
@@ -33,7 +35,9 @@ public class NailgunLauncher{
((File) get(context, "compatibilityTarget")).toString() + "/",
new ClassLoaderCache(
(HashMap) get(context, "persistentCache")
- )
+ ),
+ (File) get(context, "cwd"),
+ (Boolean) get(context, "loop")
);
return
res
@@ -97,8 +101,11 @@ public class NailgunLauncher{
String nailgunTarget = CBT_HOME + "/" + NAILGUN + TARGET;
long nailgunLauncherLastModified = new File( nailgunTarget + "../classes.last-success" ).lastModified();
+ File cwd = new File(args[1]);
+ boolean loop = args[2].equals("0");
+
BuildStage1Result res = buildStage1(
- nailgunLauncherLastModified, start, cache, CBT_HOME, compatibilityTarget, classLoaderCache
+ nailgunLauncherLastModified, start, cache, CBT_HOME, compatibilityTarget, classLoaderCache, cwd, loop
);
try{
@@ -106,9 +113,9 @@ public class NailgunLauncher{
.classLoader
.loadClass("cbt.Stage1")
.getMethod(
- "run", String[].class, File.class, File.class, BuildStage1Result.class, Map.class
+ "run", String[].class, File.class, File.class, boolean.class, BuildStage1Result.class, Map.class
).invoke(
- null, (Object) args, new File(cache), new File(CBT_HOME), res, classLoaderCache.hashMap
+ null, (Object) args, new File(cache), new File(CBT_HOME), loop, res, classLoaderCache.hashMap
);
System.exit( exitCode );
@@ -132,7 +139,7 @@ public class NailgunLauncher{
public static BuildStage1Result buildStage1(
final long lastModified, final long start, final String cache, final String cbtHome,
- final String compatibilityTarget, final ClassLoaderCache classLoaderCache
+ final String compatibilityTarget, final ClassLoaderCache classLoaderCache, File cwd, boolean loop
) throws Throwable {
_assert(TARGET != null, "environment variable TARGET not defined");
String nailgunSources = cbtHome + "/" + NAILGUN;
@@ -142,6 +149,10 @@ public class NailgunLauncher{
File compatibilitySources = new File(cbtHome + "/compatibility");
String mavenCache = cache + "maven";
String mavenUrl = "https://repo1.maven.org/maven2";
+ File loopFile = new File(cwd + "/target/.cbt-loop.tmp");
+ if(loop){
+ loopFile.getParentFile().mkdirs();
+ }
ClassLoader rootClassLoader = new CbtURLClassLoader( new URL[]{}, ClassLoader.getSystemClassLoader().getParent() ); // wrap for caching
EarlyDependencies earlyDeps = new EarlyDependencies(mavenCache, mavenUrl, classLoaderCache, rootClassLoader);
@@ -166,7 +177,16 @@ public class NailgunLauncher{
}
}
- compatibilityLastModified = compile( 0L, "", compatibilityTarget, earlyDeps, compatibilitySourceFiles);
+ if(loop){
+ File[] _compatibilitySourceFiles = new File[compatibilitySourceFiles.size()];
+ compatibilitySourceFiles.toArray(_compatibilitySourceFiles);
+ write(
+ loopFile.toPath(),
+ (mkString( "\n", _compatibilitySourceFiles ) + "\n").getBytes(),
+ StandardOpenOption.CREATE
+ );
+ }
+ compatibilityLastModified = compile( 0L, "", compatibilityTarget, earlyDeps, compatibilitySourceFiles) ;
if( !classLoaderCache.containsKey( compatibilityTarget, compatibilityLastModified ) ){
classLoaderCache.put( compatibilityTarget, classLoader(compatibilityTarget, rootClassLoader), compatibilityLastModified );
@@ -197,6 +217,17 @@ public class NailgunLauncher{
lastModified,
Math.max( lastModified, compatibilityLastModified )
);
+
+ if(loop){
+ File[] _stage1SourceFiles = new File[stage1SourceFiles.size()];
+ stage1SourceFiles.toArray(_stage1SourceFiles);
+ write(
+ loopFile.toPath(),
+ (mkString( "\n", _stage1SourceFiles ) + "\n").getBytes(),
+ StandardOpenOption.APPEND
+ );
+ }
+
final long stage1LastModified = compile(
stage0LastModified, stage1Classpath, stage1Target, earlyDeps, stage1SourceFiles
);
diff --git a/stage1/ContextImplementation.scala b/stage1/ContextImplementation.scala
index 90d9d5f..6762cb8 100644
--- a/stage1/ContextImplementation.scala
+++ b/stage1/ContextImplementation.scala
@@ -16,7 +16,7 @@ class ContextImplementation(
override val cbtRootHome: File,
override val compatibilityTarget: File,
override val parentBuildOrNull: BuildInterface,
- override val triggerLoopFilesArray: Array[File]
+ override val loop: Boolean
) extends Context{
@deprecated("this method is replaced by workingDirectory","")
def projectDirectory = workingDirectory
diff --git a/stage1/Stage1.scala b/stage1/Stage1.scala
index 48d0ae9..e419e67 100644
--- a/stage1/Stage1.scala
+++ b/stage1/Stage1.scala
@@ -42,7 +42,8 @@ class Stage2Args(
val cache: File,
val cbtHome: File,
val compatibilityTarget: File,
- val stage2sourceFiles: Seq[File]
+ val stage2sourceFiles: Seq[File],
+ val loop: Boolean
)(
implicit val transientCache: java.util.Map[AnyRef,AnyRef], val classLoaderCache: ClassLoaderCache, val logger: Logger
){
@@ -60,7 +61,9 @@ object Stage1{
val (_, cbtLastModified, classLoader) = buildStage2(
buildStage1,
context.cbtHome,
- context.cache
+ context.cache,
+ context.cwd,
+ context.loop
)(context.transientCache, new ClassLoaderCache( context.persistentCache ), logger)
classLoader
@@ -75,7 +78,7 @@ object Stage1{
}
def buildStage2(
- buildStage1: BuildStage1Result, cbtHome: File, cache: File
+ buildStage1: BuildStage1Result, cbtHome: File, cache: File, cwd: File, loop: Boolean
)(
implicit transientCache: java.util.Map[AnyRef,AnyRef], classLoaderCache: ClassLoaderCache, logger: Logger
): (Seq[File], Long, ClassLoader) = {
@@ -99,6 +102,8 @@ object Stage1{
)
logger.stage1("Compiling stage2 if necessary")
+ if(loop)
+ lib.addLoopFiles( cwd, stage2sourceFiles.toSet )
val Some( stage2LastModified ) = compile(
buildStage1.stage1LastModified,
stage2sourceFiles, stage2Target, stage2StatusFile,
@@ -158,6 +163,7 @@ object Stage1{
_args: Array[String],
cache: File,
cbtHome: File,
+ loop: Boolean,
buildStage1: BuildStage1Result,
persistentCache: java.util.Map[AnyRef,AnyRef]
): Int = {
@@ -168,17 +174,19 @@ object Stage1{
implicit val transientCache: java.util.Map[AnyRef,AnyRef] = new java.util.HashMap
implicit val classLoaderCache = new ClassLoaderCache( persistentCache )
- val (stage2sourceFiles, stage2LastModified, classLoader) = buildStage2( buildStage1, cbtHome, cache )
+ val cwd = new File( args.args(0) )
+ val (stage2sourceFiles, stage2LastModified, classLoader) = buildStage2( buildStage1, cbtHome, cache, cwd, loop )
val stage2Args = new Stage2Args(
- new File( args.args(0) ),
- args.args.drop(1).toVector,
+ cwd,
+ args.args.drop(2).toVector,
// launcher changes cause entire nailgun restart, so no need for them here
stage2LastModified = stage2LastModified,
cache,
cbtHome,
new File(buildStage1.compatibilityClasspath),
- stage2sourceFiles
+ stage2sourceFiles,
+ loop
)
logger.stage1(s"Run Stage2")
diff --git a/stage1/Stage1Lib.scala b/stage1/Stage1Lib.scala
index d99354c..1cda9fd 100644
--- a/stage1/Stage1Lib.scala
+++ b/stage1/Stage1Lib.scala
@@ -204,7 +204,6 @@ class Stage1Lib( logger: Logger ) extends BaseLib{
}
}
-
def compile(
cbtLastModified: Long,
sourceFiles: Seq[File],
@@ -475,6 +474,13 @@ ${sourceFiles.sorted.mkString(" \\\n")}
cache.get( cp, lastModified )
}
+ def addLoopFiles(cwd: File, files: Set[File]) = {
+ lib.write(
+ cwd / "target/.cbt-loop.tmp",
+ files.map(_ + "\n").mkString,
+ StandardOpenOption.APPEND
+ )
+ }
}
import scala.reflect._
diff --git a/stage1/cbt.scala b/stage1/cbt.scala
index 05737d0..cb6cb10 100644
--- a/stage1/cbt.scala
+++ b/stage1/cbt.scala
@@ -84,13 +84,6 @@ object `package`{
}
}
}
- implicit class BuildInterfaceExtensions(build: BuildInterface){
- import build._
- // TODO: if every build has a method triggers a callback if files change
- // then we wouldn't need this and could provide this method from a
- // plugin rather than hard-coding trigger files stuff in cbt
- def triggerLoopFiles: Set[File] = triggerLoopFilesArray.to
- }
implicit class ArtifactInfoExtensions(subject: ArtifactInfo){
import subject._
def str = s"$groupId:$artifactId:$version"
@@ -121,9 +114,6 @@ object `package`{
def scalaVersion = Option(scalaVersionOrNull)
def parentBuild = Option(parentBuildOrNull)
def cbtLastModified: scala.Long = subject.cbtLastModified
- def triggerLoopFiles: Set[File] = triggerLoopFilesArray.toSet[File]
-
- private[cbt] def loopFile = cwd / "target/.cbt-loop.tmp"
def copy(
workingDirectory: File = workingDirectory,
@@ -133,7 +123,9 @@ object `package`{
scalaVersion: Option[String] = scalaVersion,
cbtHome: File = cbtHome,
parentBuild: Option[BuildInterface] = None,
- triggerLoopFiles: Set[File] = Set()
+ transientCache: java.util.Map[AnyRef,AnyRef] = transientCache,
+ persistentCache: java.util.Map[AnyRef,AnyRef] = persistentCache,
+ loop: Boolean = loop
): Context = new ContextImplementation(
workingDirectory,
cwd,
@@ -149,7 +141,7 @@ object `package`{
cbtRootHome,
compatibilityTarget,
parentBuild.getOrElse(null),
- (triggerLoopFiles ++ triggerLoopFilesArray.toSet[File]).toArray
+ loop
)
}
}
diff --git a/stage1/resolver.scala b/stage1/resolver.scala
index 1304f76..b40fb7b 100644
--- a/stage1/resolver.scala
+++ b/stage1/resolver.scala
@@ -29,7 +29,6 @@ trait DependencyImplementation extends Dependency{
@deprecated("this method was replaced by dependenciesArray","")
def dependencyClasspathArray = dependencyClasspath.files.toArray
-
/*
//private type BuildCache = KeyLockedLazyCache[Dependency, Future[ClassPath]]
def exportClasspathConcurrently: ClassPath = {
@@ -195,7 +194,6 @@ case class CbtDependencies(mavenCache: File, nailgunTarget: File, stage1Target:
stage2Target,
stage1Dependency +:
MavenResolver(cbtLastModified, mavenCache,mavenCentral).bind(
- MavenDependency("net.incongru.watchservice","barbary-watchservice","1.0"),
MavenDependency("org.eclipse.jgit", "org.eclipse.jgit", "4.2.0.201601211800-r")
)
)
diff --git a/stage2/BasicBuild.scala b/stage2/BasicBuild.scala
index 1308155..37b1786 100644
--- a/stage2/BasicBuild.scala
+++ b/stage2/BasicBuild.scala
@@ -5,7 +5,7 @@ import java.net._
import java.nio.file._
class BasicBuild(final val context: Context) extends BaseBuild
-trait BaseBuild extends BuildInterface with DependencyImplementation with TriggerLoop with SbtDependencyDsl{
+trait BaseBuild extends BuildInterface with DependencyImplementation with SbtDependencyDsl{
//* DO NOT OVERRIDE CONTEXT in non-idempotent ways, because .copy and new Build
// will create new instances given the context, which means operations in the
// overrides will happen multiple times and if they are not idempotent stuff likely breaks
@@ -127,12 +127,6 @@ trait BaseBuild extends BuildInterface with DependencyImplementation with Trigge
pathToNestedBuild: _*
)
- def triggerLoopFiles: Set[File] = (
- context.triggerLoopFiles
- ++ sources
- ++ transitiveDependencies.collect{ case b: TriggerLoop => b.triggerLoopFiles }.flatten
- )
-
def localJars: Seq[File] =
Seq(projectDirectory ++ "/lib")
.filter(_.exists)
@@ -164,6 +158,14 @@ trait BaseBuild extends BuildInterface with DependencyImplementation with Trigge
final def lastModified: Long = compile.getOrElse(0L)
+ def triggerLoopFiles: Set[File] = sources.toSet
+
+ if(context.loop){
+ taskCache[BasicBuild]( "loop-file-cache" ).memoize{
+ lib.addLoopFiles( context.cwd, triggerLoopFiles )
+ }
+ }
+
def compile: Option[Long] = taskCache[BaseBuild]("_compile").memoize{
lib.compile(
Math.max( context.cbtLastModified, context.parentBuild.map(_.lastModified).getOrElse(0L) ),
@@ -325,17 +327,4 @@ trait BaseBuild extends BuildInterface with DependencyImplementation with Trigge
final def crossScalaVersionsArray = Array(scalaVersion)
def publish: Seq[URL] = Seq()
-
- def loop = {
- lib.callReflective(this, context.args.headOption, context.copy(args=context.args.drop(1)))
- val files = triggerLoopFiles
- lib.watch{ () =>
- logger.loop("Looping change detection over:\n - "++files.mkString("\n - "))
- files
- }()
- context.loopFile.getParentFile.mkdirs
- lib.write( context.loopFile, files.mkString("\n"), StandardOpenOption.CREATE )
-
- ExitCode(253) // signal bash script to restart
- }
}
diff --git a/stage2/BuildBuild.scala b/stage2/BuildBuild.scala
index 9811db5..7be4714 100644
--- a/stage2/BuildBuild.scala
+++ b/stage2/BuildBuild.scala
@@ -37,8 +37,7 @@ trait BuildBuildWithoutEssentials extends BaseBuild{
protected def managedContext = context.copy(
workingDirectory = managedBuildDirectory,
- parentBuild=Some(this),
- triggerLoopFiles = triggerLoopFiles
+ parentBuild=Some(this)
)
override def dependencies =
diff --git a/stage2/BuildDependency.scala b/stage2/BuildDependency.scala
index 9e35c41..bd3ed9e 100644
--- a/stage2/BuildDependency.scala
+++ b/stage2/BuildDependency.scala
@@ -10,10 +10,6 @@ sealed abstract class ProjectProxy extends Ha{
def dependencies = Seq(delegate)
}
*/
-trait TriggerLoop extends DependencyImplementation{
- final def triggerLoopFilesArray = triggerLoopFiles.toArray
- def triggerLoopFiles: Set[File]
-}
/** You likely want to use the factory method in the BasicBuild class instead of this. */
object DirectoryDependency{
def apply(context: Context, pathToNestedBuild: String*): BuildInterface = {
@@ -52,7 +48,6 @@ object DirectoryDependency{
/*
case class DependencyOr(first: DirectoryDependency, second: JavaDependency) extends ProjectProxy with DirectoryDependencyBase{
val isFirst = new File(first.projectDirectory).exists
- def triggerLoopFiles = if(isFirst) first.triggerLoopFiles else Set()
protected val delegate = if(isFirst) first else second
}
*/
diff --git a/stage2/GitDependency.scala b/stage2/GitDependency.scala
index f6812e4..20e1d36 100644
--- a/stage2/GitDependency.scala
+++ b/stage2/GitDependency.scala
@@ -26,7 +26,8 @@ object GitDependency{
val c = taskCache[Dependency]("checkout").memoize{ checkout( url, ref ) }
DirectoryDependency(
context.copy(
- workingDirectory = subDirectory.map(c / _).getOrElse(c)
+ workingDirectory = subDirectory.map(c / _).getOrElse(c),
+ loop = false
),
pathToNestedBuild: _*
)
diff --git a/stage2/Lib.scala b/stage2/Lib.scala
index 5e35ea7..ceea004 100644
--- a/stage2/Lib.scala
+++ b/stage2/Lib.scala
@@ -151,6 +151,7 @@ final class Lib(val logger: Logger) extends Stage1Lib(logger){
case c: ClassPath => c.string
case ExitCode(int) => System.err.println(int); System.exit(int); ???
case s: Seq[_] => s.map(render).mkString("\n")
+ case s: Set[_] => s.map(render).toSeq.sorted.mkString("\n")
case _ => obj.toString
}
}
@@ -478,54 +479,6 @@ final class Lib(val logger: Logger) extends Stage1Lib(logger){
url
}
-
- // code for continuous compile
- def watch[T]( files: () => Set[File] )(
- action: Seq[File] => Option[T] = (f: Seq[File]) => Some(f)
- ): T = {
- import com.barbarysoftware.watchservice._
- import scala.collection.JavaConversions._
- Iterator.continually{
- val watcher = WatchService.newWatchService
- val realFiles = files().map(realpath)
- realFiles.map{
- // WatchService can only watch folders
- case file if file.isFile => dirname(file)
- case file => file
- }.map{ file =>
- val watchableFile = new WatchableFile(file)
- val key = watchableFile.register(
- watcher,
- StandardWatchEventKind.ENTRY_CREATE,
- StandardWatchEventKind.ENTRY_DELETE,
- StandardWatchEventKind.ENTRY_MODIFY
- )
- }
- Option( watcher.take ) -> realFiles
- }.collect{
- case (Some(key),f) => key -> f
- }.map{ case (key, realFiles) =>
- logger.loop("Waiting for file changes...")
- val changedFiles = key
- .pollEvents
- .toVector
- .filterNot(_.kind == StandardWatchEventKind.OVERFLOW)
- .map(_.context.toString)
- // make sure we don't react on other files changed
- // in the same folder like the files we care about
- .filter{ name => realFiles.exists(name startsWith _.toString) }
- .map(new File(_))
-
- changedFiles.foreach( f => logger.loop( "Changed: " ++ f.toString ) )
- val res = action(changedFiles)
- key.reset
- res
- }.filterNot(_.isEmpty)
- .take(1)
- .toList
- .head.get
- }
-
def findInnerMostModuleDirectory(directory: File): File = {
val buildDir = realpath( directory ++ ("/" ++ lib.buildDirectoryName) )
// do not appent buildFileName here, so that we detect empty build folders
diff --git a/stage2/Stage2.scala b/stage2/Stage2.scala
index 33a67ac..eaf776e 100644
--- a/stage2/Stage2.scala
+++ b/stage2/Stage2.scala
@@ -32,40 +32,11 @@ object Stage2 extends Stage2Base{
args.cbtHome,
args.compatibilityTarget,
null,
- NailgunLauncher.compatibilitySourceFiles.asScala.toArray[File]
- ++ NailgunLauncher.nailgunLauncherSourceFiles.asScala.toArray[File]
- ++ NailgunLauncher.stage1SourceFiles.asScala.toArray[File]
- ++ args.stage2sourceFiles.toArray[File]
+ args.loop
)
- def loop( code: ExitCode, files: () => Set[File] ): ExitCode = {
- code match {
- case c@ExitCode(253 | 130) => c // CBT signals loop | user pressed ctrl+C
- case c if !task.contains("loop") => c
- case c =>
- // this allows looping over broken builds
- lib.watch{ files }()
- c
- }
- }
- val code = lib.trapExitCode{
- val first = lib.loadRoot( context )
- val build = first.finalBuild( context.cwd )
- val code = lib.callReflective(build, task, context)
- if( !context.loopFile.exists ){
- loop( code, () => build.triggerLoopFiles )
- }
- code
- }
- if( context.loopFile.exists ){
- loop(
- code,
- () => {
- val files = context.loopFile.readAsString.split("\n").map(new File(_)).toSet
- logger.loop("Looping change detection over:\n - "++files.mkString("\n - "))
- files
- }
- )
- }
+ val first = lib.loadRoot( context )
+ val build = first.finalBuild( context.cwd )
+ val code = lib.callReflective(build, task, context)
logger.stage2(s"Stage2 end with exit code "+code.integer)
code
}
diff --git a/test/test.scala b/test/test.scala
index bbecd94..c49d1e5 100644
--- a/test/test.scala
+++ b/test/test.scala
@@ -144,7 +144,7 @@ object Main{
cbtHome,
cbtHome ++ "/compatibilityTarget",
null,
- Array()
+ false
)
val b = new BasicBuild(noContext){