aboutsummaryrefslogtreecommitdiff
path: root/stage2
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 /stage2
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
Diffstat (limited to 'stage2')
-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
7 files changed, 109 insertions, 85 deletions
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))
}
}