summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoman Timushev <rtimush@gmail.com>2017-12-29 22:23:57 +0100
committerGitHub <noreply@github.com>2017-12-29 22:23:57 +0100
commit81d4cfb6f63512b314fbbf49c4ba415aedc469d7 (patch)
tree384952c3be58592a9d54af40f4ce7edefed3eee5
parent19ecb2b4b9d6e91c6a765e7dbae79e3c1e968888 (diff)
parent417f2ea33202c4dd5d3732cb9fbc6a35f8b7b4ed (diff)
downloadmill-81d4cfb6f63512b314fbbf49c4ba415aedc469d7.tar.gz
mill-81d4cfb6f63512b314fbbf49c4ba415aedc469d7.tar.bz2
mill-81d4cfb6f63512b314fbbf49c4ba415aedc469d7.zip
Merge pull request #67 from lihaoyi/scalajs
Scala.js support
-rw-r--r--build.sbt41
-rwxr-xr-xbuild.sc37
-rw-r--r--core/src/test/scala/mill/util/TestEvaluator.scala (renamed from scalaplugin/src/test/scala/mill/scalaplugin/TestEvaluator.scala)3
-rw-r--r--scalajsplugin/bridge_0_6/src/main/scala/mill/scalajsplugin/bridge/ScalaJSLinkerBridge.scala24
-rw-r--r--scalajsplugin/bridge_1_0/src/main/scala/mill/scalajsplugin/bridge/ScalaJSLinkerBridge.scala24
-rw-r--r--scalajsplugin/src/main/scala/mill/scalajsplugin/Lib.scala62
-rw-r--r--scalajsplugin/src/main/scala/mill/scalajsplugin/ScalaJSLinkerBridge.scala12
-rw-r--r--scalajsplugin/src/main/scala/mill/scalajsplugin/ScalaJSModule.scala69
-rw-r--r--scalajsplugin/src/test/resource/hello-js-world/src/main/scala/Main.scala3
-rw-r--r--scalajsplugin/src/test/scala/mill/scalajsplugin/HelloJSWorldTests.scala160
-rw-r--r--scalaplugin/src/main/scala/mill/scalaplugin/Lib.scala4
-rw-r--r--scalaplugin/src/main/scala/mill/scalaplugin/ScalaModule.scala31
-rw-r--r--scalaplugin/src/test/scala/mill/scalaplugin/AcyclicTests.scala5
-rw-r--r--scalaplugin/src/test/scala/mill/scalaplugin/BetterFilesTests.scala1
-rw-r--r--scalaplugin/src/test/scala/mill/scalaplugin/HelloWorldTests.scala3
-rw-r--r--scalaplugin/src/test/scala/mill/scalaplugin/JawnTests.scala1
-rwxr-xr-xtest.sh5
17 files changed, 456 insertions, 29 deletions
diff --git a/build.sbt b/build.sbt
index 31a21556..716559bd 100644
--- a/build.sbt
+++ b/build.sbt
@@ -1,3 +1,5 @@
+import java.io.File
+
val sharedSettings = Seq(
scalaVersion := "2.12.4",
organization := "com.lihaoyi",
@@ -131,9 +133,42 @@ lazy val scalaplugin = project
baseDirectory in Test := (baseDirectory in Test).value / "..",
javaOptions := bridgeProps.value.toSeq
)
-
+lazy val scalajsplugin = project
+ .dependsOn(scalaplugin % "compile->compile;test->test")
+ .settings(
+ sharedSettings,
+ name := "mill-scalajsplugin",
+ fork in Test := true,
+ baseDirectory in (Test, test) := (baseDirectory in (Test, test)).value / "..",
+ javaOptions in (Test, test) := jsbridgeProps.value.toSeq
+ )
+def jsbridge(binary: String, version: String) =
+ Project(
+ id = "scalajsbridge_" + binary.replace('.', '_'),
+ base = file("scalajsplugin/bridge_" + binary.replace('.', '_'))
+ )
+ .settings(
+ organization := "com.lihaoyi",
+ scalaVersion := "2.12.4",
+ name := "mill-js-bridge",
+ libraryDependencies ++= Seq("org.scala-js" %% "scalajs-tools" % version)
+ )
+lazy val scalajsbridge_0_6 = jsbridge("0.6", "0.6.21")
+lazy val scalajsbridge_1_0 = jsbridge("1.0", "1.0.0-M2")
+val jsbridgeProps = Def.task{
+ def bridgeClasspath(depClasspath: Classpath, jar: File) = {
+ (depClasspath.files :+ jar).map(_.absolutePath).mkString(File.pathSeparator)
+ }
+ val mapping = Map(
+ "MILL_SCALAJS_BRIDGE_0_6" -> bridgeClasspath((dependencyClasspath in (scalajsbridge_0_6, Compile)).value,
+ (packageBin in (scalajsbridge_0_6, Compile)).value),
+ "MILL_SCALAJS_BRIDGE_1_0" -> bridgeClasspath((dependencyClasspath in (scalajsbridge_1_0, Compile)).value,
+ (packageBin in (scalajsbridge_1_0, Compile)).value)
+ )
+ for((k, v) <- mapping) yield s"-D$k=$v"
+}
lazy val bin = project
- .dependsOn(scalaplugin)
+ .dependsOn(scalaplugin, scalajsplugin)
.settings(
sharedSettings,
fork := true,
@@ -146,7 +181,7 @@ lazy val bin = project
prependShellScript = Some(
Seq(
"#!/usr/bin/env sh",
- s"""exec java ${bridgeProps.value.mkString(" ")} -cp "$$0" mill.Main "$$@" """
+ s"""exec java ${(bridgeProps.value ++ jsbridgeProps.value).mkString(" ")} -cp "$$0" mill.Main "$$@" """
)
)
)
diff --git a/build.sc b/build.sc
index 9ee3001d..a9e63b43 100755
--- a/build.sc
+++ b/build.sc
@@ -1,4 +1,6 @@
import $file.shared
+import java.io.File
+
import ammonite.ops._
import coursier.maven.MavenRepository
import mill._
@@ -10,7 +12,7 @@ trait MillPublishModule extends PublishModule {
def pomSettings = PomSettings(
organization = "com.lihaoyi",
- description = publishName(),
+ description = artifactId(),
developers = Seq(Developer("lihaoyi", "Li Haoyi", "https://github.com/lihaoyi/mill")),
licenses = Seq(License("MIT License", "https://spdx.org/licenses/MIT.html#licenseText")),
scm = SCM("https://github.com/lihaoyi/mill", "scm:git:https://github.com/lihaoyi/mill.git"),
@@ -143,8 +145,37 @@ object ScalaPlugin extends MillModule {
}
}
}
+val jsbridges = for{
+ scalajsBinary <- mill.define.Cross("0.6", "1.0")
+} yield new MillModule{
+ def basePath = pwd / 'scalajsplugin / s"bridge_${scalajsBinary.replace('.', '_')}"
+ val scalajsVersion = scalajsBinary match {
+ case "0.6" => "0.6.21"
+ case "1.0" => "1.0.0-M2"
+ }
+ override def ivyDeps = Seq(
+ Dep("org.scala-js", "scalajs-tools", scalajsVersion)
+ )
+}
+
+object ScalaJSPlugin extends MillModule {
+
+ def projectDeps = Seq(ScalaPlugin)
+ def basePath = pwd / 'scalajsplugin
+
+ def bridgeClasspath(runDepClasspath: Seq[PathRef], classes: PathRef) =
+ (runDepClasspath :+ classes).map(_.path).mkString(File.pathSeparator)
+ def testArgs = T{
+ val mapping = Map(
+ "MILL_SCALAJS_BRIDGE_0_6" -> bridgeClasspath(jsbridges("0.6").runDepClasspath(), jsbridges("0.6").compile().classes),
+ "MILL_SCALAJS_BRIDGE_1_0" -> bridgeClasspath(jsbridges("1.0").runDepClasspath(), jsbridges("1.0").compile().classes)
+ )
+ for((k, v) <- mapping.toSeq) yield s"-D$k=$v"
+ }
+
+}
-val assemblyProjects = Seq(ScalaPlugin)
+val assemblyProjects = Seq(ScalaPlugin, ScalaJSPlugin)
def assemblyClasspath = mill.define.Task.traverse(assemblyProjects)(_.assemblyClasspath)
@@ -163,7 +194,7 @@ def assemblyBase(classpath: Seq[Path], extraArgs: String)
}
def devAssembly = T{
- assemblyBase(assemblyClasspath().flatten, ScalaPlugin.testArgs().mkString(" "))
+ assemblyBase(assemblyClasspath().flatten, (ScalaPlugin.testArgs() ++ ScalaJSPlugin.testArgs()).mkString(" "))
}
def releaseAssembly = T{
diff --git a/scalaplugin/src/test/scala/mill/scalaplugin/TestEvaluator.scala b/core/src/test/scala/mill/util/TestEvaluator.scala
index f1cee213..5d16de43 100644
--- a/scalaplugin/src/test/scala/mill/scalaplugin/TestEvaluator.scala
+++ b/core/src/test/scala/mill/util/TestEvaluator.scala
@@ -1,10 +1,9 @@
-package mill.scalaplugin
+package mill.util
import ammonite.ops.Path
import mill.define.{Target, Task}
import mill.discover.{Discovered, Mirror}
import mill.eval.{Evaluator, Result}
-import mill.util.{DummyLogger, OSet, PrintLogger}
class TestEvaluator(mapping: Discovered.Mapping[_], workspacePath: Path){
val evaluator = new Evaluator(workspacePath, mapping, DummyLogger)
diff --git a/scalajsplugin/bridge_0_6/src/main/scala/mill/scalajsplugin/bridge/ScalaJSLinkerBridge.scala b/scalajsplugin/bridge_0_6/src/main/scala/mill/scalajsplugin/bridge/ScalaJSLinkerBridge.scala
new file mode 100644
index 00000000..82295740
--- /dev/null
+++ b/scalajsplugin/bridge_0_6/src/main/scala/mill/scalajsplugin/bridge/ScalaJSLinkerBridge.scala
@@ -0,0 +1,24 @@
+package mill
+package scalajsplugin
+package bridge
+
+import java.io.File
+
+import org.scalajs.core.tools.io.IRFileCache.IRContainer
+import org.scalajs.core.tools.io.{AtomicWritableFileVirtualJSFile, FileVirtualBinaryFile, FileVirtualScalaJSIRFile, VirtualJarFile}
+import org.scalajs.core.tools.linker.{ModuleInitializer, StandardLinker}
+import org.scalajs.core.tools.logging.ScalaConsoleLogger
+
+class ScalaJSLinkerBridge {
+ def link(sources: Array[File], libraries: Array[File], dest: File, main: String, fullOpt: Boolean): Unit = {
+ val config = StandardLinker.Config().withOptimizer(fullOpt)
+ val linker = StandardLinker(config)
+ val sourceSJSIRs = sources.map(new FileVirtualScalaJSIRFile(_))
+ val jars = libraries.map(jar => IRContainer.Jar(new FileVirtualBinaryFile(jar) with VirtualJarFile))
+ val jarSJSIRs = jars.flatMap(_.jar.sjsirFiles)
+ val destFile = AtomicWritableFileVirtualJSFile(dest)
+ val logger = new ScalaConsoleLogger
+ val initializer = Option(main).map { cls => ModuleInitializer.mainMethodWithArgs(cls, "main") }
+ linker.link(sourceSJSIRs ++ jarSJSIRs, initializer.toSeq, destFile, logger)
+ }
+} \ No newline at end of file
diff --git a/scalajsplugin/bridge_1_0/src/main/scala/mill/scalajsplugin/bridge/ScalaJSLinkerBridge.scala b/scalajsplugin/bridge_1_0/src/main/scala/mill/scalajsplugin/bridge/ScalaJSLinkerBridge.scala
new file mode 100644
index 00000000..7d54ae52
--- /dev/null
+++ b/scalajsplugin/bridge_1_0/src/main/scala/mill/scalajsplugin/bridge/ScalaJSLinkerBridge.scala
@@ -0,0 +1,24 @@
+package mill
+package scalajsplugin
+package bridge
+
+import java.io.File
+
+import org.scalajs.core.tools.io._
+import org.scalajs.core.tools.linker.{ModuleInitializer, StandardLinker}
+import org.scalajs.core.tools.logging.ScalaConsoleLogger
+
+class ScalaJSLinkerBridge {
+ def link(sources: Array[File], libraries: Array[File], dest: File, main: String, fullOpt: Boolean): Unit = {
+ val config = StandardLinker.Config().withOptimizer(fullOpt)
+ val linker = StandardLinker(config)
+ val cache = new IRFileCache().newCache
+ val sourceIRs = sources.map(FileVirtualScalaJSIRFile)
+ val irContainers = FileScalaJSIRContainer.fromClasspath(libraries)
+ val libraryIRs = cache.cached(irContainers)
+ val destFile = AtomicWritableFileVirtualJSFile(dest)
+ val logger = new ScalaConsoleLogger
+ val initializer = Option(main).map { cls => ModuleInitializer.mainMethodWithArgs(cls, "main") }
+ linker.link(sourceIRs ++ libraryIRs, initializer.toSeq, destFile, logger)
+ }
+} \ No newline at end of file
diff --git a/scalajsplugin/src/main/scala/mill/scalajsplugin/Lib.scala b/scalajsplugin/src/main/scala/mill/scalajsplugin/Lib.scala
new file mode 100644
index 00000000..980a728a
--- /dev/null
+++ b/scalajsplugin/src/main/scala/mill/scalajsplugin/Lib.scala
@@ -0,0 +1,62 @@
+package mill
+package scalajsplugin
+
+import java.io.File
+import java.net.URLClassLoader
+
+import ammonite.ops.{Path, mkdir, rm, _}
+import mill.eval.PathRef
+import mill.scalaplugin.Dep
+import mill.util.Ctx
+
+import scala.collection.breakOut
+import scala.language.reflectiveCalls
+
+private object LinkerBridge {
+ @volatile var scalaInstanceCache = Option.empty[(Long, ScalaJSLinkerBridge)]
+}
+
+object Lib {
+
+ def scalaJSLinkerIvyDep(scalaJSVersion: String): Dep =
+ Dep("com.lihaoyi", s"mill-jsbridge_${scalaJSVersion.replace('.', '_')}", "0.1-SNAPSHOT")
+
+ def scalaJSLinkerBridge(classPath: Seq[Path]): ScalaJSLinkerBridge = {
+ val classloaderSig = classPath.map(p => p.toString().hashCode + p.mtime.toMillis).sum
+ LinkerBridge.scalaInstanceCache match {
+ case Some((`classloaderSig`, linker)) => linker
+ case _ =>
+ val cl = new URLClassLoader(classPath.map(_.toIO.toURI.toURL).toArray)
+ val bridge = cl.loadClass("mill.scalajsplugin.bridge.ScalaJSLinkerBridge")
+ .getDeclaredConstructor().newInstance().asInstanceOf[ {
+ def link(sources: Array[File], libraries: Array[File], dest: File, main: String, fullOpt: Boolean): Unit
+ }]
+ val linker: ScalaJSLinkerBridge = (sources: Seq[File],
+ libraries: Seq[File],
+ dest: File,
+ main: Option[String],
+ mode: OptimizeMode) =>
+ bridge.link(sources.toArray, libraries.toArray, dest, main.orNull, mode == FullOpt)
+ LinkerBridge.scalaInstanceCache = Some((classloaderSig, linker))
+ linker
+ }
+ }
+
+ def link(main: Option[String],
+ inputPaths: Seq[Path],
+ libraries: Seq[Path],
+ linker: ScalaJSLinkerBridge,
+ mode: OptimizeMode)
+ (implicit ctx: Ctx.DestCtx): PathRef = {
+ val outputPath = ctx.dest.copy(segments = ctx.dest.segments.init :+ (ctx.dest.segments.last + ".js"))
+ rm(outputPath)
+ if (inputPaths.nonEmpty) {
+ mkdir(outputPath / up)
+ val inputFiles: Vector[File] = inputPaths.map(ls).flatMap(_.filter(_.ext == "sjsir")).map(_.toIO)(breakOut)
+ val inputLibraries: Vector[File] = libraries.filter(_.ext == "jar").map(_.toIO)(breakOut)
+ linker.link(inputFiles, inputLibraries, outputPath.toIO, main, mode)
+ }
+ PathRef(outputPath)
+ }
+
+}
diff --git a/scalajsplugin/src/main/scala/mill/scalajsplugin/ScalaJSLinkerBridge.scala b/scalajsplugin/src/main/scala/mill/scalajsplugin/ScalaJSLinkerBridge.scala
new file mode 100644
index 00000000..1aa8da8c
--- /dev/null
+++ b/scalajsplugin/src/main/scala/mill/scalajsplugin/ScalaJSLinkerBridge.scala
@@ -0,0 +1,12 @@
+package mill.scalajsplugin
+
+import java.io.File
+
+sealed trait OptimizeMode
+
+object FastOpt extends OptimizeMode
+object FullOpt extends OptimizeMode
+
+trait ScalaJSLinkerBridge {
+ def link(sources: Seq[File], libraries: Seq[File], dest: File, main: Option[String], mode: OptimizeMode): Unit
+}
diff --git a/scalajsplugin/src/main/scala/mill/scalajsplugin/ScalaJSModule.scala b/scalajsplugin/src/main/scala/mill/scalajsplugin/ScalaJSModule.scala
new file mode 100644
index 00000000..c3e8a89d
--- /dev/null
+++ b/scalajsplugin/src/main/scala/mill/scalajsplugin/ScalaJSModule.scala
@@ -0,0 +1,69 @@
+package mill
+package scalajsplugin
+
+import java.io.File
+
+import ammonite.ops.Path
+import mill.eval.Result.Success
+import mill.scalajsplugin.Lib._
+import mill.scalaplugin.Lib.resolveDependencies
+import mill.scalaplugin.{Dep, PublishModule, ScalaModule, TestScalaModule}
+
+trait ScalaJSModule extends ScalaModule { outer =>
+
+ def scalaJSVersion: T[String]
+
+ private val ReleaseVersion = raw"""(\d+)\.(\d+)\.(\d+)""".r
+ private val MinorSnapshotVersion = raw"""(\d+)\.(\d+)\.([1-9]\d*)-SNAPSHOT""".r
+
+ def scalaJSBinaryVersion = T{
+ scalaJSVersion() match {
+ case ReleaseVersion(major, minor, _) => s"$major.$minor"
+ case MinorSnapshotVersion(major, minor, _) => s"$major.$minor"
+ case _ => scalaJSVersion()
+ }
+ }
+
+ def scalaJSBridgeVersion = T{ scalaJSVersion().split('.').dropRight(1).mkString(".") }
+
+ def scalaJSLinkerClasspath: T[Seq[PathRef]] = T{
+ val jsBridgeKey = "MILL_SCALAJS_BRIDGE_" + scalaJSBridgeVersion().replace('.', '_')
+ val jsBridgePath = sys.props(jsBridgeKey)
+ if (jsBridgePath != null) Success(jsBridgePath.split(File.pathSeparator).map(f => PathRef(Path(f), quick = true)).toVector)
+ else {
+ val dep = scalaJSLinkerIvyDep(scalaJSBridgeVersion())
+ resolveDependencies(
+ repositories,
+ scalaVersion(),
+ scalaBinaryVersion(),
+ Seq(dep)
+ )
+ }
+ }
+
+ def fastOpt = T{
+ val linker = scalaJSLinkerBridge(scalaJSLinkerClasspath().map(_.path))
+ link(mainClass(), Seq(compile().classes.path), compileDepClasspath().map(_.path), linker, FastOpt)
+ }
+
+ def fullOpt = T{
+ val linker = scalaJSLinkerBridge(scalaJSLinkerClasspath().map(_.path))
+ link(mainClass(), Seq(compile().classes.path), compileDepClasspath().map(_.path), linker, FullOpt)
+ }
+
+ override def scalacPluginIvyDeps = T{ Seq(Dep.Point("org.scala-js", "scalajs-compiler", scalaJSVersion())) }
+
+ override def ivyDeps = T{ Seq(Dep("org.scala-js", "scalajs-library", scalaJSVersion())) }
+
+ // publish artifact with name "mill_sjs0.6.4_2.12" instead of "mill_sjs0.6_2.12"
+ def crossFullScalaJSVersion: T[Boolean] = false
+ def artifactScalaJSVersion: T[String] = T {
+ if (crossFullScalaJSVersion()) scalaJSVersion()
+ else scalaJSBinaryVersion()
+ }
+
+ override def artifactId: T[String] = T { s"${artifactName()}_sjs${artifactScalaJSVersion()}_${artifactScalaVersion()}" }
+
+}
+
+trait TestScalaJSModule extends ScalaJSModule with TestScalaModule \ No newline at end of file
diff --git a/scalajsplugin/src/test/resource/hello-js-world/src/main/scala/Main.scala b/scalajsplugin/src/test/resource/hello-js-world/src/main/scala/Main.scala
new file mode 100644
index 00000000..60cef56d
--- /dev/null
+++ b/scalajsplugin/src/test/resource/hello-js-world/src/main/scala/Main.scala
@@ -0,0 +1,3 @@
+object Main extends App {
+ println("Hello " + sys.props("java.vm.name"))
+}
diff --git a/scalajsplugin/src/test/scala/mill/scalajsplugin/HelloJSWorldTests.scala b/scalajsplugin/src/test/scala/mill/scalajsplugin/HelloJSWorldTests.scala
new file mode 100644
index 00000000..e199c1da
--- /dev/null
+++ b/scalajsplugin/src/test/scala/mill/scalajsplugin/HelloJSWorldTests.scala
@@ -0,0 +1,160 @@
+package mill.scalajsplugin
+
+import java.io.{FileReader, StringWriter}
+import java.util.jar.JarFile
+import javax.script.{ScriptContext, ScriptEngineManager}
+
+import ammonite.ops._
+import mill._
+import mill.define.Cross
+import mill.discover.Discovered
+import mill.scalaplugin.PublishModule
+import mill.scalaplugin.publish.{Developer, License, PomSettings, SCM}
+import mill.util.TestEvaluator
+import utest._
+
+import scala.collection.JavaConverters._
+
+trait HelloJSWorldModule extends ScalaJSModule with PublishModule {
+ def basePath = HelloJSWorldTests.workspacePath
+ override def mainClass = Some("Main")
+}
+
+object HelloJSWorld {
+ val build = for {
+ scalaJS <- Cross("0.6.20", "0.6.21", "1.0.0-M2")
+ scala <- Cross("2.11.8", "2.12.3", "2.12.4")
+ } yield
+ new HelloJSWorldModule {
+ def scalaVersion = scala
+ def scalaJSVersion = scalaJS
+ def pomSettings = PomSettings(
+ organization = "com.lihaoyi",
+ description = "hello js world ready for real world publishing",
+ url = "https://github.com/lihaoyi/hello-world-publish",
+ licenses = Seq(
+ License("Apache License, Version 2.0",
+ "http://www.apache.org/licenses/LICENSE-2.0")),
+ scm = SCM(
+ "https://github.com/lihaoyi/hello-world-publish",
+ "scm:git:https://github.com/lihaoyi/hello-world-publish"
+ ),
+ developers =
+ Seq(Developer("lihaoyi", "Li Haoyi", "https://github.com/lihaoyi"))
+ )
+ }
+}
+
+object HelloJSWorldTests extends TestSuite {
+
+ val srcPath = pwd / 'scalajsplugin / 'src / 'test / 'resource / "hello-js-world"
+ val workspacePath = pwd / 'target / 'workspace / "hello-js-world"
+ val outputPath = workspacePath / 'out
+ val mainObject = workspacePath / 'src / 'main / 'scala / "Main.scala"
+
+ val helloWorldEvaluator = new TestEvaluator(
+ Discovered.mapping(HelloJSWorld),
+ workspacePath
+ )
+
+ class Console {
+ val out = new StringWriter()
+ def log(s: String): Unit = out.append(s)
+ }
+
+ def runJS(path: Path): String = {
+ val engineManager = new ScriptEngineManager
+ val engine = engineManager.getEngineByName("nashorn")
+ val console = new Console
+ engine.getBindings(ScriptContext.ENGINE_SCOPE).put("console", console)
+ engine.eval(new FileReader(path.toIO))
+ console.out.toString
+ }
+
+ def tests: Tests = Tests {
+ prepareWorkspace()
+ 'compile - {
+ def testCompileFromScratch(scalaVersion: String,
+ scalaJSVersion: String): Unit = {
+ val Right((result, evalCount)) =
+ helloWorldEvaluator(HelloJSWorld.build(scalaVersion, scalaJSVersion).compile)
+
+ val outPath = result.classes.path
+ val outputFiles = ls.rec(outPath)
+ val expectedClassfiles = compileClassfiles(outPath)
+ assert(
+ outputFiles.toSet == expectedClassfiles,
+ evalCount > 0
+ )
+
+ // don't recompile if nothing changed
+ val Right((_, unchangedEvalCount)) =
+ helloWorldEvaluator(HelloJSWorld.build(scalaVersion, scalaJSVersion).compile)
+ assert(unchangedEvalCount == 0)
+ }
+
+ 'fromScratch_2124_0621 - testCompileFromScratch("2.12.4", "0.6.21")
+ 'fromScratch_2123_0621 - testCompileFromScratch("2.12.3", "0.6.21")
+ 'fromScratch_2118_0621 - testCompileFromScratch("2.11.8", "0.6.21")
+ 'fromScratch_2124_100M2 - testCompileFromScratch("2.11.8", "1.0.0-M2")
+ }
+
+ def testRun(scalaVersion: String,
+ scalaJSVersion: String,
+ mode: OptimizeMode): Unit = {
+ val task = mode match {
+ case FullOpt => HelloJSWorld.build(scalaVersion, scalaJSVersion).fullOpt
+ case FastOpt => HelloJSWorld.build(scalaVersion, scalaJSVersion).fastOpt
+ }
+ val Right((result, evalCount)) = helloWorldEvaluator(task)
+ val output = runJS(result.path)
+ assert(output == "Hello Scala.js")
+ }
+
+ 'fullOpt - {
+ 'run_2124_0621 - testRun("2.12.4", "0.6.21", FullOpt)
+ 'run_2123_0621 - testRun("2.12.3", "0.6.21", FullOpt)
+ 'run_2118_0621 - testRun("2.11.8", "0.6.21", FullOpt)
+ 'run_2124_100M2 - testRun("2.11.8", "1.0.0-M2", FullOpt)
+ }
+ 'fastOpt - {
+ 'run_2124_0621 - testRun("2.12.4", "0.6.21", FastOpt)
+ 'run_2123_0621 - testRun("2.12.3", "0.6.21", FastOpt)
+ 'run_2118_0621 - testRun("2.11.8", "0.6.21", FastOpt)
+ 'run_2124_100M2 - testRun("2.11.8", "1.0.0-M2", FastOpt)
+ }
+ 'jar - {
+ 'containsSJSIRs - {
+ val Right((result, evalCount)) = helloWorldEvaluator(HelloJSWorld.build("2.12.4", "0.6.21").jar)
+ val jar = result.path
+ val entries = new JarFile(jar.toIO).entries().asScala.map(_.getName)
+ assert(entries.contains("Main$.sjsir"))
+ }
+ }
+ 'publish - {
+ def testArtifactId(scalaVersion: String,
+ scalaJSVersion: String,
+ artifactId: String): Unit = {
+ val Right((result, evalCount)) = helloWorldEvaluator(HelloJSWorld.build(scalaVersion, scalaJSVersion).artifact)
+ assert(result.id == artifactId)
+ }
+ 'artifactId_0621 - testArtifactId("2.12.4", "0.6.21", "hello-js-world_sjs0.6_2.12")
+ 'artifactId_0621 - testArtifactId("2.12.4", "1.0.0-M2", "hello-js-world_sjs1.0.0-M2_2.12")
+ }
+ }
+
+ def compileClassfiles(parentDir: Path) = Set(
+ parentDir / "Main.class",
+ parentDir / "Main$.class",
+ parentDir / "Main$delayedInit$body.class",
+ parentDir / "Main$.sjsir",
+ parentDir / "Main$delayedInit$body.sjsir"
+ )
+
+ def prepareWorkspace(): Unit = {
+ rm(workspacePath)
+ mkdir(workspacePath / up)
+ cp(srcPath, workspacePath)
+ }
+
+}
diff --git a/scalaplugin/src/main/scala/mill/scalaplugin/Lib.scala b/scalaplugin/src/main/scala/mill/scalaplugin/Lib.scala
index 7dad791d..de1906ab 100644
--- a/scalaplugin/src/main/scala/mill/scalaplugin/Lib.scala
+++ b/scalaplugin/src/main/scala/mill/scalaplugin/Lib.scala
@@ -26,8 +26,8 @@ object ZincWorker extends Worker[ZincWorker]{
def make() = new ZincWorker
}
class ZincWorker{
- var scalaClassloaderCache = Option.empty[(Long, ClassLoader)]
- var scalaInstanceCache = Option.empty[(Long, ScalaInstance)]
+ @volatile var scalaClassloaderCache = Option.empty[(Long, ClassLoader)]
+ @volatile var scalaInstanceCache = Option.empty[(Long, ScalaInstance)]
}
object Lib{
case class MockedLookup(am: File => Optional[CompileAnalysis]) extends PerClasspathEntryLookup {
diff --git a/scalaplugin/src/main/scala/mill/scalaplugin/ScalaModule.scala b/scalaplugin/src/main/scala/mill/scalaplugin/ScalaModule.scala
index b1102234..13e91c2a 100644
--- a/scalaplugin/src/main/scala/mill/scalaplugin/ScalaModule.scala
+++ b/scalaplugin/src/main/scala/mill/scalaplugin/ScalaModule.scala
@@ -257,29 +257,32 @@ trait ScalaModule extends Module with TaskModule { outer =>
options = Seq("-usejavacp")
)
}
-}
-
-trait PublishModule extends ScalaModule { outer =>
- import mill.scalaplugin.publish._
-
- def publishName: T[String] = basePath.last.toString
- def publishVersion: T[String] = "0.0.1-SNAPSHOT"
- def pomSettings: T[PomSettings]
// publish artifact with name "mill_2.12.4" instead of "mill_2.12"
- def publishWithFullScalaVersion: Boolean = false
+ def crossFullScalaVersion: T[Boolean] = false
+ def artifactName: T[String] = basePath.last.toString
def artifactScalaVersion: T[String] = T {
- if (publishWithFullScalaVersion) scalaVersion()
+ if (crossFullScalaVersion()) scalaVersion()
else scalaBinaryVersion()
}
+ def artifactId: T[String] = T { s"${artifactName()}_${artifactScalaVersion()}" }
+
+}
+
+trait PublishModule extends ScalaModule { outer =>
+ import mill.scalaplugin.publish._
+
+ def pomSettings: T[PomSettings]
+ def publishVersion: T[String] = "0.0.1-SNAPSHOT"
+
def pom = T {
val dependencies =
ivyDeps().map(Artifact.fromDep(_, scalaVersion(), scalaBinaryVersion()))
- val pom = Pom(artifact(), dependencies, publishName(), pomSettings())
+ val pom = Pom(artifact(), dependencies, artifactName(), pomSettings())
- val pomPath = T.ctx().dest / s"${publishName()}_${artifactScalaVersion()}-${publishVersion()}.pom"
+ val pomPath = T.ctx().dest / s"${artifactId()}-${publishVersion()}.pom"
write.over(pomPath, pom)
PathRef(pomPath)
}
@@ -294,7 +297,7 @@ trait PublishModule extends ScalaModule { outer =>
}
def artifact: T[Artifact] = T {
- Artifact(pomSettings().organization, s"${publishName()}_${artifactScalaVersion()}", publishVersion())
+ Artifact(pomSettings().organization, artifactId(), publishVersion())
}
def publishLocal(): define.Command[Unit] = T.command {
@@ -313,7 +316,7 @@ trait PublishModule extends ScalaModule { outer =>
def sonatypeSnapshotUri: String = "https://oss.sonatype.org/content/repositories/snapshots"
def publish(credentials: String, gpgPassphrase: String): define.Command[Unit] = T.command {
- val baseName = s"${publishName()}_${artifactScalaVersion()}-${publishVersion()}"
+ val baseName = s"${artifactId()}-${publishVersion()}"
val artifacts = Seq(
jar().path -> s"${baseName}.jar",
sourcesJar().path -> s"${baseName}-sources.jar",
diff --git a/scalaplugin/src/test/scala/mill/scalaplugin/AcyclicTests.scala b/scalaplugin/src/test/scala/mill/scalaplugin/AcyclicTests.scala
index 13349aec..131ee6bb 100644
--- a/scalaplugin/src/test/scala/mill/scalaplugin/AcyclicTests.scala
+++ b/scalaplugin/src/test/scala/mill/scalaplugin/AcyclicTests.scala
@@ -7,16 +7,17 @@ import mill.discover.Discovered
import mill.scalaplugin.publish._
import utest._
import mill.util.JsonFormatters._
+import mill.util.TestEvaluator
object AcyclicBuild{
val acyclic =
for(crossVersion <- Cross("2.10.6", "2.11.8", "2.12.3", "2.12.4"))
yield new SbtScalaModule with PublishModule {outer =>
def basePath = AcyclicTests.workspacePath
- def publishName = "acyclic"
+ def artifactName = "acyclic"
def publishVersion = "0.1.7"
def pomSettings = PomSettings(
- description = publishName(),
+ description = artifactName(),
organization = "com.lihaoyi",
url = "https://github.com/lihaoyi/acyclic",
licenses = Seq(
diff --git a/scalaplugin/src/test/scala/mill/scalaplugin/BetterFilesTests.scala b/scalaplugin/src/test/scala/mill/scalaplugin/BetterFilesTests.scala
index f2e21a81..f608f311 100644
--- a/scalaplugin/src/test/scala/mill/scalaplugin/BetterFilesTests.scala
+++ b/scalaplugin/src/test/scala/mill/scalaplugin/BetterFilesTests.scala
@@ -5,6 +5,7 @@ import ammonite.ops._
import mill.discover.Discovered
import utest._
import mill.util.JsonFormatters._
+import mill.util.TestEvaluator
object BetterFilesBuild{
trait BetterFilesModule extends SbtScalaModule{ outer =>
diff --git a/scalaplugin/src/test/scala/mill/scalaplugin/HelloWorldTests.scala b/scalaplugin/src/test/scala/mill/scalaplugin/HelloWorldTests.scala
index 51d1dc94..4272654a 100644
--- a/scalaplugin/src/test/scala/mill/scalaplugin/HelloWorldTests.scala
+++ b/scalaplugin/src/test/scala/mill/scalaplugin/HelloWorldTests.scala
@@ -9,6 +9,7 @@ import mill.define.{Cross, Target}
import mill.discover.Discovered
import mill.eval.{Evaluator, Result}
import mill.scalaplugin.publish._
+import mill.util.TestEvaluator
import sbt.internal.inc.CompileFailed
import utest._
@@ -41,7 +42,7 @@ object HelloWorldFatalWarnings extends HelloWorldModule {
}
object HelloWorldWithPublish extends HelloWorldModule with PublishModule {
- def publishName = "hello-world"
+ def artifactName = "hello-world"
def publishVersion = "0.0.1"
def pomSettings = PomSettings(
diff --git a/scalaplugin/src/test/scala/mill/scalaplugin/JawnTests.scala b/scalaplugin/src/test/scala/mill/scalaplugin/JawnTests.scala
index 97dff017..81d6e71a 100644
--- a/scalaplugin/src/test/scala/mill/scalaplugin/JawnTests.scala
+++ b/scalaplugin/src/test/scala/mill/scalaplugin/JawnTests.scala
@@ -8,6 +8,7 @@ import mill.discover.Discovered
import mill.eval.Result
import utest._
import mill.util.JsonFormatters._
+import mill.util.TestEvaluator
object JawnBuild{
val Jawn = Cross("2.10.6", "2.11.11", "2.12.3").map(new Jawn(_))
diff --git a/test.sh b/test.sh
index 5f7a753d..e73405ee 100755
--- a/test.sh
+++ b/test.sh
@@ -6,12 +6,13 @@ set -eux
git clean -xdf
# First build & run tests using SBT
-sbt core/test scalaplugin/test bin/test:assembly
+sbt core/test scalaplugin/test scalajsplugin/test bin/test:assembly
# Build Mill using SBT
bin/target/mill devAssembly
-# Secpmd build & run tests using Mill
+# Second build & run tests using Mill
out/devAssembly Core.test
out/devAssembly ScalaPlugin.test
+out/devAssembly ScalaJSPlugin.test
out/devAssembly devAssembly \ No newline at end of file