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/DirectoryDependency.scala | |
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/DirectoryDependency.scala')
-rw-r--r-- | stage2/DirectoryDependency.scala | 124 |
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 ) + } +} |