package cbt
import java.io.File
import java.net.URL
import java.nio.file.Files
import java.nio.file.attribute.FileTime
trait Frege extends BaseBuild{
def fregeVersion: String = "3.24.100.1"
def classifier: Option[String] = Some("jdk8")
def fregeTarget: String = "1.8"
def enableMakeMode = true
def enableOptimisation = true
def fregeDependencies: Seq[Dependency] = dependencies
def inline = true
private def fregeOptions: Seq[String] = {
val opts : Seq[(String, Boolean)] = Seq(("-make", enableMakeMode), ("-O", enableOptimisation), ("-inline", inline) )
opts.filter(_._2).map(_._1)
}
override def scalaTarget: File = target ++ s"/frege-$fregeVersion"
private lazy val fregeLib = new FregeLib(
context.cbtLastModified, context.paths.mavenCache,
fregeVersion = fregeVersion, classifier = classifier,
fregeDependencies = fregeDependencies, fregeTarget = fregeTarget
)
override def sourceFileFilter(file: File): Boolean = file.toString.endsWith(".fr") || file.toString.endsWith(".java")
override def compile: Option[Long] = taskCache[Frege]("compile").memoize{
fregeLib.compile(
sourceFiles, compileTarget, compileStatusFile, dependencies, fregeOptions
)
}
override def dependencies = Resolver(mavenCentral).bind(
MavenDependency("org.frege-lang","frege",fregeVersion, Classifier(classifier))
)
}
class FregeLib(
cbtLastModified: Long,
mavenCache: File,
fregeVersion: String,
classifier: Option[String],
fregeDependencies: Seq[Dependency],
fregeTarget: String
)(implicit transientCache: java.util.Map[AnyRef,AnyRef], classLoaderCache: ClassLoaderCache, logger: Logger){
val lib = new Lib(logger)
import lib._
private def Resolver(urls: URL*) = MavenResolver(cbtLastModified, mavenCache, urls: _*)
private lazy val fregeDependency = Resolver(mavenCentral).bindOne(
MavenDependency("org.frege-lang","frege",fregeVersion, Classifier(classifier))
)
def compile(
sourceFiles: Seq[File],
compileTarget: File,
statusFile: File,
dependencies: Seq[Dependency],
fregeOptions: Seq[String]
)(implicit classLoaderCache: ClassLoaderCache): Option[Long] = {
val d = Dependencies(dependencies)
val classpath = d.classpath
val cp = classpath.string
def lastModified = (
cbtLastModified +: d.lastModified +: sourceFiles.map(_.lastModified)
).max
if( sourceFiles.isEmpty ){
None
} else {
val start = System.currentTimeMillis
val lastCompiled = statusFile.lastModified
if( lastModified > lastCompiled ){
val _class = "frege.compiler.Main"
val fp = (fregeDependency.classpath.strings ++ fregeDependencies.map(_.classpath.string))
val dualArgs =
Seq(
"-target", fregeTarget,
"-d", compileTarget.toString
) ++ (
if(fp.isEmpty) Nil else Seq("-fp", fp.mkString(":"))
)
val singleArgs = fregeOptions
val code =
try{
System.err.println("Compiling with Frege to " ++ compileTarget.toString)
compileTarget.mkdirs
redirectOutToErr{
fregeDependency.runMain(
_class,
dualArgs ++ singleArgs ++ sourceFiles.map(_.toString)
)
}
} catch {
case e: Exception =>
System.err.println(red("Frege crashed. To reproduce run:"))
System.out.println(s"""
java -cp \\
${fregeDependency.classpath.strings.mkString(":\\\n")} \\
\\
${_class} \\
\\
${dualArgs.grouped(2).map(_.mkString(" ")).mkString(" \\\n")} \\
\\
${singleArgs.mkString(" \\\n")} \\
\\
-bootclasspath \\
${fregeDependency.classpath.strings.mkString(":\\\n")} \\
${if(classpath.strings.isEmpty) "" else (" -fp \\\n" ++ classpath.strings.mkString(":\\\n"))} \\
\\
${sourceFiles.sorted.mkString(" \\\n")}
"""
)
ExitCode.Failure
}
if(code == ExitCode.Success){
// write version and when last compilation started so we can trigger
// recompile if cbt version changed or newer source files are seen
write(statusFile, "")//cbtVersion.getBytes)
Files.setLastModifiedTime(statusFile.toPath, FileTime.fromMillis(start) )
} else {
System.exit(code.integer) // FIXME: let's find a better solution for error handling. Maybe a monad after all.
}
Some( start )
} else {
Some( lastCompiled )
}
}
}
}