diff options
author | Christopher Vogt <oss.nsp@cvogt.org> | 2017-02-15 03:30:46 -0500 |
---|---|---|
committer | Christopher Vogt <oss.nsp@cvogt.org> | 2017-02-15 04:07:21 -0500 |
commit | 4f4c34f3f52b87057626682dd43a21cd83e2ff7a (patch) | |
tree | 12396906bde5f51cc8fc274c387d77e438bd2c23 | |
parent | 7ee113962134e3c9c4659d6de9de3c7015174d3f (diff) | |
download | cbt-4f4c34f3f52b87057626682dd43a21cd83e2ff7a.tar.gz cbt-4f4c34f3f52b87057626682dd43a21cd83e2ff7a.tar.bz2 cbt-4f4c34f3f52b87057626682dd43a21cd83e2ff7a.zip |
add package to generated Build in build.scala and in-package discovery
This should allow for build to add other builds to their dependencies
and interact with them in a type-safe way. And ever regardless it seems
like good practice to never have the same class existing in the same
package or the top-level package even if they don’t end up on the same
classpath. This might also help make stack traces easier to understand.
Also improve error messages for mistakes with the build class, e.g.
constructor, super classes, etc.
-rw-r--r-- | stage1/Stage1Lib.scala | 54 | ||||
-rw-r--r-- | stage2/BuildBuild.scala | 30 | ||||
-rw-r--r-- | stage2/Scaffold.scala | 3 | ||||
-rw-r--r-- | test/broken-build/build-class-with-wrong-arguments/build/build.scala | 3 | ||||
-rw-r--r-- | test/broken-build/build-class-with-wrong-parent/build/build.scala (renamed from test/forgot-extend/build/build.scala) | 0 | ||||
-rw-r--r-- | test/broken-build/empty-build-file/Main.scala (renamed from test/empty-build-file/Main.scala) | 0 | ||||
-rw-r--r-- | test/broken-build/empty-build-file/build/build.scala (renamed from test/empty-build-file/build/build.scala) | 0 | ||||
-rw-r--r-- | test/broken-build/no-build-file/Main.scala (renamed from test/no-build-file/Main.scala) | 0 | ||||
-rw-r--r-- | test/broken-build/no-build-file/build/foo.scala (renamed from test/no-build-file/build/foo.scala) | 0 | ||||
-rw-r--r-- | test/test.scala | 19 |
10 files changed, 71 insertions, 38 deletions
diff --git a/stage1/Stage1Lib.scala b/stage1/Stage1Lib.scala index 505b298..79df450 100644 --- a/stage1/Stage1Lib.scala +++ b/stage1/Stage1Lib.scala @@ -146,35 +146,43 @@ class Stage1Lib( logger: Logger ) extends BaseLib{ pickOne( "Which one do you want to run?", mainClasses )( _.toString ) } - def mainClasses( targetDirectory: File, classLoader : ClassLoader ): Seq[Class[_]] = { - val arrayClass = classOf[Array[String]] - val unitClass = classOf[Unit] - - listFilesRecursive(targetDirectory) + /** Given a directory corresponding to the root package, iterate + the names of all classes derived from the class files found */ + def iterateClassNames( classesRootDirectory: File ): Seq[String] = + listFilesRecursive(classesRootDirectory) .filter(_.isFile) .map(_.getPath) .collect{ // no $ to avoid inner classes case path if !path.contains("$") && path.endsWith(".class") => - try{ - classLoader.loadClass( - path - .stripSuffix(".class") - .stripPrefix(targetDirectory.getPath) - .stripPrefix(File.separator) // 1 for the slash - .replace(File.separator, ".") - ) - } catch { - case e: ClassNotFoundException => null - case e: NoClassDefFoundError => null - } - }.filterNot(_ == null).filter( - _.getDeclaredMethods().exists( m => - m.getName == "main" - && m.getParameterTypes.toList == List(arrayClass) - && m.getReturnType == unitClass - ) + path.stripSuffix(".class") + .stripPrefix(classesRootDirectory.getPath) + .stripPrefix(File.separator) // 1 for the slash + .replace(File.separator, ".") + } + + /** ignoreMissingClasses allows ignoring other classes root directories which are subdirectories of this one */ + def iterateClasses( classesRootDirectory: File, classLoader: ClassLoader, ignoreMissingClasses: Boolean ) = + iterateClassNames(classesRootDirectory).map{ name => + try{ + classLoader.loadClass(name) + } catch { + case e: ClassNotFoundException if ignoreMissingClasses => null + case e: NoClassDefFoundError if ignoreMissingClasses => null + } + }.filterNot(ignoreMissingClasses && _ == null) + + def mainClasses( classesRootDirectory: File, classLoader: ClassLoader ): Seq[Class[_]] = { + val arrayClass = classOf[Array[String]] + val unitClass = classOf[Unit] + + iterateClasses( classesRootDirectory, classLoader, true ).filter( + _.getDeclaredMethods().exists( m => + m.getName == "main" + && m.getParameterTypes.toList == List(arrayClass) + && m.getReturnType == unitClass ) + ) } implicit class ClassLoaderExtensions(classLoader: ClassLoader){ diff --git a/stage2/BuildBuild.scala b/stage2/BuildBuild.scala index 798bd7d..2eebcbc 100644 --- a/stage2/BuildBuild.scala +++ b/stage2/BuildBuild.scala @@ -63,15 +63,29 @@ trait BuildBuildWithoutEssentials extends BaseBuild{ .invoke( null, ctx ) } }.getOrElse{ - try{ - classLoader - .loadClass(lib.buildClassName) - .getConstructors.head - .newInstance(managedContext) - } catch { - case e: ClassNotFoundException if e.getMessage == lib.buildClassName => - throw new Exception("You need to define a class Build in build.scala in: "+projectDirectory) + 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) + } } } ) diff --git a/stage2/Scaffold.scala b/stage2/Scaffold.scala index b110258..68a966b 100644 --- a/stage2/Scaffold.scala +++ b/stage2/Scaffold.scala @@ -59,7 +59,8 @@ object Main{ def createBuild( projectDirectory: File ): Unit = { - createFile(projectDirectory, "build/build.scala", s"""import cbt._ + createFile(projectDirectory, lib.buildDirectoryName++"/"++lib.buildFileName, s"""package cbt_build.${packageFromDirectory(projectDirectory)} +import cbt._ class Build(val context: Context) extends BaseBuild{ override def dependencies = super.dependencies ++ // don't forget super.dependencies here for scala-library, etc. diff --git a/test/broken-build/build-class-with-wrong-arguments/build/build.scala b/test/broken-build/build-class-with-wrong-arguments/build/build.scala new file mode 100644 index 0000000..cf040a1 --- /dev/null +++ b/test/broken-build/build-class-with-wrong-arguments/build/build.scala @@ -0,0 +1,3 @@ +package cbt_build.build_class_with_wrong_arguments +import cbt._ +class Build(i: Int, val context: Context) extends BaseBuild diff --git a/test/forgot-extend/build/build.scala b/test/broken-build/build-class-with-wrong-parent/build/build.scala index 9181a5d..9181a5d 100644 --- a/test/forgot-extend/build/build.scala +++ b/test/broken-build/build-class-with-wrong-parent/build/build.scala diff --git a/test/empty-build-file/Main.scala b/test/broken-build/empty-build-file/Main.scala index 19d4beb..19d4beb 100644 --- a/test/empty-build-file/Main.scala +++ b/test/broken-build/empty-build-file/Main.scala diff --git a/test/empty-build-file/build/build.scala b/test/broken-build/empty-build-file/build/build.scala index e69de29..e69de29 100644 --- a/test/empty-build-file/build/build.scala +++ b/test/broken-build/empty-build-file/build/build.scala diff --git a/test/no-build-file/Main.scala b/test/broken-build/no-build-file/Main.scala index 19d4beb..19d4beb 100644 --- a/test/no-build-file/Main.scala +++ b/test/broken-build/no-build-file/Main.scala diff --git a/test/no-build-file/build/foo.scala b/test/broken-build/no-build-file/build/foo.scala index e69de29..e69de29 100644 --- a/test/no-build-file/build/foo.scala +++ b/test/broken-build/no-build-file/build/foo.scala diff --git a/test/test.scala b/test/test.scala index 778fcda..d8714c0 100644 --- a/test/test.scala +++ b/test/test.scala @@ -240,21 +240,28 @@ object Main{ } { - val res = runCbt("forgot-extend", Seq("run")) + val res = runCbt("broken-build/build-class-with-wrong-arguments", Seq("run")) assert(!res.exit0) - assert(res.err contains s"${lib.buildClassName} cannot be cast to cbt.BuildInterface", res.err) + assert(res.err contains s"Expected class ${lib.buildClassName}(val context: Context), but found different constructor", res.err) + assert(res.err contains s"${lib.buildClassName}(int, interface cbt.Context)", res.err) } { - val res = runCbt("no-build-file", Seq("run")) + val res = runCbt("broken-build/build-class-with-wrong-parent", Seq("run")) assert(!res.exit0) - assert(res.err contains s"No file ${lib.buildFileName} (lower case) found in", res.err) + assert(res.err contains s"You need to define a class ${lib.buildClassName} extending an appropriate super class", res.err) } { - val res = runCbt("empty-build-file", Seq("run")) + val res = runCbt("broken-build/no-build-file", Seq("run")) assert(!res.exit0) - assert(res.err contains s"You need to define a class ${lib.buildClassName} in", res.err) + assert(res.err contains s"No file ${lib.buildFileName} (lower case) found", res.err) + } + + { + val res = runCbt("broken-build/empty-build-file", Seq("run")) + assert(!res.exit0) + assert(res.err contains s"You need to define a class ${lib.buildClassName}", res.err) } { |