package cbt import cbt.paths._ import java.io._ import java.lang.reflect.InvocationTargetException import java.net._ import java.nio.file.{Path =>_,_} import java.nio.file.Files.readAllBytes import java.security.MessageDigest import java.util.jar._ import scala.collection.immutable.Seq import scala.reflect.runtime.{universe => ru} import scala.util._ import ammonite.ops.{cwd => _,_} abstract class PackageBuild(context: Context) extends Build(context) with ArtifactInfo{ def `package`: Seq[File] = lib.concurrently( enableConcurrency )( Seq(() => jar, () => docJar, () => srcJar) )( _() ) private object cacheJarBasicBuild extends Cache[File] def jar: File = cacheJarBasicBuild{ lib.jar( artifactId, version, compile, jarTarget ) } private object cacheSrcJarBasicBuild extends Cache[File] def srcJar: File = cacheSrcJarBasicBuild{ lib.srcJar(sources, artifactId, version, scalaTarget) } private object cacheDocBasicBuild extends Cache[File] def docJar: File = cacheDocBasicBuild{ lib.docJar( sources, dependencyClasspath, apiTarget, jarTarget, artifactId, version, scalacOptions ) } override def jars = jar +: dependencyJars override def exportedJars: Seq[File] = Seq(jar) } abstract class PublishBuild(context: Context) extends PackageBuild(context){ def name = artifactId def description: String def url: URL def developers: Seq[Developer] def licenses: Seq[License] def scmUrl: String def scmConnection: String def pomExtra: Seq[scala.xml.Node] = Seq() // ========== package ========== /** put additional xml that should go into the POM file in here */ def pom: File = lib.pom( groupId = groupId, artifactId = artifactId, version = version, name = name, description = description, url = url, developers = developers, licenses = licenses, scmUrl = scmUrl, scmConnection = scmConnection, dependencies = dependencies, pomExtra = pomExtra, jarTarget = jarTarget ) // ========== publish ========== final protected def releaseFolder = s"/${groupId.replace(".","/")}/$artifactId/$version/" def snapshotUrl = new URL("https://oss.sonatype.org/content/repositories/snapshots") def releaseUrl = new URL("https://oss.sonatype.org/service/local/staging/deploy/maven2") def publishSnapshot: Unit = lib.publishSnapshot(sourceFiles, pom +: `package`, new URL(snapshotUrl + releaseFolder) ) def publishSigned: Unit = lib.publishSigned(sourceFiles, pom +: `package`, new URL(releaseUrl + releaseFolder) ) } class BasicBuild(context: Context) extends Build(context) class Build(val context: Context) extends Dependency with TriggerLoop{ // library available to builds final val logger = context.logger override final protected val lib: Lib = new Lib(logger) // ========== general stuff ========== def enableConcurrency = false final def projectDirectory: File = new File(context.cwd) assert( projectDirectory.exists, "projectDirectory does not exist: "+projectDirectory ) final def usage: Unit = new lib.ReflectBuild(this).usage /* def scaffold: Unit = lib.generateBasicBuildFile( projectDirectory, scalaVersion, groupId, artifactId, version ) */ // ========== meta data ========== def scalaVersion: String = constants.scalaVersion final def scalaMajorVersion: String = scalaVersion.split("\\.").take(2).mkString(".") def zincVersion = "0.3.9" def dependencies: Seq[Dependency] = Seq( "org.scala-lang" % "scala-library" % scalaVersion ) // ========== paths ========== final private val defaultSourceDirectory = new File(projectDirectory+"/src/") /** base directory where stuff should be generated */ def target = new File(projectDirectory+"/target") /** base directory where stuff should be generated for this scala version*/ def scalaTarget = new File(target + s"/scala-$scalaMajorVersion") /** directory where jars (and the pom file) should be put */ def jarTarget = scalaTarget /** directory where the scaladoc should be put */ def apiTarget = new File(scalaTarget + "/api") /** directory where the class files should be put (in package directories) */ def compileTarget = new File(scalaTarget + "/classes") /** Source directories and files. Defaults to .scala and .java files in src/ and top-level. */ def sources: Seq[File] = Seq(defaultSourceDirectory) ++ projectDirectory.listFiles.toVector.filter(sourceFileFilter) /** Which file endings to consider being source files. */ def sourceFileFilter(file: File): Boolean = file.toString.endsWith(".scala") || file.toString.endsWith(".java") /** Absolute path names for all individual files found in sources directly or contained in directories. */ final def sourceFiles: Seq[File] = for { base <- sources.filter(_.exists).map(lib.realpath) file <- lib.listFilesRecursive(base) if file.isFile && sourceFileFilter(file) } yield file protected def assertSourceDirectories(): Unit = { val nonExisting = sources .filterNot( _.exists ) .diff( Seq(defaultSourceDirectory) ) assert( nonExisting.isEmpty, "Some sources do not exist: \n"+nonExisting.mkString("\n") ) } assertSourceDirectories() /** SBT-like dependency builder DSL */ class GroupIdAndArtifactId( groupId: String, artifactId: String ){ def %(version: String) = new MavenDependency(groupId, artifactId, version)(lib.logger) } implicit class DependencyBuilder(groupId: String){ def %%(artifactId: String) = new GroupIdAndArtifactId( groupId, artifactId+"_"+scalaMajorVersion ) def %(artifactId: String) = new GroupIdAndArtifactId( groupId, artifactId ) } final def BuildDependency(path: String) = cbt.BuildDependency( context.copy( cwd = path, args = Seq() ) ) def triggerLoopFiles: Seq[File] = sources ++ transitiveDependencies.collect{ case b: TriggerLoop => b.triggerLoopFiles }.flatten def localJars : Seq[File] = Seq(projectDirectory + "/lib/") .map(new File(_)) .filter(_.exists) .flatMap(_.listFiles) .filter(_.toString.endsWith(".jar")) //def cacheJar = false override def dependencyClasspath : ClassPath = ClassPath(localJars) ++ super.dependencyClasspath override def dependencyJars : Seq[File] = localJars ++ super.dependencyJars def exportedClasspath : ClassPath = ClassPath(Seq(compile)) def exportedJars: Seq[File] = Seq() // ========== compile, run, test ========== /** scalac options used for zinc and scaladoc */ def scalacOptions: Seq[String] = Seq( "-feature", "-deprecation", "-unchecked" ) val updated: Boolean = { val existingClassFiles = lib.listFilesRecursive(compileTarget) val sourcesChanged = existingClassFiles.nonEmpty && { val oldestClassFile = existingClassFiles.sortBy(_.lastModified).head val oldestClassFileAge = oldestClassFile.lastModified val changedSourceFiles = sourceFiles.filter(_.lastModified > oldestClassFileAge) if(changedSourceFiles.nonEmpty){ /* println(changedSourceFiles) println(changedSourceFiles.map(_.lastModified)) println(changedSourceFiles.map(_.lastModified > oldestClassFileAge)) println(oldestClassFile) println(oldestClassFileAge) println("-"*80) */ } changedSourceFiles.nonEmpty } sourcesChanged || transitiveDependencies.map(_.updated).fold(false)(_ || _) } private object cacheCompileBasicBuild extends Cache[File] def compile: File = cacheCompileBasicBuild{ //println(transitiveDependencies.filter(_.updated).mkString("\n")) lib.compile( updated, sourceFiles, compileTarget, dependencyClasspath, scalacOptions, zincVersion = zincVersion, scalaVersion = scalaVersion ) } def runClass: String = "Main" def run: Unit = lib.runMainIfFound( runClass, Seq(), classLoader ) def test: Unit = lib.test(context) context.logger.composition(">"*80) context.logger.composition("class "+this.getClass) context.logger.composition("dir "+context.cwd) context.logger.composition("sources "+sources.toList.mkString(" ")) context.logger.composition("target "+target) context.logger.composition("dependencyTree\n"+dependencyTree) context.logger.composition("<"*80) // ========== cbt internals ========== private[cbt] def finalBuild = this override def show = this.getClass.getSimpleName + "("+context.cwd+")" }