aboutsummaryrefslogblamecommitdiff
path: root/stage2/DefaultBuild.scala
blob: c0072ffea3eb304db59f00b0c3fab443984b9835 (plain) (tree)








































































































































































































































                                                                                                                                  
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+")"
}