aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md25
-rw-r--r--examples/frege-example/build/build.scala8
-rw-r--r--examples/frege-example/src/Main.fr3
-rw-r--r--stage2/BasicBuild.scala7
-rw-r--r--stage2/Lib.scala5
-rw-r--r--stage2/plugins/Frege.scala136
6 files changed, 165 insertions, 19 deletions
diff --git a/README.md b/README.md
index a21ce0f..c353503 100644
--- a/README.md
+++ b/README.md
@@ -36,25 +36,25 @@ How is CBT different from other build tools?
Not all build tools allow you to write builds in a full programming language.
CBT is based on the assumption that builds are complex enough problems to
warrant this and abstraction and re-use is better handled through libraries
-rather than some restricted, declarative DSL. CBT shares this philosophy with SBT.
+rather than some restricted, declarative DSL. CBT shares this philosophy with sbt.
(This also means that integration with external tools such as an IDE better happens
programmatically through an api rather than a static data representation such as xml.)
-Like SBT, CBT chooses Scala as its language of choice, trying to appeal to
+Like sbt, CBT chooses Scala as its language of choice, trying to appeal to
Scala programmers allowing them to re-use their knowledge and the safety of the language.
-Unlike SBT 0.11 and later, CBT maps task execution to JVM method invocations.
-SBT implements its own self-contained task graph model and interpreter.
-This allows SBT to have its model exactly fit the requirements.
+Unlike sbt 0.11 and later, CBT maps task execution to JVM method invocations.
+sbt implements its own self-contained task graph model and interpreter.
+This allows sbt to have its model exactly fit the requirements.
CBT instead uses existing JVM concepts for the solution and adds
custom concepts only when necessary. CBT assumes this to lead to better
ease of use due to familarity and better integration with existing tools
such as interactive debuggers or stack traces because CBT's task call stack
IS the JVM call stack.
-SBT 0.7 shared this design decision as many may have forgotten.
-However, CBT is still quite a bit simpler than even SBT 0.7 as
-CBT gets away with fewer concepts. SBT 0.7 had notions of
+sbt 0.7 shared this design decision as many may have forgotten.
+However, CBT is still quite a bit simpler than even sbt 0.7 as
+CBT gets away with fewer concepts. sbt 0.7 had notions of
main vs test sources, multi-project builds, task-dependencies
which weren't invocations and other concepts. CBT maps all of
these to def invocations and build composition instead.
@@ -153,7 +153,7 @@ $ cbt tools createBuild
Now there should be a file `build/build.scala` with a sample `Build` class.
-Btw., a build file can have its own build and so on recursively like in SBT.
+Btw., a build file can have its own build and so on recursively like in sbt.
When you create a file `build/build/build.scala` and change `Build` class in there
to extend `BuildBuild`, it will be used to build your `build/build.scala`. You can
add built-time dependencies like plugins this way.
@@ -163,7 +163,7 @@ add built-time dependencies like plugins this way.
In the generated `build/build.scala` there are
several examples for dependencies. We recommend using the constructor syntax
`ScalaDependency` (for automatically adding the scala version to the artifact id)
-or `MavenDependency` (for leaving the artifact id as is). The SBT-Style `%`-DSL
+or `MavenDependency` (for leaving the artifact id as is). The sbt-style `%`-DSL
syntax is also supported for copy-and-paste convenience, but discouraged.
Alright, let's enable the `override def dependencies`. Make sure to include
@@ -468,7 +468,7 @@ Scala.js support
----------------
CBT supports cross-project Scala.js builds.
-It preserves same structure as in SBT (https://www.scala-js.org/doc/project/cross-build.html)
+It preserves same structure as in sbt (https://www.scala-js.org/doc/project/cross-build.html)
1. Example for user scalajs project is in: `$CBT_HOME/cbt/examples/build-scalajs`
2. `$CBT_HOME/cbt compile`
@@ -494,5 +494,4 @@ CBT productivity hacks
only show first 20 lines of type errors to catch the root ones
```
cbt c 2>&1 | head -n 20
-``
-
+```
diff --git a/examples/frege-example/build/build.scala b/examples/frege-example/build/build.scala
new file mode 100644
index 0000000..26eac90
--- /dev/null
+++ b/examples/frege-example/build/build.scala
@@ -0,0 +1,8 @@
+import cbt._
+
+class Build(val context: Context) extends Frege {
+ override def classifier = Some("jdk7")
+ override def inline = false
+ override def fregeSource = "1.7"
+ override def fregeTarget = "1.7"
+}
diff --git a/examples/frege-example/src/Main.fr b/examples/frege-example/src/Main.fr
new file mode 100644
index 0000000..b485101
--- /dev/null
+++ b/examples/frege-example/src/Main.fr
@@ -0,0 +1,3 @@
+module Main where
+
+main = putStrLn "Hello World."
diff --git a/stage2/BasicBuild.scala b/stage2/BasicBuild.scala
index ef5411a..750b0bc 100644
--- a/stage2/BasicBuild.scala
+++ b/stage2/BasicBuild.scala
@@ -84,10 +84,13 @@ trait BaseBuild extends BuildInterface with DependencyImplementation with Trigge
def compileStatusFile: File = compileTarget ++ ".last-success"
/** 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(lib.sourceFileFilter)
+ 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] = lib.sourceFiles(sources)
+ final def sourceFiles: Seq[File] = lib.sourceFiles(sources, sourceFileFilter)
protected def logEmptySourceDirectories(): Unit = {
val nonExisting =
diff --git a/stage2/Lib.scala b/stage2/Lib.scala
index a76e281..45803b4 100644
--- a/stage2/Lib.scala
+++ b/stage2/Lib.scala
@@ -266,10 +266,7 @@ final class Lib(val logger: Logger) extends Stage1Lib(logger) with Scaffold{
def dirname(path: File): File = new File(realpath(path).string.stripSuffix("/").split("/").dropRight(1).mkString("/"))
def nameAndContents(file: File) = basename(file) -> readAllBytes(file.toPath)
- /** Which file endings to consider being source files. */
- def sourceFileFilter(file: File): Boolean = file.toString.endsWith(".scala") || file.toString.endsWith(".java")
-
- def sourceFiles( sources: Seq[File], sourceFileFilter: File => Boolean = sourceFileFilter ): Seq[File] = {
+ def sourceFiles( sources: Seq[File], sourceFileFilter: File => Boolean ): Seq[File] = {
for {
base <- sources.filter(_.exists).map(lib.realpath)
file <- lib.listFilesRecursive(base) if file.isFile && sourceFileFilter(file)
diff --git a/stage2/plugins/Frege.scala b/stage2/plugins/Frege.scala
new file mode 100644
index 0000000..88e1f59
--- /dev/null
+++ b/stage2/plugins/Frege.scala
@@ -0,0 +1,136 @@
+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[String] = dependencies.map{ _.classpath.string}
+ 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(
+ logger, context.cbtHasChanged, context.paths.mavenCache,
+ context.classLoaderCache, fregeVersion = fregeVersion, classifier = classifier,
+ fregeDependencies = fregeDependencies, fregeTarget = fregeTarget
+ )
+
+ override def sourceFileFilter(file: File): Boolean = file.toString.endsWith(".fr") || file.toString.endsWith(".java")
+
+ private object compileCache extends Cache[Option[File]]
+ override def compile: Option[File] = compileCache{
+ fregeLib.compile(
+ needsUpdate || context.parentBuild.map(_.needsUpdate).getOrElse(false),
+ sourceFiles, compileTarget, compileStatusFile, compileClasspath,
+ fregeOptions
+ )
+ }
+
+ override def dependencies = Resolver(mavenCentral).bind(
+ MavenDependency("org.frege-lang","frege",fregeVersion, Classifier(classifier))
+ )
+
+}
+
+class FregeLib(
+ logger: Logger,
+ cbtHasChanged: Boolean,
+ mavenCache: File,
+ classLoaderCache: ClassLoaderCache,
+ fregeVersion: String,
+ classifier: Option[String],
+ fregeDependencies: Seq[String],
+ fregeTarget: String
+){
+ val lib = new Lib(logger)
+ import lib._
+
+ private def Resolver(urls: URL*) = MavenResolver(cbtHasChanged, mavenCache, urls: _*)
+ private lazy val fregeDependency = Resolver(mavenCentral).bindOne(
+ MavenDependency("org.frege-lang","frege",fregeVersion, Classifier(classifier))
+ )
+
+ def compile(
+ needsRecompile: Boolean,
+ files: Seq[File],
+ compileTarget: File,
+ statusFile: File,
+ classpath: ClassPath,
+ fregeOptions: Seq[String]
+ ): Option[File] = {
+
+ if(classpath.files.isEmpty)
+ throw new Exception("Trying to compile with empty classpath. Source files: " ++ files.toString)
+
+ if( files.isEmpty ){
+ None
+ }else{
+ if( needsRecompile ){
+ val start = System.currentTimeMillis
+
+ val _class = "frege.compiler.Main"
+ val dualArgs =
+ Seq(
+ "-target", fregeTarget,
+ "-d", compileTarget.toString,
+ "-fp", (fregeDependency.classpath.strings ++ fregeDependencies).mkString(":")
+ )
+ val singleArgs = fregeOptions
+ val code =
+ try{
+ System.err.println("Compiling with Frege to " ++ compileTarget.toString)
+ compileTarget.mkdirs
+ redirectOutToErr{
+ lib.runMain(
+ _class,
+ dualArgs ++ singleArgs ++ files.map(_.toString),
+ fregeDependency.classLoader(classLoaderCache)
+ )
+ }
+ } 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")} \\
+-classpath \\
+${classpath.strings.mkString(":\\\n")} \\
+\\
+${files.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( compileTarget )
+ }
+ }
+}