diff options
Diffstat (limited to 'stage2')
-rw-r--r-- | stage2/BasicBuild.scala | 4 | ||||
-rw-r--r-- | stage2/BuildBuild.scala | 42 | ||||
-rw-r--r-- | stage2/Lib.scala | 28 | ||||
-rw-r--r-- | stage2/Scaffold.scala | 73 | ||||
-rw-r--r-- | stage2/ToolsTasks.scala | 5 |
5 files changed, 119 insertions, 33 deletions
diff --git a/stage2/BasicBuild.scala b/stage2/BasicBuild.scala index e5b3507..f32d4d9 100644 --- a/stage2/BasicBuild.scala +++ b/stage2/BasicBuild.scala @@ -24,12 +24,12 @@ trait BaseBuild extends BuildInterface with DependencyImplementation with Trigge def projectDirectory: File = lib.realpath(context.workingDirectory) assert( projectDirectory.exists, "projectDirectory does not exist: " ++ projectDirectory.string ) assert( - projectDirectory.getName =!= "build" || + projectDirectory.getName =!= lib.buildDirectoryName || { def transitiveInterfaces(cls: Class[_]): Vector[Class[_]] = cls.getInterfaces.toVector.flatMap(i => i +: transitiveInterfaces(i)) transitiveInterfaces(this.getClass).contains(classOf[BuildBuildWithoutEssentials]) }, - "You need to extend BuildBuild in: " + projectDirectory + "/build" + s"You need to extend ${lib.buildBuildClassName} in: " + projectDirectory + "/" ++ lib.buildDirectoryName ) final def usage: String = lib.usage(this.getClass, show) diff --git a/stage2/BuildBuild.scala b/stage2/BuildBuild.scala index 474599a..2eebcbc 100644 --- a/stage2/BuildBuild.scala +++ b/stage2/BuildBuild.scala @@ -7,8 +7,8 @@ trait BuildBuild extends BuildBuildWithoutEssentials{ } trait BuildBuildWithoutEssentials extends BaseBuild{ assert( - projectDirectory.getName === "build", - "You can't extend BuildBuild in: " + projectDirectory + "/build" + projectDirectory.getName === lib.buildDirectoryName, + "You can't extend ${lib.buildBuildClassName} in: " + projectDirectory + "/" + lib.buildDirectoryName ) protected final val managedContext = context.copy( @@ -33,12 +33,12 @@ trait BuildBuildWithoutEssentials extends BaseBuild{ super.dependencies :+ context.cbtDependency def managedBuildDirectory: java.io.File = lib.realpath( projectDirectory.parent ) def managedBuild = taskCache[BuildBuildWithoutEssentials]("managedBuild").memoize{ - val managedBuildFile = projectDirectory++"/build.scala" + val managedBuildFile = projectDirectory++("/"++lib.buildFileName) logger.composition("Loading build at " ++ managedBuildDirectory.toString) val build = ( if( !managedBuildFile.exists ){ throw new Exception( - "No file build.scala (lower case) found in " ++ projectDirectory.getPath + s"No file ${lib.buildFileName} (lower case) found in " ++ projectDirectory.getPath ) } else { val contents = new String(Files.readAllBytes(managedBuildFile.toPath)) @@ -63,23 +63,37 @@ 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) + } } } ) try{ build.asInstanceOf[BuildInterface] } catch { - case e: ClassCastException if e.getMessage.contains("Build cannot be cast to cbt.BuildInterface") => - throw new Exception("Your Build class needs to extend BaseBuild in: "+projectDirectory, e) + 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) } } override def triggerLoopFiles = super.triggerLoopFiles ++ managedBuild.triggerLoopFiles diff --git a/stage2/Lib.scala b/stage2/Lib.scala index 2642ec9..fc53c4f 100644 --- a/stage2/Lib.scala +++ b/stage2/Lib.scala @@ -15,9 +15,11 @@ import scala.util._ case class Developer(id: String, name: String, timezone: String, url: URL) /** Don't extend. Create your own libs :). */ -final class Lib(val logger: Logger) extends Stage1Lib(logger) with Scaffold{ +final class Lib(val logger: Logger) extends Stage1Lib(logger){ lib => + val buildFileName = "build.scala" + val buildDirectoryName = "build" val buildClassName = "Build" val buildBuildClassName = "BuildBuild" @@ -26,18 +28,13 @@ final class Lib(val logger: Logger) extends Stage1Lib(logger) with Scaffold{ 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 = { - def findStartDir(directory: File): File = { - val buildDir = realpath( directory ++ "/build" ) - if(buildDir.exists) findStartDir(buildDir) else directory - } - val directory = context.workingDirectory context.logger.composition( context.logger.showInvocation("Build.loadRoot",directory) ) - val start = findStartDir(directory) + val start = lib.findInnerMostModuleDirectory(directory) - val useBasicBuild = directory == start && start.getName != "build" + val useBasicBuild = directory == start && start.getName != buildDirectoryName try{ if(useBasicBuild) { @@ -54,8 +51,8 @@ final class Lib(val logger: Logger) extends Stage1Lib(logger) with Scaffold{ else new cbt.BasicBuild( context.copy( workingDirectory = start ) ) with BuildBuild } catch { - case e:ClassNotFoundException if e.getMessage == "Build" => - throw new Exception(s"no class Build found in " ++ start.string) + case e:ClassNotFoundException if e.getMessage == buildClassName => + throw new Exception(s"no class ${buildClassName} found in " ++ start.string) } } @@ -532,4 +529,15 @@ final class Lib(val logger: Logger) extends Stage1Lib(logger) with Scaffold{ } } } + + 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 + ) findOuterMostModuleDirectory(directory.getParentFile) else directory + } } diff --git a/stage2/Scaffold.scala b/stage2/Scaffold.scala index 866e5da..68a966b 100644 --- a/stage2/Scaffold.scala +++ b/stage2/Scaffold.scala @@ -2,8 +2,8 @@ package cbt import java.io._ import java.nio.file._ import java.net._ -trait Scaffold{ - def logger: Logger +class Scaffold( logger: Logger ){ + val lib = new Lib(logger) private def createFile( projectDirectory: File, fileName: String, code: String ){ val outputFile = projectDirectory ++ ("/" ++ fileName) @@ -12,10 +12,42 @@ trait Scaffold{ println( GREEN ++ "Created " ++ fileName ++ RESET ) } + private[cbt] def packageName(name: String) = { + def stripNonAlPrefix = (_:String).dropWhile( + !(('a' to 'z') ++ ('A' to 'Z') ++ Seq('_')).contains(_) + ) + def removeNonAlNumPlusSelected = "([^-a-zA-Z0-9_\\.\\\\/])".r.replaceAllIn(_:String, "") + def replaceSpecialWithUnderscore = "([-\\. ])".r.replaceAllIn(_:String, "_") + def removeRepeatedDots = "\\.+".r.replaceAllIn(_:String, ".") + val transform = ( + ( + stripNonAlPrefix + andThen + removeNonAlNumPlusSelected + andThen + replaceSpecialWithUnderscore + ).andThen( + (_:String).replace("/",".").replace("\\",".").toLowerCase + ) andThen removeRepeatedDots + + ) + + transform( name ) + } + + private[cbt] def packageFromDirectory(directory: File) = { + packageName( + directory.getAbsolutePath.stripPrefix( + lib.findOuterMostModuleDirectory( directory ).getParentFile.getAbsolutePath + ) + ) + } + def createMain( projectDirectory: File - ): Unit = { - createFile(projectDirectory, "Main.scala", s"""object Main{ + ): Unit = { + createFile(projectDirectory, "Main.scala", s"""package ${packageFromDirectory(projectDirectory)} +object Main{ def main( args: Array[String] ): Unit = { println( Console.GREEN ++ "Hello World" ++ Console.RESET ) } @@ -27,7 +59,8 @@ trait Scaffold{ 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. @@ -50,3 +83,33 @@ class Build(val context: Context) extends BaseBuild{ ) } } +object ScaffoldTest{ + val scaffold = new Scaffold(new Logger(None,System.currentTimeMillis)) + import scaffold._ + def main(args: Array[String]): Unit = { + def assertEquals[T](left: T, right: T) = { + assert( left == right, left + " == " + right ) + } + assertEquals( + packageName( "AsdfAsdfAsdf" ), "asdfasdfasdf" + ) + assertEquals( + packageName( "_AsdfA4sdf" ), "_asdfa4sdf" + ) + assertEquals( + packageName( "-AsdfAsdf" ), "asdfasdf" + ) + assertEquals( + packageName( "asdf 4aSdf" ), "asdf4asdf" + ) + assertEquals( + packageName( "&/(&%$&&/(asdf" ), "asdf" + ) + assertEquals( + packageName( "AAA" ), "aaa" + ) + assertEquals( + packageName( "/AAA/a_a/a.a" ), "aaa.a_a.a_a" + ) + } +} diff --git a/stage2/ToolsTasks.scala b/stage2/ToolsTasks.scala index 25156fb..943f096 100644 --- a/stage2/ToolsTasks.scala +++ b/stage2/ToolsTasks.scala @@ -15,8 +15,9 @@ class ToolsTasks( implicit val logger: Logger = lib.logger implicit val transientCache: java.util.Map[AnyRef,AnyRef] = new java.util.HashMap private def Resolver( urls: URL* ) = MavenResolver(cbtLastModified,mavenCache,urls: _*) - def createMain: Unit = lib.createMain( cwd ) - def createBuild: Unit = lib.createBuild( cwd ) + val scaffold = new Scaffold(logger) + def createMain: Unit = scaffold.createMain( cwd ) + def createBuild: Unit = scaffold.createBuild( cwd ) def gui = NailgunLauncher.main(Array( "0.0", (cbtHome / "tools" / "gui").getAbsolutePath, |