diff options
author | Jan Christopher Vogt <oss.nsp@cvogt.org> | 2017-02-19 11:34:43 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-02-19 11:34:43 +0800 |
commit | cf1cf8678e7ad5366c9ac40451cfa9353bd8b7f3 (patch) | |
tree | 5e169d931f04c588bed876254c7c147825848563 | |
parent | 86552d373be1b2fbf3e11d1ed223ebc4bdb6b280 (diff) | |
parent | fbd06c94329655346d265574a7a28b103abdec93 (diff) | |
download | cbt-cf1cf8678e7ad5366c9ac40451cfa9353bd8b7f3.tar.gz cbt-cf1cf8678e7ad5366c9ac40451cfa9353bd8b7f3.tar.bz2 cbt-cf1cf8678e7ad5366c9ac40451cfa9353bd8b7f3.zip |
Merge pull request #348 from cvogt/composing-sub-builds
Composing sub builds
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( |