aboutsummaryrefslogtreecommitdiff
path: root/stage2/DirectoryDependency.scala
diff options
context:
space:
mode:
authorChristopher Vogt <oss.nsp@cvogt.org>2017-03-19 19:51:38 -0400
committerChristopher Vogt <oss.nsp@cvogt.org>2017-03-20 02:56:26 -0400
commitd6245c8dc5c7b2f885d538b39f685327da252863 (patch)
tree846bdd92ad022dbe5a7a45e0b9d5e75bbf7779c8 /stage2/DirectoryDependency.scala
parentca099eba708f3618bed75a5940a5a5ae1d10b684 (diff)
downloadcbt-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/DirectoryDependency.scala')
-rw-r--r--stage2/DirectoryDependency.scala124
1 files changed, 124 insertions, 0 deletions
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 )
+ }
+}