diff options
author | Christopher Vogt <oss.nsp@cvogt.org> | 2017-03-19 19:51:38 -0400 |
---|---|---|
committer | Christopher Vogt <oss.nsp@cvogt.org> | 2017-03-20 02:56:26 -0400 |
commit | d6245c8dc5c7b2f885d538b39f685327da252863 (patch) | |
tree | 846bdd92ad022dbe5a7a45e0b9d5e75bbf7779c8 /stage2 | |
parent | ca099eba708f3618bed75a5940a5a5ae1d10b684 (diff) | |
download | cbt-d6245c8dc5c7b2f885d538b39f685327da252863.tar.gz cbt-d6245c8dc5c7b2f885d538b39f685327da252863.tar.bz2 cbt-d6245c8dc5c7b2f885d538b39f685327da252863.zip |
Unify reflectively loading builds from directories.
THis is mostly cleanup and a little bit feature.
Before it was done partially in 3 places, BuildBuild,
loadRoot and GitDependency. Now DirectoryDependencies
also support referencing sub-builds.
Also introduce scalariform for the first few files
of cbt's core code :).
Diffstat (limited to 'stage2')
-rw-r--r-- | stage2/BasicBuild.scala | 15 | ||||
-rw-r--r-- | stage2/BuildBuild.scala | 90 | ||||
-rw-r--r-- | stage2/BuildDependency.scala | 53 | ||||
-rw-r--r-- | stage2/DirectoryDependency.scala | 124 | ||||
-rw-r--r-- | stage2/GitDependency.scala | 24 | ||||
-rw-r--r-- | stage2/LazyDependency.scala | 17 | ||||
-rw-r--r-- | stage2/Lib.scala | 145 | ||||
-rw-r--r-- | stage2/Stage2.scala | 11 |
8 files changed, 227 insertions, 252 deletions
diff --git a/stage2/BasicBuild.scala b/stage2/BasicBuild.scala index fcda629..4158040 100644 --- a/stage2/BasicBuild.scala +++ b/stage2/BasicBuild.scala @@ -122,11 +122,6 @@ trait BaseBuild extends BuildInterface with DependencyImplementation with SbtDep scalaVersion: String = scalaMajorVersion, verifyHash: Boolean = true ) = lib.ScalaDependency( groupId, artifactId, version, classifier, scalaVersion, verifyHash ) - final def DirectoryDependency(path: File, pathToNestedBuild: String*) = cbt.DirectoryDependency( - context.copy( workingDirectory = path ), - pathToNestedBuild: _* - ) - def localJars: Seq[File] = Seq(projectDirectory ++ "/lib") .filter(_.exists) @@ -220,8 +215,7 @@ trait BaseBuild extends BuildInterface with DependencyImplementation with SbtDep def test: Dependency = { val testDirectory = projectDirectory / "test" if( (testDirectory / lib.buildDirectoryName / lib.buildFileName).exists ){ - // FIYME: maybe we can make loadRoot(...).finalBuild an Option some - DirectoryDependency( testDirectory ) + DirectoryDependency( testDirectory ).dependency } else { new BasicBuild( context.copy(workingDirectory = testDirectory) ){ override def dependencies = Seq( @@ -292,13 +286,6 @@ trait BaseBuild extends BuildInterface with DependencyImplementation with SbtDep context.logger.composition("<"*80) */ - // ========== 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 ++ ")" override def toString = show diff --git a/stage2/BuildBuild.scala b/stage2/BuildBuild.scala index dea0f12..71a229c 100644 --- a/stage2/BuildBuild.scala +++ b/stage2/BuildBuild.scala @@ -5,11 +5,7 @@ import java.io.File class ConcreteBuildBuild(val context: Context) extends BuildBuild class plugins(implicit context: Context){ // TODO: move this out of the OO - private def plugin(dir: String) = cbt.DirectoryDependency( - context.copy( - workingDirectory = context.cbtHome / "plugins" / dir - ) - ) + private def plugin(dir: String) = cbt.DirectoryDependency(context.cbtHome / "plugins" / dir) final lazy val googleJavaFormat = plugin( "google-java-format" ) final lazy val proguard = plugin( "proguard" ) final lazy val sbtLayout = plugin( "sbt_layout" ) @@ -24,94 +20,14 @@ class plugins(implicit context: Context){ } trait BuildBuild extends BaseBuild{ + override def dependencies = super.dependencies :+ context.cbtDependency + object plugins extends plugins assert( projectDirectory.getName === lib.buildDirectoryName, s"You can't extend ${lib.buildBuildClassName} in: " + projectDirectory + "/" + lib.buildDirectoryName ) - - protected def managedContext = context.copy( - workingDirectory = managedBuildDirectory, - parentBuild=Some(this) - ) - - override def dependencies = - super.dependencies :+ context.cbtDependency - - def managedBuildDirectory: java.io.File = lib.realpath( projectDirectory.parent ) - def managedBuild = taskCache[BuildBuild]("managedBuild").memoize{ - val managedBuildFile = projectDirectory++("/"++lib.buildFileName) - logger.composition("Loading build at " ++ managedBuildDirectory.toString) - val build = ( - if( !managedBuildFile.exists ){ - throw new Exception( - s"No file ${lib.buildFileName} (lower case) found in " ++ projectDirectory.getPath - ) - } else { - val contents = new String(Files.readAllBytes(managedBuildFile.toPath)) - val cbtUrl = ("cbt:"++GitDependency.GitUrl.regex++"#[a-z0-9A-Z]+").r - cbtUrl - .findFirstIn(contents) - .flatMap{ - url => - val Array(base,hash) = url.drop(4).split("#") - if(context.cbtHome.string.contains(hash)) - None - else Some{ - // Note: cbt can't use an old version of itself for building, - // otherwise we'd have to recursively build all versions since - // the beginning. Instead CBT always needs to build the pure Java - // Launcher in the checkout with itself and then run it via reflection. - val build = GitDependency(base, hash, Some("nailgun_launcher")).asInstanceOf[BaseBuild] - val ctx = managedContext.copy( cbtHome = build.projectDirectory.getParentFile ) - build.classLoader - .loadClass( "cbt.NailgunLauncher" ) - .getMethod( "getBuild", classOf[AnyRef] ) - .invoke( null, ctx ) - } - }.getOrElse{ - val buildClasses = - lib.iterateClasses( compileTarget, classLoader, false ) - .filter(_.getSimpleName == lib.buildClassName) - .filter(classOf[BaseBuild] isAssignableFrom _) - if( buildClasses.size == 0 ){ - throw new Exception( - s"You need to define a class ${lib.buildClassName} extending an appropriate super class in\n" - + (projectDirectory / lib.buildFileName) ++ "\nbut none found." - ) - } else if( buildClasses.size > 1 ){ - throw new Exception( - s"You need to define exactly one class ${lib.buildClassName} extending an appropriate build super class, but multiple found in " + projectDirectory + ":\n" + buildClasses.mkString("\n") - ) - } else { - val buildClass = buildClasses.head - if( !buildClass.getConstructors.exists(_.getParameterTypes.toList == List(classOf[Context])) ){ - throw new Exception( - s"Expected class ${lib.buildClassName}(val context: Context), but found different constructor in\n" - + projectDirectory ++ "\n" - + buildClass ++ "(" ++ buildClass.getConstructors.map(_.getParameterTypes.mkString(", ")).mkString("; ") + ")" ) - } - buildClass.getConstructors.head.newInstance(managedContext) - } - } - } - ) - try{ - build.asInstanceOf[BuildInterface] - } catch { - case e: ClassCastException if e.getMessage.contains(s"${lib.buildClassName} cannot be cast to cbt.BuildInterface") => - throw new Exception(s"Your ${lib.buildClassName} class needs to extend BaseBuild in: "+projectDirectory, e) - } - } - - @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 ) - } } trait CbtInternal extends BuildBuild{ diff --git a/stage2/BuildDependency.scala b/stage2/BuildDependency.scala deleted file mode 100644 index bd3ed9e..0000000 --- a/stage2/BuildDependency.scala +++ /dev/null @@ -1,53 +0,0 @@ -package cbt -import java.io.File -/* -sealed abstract class ProjectProxy extends Ha{ - protected def delegate: ProjectMetaData - def artifactId: String = delegate.artifactId - def groupId: String = delegate.groupId - def version: String = delegate.version - def exportedClasspath = delegate.exportedClasspath - def dependencies = Seq(delegate) -} -*/ -/** You likely want to use the factory method in the BasicBuild class instead of this. */ -object DirectoryDependency{ - def apply(context: Context, pathToNestedBuild: String*): BuildInterface = { - val lib: Lib = new Lib(context.logger) - - // TODO: move this into finalBuild probably - // TODO: unify this with lib.callReflective - def selectNestedBuild( build: BuildInterface, names: Seq[String], previous: Seq[String] ): BuildInterface = { - names.headOption.map{ name => - if( lib.taskNames(build.getClass).contains(name) ){ - val method = build.getClass.getMethod(name) - val returnType = method.getReturnType - if( classOf[BuildInterface] isAssignableFrom returnType ){ - selectNestedBuild( - method.invoke(build).asInstanceOf[BuildInterface], - names.tail, - previous :+ name - ) - } else { - throw new RuntimeException( - s"Expected subtype of BuildInterface, found $returnType for " + previous.mkString(".") + " in " + build - ) - } - } else { - throw new RuntimeException( (previous :+ name).mkString(".") + " not found in " + build ) - } - }.getOrElse( build ) - } - selectNestedBuild( - lib.loadRoot( context ).finalBuild(context.workingDirectory), - pathToNestedBuild, - Nil - ) - } -} -/* -case class DependencyOr(first: DirectoryDependency, second: JavaDependency) extends ProjectProxy with DirectoryDependencyBase{ - val isFirst = new File(first.projectDirectory).exists - protected val delegate = if(isFirst) first else second -} -*/ diff --git a/stage2/DirectoryDependency.scala b/stage2/DirectoryDependency.scala new file mode 100644 index 0000000..cfc0bfd --- /dev/null +++ b/stage2/DirectoryDependency.scala @@ -0,0 +1,124 @@ +package cbt +import java.io.File +/** You likely want to use the factory method in the BasicBuild class instead of this. */ +object DirectoryDependency { + def apply( path: File, subBuild: String )( implicit context: Context ): LazyDependency = + apply( path, Some( subBuild ) ) + def apply( path: File, subBuild: Option[String] = None )( implicit context: Context ): LazyDependency = + apply( context.copy( workingDirectory = path ), subBuild ) + + def apply( context: Context, subBuild: Option[String] ): LazyDependency = { + val lib: Lib = new Lib( context.logger ) + val d = context.workingDirectory + val relativeBuildFile = lib.buildDirectoryName ++ File.separator ++ lib.buildFileName + val buildDirectory = d / lib.buildDirectoryName + val buildFile = buildDirectory / lib.buildFileName + + if ( !buildFile.isFile && ( buildDirectory / "Build.scala" ).isFile ) { + throw new Exception( + s"""expected $relativeBuildFile but found ${lib.buildDirectoryName ++ File.separator ++ "Build.scala"} in $d""" + ) + } + + if ( buildDirectory.exists && buildDirectory.listOrFail.nonEmpty && !buildFile.exists ) { + throw new Exception( + s"No file ${lib.buildFileName} (lower case) found in " ++ buildDirectory.string + ) + } + + def loadBuild: AnyRef = { + val actualBuildFile = ( + buildFile.getParentFile.getCanonicalFile.getName ++ File.separator ++ buildFile.getCanonicalFile.getName + ) + if ( actualBuildFile =!= relativeBuildFile ) { + throw new Exception( s"expected $relativeBuildFile but found $actualBuildFile in " ++ context.workingDirectory.string ) + } + + if ( buildFile.isFile ) ( + loadCustomBuildWithDifferentCbtVersion + getOrElse loadCustomBuild + ) + else if ( d.getCanonicalFile.getName === lib.buildDirectoryName && ( d / lib.buildFileName ).exists ) + new cbt.ConcreteBuildBuild( context ) + else + new BasicBuild( context ) + } + + def loadCustomBuildWithDifferentCbtVersion: Option[AnyRef] = { + ( "cbt:" ++ GitDependency.GitUrl.regex ++ "#[a-z0-9A-Z]+" ).r + .findFirstIn( buildFile.readAsString ) + .map( _.drop( 4 ).split( "#" ) ) + .flatMap { + case Array( base, hash ) => + val sameCbtVersion = context.cbtHome.string.contains( hash ) + if ( sameCbtVersion ) None else Some { + // Note: cbt can't use an old version of itself for building, + // otherwise we'd have to recursively build all versions since + // the beginning. Instead CBT always needs to build the pure Java + // Launcher in the checkout with itself and then run it via reflection. + val ( checkoutDirectory, dependency ) = + GitDependency.withCheckoutDirectory( base, hash, Some( "nailgun_launcher" ) )( context ) + dependency + .dependency + .asInstanceOf[BaseBuild] // should work because nailgun_launcher/ has no cbt build of it's own + .classLoader + .loadClass( "cbt.NailgunLauncher" ) + .getMethod( "getBuild", classOf[AnyRef] ) + .invoke( null, context.copy( cbtHome = checkoutDirectory ) ) + } + } + } + + def loadCustomBuild: AnyRef = { + lib.logger.composition( "Loading build at " ++ buildDirectory.string ) + val buildBuild = apply( buildDirectory, None )( context ).dependency.asInstanceOf[BuildInterface] + import buildBuild._ + val managedContext = context.copy( parentBuild = Some( buildBuild ) ) + + val buildClasses = + buildBuild.exportedClasspath.files.flatMap( + lib.iterateClasses( _, classLoader, false ) + .filter( _.getSimpleName === lib.buildClassName ) + .filter( classOf[BaseBuild] isAssignableFrom _ ) + ) + + if ( buildClasses.size === 0 ) { + throw new Exception( + s"You need to define a class ${lib.buildClassName} extending an appropriate super class in\n" + + buildFile ++ "\nbut none found." + ) + } else if ( buildClasses.size > 1 ) { + throw new Exception( + s"You need to define exactly one class ${ + lib.buildClassName + } extending an appropriate build super class, but multiple found in " + + buildDirectory + ":\n" + buildClasses.mkString( "\n" ) + ) + } else { + val buildClass = buildClasses.head + buildClass.getConstructors.find( _.getParameterTypes.toList === List( classOf[Context] ) ).map { + _.newInstance( managedContext ).asInstanceOf[AnyRef] + }.getOrElse { + throw new Exception( + s"Expected class ${lib.buildClassName}(val context: Context), but found different constructor in\n" + + buildDirectory ++ "\n" + + buildClass ++ "(" ++ buildClass.getConstructors.map( _.getParameterTypes.mkString( ", " ) ).mkString( "; " ) + ")" + ) + } + } + } + + // wrapping this in lazy so builds are not loaded from disk immediately + // this definitely has the advantages that cbt can detect cycles in the + // dependencies, but might also positively affect performance + new LazyDependency( { + val build = lib.getReflective( loadBuild, subBuild )( context ) + try { + build.asInstanceOf[Dependency] + } catch { + case e: ClassCastException => + throw new RuntimeException( "Your class " ++ lib.buildClassName ++ " needs to extend class BaseBuild in $buildFile", e ) + } + } )( context.logger, context.transientCache, context.classLoaderCache ) + } +} diff --git a/stage2/GitDependency.scala b/stage2/GitDependency.scala index 20e1d36..8a4a441 100644 --- a/stage2/GitDependency.scala +++ b/stage2/GitDependency.scala @@ -11,25 +11,29 @@ object GitDependency{ val GitUrl = "(git@|git://|https://|file:///)([^/:]+)[/:](.+)".r def apply( url: String, ref: String, subDirectory: Option[String] = None, // example: git://github.com/cvogt/cbt.git#<some-hash> - pathToNestedBuild: Seq[String] = Seq() - )(implicit context: Context ): BuildInterface = { - def moduleKey = ( + subBuild: Option[String] = None + )(implicit context: Context ): LazyDependency = withCheckoutDirectory(url, ref, subDirectory, subBuild)._2 + def withCheckoutDirectory( + url: String, ref: String, subDirectory: Option[String] = None, // example: git://github.com/cvogt/cbt.git#<some-hash> + subBuild: Option[String] = None + )(implicit context: Context ): ( File, LazyDependency ) = { + lazy val moduleKey = ( this.getClass.getName ++ "(" ++ url ++ subDirectory.map("/" ++ _).getOrElse("") ++ "#" ++ ref ++ ", " - ++ pathToNestedBuild.mkString(", ") + ++ subBuild.mkString(", ") ++ ")" ) - val taskCache = new PerClassCache(context.transientCache, moduleKey)(context.logger) - - val c = taskCache[Dependency]("checkout").memoize{ checkout( url, ref ) } - DirectoryDependency( + val taskCache = new PerClassCache( context.transientCache, moduleKey )( context.logger ) + val c = taskCache[Dependency]("checkout").memoize{ checkout(url, ref) } + val workingDirectory = subDirectory.map(c / _).getOrElse(c) + c -> DirectoryDependency( context.copy( - workingDirectory = subDirectory.map(c / _).getOrElse(c), + workingDirectory = workingDirectory, loop = false ), - pathToNestedBuild: _* + subBuild ) } def checkout(url: String, ref: String)(implicit context: Context): File = { diff --git a/stage2/LazyDependency.scala b/stage2/LazyDependency.scala new file mode 100644 index 0000000..afc6263 --- /dev/null +++ b/stage2/LazyDependency.scala @@ -0,0 +1,17 @@ +package cbt +class LazyDependency( _dependency: => Dependency )( implicit logger: Logger, transientCache: java.util.Map[AnyRef, AnyRef], classLoaderCache: ClassLoaderCache ) extends Dependency { + lazy val dependency = _dependency + def classLoader = dependency.classLoader + def dependenciesArray = Array( dependency ) + def exportedClasspathArray = Array() + def lastModified = dependency.lastModified + lazy val moduleKey = show + def show = s"LazyDependency(${dependency.show})" + override def toString = show + override def equals( other: Any ) = other match { + case d: LazyDependency => d.dependency === dependency + case _ => false + } + def dependencyClasspathArray = dependency.classpath.files.toArray + def needsUpdateCompat = false +} diff --git a/stage2/Lib.scala b/stage2/Lib.scala index 04e68f1..4fdcf2d 100644 --- a/stage2/Lib.scala +++ b/stage2/Lib.scala @@ -24,30 +24,6 @@ final class Lib(val logger: Logger) extends Stage1Lib(logger){ val buildClassName = "Build" val buildBuildClassName = "BuildBuild" - /** - Loads whatever Build needs to be executed first in order to eventually build the build for the given context. - This can either the Build itself, of if exists a BuildBuild or a BuildBuild for a BuildBuild and so on. - */ - def loadRoot(context: Context): BuildInterface = { - val directory = context.workingDirectory - - context.logger.composition( context.logger.showInvocation("Lib.loadRoot",directory) ) - - val start = lib.findInnerMostModuleDirectory(directory) - - val useBasicBuild = directory == start && start.getName != buildDirectoryName - - try{ - if(useBasicBuild) - new BasicBuild( context.copy( workingDirectory = directory ) ) - else - new cbt.ConcreteBuildBuild( context.copy( workingDirectory = start ) ) - } catch { - case e:ClassNotFoundException if e.getMessage == buildClassName => - throw new Exception(s"no class ${buildClassName} found in " ++ start.string) - } - } - def scaladoc( cbtLastModified: Long, scalaVersion: String, @@ -118,20 +94,31 @@ final class Lib(val logger: Logger) extends Stage1Lib(logger){ } def callReflective[T <: AnyRef]( obj: T, code: Option[String], context: Context ): ExitCode = { - callInternal( obj, code.toSeq.flatMap(_.split("\\.").map( NameTransformer.encode )), Nil, context ).map { - 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 - }.reduceOption(_ && _).getOrElse( ExitCode.Failure ) + val result = getReflective( obj, code, f => { + def g( a: AnyRef): AnyRef = a match { + case bs: Seq[_] if bs.size > 0 && bs.forall(_.isInstanceOf[BaseBuild]) => + bs.map(_.asInstanceOf[BaseBuild]).map(f) + case obj: LazyDependency => g(obj.dependency) + case obj => f(obj) + } + g(_) + } )( context ) + + val applied = result.getClass.getMethods.find(m => m.getName == "apply" && m.getParameterCount == 0).map( + _.invoke( result ) + ).getOrElse( result ) + + applied match { + case e: ExitCode => e + case s: Seq[_] => + val v = render(applied) + if(v.nonEmpty) System.out.println(v) + s.collect{ case e: ExitCode => e }.reduceOption(_ && _).getOrElse( ExitCode.Success ) + case other => + val s = render(applied) + if(s.nonEmpty) System.out.println(s) + ExitCode.Success + } } private def render( obj: Any ): String = { @@ -141,55 +128,54 @@ final class Lib(val logger: Logger) extends Stage1Lib(logger){ case url: URL => url.show // to remove credentials case d: Dependency => lib.usage(d.getClass, d.show()) case c: ClassPath => c.string - case ExitCode(int) => System.err.println(int); System.exit(int); ??? + //case e: ExitCode => System.err.println(e.integer); System.exit(e.integer); ??? case s: Seq[_] => s.map(render).mkString("\n") case s: Set[_] => s.map(render).toSeq.sorted.mkString("\n") case _ => obj.toString } } - private def callInternal[T <: AnyRef]( obj: T, members: Seq[String], previous: Seq[String], context: Context ): Seq[(Option[Object], Option[ExitCode], Option[String])] = { - members.headOption.map{ taskName => - val name = NameTransformer.decode(taskName) - logger.lib("Calling task " ++ taskName.toString) - taskMethods(obj.getClass).get(name).map{ method => - Option(trapExitCodeOrValue(method.invoke(obj)).merge /* null in case of Unit */ ).getOrElse(().asInstanceOf[AnyRef]) match { - case code if code.getClass.getSimpleName == "ExitCode" => - // FIXME: ExitCode needs to be part of the compatibility interfaces - Seq((None, Some(ExitCode(Stage0Lib.get(code,"integer").asInstanceOf[Int])), None)) - case bs: Seq[_] if bs.size > 0 && bs.forall(_.isInstanceOf[BaseBuild]) => - bs.flatMap( b => callInternal(b.asInstanceOf[BaseBuild], members.tail, previous :+ taskName, context) ) - case result => - callInternal(result, members.tail, previous :+ taskName, context) - } - }.getOrElse{ - if( context != null && (context.workingDirectory / name).exists ){ - val newContext = context.copy( workingDirectory = context.workingDirectory / name ) + def getReflective[T <: AnyRef]( + obj: T, code: Option[String], callback: (AnyRef => AnyRef) => AnyRef => AnyRef = f => f(_) + )( implicit context: Context ) = + callInternal( obj, code.toSeq.flatMap(_.split("\\.").map( NameTransformer.encode )), Nil, context, callback ) + + private def callInternal[T <: AnyRef]( + _obj: T, members: Seq[String], previous: Seq[String], context: Context, + callback: (AnyRef => AnyRef) => AnyRef => AnyRef + ): Object = { + callback{ obj => + members.headOption.map{ taskName => + val name = NameTransformer.decode(taskName) + logger.lib("Calling task " ++ taskName.toString) + taskMethods(obj.getClass).get(name).map{ method => callInternal( - lib.loadRoot( - newContext - ).finalBuild, + Option(trapExitCodeOrValue(method.invoke(obj)).merge /* null in case of Unit */ ).getOrElse( + ().asInstanceOf[AnyRef] + ), members.tail, - previous, - newContext + previous :+ taskName, + context, + callback ) - } else { - val p = previous.mkString(".") - val msg = (if(p.nonEmpty) p ++ s" has" else "") - Seq( ( Some(obj), None, Some( msg ++ s"no method $name\n") ) ) + }.getOrElse{ + if( context =!= null && (context.workingDirectory / name).exists ){ + val newContext = context.copy( workingDirectory = context.workingDirectory / name ) + callInternal( + DirectoryDependency( newContext.cwd )( newContext ).dependency, + members.tail, + previous :+ taskName, + newContext, + callback + ) + } else { + val p = previous.mkString(".") + val msg = (if(p.nonEmpty) p ++ s" has " else " ") + throw new RuntimeException( msg ++ lib.red(s"no method `$name` in\n") + render(obj) ) + } } - } - }.getOrElse{ - Seq(( - Some( - obj.getClass.getMethods.find(m => m.getName == "apply" && m.getParameterCount == 0).map( - _.invoke(obj) - ).getOrElse( obj ) - ), - None, - None - )) - } + }.getOrElse( obj ) + }(_obj) } def consoleOrFail(msg: String) = { @@ -481,11 +467,6 @@ final class Lib(val logger: Logger) extends Stage1Lib(logger){ url } - def findInnerMostModuleDirectory(directory: File): File = { - val buildDir = realpath( directory ++ ("/" ++ lib.buildDirectoryName) ) - // do not appent buildFileName here, so that we detect empty build folders - if(buildDir.exists) findInnerMostModuleDirectory(buildDir) else directory - } def findOuterMostModuleDirectory(directory: File): File = { if( ( directory.getParentFile ++ ("/" ++ lib.buildDirectoryName) ).exists diff --git a/stage2/Stage2.scala b/stage2/Stage2.scala index eaf776e..9eb47fe 100644 --- a/stage2/Stage2.scala +++ b/stage2/Stage2.scala @@ -3,9 +3,7 @@ import java.io._ import java.util.{Set=>_,_} object Stage2 extends Stage2Base{ - def getBuild(context: Context) = { - new Lib( context.logger ).loadRoot( context ).finalBuild( context.cwd ) - } + def getBuild(context: Context): Dependency = DirectoryDependency( context, None ) def run( args: Stage2Args ): ExitCode = { import args.logger @@ -34,9 +32,10 @@ object Stage2 extends Stage2Base{ null, args.loop ) - val first = lib.loadRoot( context ) - val build = first.finalBuild( context.cwd ) - val code = lib.callReflective(build, task, context) + val code = lib.callReflective( + DirectoryDependency( context, None ), + task, context + ) logger.stage2(s"Stage2 end with exit code "+code.integer) code } |