diff options
Diffstat (limited to 'stage2/plugins/ExportBuildInformation.scala')
-rw-r--r-- | stage2/plugins/ExportBuildInformation.scala | 236 |
1 files changed, 156 insertions, 80 deletions
diff --git a/stage2/plugins/ExportBuildInformation.scala b/stage2/plugins/ExportBuildInformation.scala index c5c69ab..37dcc38 100644 --- a/stage2/plugins/ExportBuildInformation.scala +++ b/stage2/plugins/ExportBuildInformation.scala @@ -4,14 +4,24 @@ import cbt._ import java.io._ import java.nio.file._ import scala.xml._ -import scala.util._ +import scala.util._ trait ExportBuildInformation { self: BaseBuild => - def buildInfoXml: String = - BuildInformationSerializer.serialize(BuildInformation.Project(self, context.args)).toString + def buildInfoXml: String = { + val parameters = BuildInformation.Project.ExportParameters(context.args) + val xml = BuildInformationSerializer.serialize(BuildInformation.Project(self, parameters)) + parameters.outputFile match { + case None => + xml.toString + case Some(file) => + XML.save(file.getPath, xml) + s"Saved to ${file.getPath}" + } + } } object BuildInformation { + case class Project( name: String, root: File, @@ -27,6 +37,7 @@ object BuildInformation { root: File, scalaVersion: String, sourceDirs: Seq[File], + moduleType: ModuleType, target: File, binaryDependencies: Seq[BinaryDependency], moduleDependencies: Seq[ModuleDependency], @@ -34,7 +45,7 @@ object BuildInformation { parentBuild: Option[String], scalacOptions: Seq[String] ) - + case class Library( name: String, jars: Seq[LibraryJar] ) case class BinaryDependency( name: String ) @@ -43,61 +54,76 @@ object BuildInformation { case class ScalaCompiler( version: String, jars: Seq[File] ) - case class LibraryJar( jar: File, jarType: JarType.JarType ) + case class LibraryJar( jar: File, jarType: JarType ) + + case class JarType( name: String ) + + object JarType { + object Binary extends JarType( "binary" ) + object Source extends JarType( "source" ) + } + + case class ModuleType( name: String ) - object JarType extends Enumeration { - type JarType = Value - val Binary = Value("binary") - val Source = Value("source") + object ModuleType { + object Default extends ModuleType( "default" ) + object Extra extends ModuleType( "extra" ) + object Test extends ModuleType( "test" ) + object Build extends ModuleType( "build" ) } object Project { - def apply(build: BaseBuild, args: Seq[String]): Project = { - val extraModuleNames: Seq[String] = args.lift(0).map(_.split(":").toSeq).getOrElse(Seq.empty) - new BuildInformationExporter(build, extraModuleNames).exportBuildInformation + def apply(build: BaseBuild, parameters: ExportParameters): Project = { + new BuildInformationExporter(build, parameters).exportBuildInformation } - class BuildInformationExporter(rootBuild: BaseBuild, extraModuleNames: Seq[String]) { + class BuildInformationExporter(rootBuild: BaseBuild, parameters: ExportParameters) { + import parameters._ + def exportBuildInformation: Project = { - val moduleBuilds = transitiveBuilds(rootBuild) - val libraries = moduleBuilds - .flatMap(_.transitiveDependencies) - .collect { case d: BoundMavenDependency => exportLibrary(d) } + val extraModuleBuilds = extraModulePaths + .map(f => DirectoryDependency(f)(rootBuild.context).dependency.asInstanceOf[BaseBuild]) + val builds = transitiveBuilds((rootBuild, ModuleType.Default) +: extraModuleBuilds.map(b => (b, ModuleType.Extra))) + val rootModule = exportModule(rootBuild, ModuleType.Default) + val modules = builds + .map(m => exportModule(m._1, m._2)) .distinct - val cbtLibraries = convertCbtLibraries - val rootModule = exportModule(rootBuild) - val extraModuleBuilds = extraModuleNames - .map(f => new File(f)) - .filter(f => f.exists && f.isDirectory) - .map(f => DirectoryDependency(f)(rootBuild.context).dependency.asInstanceOf[BaseBuild]) - .flatMap(transitiveBuilds) - val modules = (moduleBuilds ++ extraModuleBuilds) - .map(exportModule) + val libraries = builds + .map(_._1) + .flatMap(_.transitiveDependencies) + .collect { case d: BoundMavenDependency => exportLibrary(d) } .distinct + val cbtLibraries = if (needCbtLibs) convertCbtLibraries else Seq.empty[Library] val scalaCompilers = modules .map(_.scalaVersion) - .map(v => ScalaCompiler(v, resolveScalaCompiler(rootBuild, v))) + .map(v => ScalaCompiler(v, resolveScalaCompiler(v))) + .distinct Project( - rootModule.name, - rootModule.root, - rootModule, - modules, - libraries, - cbtLibraries, - scalaCompilers + name = rootModule.name, + root = rootModule.root, + rootModule = rootModule, + modules = modules, + libraries = libraries, + cbtLibraries = cbtLibraries, + scalaCompilers = scalaCompilers ) } - private def convertCbtLibraries = - transitiveBuilds(DirectoryDependency(rootBuild.context.cbtHome)(rootBuild.context).dependenciesArray.head.asInstanceOf[BaseBuild]) + + private def convertCbtLibraries = { + val cbtBuild = + DirectoryDependency(rootBuild.context.cbtHome)(rootBuild.context).dependenciesArray.head.asInstanceOf[BaseBuild] + transitiveBuilds(Seq((cbtBuild, ModuleType.Default)), skipTests = true) + .map(_._1) .collect { case d: BoundMavenDependency => d.jar case d: PackageJars => d.jar.get } .map(exportLibrary) .distinct + } private def collectDependencies(dependencies: Seq[Dependency]): Seq[ModuleDependency] = dependencies @@ -107,7 +133,7 @@ object BuildInformation { } .flatten - private def exportModule(build: BaseBuild): Module = { + private def exportModule(build: BaseBuild, moduleType: ModuleType): Module = { val moduleDependencies = collectDependencies(build.dependencies) val mavenDependencies = build.dependencies .collect { case d: BoundMavenDependency => BinaryDependency(formatMavenDependency(d.mavenDependency)) } @@ -130,6 +156,7 @@ object BuildInformation { scalaVersion = build.scalaVersion, sourceDirs = sourceDirs, target = build.target, + moduleType = moduleType, binaryDependencies = mavenDependencies, moduleDependencies = moduleDependencies, classpath = classpath, @@ -153,30 +180,33 @@ object BuildInformation { .map(_.toFile) } - private def collectLazyBuilds(dependency: Dependency): Option[BaseBuild] = - dependency match { - case l: LazyDependency => - l.dependency match { - case d: BaseBuild => Some(d) - case d: LazyDependency => collectLazyBuilds(d.dependency) - case _ => None - } - case d: BaseBuild => Some(d) - case _ => None - } - - - private def transitiveBuilds(build: BaseBuild): Seq[BaseBuild] = - (build +: build.transitiveDependencies) - .collect { - case d: BaseBuild => d +: collectParentBuilds(d).flatMap(transitiveBuilds) - case d: LazyDependency => - collectLazyBuilds(d.dependency) - .toSeq - .flatMap(transitiveBuilds) + // More effectively to call on a all builds at once rather than on one per time + private def transitiveBuilds(builds: Seq[(BaseBuild, ModuleType)], skipTests: Boolean = false): Seq[(BaseBuild, ModuleType)] = { + def traverse(visitedProd: (Seq[BaseBuild], Seq[ModuleType]), buildProd: (BaseBuild, ModuleType)): (Seq[BaseBuild], Seq[ModuleType]) = { + val (visited, moduleTypes) = visitedProd + val (build, moduleType) = buildProd + if (visited.contains(build)) + (visited, moduleTypes) + else { + val testBuildSeq = if (!skipTests) testBuild(build) else Seq.empty + (build.transitiveDependencies.map(d => (d, ModuleType.Default)) ++ + parentBuild(build).map(d => (d, ModuleType.Build)) ++ + testBuildSeq.map(d => (d, ModuleType.Test)) + ) + .collect { + case (d: BaseBuild, t) => + (d, t) + case (d: LazyDependency, t) if d.dependency.isInstanceOf[BaseBuild] => + (d.dependency.asInstanceOf[BaseBuild], t) + } + .filterNot(b => visited.contains(b._1)) + .foldLeft(build +: visited, moduleType +: moduleTypes)(traverse) } - .flatten - .distinct + } + val (collectedBuilds, collectedTypes) = builds + .foldLeft(Seq.empty[BaseBuild], Seq.empty[ModuleType])(traverse) + collectedBuilds.zip(collectedTypes) + } private def exportLibrary(dependency: BoundMavenDependency) = { val name = formatMavenDependency(dependency.mavenDependency) @@ -190,14 +220,14 @@ object BuildInformation { implicit val transientCache: java.util.Map[AnyRef, AnyRef] = rootBuild.context.transientCache implicit val classLoaderCache: ClassLoaderCache = rootBuild.context.classLoaderCache val sourceJars = jars - .map { d => + .map { d => Try(d.copy(mavenDependency = d.mavenDependency.copy(classifier = Classifier.sources)).jar) - } + } .flatMap { case Success(j) => Some(j) case Failure(e) => logger.log("ExportBuildInformation", s"Can not load a $name library sources. Skipping") - None + None } .map(LibraryJar(_, JarType.Source)) Library(name, binaryJars ++ sourceJars) @@ -206,15 +236,22 @@ object BuildInformation { private def exportLibrary(file: File) = Library("CBT:" + file.getName.stripSuffix(".jar"), Seq(LibraryJar(file, JarType.Binary))) - private def collectParentBuilds(build: BaseBuild): Seq[BaseBuild] = + private def parentBuild(build: BaseBuild): Seq[BaseBuild] = build.context.parentBuild .map(_.asInstanceOf[BaseBuild]) - .map(b => b +: collectParentBuilds(b)) .toSeq - .flatten - private def resolveScalaCompiler(build: BaseBuild, scalaVersion: String) = - build.Resolver(mavenCentral, sonatypeReleases).bindOne( + private def testBuild(build: BaseBuild): Seq[BaseBuild] = + Try(build.test) + .toOption + .toSeq + .flatMap { + case testBuild: BaseBuild => Seq(testBuild) + case _ => Seq.empty + } + + private def resolveScalaCompiler(scalaVersion: String) = + rootBuild.Resolver(mavenCentral, sonatypeReleases).bindOne( MavenDependency("org.scala-lang", "scala-compiler", scalaVersion) ).classpath.files @@ -222,8 +259,7 @@ object BuildInformation { if (source.isDirectory) source else - source.getParentFile //Let's asume that for now - + source.getParentFile //Let's assume that for now private def formatMavenDependency(dependency: cbt.MavenDependency) = s"${dependency.groupId}:${dependency.artifactId}:${dependency.version}" @@ -237,12 +273,30 @@ object BuildInformation { .stripPrefix("/") .replace("/", "-") } + + case class ExportParameters(extraModulePaths: Seq[File], needCbtLibs: Boolean, outputFile: Option[File]) + + object ExportParameters { + def apply(args: Seq[String]): ExportParameters = { + val argumentParser = new ArgumentParser(args) + val extraModulePaths = argumentParser.value("extraModules") + .map(_.split(":").toSeq) + .getOrElse(Seq.empty) + .map(p => new File(p)) + .filter(f => f.exists && f.isDirectory) + val needCbtLibs: Boolean = argumentParser.value("needCbtLibs").forall(_.toBoolean) + val outputFile = argumentParser.value("outputFile").map(p => new File(p)) + ExportParameters(extraModulePaths, needCbtLibs, outputFile) + } + } + } + } object BuildInformationSerializer { def serialize(project: BuildInformation.Project): Node = - <project name={project.name} root={project.root.toString} rootModule={project.rootModule.name}> + <project name={project.name} root={project.root.getPath} rootModule={project.rootModule.name}> <modules> {project.modules.map(serialize)} </modules> @@ -258,21 +312,21 @@ object BuildInformationSerializer { </project> private def serialize(module: BuildInformation.Module): Node = - <module name={module.name} root={module.root.toString} target={module.target.toString} scalaVersion={module.scalaVersion}> + <module name={module.name} root={module.root.getPath} target={module.target.getPath} scalaVersion={module.scalaVersion} type={module.moduleType.name}> <sourceDirs> - {module.sourceDirs.map(d => <dir>{d}</dir>)} + {module.sourceDirs.map(d => <dir>{d.getPath: String}</dir>)} </sourceDirs> <scalacOptions> - {module.scalacOptions.map(o => <option>{o}</option>)} + {module.scalacOptions.map(o => <option>{o: String}</option>)} </scalacOptions> <dependencies> {module.binaryDependencies.map(serialize)} {module.moduleDependencies.map(serialize)} </dependencies> <classpath> - {module.classpath.map(c => <classpathItem>{c.toString}</classpathItem>)} + {module.classpath.map(c => <classpathItem>{c.getPath: String}</classpathItem>)} </classpath> - {module.parentBuild.map(p => <parentBuild>{p}</parentBuild>).getOrElse(NodeSeq.Empty)} + {module.parentBuild.map(p => <parentBuild>{p: String}</parentBuild>).getOrElse(NodeSeq.Empty)} </module> private def serialize(binaryDependency: BuildInformation.BinaryDependency): Node = @@ -280,14 +334,36 @@ object BuildInformationSerializer { private def serialize(library: BuildInformation.Library): Node = <library name={library.name}> - {library.jars.map(j => <jar type={j.jarType.toString}>{j.jar}</jar>)} + {library.jars.map(j => <jar type={j.jarType.name}>{j.jar.getPath: String}</jar>)} </library> + private def serialize(moduleDependency: BuildInformation.ModuleDependency): Node = + <moduleDependency> + {moduleDependency.name: String} + </moduleDependency> + private def serialize(compiler: BuildInformation.ScalaCompiler): Node = <compiler version={compiler.version}> - {compiler.jars.map(j => <jar>{j}</jar>)} + {compiler.jars.map(j => <jar>{j.getPath: String}</jar>)} </compiler> +} - private def serialize(moduleDependency: BuildInformation.ModuleDependency): Node = - <moduleDependency>{moduleDependency.name}</moduleDependency> + +class ArgumentParser(arguments: Seq[String]) { + private val argumentsMap = (arguments :+ "") + .sliding(2) + .map(_.toList) + .foldLeft(Map.empty[String, Option[String]]) { + case (m, Seq(k: String, v: String)) if k.startsWith("--") && !v.startsWith("--") => + m + (k.stripPrefix("--") -> Some(v)) + case (m, (k: String) :: _) if k.startsWith("--") => + m + (k.stripPrefix("--") -> None) + case (m, _) => m + } + + def value(key: String): Option[String] = + argumentsMap.get(key.stripPrefix("--")).flatten + + def persists(key: String): Boolean = + argumentsMap.isDefinedAt(key) }
\ No newline at end of file |