aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan Christopher Vogt <oss.nsp@cvogt.org>2017-02-19 11:34:43 +0800
committerGitHub <noreply@github.com>2017-02-19 11:34:43 +0800
commitcf1cf8678e7ad5366c9ac40451cfa9353bd8b7f3 (patch)
tree5e169d931f04c588bed876254c7c147825848563
parent86552d373be1b2fbf3e11d1ed223ebc4bdb6b280 (diff)
parentfbd06c94329655346d265574a7a28b103abdec93 (diff)
downloadcbt-cf1cf8678e7ad5366c9ac40451cfa9353bd8b7f3.tar.gz
cbt-cf1cf8678e7ad5366c9ac40451cfa9353bd8b7f3.tar.bz2
cbt-cf1cf8678e7ad5366c9ac40451cfa9353bd8b7f3.zip
Merge pull request #348 from cvogt/composing-sub-builds
Composing sub builds
-rw-r--r--compatibility/BuildInterface.java8
-rw-r--r--examples/multi-combined-example/build/build.scala67
-rw-r--r--examples/multi-combined-example/build/build/build.scala8
-rw-r--r--examples/multi-combined-example/shared/SomeSharedClass.scala2
-rw-r--r--examples/multi-combined-example/sub1/SomeConcreteClass.scala2
-rw-r--r--examples/multi-combined-example/sub2/SomeOtherConcreteClass.scala7
-rw-r--r--examples/multi-combined-example/sub3/Main.scala6
-rw-r--r--examples/multi-combined-example/sub4/Class4.scala2
-rw-r--r--examples/multi-combined-example/sub4/build/build.scala13
-rw-r--r--examples/multi-combined-example/sub4/sub41/sub42/Sub4Example.scala2
-rw-r--r--examples/multi-combined-example/sub5/Main.scala6
-rw-r--r--examples/multi-combined-example/sub6/Sub6Example.scala2
-rw-r--r--plugins/essentials/CommandLineOverrides.scala16
-rw-r--r--stage1/ClassPath.scala3
-rw-r--r--stage2/BasicBuild.scala21
-rw-r--r--stage2/BuildBuild.scala9
-rw-r--r--stage2/BuildDependency.scala31
-rw-r--r--stage2/GitDependency.scala14
-rw-r--r--stage2/Lib.scala100
-rw-r--r--stage2/Stage2.scala14
-rw-r--r--stage2/ToolsStage2.scala5
-rw-r--r--test/test.scala10
22 files changed, 221 insertions, 127 deletions
diff --git a/compatibility/BuildInterface.java b/compatibility/BuildInterface.java
index ebcfb99..eb60960 100644
--- a/compatibility/BuildInterface.java
+++ b/compatibility/BuildInterface.java
@@ -1,11 +1,15 @@
package cbt;
import java.io.*;
-public abstract class BuildInterface implements Dependency{
- public abstract BuildInterface finalBuild(); // needed to propagage through build builds. Maybe we can get rid of this.
+public interface BuildInterface extends Dependency{
+ // needed to propagage through build builds. Maybe we can get rid of this.
+ public default BuildInterface finalBuild(File current){
+ return finalBuild(); // legacy forwarder
+ }
public abstract File[] triggerLoopFilesArray(); // needed for watching files across composed builds
// 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.
public abstract BuildInterface copy(Context context);
public abstract String scalaVersion();
public abstract String[] crossScalaVersionsArray();
diff --git a/examples/multi-combined-example/build/build.scala b/examples/multi-combined-example/build/build.scala
index 7d1ff9f..bdeacc7 100644
--- a/examples/multi-combined-example/build/build.scala
+++ b/examples/multi-combined-example/build/build.scala
@@ -1,44 +1,69 @@
+package cbt_build.cbt_examples.multi_combined_example
import cbt._
-import cbt._
-trait SharedCbtBuild extends BaseBuild{
+
+trait ScalaVersion extends BaseBuild{
override def defaultScalaVersion = "2.10.6"
}
+trait SharedBuild extends ScalaVersion{
+ override def dependencies: Seq[Dependency] = Seq( new SharedDependency(context) )
+ // Type-safe embedding of other build. Requires dependency in build/build/build.scala
+ def sub4 = new cbt_build.cbt_examples.multi_combined_example.sub4.Build(
+ // currently you'll have to provide the correct working directory for that build here
+ context.copy( workingDirectory = projectDirectory / "sub4" )
+ )
+ def sub6 = DirectoryDependency(context.workingDirectory / "sub6")
+}
-class Shared(val context: Context) extends SharedCbtBuild
+class SharedDependency(val context: Context) extends ScalaVersion{
+ override def projectDirectory = context.workingDirectory / "shared"
+}
-class Sub(val context:Context) extends SharedCbtBuild{
- override def dependencies = Seq(new Shared(
- context.copy(
- workingDirectory = projectDirectory ++ "/../shared"
- )
- ))
+class Sub1(val context: Context) extends SharedBuild{
+ override def projectDirectory = context.workingDirectory / "sub1"
}
-class Build(val context: Context) extends BaseBuild{
+class Sub2(val context: Context) extends SharedBuild{
+ override def projectDirectory = context.workingDirectory / "sub2"
+ override def dependencies = super.dependencies ++ Seq( sub6 )
+}
+
+class Sub3(val context: Context) extends SharedBuild{
+ override def projectDirectory = context.workingDirectory / "sub3"
+ override def dependencies = Seq(
+ // Embed another sub build reflectively. Convenient for simple dependencies
+ DirectoryDependency(context.workingDirectory / "sub4", "sub41", "sub42")
+ )
+}
+
+class Sub5(val context: Context) extends SharedBuild{
+ override def projectDirectory = context.workingDirectory / "sub5"
+ override def dependencies = Seq( sub4.sub41.sub42 )
+}
+
+class Build(val context: Context) extends SharedBuild{
/*
Currently each sub build nested into the main build needs to be an instance
of a top-level class taking a Context as the sole parameter, similar to the
Build class itself. This restriction may be lifted for more flexibility at
some point, see https://github.com/cvogt/cbt/issues/306
*/
- def sub1 = new Sub(
- context.copy(
- workingDirectory = projectDirectory ++ "/sub1"
- )
- )
- def sub2 = new Sub(
- context.copy(
- workingDirectory = projectDirectory ++ "/sub2"
- )
- )
+ def sub1 = new Sub1(context)
+ def sub2 = new Sub2(context)
+ def sub3 = new Sub3(context)
+ def sub5 = new Sub3(context)
+
+ def helloFromSub42 = sub4.sub41.sub42.hello
- def sub3 = // DON'T DO THIS, anonymous classes are currently not supported here.
+ /*
+ // DON'T DO THIS, anonymous classes are currently not supported here.
+ def sub3 =
new SharedCbtBuild{
def context = Build.this.context.copy(
workingDirectory = Build.this.projectDirectory ++ "/sub3"
)
}
+ */
override def dependencies = Seq( sub1, sub2 ) // assembles all projects
}
diff --git a/examples/multi-combined-example/build/build/build.scala b/examples/multi-combined-example/build/build/build.scala
new file mode 100644
index 0000000..b87351d
--- /dev/null
+++ b/examples/multi-combined-example/build/build/build.scala
@@ -0,0 +1,8 @@
+package cbt_build.cbt_examples.multi_combined_example.build
+import cbt._
+class Build(val context: Context) extends BuildBuild{
+ //println(DirectoryDependency( projectDirectory / ".." / "sub4" / "build" ).dependency.exportedClasspath)
+ override def dependencies: Seq[cbt.Dependency] =
+ super.dependencies :+ DirectoryDependency( projectDirectory / ".." / "sub4" / "build" ).dependency
+ def foo = DirectoryDependency( projectDirectory / ".." / "sub4" / "build" )
+}
diff --git a/examples/multi-combined-example/shared/SomeSharedClass.scala b/examples/multi-combined-example/shared/SomeSharedClass.scala
index d7d99e4..339d45d 100644
--- a/examples/multi-combined-example/shared/SomeSharedClass.scala
+++ b/examples/multi-combined-example/shared/SomeSharedClass.scala
@@ -1,4 +1,4 @@
-package cbt.examples.multi_combined
+package cbt_examples.multi_combined
class SomeSharedClass{
def main(args: Array[String]): Unit = {
println(this.getClass.getSimpleName)
diff --git a/examples/multi-combined-example/sub1/SomeConcreteClass.scala b/examples/multi-combined-example/sub1/SomeConcreteClass.scala
index e55cb12..8039e13 100644
--- a/examples/multi-combined-example/sub1/SomeConcreteClass.scala
+++ b/examples/multi-combined-example/sub1/SomeConcreteClass.scala
@@ -1,2 +1,2 @@
-package cbt.examples.multi_combined
+package cbt_examples.multi_combined
object SomeConcreteClass extends SomeSharedClass
diff --git a/examples/multi-combined-example/sub2/SomeOtherConcreteClass.scala b/examples/multi-combined-example/sub2/SomeOtherConcreteClass.scala
index 399aee6..e339e24 100644
--- a/examples/multi-combined-example/sub2/SomeOtherConcreteClass.scala
+++ b/examples/multi-combined-example/sub2/SomeOtherConcreteClass.scala
@@ -1,2 +1,5 @@
-package cbt.examples.multi_combined
-object SomeOtherConcreteClass extends SomeSharedClass
+package cbt_examples.multi_combined
+object SomeOtherConcreteClass extends SomeSharedClass{
+ new cbt_examples.multi_combined_example.sub6.Sub6Example
+}
+
diff --git a/examples/multi-combined-example/sub3/Main.scala b/examples/multi-combined-example/sub3/Main.scala
new file mode 100644
index 0000000..00f8f77
--- /dev/null
+++ b/examples/multi-combined-example/sub3/Main.scala
@@ -0,0 +1,6 @@
+package cbt_examples.multi_combined_example.sub3
+object Main{
+ def main( args: Array[String] ): Unit = {
+ println( Console.GREEN ++ "Hello " ++ Console.RESET + new cbt_examples.multi_combined_example.sub4.sub41.sub42.Sub4Example )
+ }
+}
diff --git a/examples/multi-combined-example/sub4/Class4.scala b/examples/multi-combined-example/sub4/Class4.scala
new file mode 100644
index 0000000..d937122
--- /dev/null
+++ b/examples/multi-combined-example/sub4/Class4.scala
@@ -0,0 +1,2 @@
+package cbt_examples.multi_combined_example.sub4
+class Sub4Example
diff --git a/examples/multi-combined-example/sub4/build/build.scala b/examples/multi-combined-example/sub4/build/build.scala
new file mode 100644
index 0000000..df5b689
--- /dev/null
+++ b/examples/multi-combined-example/sub4/build/build.scala
@@ -0,0 +1,13 @@
+package cbt_build.cbt_examples.multi_combined_example.sub4
+import cbt._
+class Build(val context: Context) extends BaseBuild{
+ def sub41 = new Sub41(context)
+}
+class Sub41(val context: Context) extends BaseBuild{
+ override def projectDirectory = context.workingDirectory / "sub41"
+ def sub42 = new Sub42(context.copy(workingDirectory=projectDirectory))
+}
+class Sub42(val context: Context) extends BaseBuild{
+ override def projectDirectory = context.workingDirectory / "sub42"
+ def hello = "Hello from Sub42"
+}
diff --git a/examples/multi-combined-example/sub4/sub41/sub42/Sub4Example.scala b/examples/multi-combined-example/sub4/sub41/sub42/Sub4Example.scala
new file mode 100644
index 0000000..a4ea402
--- /dev/null
+++ b/examples/multi-combined-example/sub4/sub41/sub42/Sub4Example.scala
@@ -0,0 +1,2 @@
+package cbt_examples.multi_combined_example.sub4.sub41.sub42
+class Sub4Example
diff --git a/examples/multi-combined-example/sub5/Main.scala b/examples/multi-combined-example/sub5/Main.scala
new file mode 100644
index 0000000..6035cc2
--- /dev/null
+++ b/examples/multi-combined-example/sub5/Main.scala
@@ -0,0 +1,6 @@
+package cbt_examples.multi_combined_example.sub5
+object Main{
+ def main( args: Array[String] ): Unit = {
+ println( Console.GREEN ++ "Hello " ++ Console.RESET ++ new cbt.examples.multi_combined_example.sub4.sub41.sub42.Sub4Example )
+ }
+}
diff --git a/examples/multi-combined-example/sub6/Sub6Example.scala b/examples/multi-combined-example/sub6/Sub6Example.scala
new file mode 100644
index 0000000..bab5993
--- /dev/null
+++ b/examples/multi-combined-example/sub6/Sub6Example.scala
@@ -0,0 +1,2 @@
+package cbt_examples.multi_combined_example.sub6
+class Sub6Example
diff --git a/plugins/essentials/CommandLineOverrides.scala b/plugins/essentials/CommandLineOverrides.scala
index 32b8403..ce68aa9 100644
--- a/plugins/essentials/CommandLineOverrides.scala
+++ b/plugins/essentials/CommandLineOverrides.scala
@@ -1,25 +1,25 @@
package cbt
trait CommandLineOverrides extends DynamicOverrides{
def `with`: Any = {
- new lib.ReflectObject(
+ lib.callReflective(
newBuild[DynamicOverrides](
context.copy(
args = context.args.drop(2)
)
)( s"""
${context.args.lift(0).getOrElse("")}
- """ )
- ){
- def usage = ""
- }.callNullary(context.args.lift(1) orElse Some("void"))
+ """ ),
+ context.args.lift(1) orElse Some("void")
+ )
}
def eval = {
- new lib.ReflectObject(
+ lib.callReflective(
newBuild[CommandLineOverrides](
context.copy(
args = ( context.args.lift(0).map("println{ "+_+" }") ).toSeq
)
- ){""}
- ){def usage = ""}.callNullary(Some("with"))
+ ){""},
+ Some("with")
+ )
}
}
diff --git a/stage1/ClassPath.scala b/stage1/ClassPath.scala
index 6e6f113..d8568c2 100644
--- a/stage1/ClassPath.scala
+++ b/stage1/ClassPath.scala
@@ -16,7 +16,7 @@ case class ClassPath(files: Seq[File] = Seq()){
nonExisting.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)
@@ -24,5 +24,4 @@ case class ClassPath(files: Seq[File] = Seq()){
def strings = files.map{
f => f.string ++ ( if(f.isDirectory) "/" else "" )
}.sorted
- def toConsole = string
}
diff --git a/stage2/BasicBuild.scala b/stage2/BasicBuild.scala
index f32d4d9..5c49395 100644
--- a/stage2/BasicBuild.scala
+++ b/stage2/BasicBuild.scala
@@ -106,8 +106,9 @@ trait BaseBuild extends BuildInterface with DependencyImplementation with Trigge
scalaVersion: String = scalaMajorVersion
) = lib.ScalaDependency( groupId, artifactId, version, classifier, scalaVersion )
- final def DirectoryDependency(path: File) = cbt.DirectoryDependency(
- context.copy( workingDirectory = path, args = Seq() )
+ final def DirectoryDependency(path: File, pathToNestedBuild: String*) = cbt.DirectoryDependency(
+ context.copy( workingDirectory = path ),
+ pathToNestedBuild: _*
)
def triggerLoopFiles: Seq[File] = sources ++ transitiveDependencies.collect{ case b: TriggerLoop => b.triggerLoopFiles }.flatten
@@ -186,9 +187,10 @@ trait BaseBuild extends BuildInterface with DependencyImplementation with Trigge
def run: ExitCode = run( context.args: _* )
def test: Any =
- Some(new lib.ReflectBuild(
- DirectoryDependency(projectDirectory++"/test").build
- ).callNullary(Some("run")))
+ lib.callReflective(
+ DirectoryDependency(projectDirectory++"/test").dependency,
+ Some("run")
+ )
def t = test
def rt = recursiveUnsafe(Some("test"))
@@ -221,13 +223,13 @@ trait BaseBuild extends BuildInterface with DependencyImplementation with Trigge
recursiveUnsafe(context.args.lift(1))
}
- def recursiveUnsafe(taskName: Option[String]): ExitCode = {
+ def recursiveUnsafe(code: Option[String]): ExitCode = {
recursiveSafe{
b =>
System.err.println(b.show)
lib.trapExitCode{ // FIXME: trapExitCode does not seem to work here
try{
- new lib.ReflectBuild(b).callNullary(taskName)
+ lib.callReflective(b,code)
ExitCode.Success
} catch {
case e: Throwable => println(e.getClass); throw e
@@ -252,7 +254,12 @@ trait BaseBuild extends BuildInterface with DependencyImplementation with Trigge
*/
// ========== cbt internals ==========
+ @deprecated("use finalbuild(File)","")
def finalBuild: BuildInterface = this
+ override def finalBuild( current: File ): BuildInterface = {
+ //assert( current.getCanonicalFile == projectDirectory.getCanonicalFile, s"$current == $projectDirectory" )
+ this
+ }
override def show = this.getClass.getSimpleName ++ "(" ++ projectDirectory.string ++ ")"
// a method that can be called only to trigger any side-effects
diff --git a/stage2/BuildBuild.scala b/stage2/BuildBuild.scala
index 2eebcbc..9ac631f 100644
--- a/stage2/BuildBuild.scala
+++ b/stage2/BuildBuild.scala
@@ -1,5 +1,6 @@
package cbt
import java.nio.file._
+import java.io.File
trait BuildBuild extends BuildBuildWithoutEssentials{
override def dependencies =
@@ -97,5 +98,11 @@ trait BuildBuildWithoutEssentials extends BaseBuild{
}
}
override def triggerLoopFiles = super.triggerLoopFiles ++ managedBuild.triggerLoopFiles
- override def finalBuild: BuildInterface = if( projectDirectory == context.cwd ) this else managedBuild.finalBuild
+ @deprecated("use finalbuild(File)","")
+ override def finalBuild: BuildInterface = finalBuild( context.cwd )
+ override def finalBuild( current: File ): BuildInterface = {
+ val p = projectDirectory.getCanonicalFile
+ val c = current.getCanonicalFile
+ if( c == p ) this else managedBuild.finalBuild( current )
+ }
}
diff --git a/stage2/BuildDependency.scala b/stage2/BuildDependency.scala
index 56069c3..0162791 100644
--- a/stage2/BuildDependency.scala
+++ b/stage2/BuildDependency.scala
@@ -15,7 +15,7 @@ trait TriggerLoop extends DependencyImplementation{
def triggerLoopFiles: Seq[File]
}
/** You likely want to use the factory method in the BasicBuild class instead of this. */
-final case class DirectoryDependency(context: Context) extends TriggerLoop{
+final case class DirectoryDependency(context: Context, pathToNestedBuild: String*) extends TriggerLoop{
def classLoaderCache = context.classLoaderCache
override def toString = show
override def show = this.getClass.getSimpleName ++ "(" ++ context.workingDirectory.string ++ ")"
@@ -23,12 +23,33 @@ final case class DirectoryDependency(context: Context) extends TriggerLoop{
lazy val logger = context.logger
override lazy val lib: Lib = new Lib(logger)
def transientCache = context.transientCache
- private lazy val root = lib.loadRoot( context.copy(args=Seq()) )
- lazy val build = root.finalBuild
+ private lazy val root = lib.loadRoot( context )
+ lazy val dependency: Dependency = {
+ def selectNestedBuild( build: Dependency, names: Seq[String], previous: Seq[String] ): Dependency = {
+ names.headOption.map{ name =>
+ if( lib.taskNames(build.getClass).contains(name) ){
+ val method = build.getClass.getMethod(name)
+ val returnType = method.getReturnType
+ if( classOf[Dependency] isAssignableFrom returnType ){
+ selectNestedBuild(
+ method.invoke(build).asInstanceOf[Dependency],
+ names.tail,
+ previous :+ name
+ )
+ } else {
+ throw new RuntimeException( s"Expected subtype of Dependency, found $returnType for " + previous.mkString(".") + " in " + show )
+ }
+ } else {
+ throw new RuntimeException( (previous :+ name).mkString(".") + " not found in " + show )
+ }
+ }.getOrElse( build )
+ }
+ selectNestedBuild( root.finalBuild(context.workingDirectory), pathToNestedBuild, Nil )
+ }
def exportedClasspath = ClassPath()
- def dependencies = Seq(build)
+ def dependencies = Seq(dependency)
def triggerLoopFiles = root.triggerLoopFiles
- def lastModified = build.lastModified
+ def lastModified = dependency.lastModified
def targetClasspath = ClassPath()
}
/*
diff --git a/stage2/GitDependency.scala b/stage2/GitDependency.scala
index ecd0ee1..f2ac7a6 100644
--- a/stage2/GitDependency.scala
+++ b/stage2/GitDependency.scala
@@ -11,12 +11,19 @@ object GitDependency{
val GitUrl = "(git:|https:|file:/)//([^/]+)/(.+)".r
}
case class GitDependency(
- url: String, ref: String, subDirectory: Option[String] = None // example: git://github.com/cvogt/cbt.git#<some-hash>
+ url: String, ref: String, subDirectory: Option[String] = None, // example: git://github.com/cvogt/cbt.git#<some-hash>
+ pathToNestedBuild: Seq[String] = Seq()
)(implicit val logger: Logger, classLoaderCache: ClassLoaderCache, context: Context ) extends DependencyImplementation{
import GitDependency._
override def lib = new Lib(logger)
def classLoaderCache = context.classLoaderCache
- def moduleKey = this.getClass.getName ++ "(" ++ url ++ subDirectory.map("/" ++ _).getOrElse("") ++ "#" ++ ref ++ ")"
+ def moduleKey = (
+ this.getClass.getName
+ ++ "(" ++ url ++ subDirectory.map("/" ++ _).getOrElse("") ++ "#" ++ ref
+ ++ ", "
+ ++ pathToNestedBuild.mkString(", ")
+ ++ ")"
+ )
def transientCache = context.transientCache
// TODO: add support for authentication via ssh and/or https
// See http://www.codeaffine.com/2014/12/09/jgit-authentication/
@@ -69,7 +76,8 @@ case class GitDependency(
DirectoryDependency(
context.copy(
workingDirectory = checkout ++ subDirectory.map("/" ++ _).getOrElse("")
- )
+ ),
+ pathToNestedBuild: _*
)
}
diff --git a/stage2/Lib.scala b/stage2/Lib.scala
index b7d1686..9e1f824 100644
--- a/stage2/Lib.scala
+++ b/stage2/Lib.scala
@@ -108,7 +108,7 @@ final class Lib(val logger: Logger) extends Stage1Lib(logger){
}
// task reflection helpers
- def tasks(cls:Class[_]): Map[String, Method] =
+ def taskMethods(cls:Class[_]): Map[String, Method] =
Stream
.iterate(cls.asInstanceOf[Class[Any]])(_.getSuperclass)
.takeWhile(_ != null)
@@ -127,7 +127,7 @@ final class Lib(val logger: Logger) extends Stage1Lib(logger){
.map(m => NameTransformer.decode(m.getName) -> m)
).toMap
- def taskNames(cls: Class[_]): Seq[String] = tasks(cls).keys.toVector.sorted
+ def taskNames(cls: Class[_]): Seq[String] = taskMethods(cls).keys.toVector.sorted
def usage(buildClass: Class[_], show: String): String = {
val baseTasks = Seq(
@@ -151,65 +151,53 @@ final class Lib(val logger: Logger) extends Stage1Lib(logger){
) ++ "\n"
}
- class ReflectBuild[T:scala.reflect.ClassTag](build: BuildInterface) extends ReflectObject(build){
- def usage = lib.usage(build.getClass, build.show)
+ def callReflective[T <: AnyRef]( obj: T, code: Option[String] ): ExitCode = {
+ callInternal( obj, code.toSeq.flatMap(_.split("\\.").map( NameTransformer.encode )), Nil ) match {
+ case (obj, code, None) =>
+ val s = render(obj)
+ if(s.nonEmpty)
+ System.out.println(s)
+ code getOrElse ExitCode.Success
+ case (obj, code, Some(msg)) =>
+ if(msg.nonEmpty)
+ System.err.println(msg)
+ val s = render(obj)
+ if(s.nonEmpty)
+ System.err.println(s)
+ code getOrElse ExitCode.Failure
+ }
+ }
+
+ private def render[T]( obj: T ): String = {
+ obj match {
+ case Some(s) => render(s)
+ case None => ""
+ case d: Dependency => lib.usage(d.getClass, d.show())
+ case c: ClassPath => c.string
+ case t:ToolsStage2.type => "Available methods: " ++ lib.taskNames(t.getClass).mkString(" ")
+ case _ => obj.toString
+ }
}
- abstract class ReflectObject[T](obj: T){
- def usage: String
- def callNullary( taskName: Option[String] ): ExitCode = {
+
+ private def callInternal[T <: AnyRef]( obj: T, members: Seq[String], previous: Seq[String] ): (Option[Object], Option[ExitCode], Option[String]) = {
+ members.headOption.map{ taskName =>
logger.lib("Calling task " ++ taskName.toString)
- val ts = tasks(obj.getClass)
- taskName.map( NameTransformer.encode ).flatMap(ts.get).map{ method =>
- val result: Option[Any] = Option(method.invoke(obj)) // null in case of Unit
- result.flatMap{
- case v: Option[_] => v
- case other => Some(other)
- }.map{
- value =>
- // Try to render console representation. Probably not the best way to do this.
- scala.util.Try( value.getClass.getDeclaredMethod("toConsole") ) match {
- case scala.util.Success(toConsole) =>
- println(toConsole.invoke(value))
- ExitCode.Success
-
- case scala.util.Failure(e) if Option(e.getMessage).getOrElse("") contains "toConsole" =>
- value match {
- case code if code.getClass.getSimpleName == "ExitCode" =>
- // FIXME: ExitCode needs to be part of the compatibility interfaces
- ExitCode(Stage0Lib.get(code,"integer").asInstanceOf[Int])
- case b: BaseBuild =>
- val context = b.context.copy(args=b.context.args.drop(1))
- val task = b.context.args.lift(0)
- new ReflectBuild( b.copy(context=context) ).callNullary( task )
- case Seq(b: BaseBuild, bs @ _*) if bs.forall(_.isInstanceOf[BaseBuild]) =>
- (b +: bs)
- .map( _.asInstanceOf[BaseBuild] )
- .map{ b =>
- val task = b.context.args.lift(0)
- new ReflectBuild(
- b.copy( context = b.context.copy(args=b.context.args.drop(1)) )
- ).callNullary( task )
- }
- .head
- case other =>
- println( other.toString ) // no method .toConsole, using to String
- ExitCode.Success
- }
-
- case scala.util.Failure(e) =>
- throw e
+ taskMethods(obj.getClass).get(taskName).map{ method =>
+ Option(method.invoke(obj) /* null in case of Unit */ ).map{ result =>
+ result match {
+ case code if code.getClass.getSimpleName == "ExitCode" =>
+ // FIXME: ExitCode needs to be part of the compatibility interfaces
+ (None, Some(ExitCode(Stage0Lib.get(code,"integer").asInstanceOf[Int])), None)
+ case Seq(bs @ _*) if bs.forall(_.isInstanceOf[BaseBuild]) =>
+ bs.map( b => callInternal(b.asInstanceOf[BaseBuild], members.tail, previous :+ taskName) ).head
+ case _ => callInternal(result, members.tail, previous :+ taskName)
}
- }.getOrElse(ExitCode.Success)
+ }.getOrElse( (None, None, None) )
}.getOrElse{
- taskName.foreach{ n =>
- System.err.println(s"Method not found: $n")
- System.err.println("")
- }
- System.err.println(usage)
- taskName.map{ _ =>
- ExitCode.Failure
- }.getOrElse( ExitCode.Success )
+ ( Some(obj), None, Some("\nMethod not found: " ++ (previous :+ taskName).mkString(".") ++ "\n") )
}
+ }.getOrElse{
+ ( Some(obj), None, None )
}
}
diff --git a/stage2/Stage2.scala b/stage2/Stage2.scala
index 25cd0ae..7ac7a2a 100644
--- a/stage2/Stage2.scala
+++ b/stage2/Stage2.scala
@@ -4,7 +4,7 @@ import java.util._
object Stage2 extends Stage2Base{
def getBuild(context: Context) = {
- new Lib( context.logger ).loadRoot( context ).finalBuild
+ new Lib( context.logger ).loadRoot( context ).finalBuild( context.cwd )
}
def run( args: Stage2Args ): ExitCode = {
@@ -39,11 +39,7 @@ object Stage2 extends Stage2Base{
null
)
val first = lib.loadRoot( context )
- val build = first.finalBuild
-
- def call(build: BuildInterface): ExitCode = {
- new lib.ReflectBuild(build).callNullary(task)
- }
+ val build = first.finalBuild( context.cwd )
val res =
if (loop) {
@@ -61,13 +57,13 @@ object Stage2 extends Stage2Base{
scala.util.control.Breaks.break
case file if triggerFiles.exists(file.toString startsWith _.toString) =>
- val build = lib.loadRoot(context).finalBuild
+ val build = lib.loadRoot(context).finalBuild( context.cwd )
logger.loop(s"Re-running $task for " ++ build.show)
- call(build)
+ lib.callReflective(build, task)
}
ExitCode.Success
} else {
- val code = call(build)
+ val code = lib.callReflective(build, task)
logger.stage2(s"Stage2 end")
code
}
diff --git a/stage2/ToolsStage2.scala b/stage2/ToolsStage2.scala
index e1c4a8e..c71f2b7 100644
--- a/stage2/ToolsStage2.scala
+++ b/stage2/ToolsStage2.scala
@@ -5,9 +5,6 @@ object ToolsStage2 extends Stage2Base{
val args = _args.args.dropWhile(Seq("tools","direct") contains _)
val lib = new Lib(_args.logger)
val toolsTasks = new ToolsTasks(lib, args, _args.cwd, _args.cache, _args.cbtHome, _args.stage2LastModified)(_args.classLoaderCache)
- new lib.ReflectObject(toolsTasks){
- def usage: String = "Available methods: " ++ lib.taskNames(toolsTasks.getClass).mkString(" ")
- }.callNullary(args.lift(0))
- ExitCode.Success
+ lib.callReflective(toolsTasks, args.lift(0))
}
}
diff --git a/test/test.scala b/test/test.scala
index d8714c0..5359029 100644
--- a/test/test.scala
+++ b/test/test.scala
@@ -68,8 +68,8 @@ object Main{
logger.test(res.toString)
val debugToken = "usage " ++ path ++ " "
assertSuccess(res,debugToken)
- assert(res.out == "", debugToken ++ res.toString)
- assert(res.err contains usageString, debugToken ++ res.toString)
+ //assert(res.out == "", res.err.toString)
+ assert(res.out contains usageString, usageString + " not found in " ++ res.toString)
}
def compile(path: String)(implicit logger: Logger) = task("compile", path)
def task(name: String, path: String)(implicit logger: Logger) = {
@@ -85,11 +85,11 @@ object Main{
val debugToken = "\n"++lib.red("Deleting") ++ " " ++ (cbtHome ++("/test/"++path++"/target")).toPath.toAbsolutePath.toString++"\n"
val debugToken2 = "\n"++lib.red("Deleting") ++ " " ++ (cbtHome ++("/test/"++path)).toPath.toAbsolutePath.toString++"\n"
assertSuccess(res,debugToken)
- assert(res.out == "", debugToken ++ " " ++ res.toString)
- assert(res.err.contains(debugToken), debugToken ++ " " ++ res.toString)
+ assert(res.out == "", "should be empty: " + res.out)
+ assert(res.err.contains(debugToken), debugToken ++ " missing from " ++ res.err.toString)
assert(
!res.err.contains(debugToken2),
- "Tried to delete too much: " ++ debugToken2 ++ " " ++ res.toString
+ "Tried to delete too much: " ++ debugToken2 ++ " found in " ++ res.err.toString
)
res.err.split("\n").filter(_.startsWith(lib.red("Deleting"))).foreach{ line =>
assert(