summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.appveyor.yml18
-rw-r--r--.travis.yml28
-rwxr-xr-xbuild.sc162
-rwxr-xr-xci/test-mill-0.sh4
-rwxr-xr-xci/test-mill-1.sh2
-rwxr-xr-xci/test-mill-2.sh2
-rwxr-xr-xci/test-mill-bootstrap.sh30
-rwxr-xr-xci/test-mill-dev.sh6
-rwxr-xr-xci/test-mill-release.sh7
-rw-r--r--client/src/mill/client/ClientServer.java (renamed from clientserver/src/mill/clientserver/ClientServer.java)2
-rw-r--r--client/src/mill/client/InputPumper.java (renamed from clientserver/src/mill/clientserver/InputPumper.java)2
-rw-r--r--client/src/mill/client/Lock.java13
-rw-r--r--client/src/mill/client/Locked.java10
-rw-r--r--client/src/mill/client/Locks.java (renamed from clientserver/src/mill/clientserver/Locks.java)24
-rw-r--r--client/src/mill/client/Main.java (renamed from clientserver/src/mill/clientserver/Client.java)11
-rw-r--r--clientserver/test/src/mill/clientserver/ClientServerTests.scala209
-rw-r--r--core/src/mill/eval/Evaluator.scala8
-rw-r--r--docs/pages/1 - Intro to Mill.md8
-rw-r--r--docs/pages/2 - Configuring Mill.md2
-rw-r--r--integration/test/resources/play-json/build.sc8
-rw-r--r--integration/test/resources/play-json/mima.sc7
-rw-r--r--integration/test/resources/play-json/playJsonVersion.sc (renamed from integration/test/resources/play-json/version.sc)0
-rw-r--r--main/src/mill/Main.scala20
-rw-r--r--main/src/mill/main/MainModule.scala6
-rw-r--r--main/src/mill/main/Server.scala (renamed from clientserver/src/mill/clientserver/Server.scala)32
-rw-r--r--main/src/mill/modules/Jvm.scala62
-rw-r--r--main/test/src/mill/eval/EvaluationTests.scala26
-rw-r--r--main/test/src/mill/eval/JavaCompileJarTests.scala4
-rw-r--r--main/test/src/mill/main/ClientServerTests.scala214
-rw-r--r--main/test/src/mill/util/ScriptTestSuite.scala6
-rw-r--r--main/test/src/mill/util/TestGraphs.scala17
-rw-r--r--readme.md35
-rw-r--r--scalajslib/src/mill/scalajslib/ScalaJSModule.scala8
-rw-r--r--scalalib/src/mill/scalalib/Dep.scala66
-rw-r--r--scalalib/src/mill/scalalib/GenIdeaImpl.scala (renamed from scalalib/src/mill/scalalib/GenIdea.scala)69
-rw-r--r--scalalib/src/mill/scalalib/JavaModule.scala284
-rw-r--r--scalalib/src/mill/scalalib/Lib.scala86
-rw-r--r--scalalib/src/mill/scalalib/PublishModule.scala8
-rw-r--r--scalalib/src/mill/scalalib/ScalaModule.scala245
-rw-r--r--scalalib/src/mill/scalalib/ScalaWorkerApi.scala5
-rw-r--r--scalalib/src/mill/scalalib/publish/Ivy.scala6
-rw-r--r--scalalib/src/mill/scalalib/publish/Pom.scala2
-rw-r--r--scalalib/src/mill/scalalib/publish/settings.scala20
-rw-r--r--scalalib/test/resources/hello-java/core/src/hello/Core.java8
-rw-r--r--scalalib/test/resources/hello-java/main/src/hello/Main.java7
-rw-r--r--scalalib/test/resources/hello-world-macros/core/src/Main.scala5
-rw-r--r--scalalib/test/src/mill/scalalib/GenIdeaTests.scala2
-rw-r--r--scalalib/test/src/mill/scalalib/HelloJavaTests.scala60
-rw-r--r--scalalib/test/src/mill/scalalib/HelloWorldTests.scala71
-rw-r--r--scalalib/test/src/mill/scalalib/ResolveDepsTests.scala14
-rw-r--r--scalalib/test/src/mill/scalalib/publish/IvyTests.scala14
-rw-r--r--scalaworker/src/mill/scalaworker/ScalaWorker.scala9
52 files changed, 1235 insertions, 739 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
index 693bd85e..955f6371 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -9,6 +9,8 @@ clone_folder: c:\mill
environment:
matrix:
+ - COMPILER: default
+ JAVA_HOME: C:\Program Files\Java\jdk9
- COMPILER: cygwin
CYGWIN_DIR: cygwin64
JAVA_HOME: C:\Program Files\Java\jdk9
@@ -17,26 +19,26 @@ environment:
MSYS2_DIR: msys64
MSYSTEM: MINGW64
JAVA_HOME: C:\Program Files\Java\jdk1.8.0
- - COMPILER: msys2
- MSYS2_ARCH: x86_64
- MSYS2_DIR: msys64
- MSYSTEM: MINGW64
- JAVA_HOME: C:\Program Files\Java\jdk9
cache:
- - '%LOCALAPPDATA%\Coursier\cache'
+ - '%LOCALAPPDATA%\Coursier\cache -> build.sc'
install:
- - SET MILL_URL=https://github.com/lihaoyi/mill/releases/download/0.1.7/0.1.7-8-b913c6
+ - SET MILL_URL=https://github.com/lihaoyi/mill/releases/download/0.1.7/0.1.7-53-cc0407
build_script:
+ - if [%COMPILER%]==[default] (
+ SET "PATH=%JAVA_HOME%\bin;%PATH%" &&
+ MD C:\bin &&
+ curl -Lo C:\bin\mill.bat %MILL_URL% &&
+ cmd /C C:\bin\mill.bat -i all __.publishLocal release &&
+ cmd /C C:\mill\out\release\dest\mill.bat -i all main.test scalajslib.test)
- if [%COMPILER%]==[msys2] (
SET "PATH=%JAVA_HOME%\bin;C:\%MSYS2_DIR%\%MSYSTEM%\bin;C:\%MSYS2_DIR%\usr\bin;%PATH%" &&
C:\%MSYS2_DIR%\usr\bin\bash -lc 'mkdir -p /usr/local/bin' &&
C:\%MSYS2_DIR%\usr\bin\bash -lc "curl -Lo /usr/local/bin/mill %MILL_URL%" &&
C:\%MSYS2_DIR%\usr\bin\bash -lc 'chmod +x /usr/local/bin/mill' &&
C:\%MSYS2_DIR%\usr\bin\bash -lc "cd /c/mill && mill -i all __.publishLocal release" &&
- rd /s /q %USERPROFILE%\.mill &&
C:\%MSYS2_DIR%\usr\bin\bash -lc "cd /c/mill && out/release/dest/mill -i all main.test scalajslib.test")
- if [%COMPILER%]==[cygwin] (
SET "PATH=%JAVA_HOME%\bin;C:\%CYGWIN_DIR%\bin;C:\%CYGWIN_DIR%\usr\bin;%PATH%" &&
diff --git a/.travis.yml b/.travis.yml
index b63d7028..3a7f5fdb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,29 +5,33 @@ dist: trusty
matrix:
include:
- stage: build
- env: CI_SCRIPT=ci/test-mill-0.sh
- jdk: oraclejdk9
- - stage: build
- env: CI_SCRIPT=ci/test-mill-0.sh
+ env: CI_SCRIPT=ci/test-mill-release.sh
jdk: oraclejdk8
- stage: build
- env: CI_SCRIPT=ci/test-mill-1.sh
- jdk: oraclejdk8
+ env: CI_SCRIPT=ci/test-mill-release.sh
+ jdk: oraclejdk9
+
- stage: build
- env: CI_SCRIPT=ci/test-mill-2.sh
+ env: CI_SCRIPT=ci/test-mill-dev.sh
jdk: oraclejdk8
- stage: build
env: CI_SCRIPT=ci/test-mill-dev.sh
jdk: oraclejdk9
- stage: build
- env: CI_SCRIPT=ci/test-mill-dev.sh
+ env: CI_SCRIPT=ci/test-mill-bootstrap.sh
+ jdk: oraclejdk9
+
+ - stage: build
+ env: CI_SCRIPT=ci/test-mill-0.sh
jdk: oraclejdk8
- stage: build
- env: CI_SCRIPT=ci/test-mill-release.sh
- jdk: oraclejdk9
+ env: CI_SCRIPT=ci/test-mill-1.sh
+ jdk: oraclejdk8
- stage: build
- env: CI_SCRIPT=ci/test-mill-release.sh
+ env: CI_SCRIPT=ci/test-mill-2.sh
jdk: oraclejdk8
+
+
- stage: release
env: CI_SCRIPT="ci/on-master.py ci/release.sh"
jdk: oraclejdk8
@@ -36,7 +40,7 @@ matrix:
jdk: oraclejdk8
script:
- - curl -L -o ~/bin/mill https://github.com/lihaoyi/mill/releases/download/0.1.7/0.1.7-8-b913c6 && chmod +x ~/bin/mill
+ - curl -L -o ~/bin/mill https://github.com/lihaoyi/mill/releases/download/0.1.7/0.1.7-53-cc0407 && chmod +x ~/bin/mill
- export PATH=~/bin/mill:$PATH
- "$CI_SCRIPT"
diff --git a/build.sc b/build.sc
index e3d4f91f..a86d7d75 100755
--- a/build.sc
+++ b/build.sc
@@ -11,7 +11,7 @@ import publish._
import mill.modules.Jvm.createAssembly
import upickle.Js
trait MillPublishModule extends PublishModule{
- def scalaVersion = "2.12.4"
+
def artifactName = "mill-" + super.artifactName()
def publishVersion = build.publishVersion()._2
@@ -27,17 +27,18 @@ trait MillPublishModule extends PublishModule{
)
def javacOptions = Seq("-source", "1.8", "-target", "1.8")
-
}
-object moduledefs extends MillPublishModule{
+
+object moduledefs extends MillPublishModule with ScalaModule{
+ def scalaVersion = T{ "2.12.4" }
def ivyDeps = Agg(
ivy"org.scala-lang:scala-compiler:${scalaVersion()}",
ivy"com.lihaoyi::sourcecode:0.1.4"
)
}
-trait MillModule extends MillPublishModule{ outer =>
-
+trait MillModule extends MillPublishModule with ScalaModule{ outer =>
+ def scalaVersion = T{ "2.12.4" }
def compileIvyDeps = Agg(ivy"com.lihaoyi::acyclic:0.1.7")
def scalacOptions = Seq("-P:acyclic:force")
def scalacPluginIvyDeps = Agg(ivy"com.lihaoyi::acyclic:0.1.7")
@@ -63,12 +64,10 @@ trait MillModule extends MillPublishModule{ outer =>
}
}
-object clientserver extends MillModule{
+object client extends MillPublishModule{
def ivyDeps = Agg(
- ivy"com.lihaoyi:::ammonite:1.1.0-7-33b728c",
ivy"org.scala-sbt.ipcsocket:ipcsocket:1.0.0"
)
- val test = new Tests(implicitly)
}
object core extends MillModule {
@@ -80,7 +79,7 @@ object core extends MillModule {
def ivyDeps = Agg(
ivy"com.lihaoyi::sourcecode:0.1.4",
- ivy"com.lihaoyi:::ammonite:1.1.0-7-33b728c",
+ ivy"com.lihaoyi:::ammonite:1.1.0-14-037b8eb",
)
def generatedSources = T {
@@ -89,7 +88,7 @@ object core extends MillModule {
}
object main extends MillModule {
- def moduleDeps = Seq(core, clientserver)
+ def moduleDeps = Seq(core, client)
def compileIvyDeps = Agg(
@@ -205,37 +204,66 @@ object integration extends MillModule{
def forkArgs = testArgs()
}
-def launcherScript(isWin: Boolean,
- jvmArgs: Seq[String],
- classPath: Agg[String]) = {
+private def universalScript(shellCommands: String,
+ cmdCommands: String,
+ shebang: Boolean = false): String = {
+ Seq(
+ if (shebang) "#!/usr/bin/env sh" else "",
+ "@ 2>/dev/null # 2>nul & echo off & goto BOF\r",
+ ":",
+ shellCommands.replaceAll("\r\n|\n", "\n"),
+ "exit",
+ Seq(
+ "",
+ ":BOF",
+ "@echo off",
+ cmdCommands.replaceAll("\r\n|\n", "\r\n"),
+ "exit /B %errorlevel%",
+ ""
+ ).mkString("\r\n")
+ ).filterNot(_.isEmpty).mkString("\n")
+}
+
+def launcherScript(jvmArgs: Seq[String],
+ shellClassPath: Agg[String],
+ cmdClassPath: Agg[String]) = {
val jvmArgsStr = jvmArgs.mkString(" ")
- val classPathStr = if (isWin) classPath.mkString(";") else classPath.mkString(":")
- if (isWin)
- s"""::#!
- |@echo off
- |if "%1" == "-i" set _I_=true
- |if "%1" == "--interactive" set _I_=true
- |if defined _I_ (
- | java $jvmArgsStr %JAVA_OPTS% -cp "$classPathStr" mill.Main %*
- |) else (
- | java $jvmArgsStr %JAVA_OPTS% -cp "$classPathStr" mill.clientserver.Client %*
- |)
- |EXIT /B %errorlevel%
- """.stripMargin.split('\n').mkString("\r\n")
- else
- s"""#!/usr/bin/env sh
- |
- |case "$$1" in
- | -i | --interactive )
- | exec java $jvmArgsStr $$JAVA_OPTS -cp "$classPathStr" mill.Main "$$@"
- | ;;
- | *)
- | exec java $jvmArgsStr $$JAVA_OPTS -cp "$classPathStr" mill.clientserver.Client "$$@"
- | ;;
- |esac
- """.stripMargin
+ universalScript(
+ shellCommands = {
+ def java(mainClass: String) =
+ s"""exec java $jvmArgsStr $$JAVA_OPTS -cp "${shellClassPath.mkString(":")}" $mainClass "$$@""""
+
+ s"""case "$$1" in
+ | -i | --interactive )
+ | ${java("mill.Main")}
+ | ;;
+ | *)
+ | ${java("mill.client.Main")}
+ | ;;
+ |esac""".stripMargin
+ },
+ cmdCommands = {
+ def java(mainClass: String) =
+ s"""java $jvmArgsStr %JAVA_OPTS% -cp "${cmdClassPath.mkString(";")}" $mainClass %*"""
+
+ s"""if "%1" == "-i" set _I_=true
+ |if "%1" == "--interactive" set _I_=true
+ |if defined _I_ (
+ | ${java("mill.Main")}
+ |) else (
+ | ${java("mill.client.Main")}
+ |)""".stripMargin
+ }
+ )
}
+val isBatch =
+ scala.util.Properties.isWin &&
+ !(org.jline.utils.OSUtils.IS_CYGWIN
+ || org.jline.utils.OSUtils.IS_MINGW
+ || "MSYS" == System.getProperty("MSYSTEM"))
+
+
object dev extends MillModule{
def moduleDeps = Seq(scalalib, scalajslib)
def forkArgs = T{
@@ -243,7 +271,7 @@ object dev extends MillModule{
}
def launcher = T{
val isWin = scala.util.Properties.isWin
- val outputPath = T.ctx().dest / (if (isWin) "run.bat" else "run")
+ val outputPath = T.ctx().dest / (if (isBatch) "run.bat" else "run")
write(outputPath, prependShellScript())
@@ -258,12 +286,15 @@ object dev extends MillModule{
}
def assembly = T{
- val filename = if (scala.util.Properties.isWin) "mill.bat" else "mill"
+ val filename = if (isBatch) "mill.bat" else "mill"
mv(super.assembly().path, T.ctx().dest / filename)
PathRef(T.ctx().dest / filename)
}
- def prependShellScript = launcherScript(scala.util.Properties.isWin, forkArgs(), runClasspath().map(_.path.toString))
+ def prependShellScript = T{
+ val classpath = runClasspath().map(_.path.toString)
+ launcherScript(forkArgs(), classpath, classpath)
+ }
def run(args: String*) = T.command{
args match{
@@ -282,22 +313,16 @@ object dev extends MillModule{
}
}
-
-private def releaseHelper(dest: Path,
- cp: Agg[Path],
- ver: String,
- isWin: Boolean)
- (implicit ctx: mill.util.Ctx.Dest): PathRef = {
- val (filename, arg) =
- if (isWin) ("mill.bat", "%~dp0%~nx0")
- else ("mill", "$0")
+def release = T{
+ val dest = T.ctx().dest
+ val filename = if (isBatch) "mill.bat" else "mill"
mv(
createAssembly(
- cp,
+ dev.runClasspath().map(_.path),
prependShellScript = launcherScript(
- isWin,
- Seq("-DMILL_VERSION=" + ver),
- Agg(arg)
+ Seq("-DMILL_VERSION=" + publishVersion()._2),
+ Agg("$0"),
+ Agg("%~dpnx0")
)
).path,
dest / filename
@@ -305,30 +330,6 @@ private def releaseHelper(dest: Path,
PathRef(dest / filename)
}
-def release = T{
- releaseHelper(
- T.ctx().dest,
- dev.runClasspath().map(_.path),
- publishVersion()._2,
- false)
-}
-
-def releaseBatch = T{
- releaseHelper(
- T.ctx().dest,
- dev.runClasspath().map(_.path),
- publishVersion()._2,
- true)
-}
-
-def releaseAll = T{
- val dest = T.ctx().dest
- val cp = dev.runClasspath().map(_.path)
- val ver = publishVersion()._2
- for (isWin <- Seq(false, true))
- yield (isWin, releaseHelper(dest, cp, ver, isWin))
-}
-
val isMasterCommit = {
sys.env.get("TRAVIS_PULL_REQUEST") == Some("false") &&
(sys.env.get("TRAVIS_BRANCH") == Some("master") || sys.env("TRAVIS_TAG") != "")
@@ -377,8 +378,5 @@ def uploadToGithub(authKey: String) = T.command{
.asString
}
- for ((isWin, pr) <- releaseAll())
- upload.apply(pr.path, releaseTag,
- if (isWin) s"mill-$label.bat" // so browser downloads it as mill-<version>.bat (?)
- else label, authKey)
+ upload.apply(release().path, releaseTag, label, authKey)
}
diff --git a/ci/test-mill-0.sh b/ci/test-mill-0.sh
index 3d1470f1..91a1ebf3 100755
--- a/ci/test-mill-0.sh
+++ b/ci/test-mill-0.sh
@@ -5,5 +5,5 @@ set -eux
# Starting from scratch...
git clean -xdf
-# Run tests using Mill built using SBT
-mill all {clientserver,main,scalalib,scalajslib}.test
+# Run tests
+mill -i all {main,scalalib,scalajslib}.test
diff --git a/ci/test-mill-1.sh b/ci/test-mill-1.sh
index 079cb519..b0ed7bc2 100755
--- a/ci/test-mill-1.sh
+++ b/ci/test-mill-1.sh
@@ -5,5 +5,5 @@ set -eux
# Starting from scratch...
git clean -xdf
-# Run tests using Mill built using SBT
+# Run tests
mill integration.test "mill.integration.local.{JawnTests,BetterFilesTests,UpickleTests}"
diff --git a/ci/test-mill-2.sh b/ci/test-mill-2.sh
index 3b0da706..ce61bb7c 100755
--- a/ci/test-mill-2.sh
+++ b/ci/test-mill-2.sh
@@ -5,5 +5,5 @@ set -eux
# Starting from scratch...
git clean -xdf
-# Run tests using Mill built using SBT
+# Run tests
mill integration.test "mill.integration.local.{AcyclicTests,AmmoniteTests}"
diff --git a/ci/test-mill-bootstrap.sh b/ci/test-mill-bootstrap.sh
new file mode 100755
index 00000000..8010e700
--- /dev/null
+++ b/ci/test-mill-bootstrap.sh
@@ -0,0 +1,30 @@
+#!/usr/bin/env bash
+
+set -eux
+
+# Starting from scratch...
+git clean -xdf
+
+# First build
+mill -i all __.publishLocal release
+mv out/release/dest/mill ~/mill-1
+
+# Clean up
+git clean -xdf
+
+rm -rf ~/.mill
+
+# Differentiate first and second builds
+echo "Build 2" > info.txt && git add info.txt && git commit -m "Add info.txt"
+
+# Second build
+~/mill-1 -i all __.publishLocal release
+mv out/release/dest/mill ~/mill-2
+
+# Clean up
+git clean -xdf
+
+rm -rf ~/.mill
+
+# Use second build to run tests using Mill
+~/mill-2 -i all {main,scalalib,scalajslib}.test \ No newline at end of file
diff --git a/ci/test-mill-dev.sh b/ci/test-mill-dev.sh
index ae8556fb..459f3eb1 100755
--- a/ci/test-mill-dev.sh
+++ b/ci/test-mill-dev.sh
@@ -8,8 +8,8 @@ git clean -xdf
# Build Mill
mill -i dev.assembly
-rm -fR ~/.mill
+rm -rf ~/.mill
-# Second build & run tests using Mill
-out/dev/assembly/dest/mill -i all {clientserver,main,scalalib,scalajslib}.test
+# Second build & run tests
+out/dev/assembly/dest/mill -i all {main,scalalib,scalajslib}.test
diff --git a/ci/test-mill-release.sh b/ci/test-mill-release.sh
index 19173827..e5ea2b67 100755
--- a/ci/test-mill-release.sh
+++ b/ci/test-mill-release.sh
@@ -5,12 +5,13 @@ set -eux
# Starting from scratch...
git clean -xdf
+# Build Mill
ci/publish-local.sh
+# Clean up
git clean -xdf
-rm -fR ~/.mill
-
-# Second build & run tests using Mill
+rm -rf ~/.mill
+# Run tests
~/mill-release -i integration.test "mill.integration.forked.{AcyclicTests,UpickleTests,PlayJsonTests}"
diff --git a/clientserver/src/mill/clientserver/ClientServer.java b/client/src/mill/client/ClientServer.java
index 15c20f41..468f8ab3 100644
--- a/clientserver/src/mill/clientserver/ClientServer.java
+++ b/client/src/mill/client/ClientServer.java
@@ -1,4 +1,4 @@
-package mill.clientserver;
+package mill.client;
import java.io.IOException;
diff --git a/clientserver/src/mill/clientserver/InputPumper.java b/client/src/mill/client/InputPumper.java
index 9f4bfb82..1789d069 100644
--- a/clientserver/src/mill/clientserver/InputPumper.java
+++ b/client/src/mill/client/InputPumper.java
@@ -1,4 +1,4 @@
-package mill.clientserver;
+package mill.client;
import java.io.InputStream;
import java.io.OutputStream;
diff --git a/client/src/mill/client/Lock.java b/client/src/mill/client/Lock.java
new file mode 100644
index 00000000..115529d3
--- /dev/null
+++ b/client/src/mill/client/Lock.java
@@ -0,0 +1,13 @@
+package mill.client;
+public abstract class Lock{
+ abstract public Locked lock() throws Exception;
+ abstract public Locked tryLock() throws Exception;
+ public void await() throws Exception{
+ lock().release();
+ }
+
+ /**
+ * Returns `true` if the lock is *available for taking*
+ */
+ abstract public boolean probe() throws Exception;
+} \ No newline at end of file
diff --git a/client/src/mill/client/Locked.java b/client/src/mill/client/Locked.java
new file mode 100644
index 00000000..6ba7dab5
--- /dev/null
+++ b/client/src/mill/client/Locked.java
@@ -0,0 +1,10 @@
+package mill.client;
+
+import java.io.RandomAccessFile;
+import java.nio.channels.FileChannel;
+import java.util.concurrent.locks.ReentrantLock;
+
+
+public interface Locked{
+ public void release() throws Exception;
+} \ No newline at end of file
diff --git a/clientserver/src/mill/clientserver/Locks.java b/client/src/mill/client/Locks.java
index 78c8dc6e..3b397fce 100644
--- a/clientserver/src/mill/clientserver/Locks.java
+++ b/client/src/mill/client/Locks.java
@@ -1,30 +1,14 @@
-package mill.clientserver;
+package mill.client;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.util.concurrent.locks.ReentrantLock;
-
-abstract class Lock{
- abstract public Locked lock() throws Exception;
- abstract public Locked tryLock() throws Exception;
- public void await() throws Exception{
- lock().release();
- }
-
- /**
- * Returns `true` if the lock is *available for taking*
- */
- abstract public boolean probe() throws Exception;
-}
-interface Locked{
- void release() throws Exception;
-}
-class Locks{
+public class Locks{
public Lock processLock;
public Lock serverLock;
public Lock clientLock;
- static Locks files(String lockBase) throws Exception{
+ public static Locks files(String lockBase) throws Exception{
return new Locks(){{
processLock = new FileLock(lockBase + "/pid");
@@ -33,7 +17,7 @@ class Locks{
clientLock = new FileLock(lockBase + "/clientLock");
}};
}
- static Locks memory(){
+ public static Locks memory(){
return new Locks(){{
this.processLock = new MemoryLock();
this.serverLock = new MemoryLock();
diff --git a/clientserver/src/mill/clientserver/Client.java b/client/src/mill/client/Main.java
index ccabc24d..109a9a9d 100644
--- a/clientserver/src/mill/clientserver/Client.java
+++ b/client/src/mill/client/Main.java
@@ -1,4 +1,4 @@
-package mill.clientserver;
+package mill.client;
import org.scalasbt.ipcsocket.*;
@@ -9,10 +9,10 @@ import java.net.URL;
import java.nio.channels.FileChannel;
import java.util.*;
-public class Client {
+public class Main {
static void initServer(String lockBase, boolean setJnaNoSys) throws IOException,URISyntaxException{
ArrayList<String> selfJars = new ArrayList<String>();
- ClassLoader current = Client.class.getClassLoader();
+ ClassLoader current = Main.class.getClassLoader();
while(current != null){
if (current instanceof java.net.URLClassLoader) {
URL[] urls = ((java.net.URLClassLoader) current).getURLs();
@@ -38,8 +38,9 @@ public class Client {
}
l.add("-cp");
l.add(String.join(File.pathSeparator, selfJars));
- l.add("mill.ServerMain");
+ l.add("mill.main.ServerMain");
l.add(lockBase);
+
new java.lang.ProcessBuilder()
.command(l)
.redirectOutput(new java.io.File(lockBase + "/logs"))
@@ -64,7 +65,7 @@ public class Client {
lockFile.close();
channel.close();
} else {
- int exitCode = Client.run(
+ int exitCode = Main.run(
lockBase,
new Runnable() {
@Override
diff --git a/clientserver/test/src/mill/clientserver/ClientServerTests.scala b/clientserver/test/src/mill/clientserver/ClientServerTests.scala
deleted file mode 100644
index ac2063ef..00000000
--- a/clientserver/test/src/mill/clientserver/ClientServerTests.scala
+++ /dev/null
@@ -1,209 +0,0 @@
-package mill.clientserver
-import java.io._
-import java.nio.file.Path
-
-import scala.collection.JavaConverters._
-import utest._
-class EchoServer extends ServerMain[Int]{
- def main0(args: Array[String],
- stateCache: Option[Int],
- mainInteractive: Boolean,
- stdin: InputStream,
- stdout: PrintStream,
- stderr: PrintStream,
- env: Map[String, String]) = {
-
- val reader = new BufferedReader(new InputStreamReader(stdin))
- val str = reader.readLine()
- if (args.nonEmpty){
- stdout.println(str + args(0))
- }
- env.toSeq.sortBy(_._1).foreach{
- case (key, value) => stdout.println(s"$key=$value")
- }
- stdout.flush()
- if (args.nonEmpty){
- stderr.println(str.toUpperCase + args(0))
- }
- stderr.flush()
- (true, None)
- }
-}
-
-object ClientServerTests extends TestSuite{
- def initStreams() = {
- val in = new ByteArrayInputStream("hello\n".getBytes())
- val out = new ByteArrayOutputStream()
- val err = new ByteArrayOutputStream()
- (in, out, err)
- }
- def init() = {
- val tmpDir = java.nio.file.Files.createTempDirectory("")
- val locks = Locks.memory()
-
- (tmpDir, locks)
- }
-
- def spawnEchoServer(tmpDir : Path, locks: Locks): Unit = {
- new Thread(() => new Server(
- tmpDir.toString,
- new EchoServer(),
- () => (),
- 1000,
- locks
- ).run()).start()
- }
-
- def runClientAux(tmpDir : Path, locks: Locks)
- (env : Map[String, String], args: Array[String]) = {
- val (in, out, err) = initStreams()
- Server.lockBlock(locks.clientLock){
- Client.run(
- tmpDir.toString,
- () => spawnEchoServer(tmpDir, locks),
- locks,
- in,
- out,
- err,
- args,
- env.asJava
- )
- Thread.sleep(100)
- (new String(out.toByteArray), new String(err.toByteArray))
- }
- }
-
- def tests = Tests{
- 'hello - {
- val (tmpDir, locks) = init()
- def runClient(s: String) = runClientAux(tmpDir, locks)(Map.empty, Array(s))
-
- // Make sure the simple "have the client start a server and
- // exchange one message" workflow works from end to end.
-
- assert(
- locks.clientLock.probe(),
- locks.serverLock.probe(),
- locks.processLock.probe()
- )
-
- val (out1, err1) = runClient("world")
-
- assert(
- out1 == "helloworld\n",
- err1 == "HELLOworld\n"
- )
-
- // Give a bit of time for the server to release the lock and
- // re-acquire it to signal to the client that it's done
- Thread.sleep(100)
-
- assert(
- locks.clientLock.probe(),
- !locks.serverLock.probe(),
- !locks.processLock.probe()
- )
-
- // A seecond client in sequence connect to the same server
- val (out2, err2) = runClient(" WORLD")
-
- assert(
- out2 == "hello WORLD\n",
- err2 == "HELLO WORLD\n"
- )
-
- // Make sure the server times out of not used for a while
- Thread.sleep(2000)
- assert(
- locks.clientLock.probe(),
- locks.serverLock.probe(),
- locks.processLock.probe()
- )
-
- // Have a third client spawn/connect-to a new server at the same path
- val (out3, err3) = runClient(" World")
- assert(
- out3 == "hello World\n",
- err3 == "HELLO World\n"
- )
- }
-
- 'envVars - {
- val (tmpDir, locks) = init()
-
- def runClient(env : Map[String, String]) = runClientAux(tmpDir, locks)(env, Array())
-
- // Make sure the simple "have the client start a server and
- // exchange one message" workflow works from end to end.
-
- assert(
- locks.clientLock.probe(),
- locks.serverLock.probe(),
- locks.processLock.probe()
- )
-
- def longString(s : String) = Array.fill(1000)(s).mkString
- val b1000 = longString("b")
- val c1000 = longString("c")
- val a1000 = longString("a")
-
- val env = Map(
- "a" -> a1000,
- "b" -> b1000,
- "c" -> c1000
- )
-
-
- val (out1, err1) = runClient(env)
- val expected = s"a=$a1000\nb=$b1000\nc=$c1000\n"
-
- assert(
- out1 == expected,
- err1 == ""
- )
-
- // Give a bit of time for the server to release the lock and
- // re-acquire it to signal to the client that it's done
- Thread.sleep(100)
-
- assert(
- locks.clientLock.probe(),
- !locks.serverLock.probe(),
- !locks.processLock.probe()
- )
-
- val path = List(
- "/Users/foo/Library/Haskell/bin",
- "/usr/local/git/bin",
- "/sw/bin/",
- "/usr/local/bin",
- "/usr/local/",
- "/usr/local/sbin",
- "/usr/local/mysql/bin",
- "/usr/local/bin",
- "/usr/bin",
- "/bin",
- "/usr/sbin",
- "/sbin",
- "/opt/X11/bin",
- "/usr/local/MacGPG2/bin",
- "/Library/TeX/texbin",
- "/usr/local/bin/",
- "/Users/foo/bin",
- "/Users/foo/go/bin",
- "~/.bloop"
- )
-
- val pathEnvVar = path.mkString(":")
- val (out2, err2) = runClient(Map("PATH" -> pathEnvVar))
-
- val expected2 = s"PATH=$pathEnvVar\n"
-
- assert(
- out2 == expected2,
- err2 == ""
- )
-
- }
- }
-}
diff --git a/core/src/mill/eval/Evaluator.scala b/core/src/mill/eval/Evaluator.scala
index 7b3634ad..a728ec97 100644
--- a/core/src/mill/eval/Evaluator.scala
+++ b/core/src/mill/eval/Evaluator.scala
@@ -174,10 +174,10 @@ case class Evaluator[T](home: Path,
newResults(labelledNamedTask.task) match{
case Result.Failure(_, Some((v, hashCode))) =>
- handleTaskResult(v, v.hashCode, paths.meta, inputsHash, labelledNamedTask)
+ handleTaskResult(v, v.##, paths.meta, inputsHash, labelledNamedTask)
case Result.Success((v, hashCode)) =>
- handleTaskResult(v, v.hashCode, paths.meta, inputsHash, labelledNamedTask)
+ handleTaskResult(v, v.##, paths.meta, inputsHash, labelledNamedTask)
case _ =>
// Wipe out any cached meta.json file that exists, so
@@ -305,7 +305,7 @@ case class Evaluator[T](home: Path,
newResults(task) = for(v <- res) yield {
(v,
if (task.isInstanceOf[Worker[_]]) inputsHash
- else v.hashCode
+ else v.##
)
}
}
@@ -359,7 +359,7 @@ object Evaluator{
def classLoaderSig = Thread.currentThread().getContextClassLoader match {
case scl: SpecialClassLoader => scl.classpathSignature
case ucl: URLClassLoader =>
- SpecialClassLoader.initialClasspathSignature(ucl).map{ case (k, v) => (Right(k), v)}
+ SpecialClassLoader.initialClasspathSignature(ucl)
case _ => Nil
}
case class Timing(label: String,
diff --git a/docs/pages/1 - Intro to Mill.md b/docs/pages/1 - Intro to Mill.md
index d0ce29c7..305721d4 100644
--- a/docs/pages/1 - Intro to Mill.md
+++ b/docs/pages/1 - Intro to Mill.md
@@ -453,13 +453,12 @@ res2: mill.scalalib.CompilationResult = CompilationResult(
)
```
-You can run `mill` alone to open a build REPL; this is a Scala console with your
+You can run `mill -i` to open a build REPL; this is a Scala console with your
`build.sc` loaded, which lets you run tasks interactively. The task-running
syntax is slightly different from the command-line, but more in-line with how
you would depend on tasks from within your build file.
-You can use this REPL to run build commands quicker, due to keeping the JVM warm
-between runs, or to interactively explore your build to see what is available.
+You can use this REPL to interactively explore your build to see what is available.
## Deploying your code
@@ -543,6 +542,5 @@ You also need to specify `release` as `true` or `false`, depending on whether
you just want to stage your module on `oss.sonatype.org` or you want Mill to
complete the release process to Maven Central.
-If you are publishing multiple artifacts, you can also use `target/bin/mill
-mill.scalalib.PublishModule/publishAll1 as described
+If you are publishing multiple artifacts, you can also use `mill mill.scalalib.PublishModule/publishAll` as described
[here](http://www.lihaoyi.com/mill/page/common-project-layouts.html#publishing)
diff --git a/docs/pages/2 - Configuring Mill.md b/docs/pages/2 - Configuring Mill.md
index 4df4d5d4..a229fa72 100644
--- a/docs/pages/2 - Configuring Mill.md
+++ b/docs/pages/2 - Configuring Mill.md
@@ -414,7 +414,7 @@ object foo extends ScalaModule {
Mill's `foo.run` by default will discover which main class to run from your
compilation output, but if there is more than one or the main class comes from
-some library you cna explicitly specify which one to use. This also adds the
+some library you can explicitly specify which one to use. This also adds the
main class to your `foo.jar` and `foo.assembly` jars.
## Downloading Non-Maven Jars
diff --git a/integration/test/resources/play-json/build.sc b/integration/test/resources/play-json/build.sc
index e5222ae1..c60eefba 100644
--- a/integration/test/resources/play-json/build.sc
+++ b/integration/test/resources/play-json/build.sc
@@ -1,5 +1,5 @@
import mill._, mill.scalalib._, mill.scalalib.publish._, mill.scalajslib._
-import $file.version
+import $file.playJsonVersion
import $file.reformat
import reformat.Scalariform
import $file.mima
@@ -47,7 +47,7 @@ trait PlayJsonModule extends BaseModule with PublishModule with MiMa {
def scalacOptions = Seq("-deprecation", "-feature", "-unchecked", "-encoding", "utf8")
def javacOptions = Seq("-encoding", "UTF-8", "-Xlint:-options")
- def publishVersion = version.current
+ def publishVersion = playJsonVersion.current
}
abstract class PlayJson(val platformSegment: String) extends PlayJsonModule {
@@ -329,14 +329,14 @@ object release extends Module {
private val ReleaseVersion = raw"""(\d+)\.(\d+)\.(\d+)""".r
private val MinorSnapshotVersion = raw"""(\d+)\.(\d+)\.(\d+)-SNAPSHOT""".r
- private val releaseVersion = version.current match {
+ private val releaseVersion = playJsonVersion.current match {
case MinorSnapshotVersion(major, minor, patch) =>
s"${major}.${minor}.${patch.toInt}"
case ReleaseVersion(major, minor, patch) =>
s"${major}.${minor}.${patch.toInt}"
}
- private val nextVersion = version.current match {
+ private val nextVersion = playJsonVersion.current match {
case v@MinorSnapshotVersion(major, minor, patch) => v
case ReleaseVersion(major, minor, patch) =>
s"${major}.${minor}.${patch.toInt + 1}-SNAPSHOT"
diff --git a/integration/test/resources/play-json/mima.sc b/integration/test/resources/play-json/mima.sc
index ebab2c72..3902f2c7 100644
--- a/integration/test/resources/play-json/mima.sc
+++ b/integration/test/resources/play-json/mima.sc
@@ -18,12 +18,7 @@ trait MiMa extends ScalaModule with PublishModule {
def previousDeps = T {
Agg.from(previousVersions().map { version =>
- Dep.Java(
- pomSettings().organization,
- artifactId(),
- version,
- cross = false
- )
+ ivy"${pomSettings().organization}:${artifactId()}:${version}"
})
}
diff --git a/integration/test/resources/play-json/version.sc b/integration/test/resources/play-json/playJsonVersion.sc
index e9dbb12f..e9dbb12f 100644
--- a/integration/test/resources/play-json/version.sc
+++ b/integration/test/resources/play-json/playJsonVersion.sc
diff --git a/main/src/mill/Main.scala b/main/src/mill/Main.scala
index c9ec00ca..a349321e 100644
--- a/main/src/mill/Main.scala
+++ b/main/src/mill/Main.scala
@@ -8,27 +8,11 @@ import ammonite.main.Cli._
import ammonite.ops._
import ammonite.util.Util
import io.github.retronym.java9rtexport.Export
+import mill.client.ClientServer
import mill.eval.Evaluator
import mill.util.DummyInputStream
-object ServerMain extends mill.clientserver.ServerMain[Evaluator.State]{
- def main0(args: Array[String],
- stateCache: Option[Evaluator.State],
- mainInteractive: Boolean,
- stdin: InputStream,
- stdout: PrintStream,
- stderr: PrintStream,
- env : Map[String, String]) = Main.main0(
- args,
- stateCache,
- mainInteractive,
- DummyInputStream,
- stdout,
- stderr,
- env
- )
-}
object Main {
def main(args: Array[String]): Unit = {
@@ -126,7 +110,7 @@ object Main {
env
)
- if (mill.clientserver.ClientServer.isJava9OrAbove) {
+ if (ClientServer.isJava9OrAbove) {
val rt = cliConfig.home / Export.rtJarName
if (!exists(rt)) {
runner.printInfo(s"Preparing Java ${System.getProperty("java.version")} runtime; this may take a minute or two ...")
diff --git a/main/src/mill/main/MainModule.scala b/main/src/mill/main/MainModule.scala
index 7c84f74a..32281407 100644
--- a/main/src/mill/main/MainModule.scala
+++ b/main/src/mill/main/MainModule.scala
@@ -30,7 +30,11 @@ trait MainModule extends mill.Module{
implicit def millDiscover: mill.define.Discover[_]
implicit def millScoptTasksReads[T] = new mill.main.Tasks.Scopt[T]()
implicit def millScoptEvaluatorReads[T] = new mill.main.EvaluatorScopt[T]()
-
+ def version() = mill.T.command {
+ val res = System.getProperty("MILL_VERSION")
+ println(res)
+ res
+ }
/**
* Resolves a mill query string and prints out the tasks it resolves to.
*/
diff --git a/clientserver/src/mill/clientserver/Server.scala b/main/src/mill/main/Server.scala
index 4deac55f..14aade4c 100644
--- a/clientserver/src/mill/clientserver/Server.scala
+++ b/main/src/mill/main/Server.scala
@@ -1,29 +1,51 @@
-package mill.clientserver
+package mill.main
import java.io._
import java.net.Socket
+import mill.Main
import scala.collection.JavaConverters._
import org.scalasbt.ipcsocket._
+import mill.client._
+import mill.eval.Evaluator
+import mill.util.DummyInputStream
trait ServerMain[T]{
+ var stateCache = Option.empty[T]
+ def main0(args: Array[String],
+ stateCache: Option[T],
+ mainInteractive: Boolean,
+ stdin: InputStream,
+ stdout: PrintStream,
+ stderr: PrintStream,
+ env : Map[String, String]): (Boolean, Option[T])
+}
+
+object ServerMain extends mill.main.ServerMain[Evaluator.State]{
def main(args0: Array[String]): Unit = {
new Server(
args0(0),
this,
() => System.exit(0),
300000,
- Locks.files(args0(0))
+ mill.client.Locks.files(args0(0))
).run()
}
- var stateCache = Option.empty[T]
def main0(args: Array[String],
- stateCache: Option[T],
+ stateCache: Option[Evaluator.State],
mainInteractive: Boolean,
stdin: InputStream,
stdout: PrintStream,
stderr: PrintStream,
- env : Map[String, String]): (Boolean, Option[T])
+ env : Map[String, String]) = Main.main0(
+ args,
+ stateCache,
+ mainInteractive,
+ DummyInputStream,
+ stdout,
+ stderr,
+ env
+ )
}
diff --git a/main/src/mill/modules/Jvm.scala b/main/src/mill/modules/Jvm.scala
index 92469988..e7fd6a79 100644
--- a/main/src/mill/modules/Jvm.scala
+++ b/main/src/mill/modules/Jvm.scala
@@ -1,6 +1,6 @@
package mill.modules
-import java.io.{ByteArrayInputStream, FileOutputStream, File}
+import java.io.{ByteArrayInputStream, File, FileOutputStream}
import java.lang.reflect.Modifier
import java.net.{URI, URLClassLoader}
import java.nio.file.{FileSystems, Files, OpenOption, StandardOpenOption}
@@ -9,7 +9,7 @@ import java.util.jar.{JarEntry, JarFile, JarOutputStream}
import ammonite.ops._
import geny.Generator
-import mill.clientserver.InputPumper
+import mill.client.InputPumper
import mill.eval.PathRef
import mill.util.{Ctx, IO}
import mill.util.Loose.Agg
@@ -277,7 +277,7 @@ object Jvm {
// Prepend shell script and make it executable
if (prependShellScript.isEmpty) mv(tmp, output)
else{
- val lineSep = if (isWin) "\r\n" else "\n"
+ val lineSep = if (!prependShellScript.endsWith("\n")) "\n\r\n" else ""
val outputStream = newOutputStream(output.toNIO)
IO.stream(new ByteArrayInputStream((prependShellScript + lineSep).getBytes()), outputStream)
IO.stream(read.getInputStream(tmp), outputStream)
@@ -319,30 +319,50 @@ object Jvm {
}
- def launcherShellScript(isWin: Boolean,
- mainClass: String,
- classPath: Agg[String],
- jvmArgs: Seq[String]) = {
- val cp = classPath.mkString(File.pathSeparator)
- if (isWin)
- s"""@echo off
- |
- |java ${jvmArgs.mkString(" ")} %JAVA_OPTS% -cp "$cp" $mainClass %*
- """.stripMargin.split('\n').mkString("\r\n")
- else
- s"""#!/usr/bin/env sh
- |
- |exec java ${jvmArgs.mkString(" ")} $$JAVA_OPTS -cp "$cp" $mainClass "$$@"
- """.stripMargin
+ def universalScript(shellCommands: String,
+ cmdCommands: String,
+ shebang: Boolean = false): String = {
+ Seq(
+ if (shebang) "#!/usr/bin/env sh" else "",
+ "@ 2>/dev/null # 2>nul & echo off & goto BOF\r",
+ ":",
+ shellCommands.replaceAll("\r\n|\n", "\n"),
+ "exit",
+ Seq(
+ "",
+ ":BOF",
+ "@echo off",
+ cmdCommands.replaceAll("\r\n|\n", "\r\n"),
+ "exit /B %errorlevel%",
+ ""
+ ).mkString("\r\n")
+ ).filterNot(_.isEmpty).mkString("\n")
+ }
+
+ def launcherUniversalScript(mainClass: String,
+ shellClassPath: Agg[String],
+ cmdClassPath: Agg[String],
+ jvmArgs: Seq[String]) = {
+ universalScript(
+ shellCommands =
+ s"""exec java ${jvmArgs.mkString(" ")} $$JAVA_OPTS -cp "${shellClassPath.mkString(":")}" $mainClass "$$@"""",
+ cmdCommands =
+ s"""java ${jvmArgs.mkString(" ")} %JAVA_OPTS% -cp "${cmdClassPath.mkString(";")}" $mainClass %*""",
+ )
}
def createLauncher(mainClass: String,
classPath: Agg[Path],
jvmArgs: Seq[String])
(implicit ctx: Ctx.Dest)= {
val isWin = scala.util.Properties.isWin
- val outputPath = ctx.dest / (if (isWin) "run.bat" else "run")
-
- write(outputPath, launcherShellScript(isWin, mainClass, classPath.map(_.toString), jvmArgs))
+ val isBatch = isWin &&
+ !(org.jline.utils.OSUtils.IS_CYGWIN
+ || org.jline.utils.OSUtils.IS_MINGW
+ || "MSYS" == System.getProperty("MSYSTEM"))
+ val outputPath = ctx.dest / (if (isBatch) "run.bat" else "run")
+ val classPathStrs = classPath.map(_.toString)
+
+ write(outputPath, launcherUniversalScript(mainClass, classPathStrs, classPathStrs, jvmArgs))
if (!isWin) {
val perms = Files.getPosixFilePermissions(outputPath.toNIO)
diff --git a/main/test/src/mill/eval/EvaluationTests.scala b/main/test/src/mill/eval/EvaluationTests.scala
index 66147963..9c215086 100644
--- a/main/test/src/mill/eval/EvaluationTests.scala
+++ b/main/test/src/mill/eval/EvaluationTests.scala
@@ -247,6 +247,32 @@ object EvaluationTests extends TestSuite{
!overriden.contains("object1")
)
}
+ 'nullTasks - {
+ import nullTasks._
+ val checker = new Checker(nullTasks)
+ checker(nullTarget1, null, Agg(nullTarget1), extraEvaled = -1)
+ checker(nullTarget1, null, Agg(), extraEvaled = -1)
+ checker(nullTarget2, null, Agg(nullTarget2), extraEvaled = -1)
+ checker(nullTarget2, null, Agg(), extraEvaled = -1)
+ checker(nullTarget3, null, Agg(nullTarget3), extraEvaled = -1)
+ checker(nullTarget3, null, Agg(), extraEvaled = -1)
+ checker(nullTarget4, null, Agg(nullTarget4), extraEvaled = -1)
+ checker(nullTarget4, null, Agg(), extraEvaled = -1)
+
+ val nc1 = nullCommand1()
+ val nc2 = nullCommand2()
+ val nc3 = nullCommand3()
+ val nc4 = nullCommand4()
+
+ checker(nc1, null, Agg(nc1), extraEvaled = -1, secondRunNoOp = false)
+ checker(nc1, null, Agg(nc1), extraEvaled = -1, secondRunNoOp = false)
+ checker(nc2, null, Agg(nc2), extraEvaled = -1, secondRunNoOp = false)
+ checker(nc2, null, Agg(nc2), extraEvaled = -1, secondRunNoOp = false)
+ checker(nc3, null, Agg(nc3), extraEvaled = -1, secondRunNoOp = false)
+ checker(nc3, null, Agg(nc3), extraEvaled = -1, secondRunNoOp = false)
+ checker(nc4, null, Agg(nc4), extraEvaled = -1, secondRunNoOp = false)
+ checker(nc4, null, Agg(nc4), extraEvaled = -1, secondRunNoOp = false)
+ }
'tasksAreUncached - {
// Make sure the tasks `left` and `middle` re-compute every time, while
diff --git a/main/test/src/mill/eval/JavaCompileJarTests.scala b/main/test/src/mill/eval/JavaCompileJarTests.scala
index 1ac00c79..2e73b339 100644
--- a/main/test/src/mill/eval/JavaCompileJarTests.scala
+++ b/main/test/src/mill/eval/JavaCompileJarTests.scala
@@ -11,10 +11,10 @@ import mill.util.Strict.Agg
import utest._
import mill._
object JavaCompileJarTests extends TestSuite{
- def compileAll(sources: Seq[PathRef])(implicit ctx: Dest) = {
+ def compileAll(sources: mill.util.Loose.Agg[PathRef])(implicit ctx: Dest) = {
mkdir(ctx.dest)
import ammonite.ops._
- %("javac", sources.map(_.path.toString()), "-d", ctx.dest)(wd = ctx.dest)
+ %("javac", sources.map(_.path.toString()).toSeq, "-d", ctx.dest)(wd = ctx.dest)
PathRef(ctx.dest)
}
diff --git a/main/test/src/mill/main/ClientServerTests.scala b/main/test/src/mill/main/ClientServerTests.scala
new file mode 100644
index 00000000..60c9c9e6
--- /dev/null
+++ b/main/test/src/mill/main/ClientServerTests.scala
@@ -0,0 +1,214 @@
+package mill.main
+import java.io._
+import java.nio.file.Path
+
+import mill.client.{ClientServer, Locks}
+
+import scala.collection.JavaConverters._
+import utest._
+class EchoServer extends ServerMain[Int]{
+ def main0(args: Array[String],
+ stateCache: Option[Int],
+ mainInteractive: Boolean,
+ stdin: InputStream,
+ stdout: PrintStream,
+ stderr: PrintStream,
+ env: Map[String, String]) = {
+
+ val reader = new BufferedReader(new InputStreamReader(stdin))
+ val str = reader.readLine()
+ if (args.nonEmpty){
+ stdout.println(str + args(0))
+ }
+ env.toSeq.sortBy(_._1).foreach{
+ case (key, value) => stdout.println(s"$key=$value")
+ }
+ stdout.flush()
+ if (args.nonEmpty){
+ stderr.println(str.toUpperCase + args(0))
+ }
+ stderr.flush()
+ (true, None)
+ }
+}
+
+object ClientServerTests extends TestSuite{
+ def initStreams() = {
+ val in = new ByteArrayInputStream("hello\n".getBytes())
+ val out = new ByteArrayOutputStream()
+ val err = new ByteArrayOutputStream()
+ (in, out, err)
+ }
+ def init() = {
+ val tmpDir = java.nio.file.Files.createTempDirectory("")
+ val locks = Locks.memory()
+
+ (tmpDir, locks)
+ }
+
+ def spawnEchoServer(tmpDir : Path, locks: Locks): Unit = {
+ new Thread(() => new Server(
+ tmpDir.toString,
+ new EchoServer(),
+ () => (),
+ 1000,
+ locks
+ ).run()).start()
+ }
+
+ def runClientAux(tmpDir : Path, locks: Locks)
+ (env : Map[String, String], args: Array[String]) = {
+ val (in, out, err) = initStreams()
+ Server.lockBlock(locks.clientLock){
+ mill.client.Main.run(
+ tmpDir.toString,
+ () => spawnEchoServer(tmpDir, locks),
+ locks,
+ in,
+ out,
+ err,
+ args,
+ env.asJava
+ )
+ Thread.sleep(100)
+ (new String(out.toByteArray), new String(err.toByteArray))
+ }
+ }
+
+ def tests = Tests{
+ 'hello - {
+ if (!ClientServer.isWindows){
+ val (tmpDir, locks) = init()
+ def runClient(s: String) = runClientAux(tmpDir, locks)(Map.empty, Array(s))
+
+ // Make sure the simple "have the client start a server and
+ // exchange one message" workflow works from end to end.
+
+ assert(
+ locks.clientLock.probe(),
+ locks.serverLock.probe(),
+ locks.processLock.probe()
+ )
+
+ val (out1, err1) = runClient("world")
+
+ assert(
+ out1 == "helloworld\n",
+ err1 == "HELLOworld\n"
+ )
+
+ // Give a bit of time for the server to release the lock and
+ // re-acquire it to signal to the client that it's done
+ Thread.sleep(100)
+
+ assert(
+ locks.clientLock.probe(),
+ !locks.serverLock.probe(),
+ !locks.processLock.probe()
+ )
+
+ // A seecond client in sequence connect to the same server
+ val (out2, err2) = runClient(" WORLD")
+
+ assert(
+ out2 == "hello WORLD\n",
+ err2 == "HELLO WORLD\n"
+ )
+
+ // Make sure the server times out of not used for a while
+ Thread.sleep(2000)
+ assert(
+ locks.clientLock.probe(),
+ locks.serverLock.probe(),
+ locks.processLock.probe()
+ )
+
+ // Have a third client spawn/connect-to a new server at the same path
+ val (out3, err3) = runClient(" World")
+ assert(
+ out3 == "hello World\n",
+ err3 == "HELLO World\n"
+ )
+ }
+
+ 'envVars - {
+ if (!ClientServer.isWindows){
+ val (tmpDir, locks) = init()
+
+ def runClient(env : Map[String, String]) = runClientAux(tmpDir, locks)(env, Array())
+
+ // Make sure the simple "have the client start a server and
+ // exchange one message" workflow works from end to end.
+
+ assert(
+ locks.clientLock.probe(),
+ locks.serverLock.probe(),
+ locks.processLock.probe()
+ )
+
+ def longString(s : String) = Array.fill(1000)(s).mkString
+ val b1000 = longString("b")
+ val c1000 = longString("c")
+ val a1000 = longString("a")
+
+ val env = Map(
+ "a" -> a1000,
+ "b" -> b1000,
+ "c" -> c1000
+ )
+
+
+ val (out1, err1) = runClient(env)
+ val expected = s"a=$a1000\nb=$b1000\nc=$c1000\n"
+
+ assert(
+ out1 == expected,
+ err1 == ""
+ )
+
+ // Give a bit of time for the server to release the lock and
+ // re-acquire it to signal to the client that it's done
+ Thread.sleep(100)
+
+ assert(
+ locks.clientLock.probe(),
+ !locks.serverLock.probe(),
+ !locks.processLock.probe()
+ )
+
+ val path = List(
+ "/Users/foo/Library/Haskell/bin",
+ "/usr/local/git/bin",
+ "/sw/bin/",
+ "/usr/local/bin",
+ "/usr/local/",
+ "/usr/local/sbin",
+ "/usr/local/mysql/bin",
+ "/usr/local/bin",
+ "/usr/bin",
+ "/bin",
+ "/usr/sbin",
+ "/sbin",
+ "/opt/X11/bin",
+ "/usr/local/MacGPG2/bin",
+ "/Library/TeX/texbin",
+ "/usr/local/bin/",
+ "/Users/foo/bin",
+ "/Users/foo/go/bin",
+ "~/.bloop"
+ )
+
+ val pathEnvVar = path.mkString(":")
+ val (out2, err2) = runClient(Map("PATH" -> pathEnvVar))
+
+ val expected2 = s"PATH=$pathEnvVar\n"
+
+ assert(
+ out2 == expected2,
+ err2 == ""
+ )
+ }
+ }
+ }
+ }
+}
diff --git a/main/test/src/mill/util/ScriptTestSuite.scala b/main/test/src/mill/util/ScriptTestSuite.scala
index f88007c5..bbca5d68 100644
--- a/main/test/src/mill/util/ScriptTestSuite.scala
+++ b/main/test/src/mill/util/ScriptTestSuite.scala
@@ -10,8 +10,8 @@ abstract class ScriptTestSuite(fork: Boolean) extends TestSuite{
def scriptSourcePath: Path
val workspacePath = pwd / 'target / 'workspace / workspaceSlug
- val stdOutErr = new PrintStream(new ByteArrayOutputStream())
-// val stdOutErr = new PrintStream(System.out)
+// val stdOutErr = new PrintStream(new ByteArrayOutputStream())
+ val stdOutErr = new PrintStream(System.out)
val stdIn = new ByteArrayInputStream(Array())
lazy val runner = new mill.main.MainRunner(
ammonite.main.Cli.Config(wd = workspacePath),
@@ -21,7 +21,7 @@ abstract class ScriptTestSuite(fork: Boolean) extends TestSuite{
if (!fork) runner.runScript(workspacePath / "build.sc", s.toList)
else{
try {
- %%(home / "mill-release", "-i", s)(workspacePath)
+ %(home / "mill-release", "-i", s)(workspacePath)
true
}catch{case e: Throwable => false}
}
diff --git a/main/test/src/mill/util/TestGraphs.scala b/main/test/src/mill/util/TestGraphs.scala
index 11f72d02..83e03576 100644
--- a/main/test/src/mill/util/TestGraphs.scala
+++ b/main/test/src/mill/util/TestGraphs.scala
@@ -196,6 +196,23 @@ object TestGraphs{
override lazy val millDiscover: Discover[this.type] = Discover[this.type]
}
+ object nullTasks extends TestUtil.BaseModule{
+ val nullString: String = null
+ def nullTask1 = T.task{ nullString }
+ def nullTask2 = T.task{ nullTask1() }
+
+ def nullTarget1 = T{ nullString }
+ def nullTarget2 = T{ nullTarget1() }
+ def nullTarget3 = T{ nullTask1() }
+ def nullTarget4 = T{ nullTask2() }
+
+ def nullCommand1() = T.command{ nullString }
+ def nullCommand2() = T.command{ nullTarget1() }
+ def nullCommand3() = T.command{ nullTask1() }
+ def nullCommand4() = T.command{ nullTask2() }
+
+ override lazy val millDiscover: Discover[this.type] = Discover[this.type]
+ }
object singleCross extends TestUtil.BaseModule {
object cross extends mill.Cross[Cross]("210", "211", "212")
diff --git a/readme.md b/readme.md
index da41853b..1eec0e66 100644
--- a/readme.md
+++ b/readme.md
@@ -328,24 +328,41 @@ rm -rf out/
## Changelog
+### Master
+
+- Universal (combined batch/sh) script generation for launcher, assembly, and release
+
+ For some shell (e.g., `ksh` or `fish`), a shebang line should be added, e.g., using GNU sed:
+
+ ```bash
+ sed -i '1s;^;#!/usr/bin/env sh\n;' <mill-path>
+ ```
+
+ Or download directly with shebang added as follows:
+
+ ```bash
+ sudo sh -c '(echo "#!/usr/bin/env sh" && curl -L <mill-url>) > /usr/local/bin/mill && chmod +x /usr/local/bin/mill'
+ ```
+
+ On Windows, save `<mill-url>` as `mill.bat`
+
+- Windows client/server improvements
+
+- Windows repl support (note: MSYS2 subsystem/shell will be supported when jline3 v3.6.3 is released)
+
+- Fixed Java 9 support
+
### 0.1.7
+- Windows batch (.bat) generation for launcher, assembly, and release
+
- Support for non-interactive (client/server) mode on Windows.
- Mill requires an `sh` environment to run on Windows;
- it is recommended to use [MSYS2](https://www.msys2.org) on Windows.
-
- [Cygwin](https://www.cygwin.com) can also be used,
- but your mileage may vary when running mill on non-interactive (client/server) mode
- (it failed intermittently in mill's AppVeyor tests).
On Cygwin, run the following after downloading mill:
```bash
sed -i '0,/-cp "\$0"/{s/-cp "\$0"/-cp `cygpath -w "\$0"`/}; 0,/-cp "\$0"/{s/-cp "\$0"/-cp `cygpath -w "\$0"`/}' <mill-path>
```
-
- Mill also runs on [Git-Bash](https://gitforwindows.org) and [WSL](https://docs.microsoft.com/windows/wsl)
- but only on interactive mode.
- More fixes for Java 9
diff --git a/scalajslib/src/mill/scalajslib/ScalaJSModule.scala b/scalajslib/src/mill/scalajslib/ScalaJSModule.scala
index bddf894e..dbf693ff 100644
--- a/scalajslib/src/mill/scalajslib/ScalaJSModule.scala
+++ b/scalajslib/src/mill/scalajslib/ScalaJSModule.scala
@@ -36,7 +36,7 @@ trait ScalaJSModule extends scalalib.ScalaModule { outer =>
MavenRepository("https://repo1.maven.org/maven2"),
MavenRepository("https://oss.sonatype.org/content/repositories/releases")
),
- "2.12.4",
+ Lib.depToDependency(_, "2.12.4", ""),
Seq(
ivy"com.lihaoyi::mill-scalajslib-jsbridges-${scalaJSBridgeVersion()}:${sys.props("MILL_VERSION")}"
)
@@ -54,7 +54,7 @@ trait ScalaJSModule extends scalalib.ScalaModule { outer =>
}
resolveDependencies(
repositories,
- "2.12.4",
+ Lib.depToDependency(_, "2.12.4", ""),
commonDeps :+ envDep
)
}
@@ -150,9 +150,7 @@ trait ScalaJSModule extends scalalib.ScalaModule { outer =>
else scalaJSBinaryVersion()
}
- override def artifactSuffix: T[String] = T {
- s"_sjs${artifactScalaJSVersion()}_${artifactScalaVersion()}"
- }
+ override def artifactSuffix: T[String] = s"${platformSuffix()}_${artifactScalaVersion()}"
override def platformSuffix = s"_sjs${artifactScalaJSVersion()}"
diff --git a/scalalib/src/mill/scalalib/Dep.scala b/scalalib/src/mill/scalalib/Dep.scala
index f20480b7..9719bd2d 100644
--- a/scalalib/src/mill/scalalib/Dep.scala
+++ b/scalalib/src/mill/scalalib/Dep.scala
@@ -3,23 +3,29 @@ import mill.util.JsonFormatters._
import upickle.default.{macroRW, ReadWriter => RW}
sealed trait Dep {
def configure(attributes: coursier.Attributes): Dep
- def exclude(exclusions: (String, String)*): Dep =
- this match {
- case dep : Dep.Java => dep.copy(dep = dep.dep.copy(exclusions = dep.dep.exclusions ++ exclusions))
- case dep : Dep.Scala => dep.copy(dep = dep.dep.copy(exclusions = dep.dep.exclusions ++ exclusions))
- case dep : Dep.Point => dep.copy(dep = dep.dep.copy(exclusions = dep.dep.exclusions ++ exclusions))
- }
+ def force: Boolean
+ def forceVersion(): Dep = this match {
+ case dep : Dep.Java => dep.copy(force = true)
+ case dep : Dep.Scala => dep.copy(force = true)
+ case dep : Dep.Point => dep.copy(force = true)
+ }
+ def exclude(exclusions: (String, String)*): Dep = this match {
+ case dep : Dep.Java => dep.copy(dep = dep.dep.copy(exclusions = dep.dep.exclusions ++ exclusions))
+ case dep : Dep.Scala => dep.copy(dep = dep.dep.copy(exclusions = dep.dep.exclusions ++ exclusions))
+ case dep : Dep.Point => dep.copy(dep = dep.dep.copy(exclusions = dep.dep.exclusions ++ exclusions))
+ }
def excludeOrg(organizations: String*): Dep = exclude(organizations.map(_ -> "*"): _*)
def excludeName(names: String*): Dep = exclude(names.map("*" -> _): _*)
- def withConfiguration(configuration: String): Dep =
- this match {
- case dep : Dep.Java => dep.copy(dep = dep.dep.copy(configuration = configuration))
- case dep : Dep.Scala => dep.copy(dep = dep.dep.copy(configuration = configuration))
- case dep : Dep.Point => dep.copy(dep = dep.dep.copy(configuration = configuration))
- }
+ def withConfiguration(configuration: String): Dep = this match {
+ case dep : Dep.Java => dep.copy(dep = dep.dep.copy(configuration = configuration))
+ case dep : Dep.Scala => dep.copy(dep = dep.dep.copy(configuration = configuration))
+ case dep : Dep.Point => dep.copy(dep = dep.dep.copy(configuration = configuration))
+ }
}
object Dep{
+ val DefaultConfiguration = "default(compile)"
+
implicit def parse(signature: String) = {
val parts = signature.split(';')
val module = parts.head
@@ -31,45 +37,45 @@ object Dep{
}
}
(module.split(':') match {
- case Array(a, b, c) => Dep.Java(a, b, c, cross = false)
- case Array(a, b, "", c) => Dep.Java(a, b, c, cross = true)
- case Array(a, "", b, c) => Dep.Scala(a, b, c, cross = false)
- case Array(a, "", b, "", c) => Dep.Scala(a, b, c, cross = true)
- case Array(a, "", "", b, c) => Dep.Point(a, b, c, cross = false)
- case Array(a, "", "", b, "", c) => Dep.Point(a, b, c, cross = true)
+ case Array(a, b, c) => Dep.Java(a, b, c, cross = false, force = false)
+ case Array(a, b, "", c) => Dep.Java(a, b, c, cross = true, force = false)
+ case Array(a, "", b, c) => Dep.Scala(a, b, c, cross = false, force = false)
+ case Array(a, "", b, "", c) => Dep.Scala(a, b, c, cross = true, force = false)
+ case Array(a, "", "", b, c) => Dep.Point(a, b, c, cross = false, force = false)
+ case Array(a, "", "", b, "", c) => Dep.Point(a, b, c, cross = true, force = false)
case _ => throw new Exception(s"Unable to parse signature: [$signature]")
}).configure(attributes = attributes)
}
def apply(org: String, name: String, version: String, cross: Boolean): Dep = {
- this(coursier.Dependency(coursier.Module(org, name), version), cross)
+ this(coursier.Dependency(coursier.Module(org, name), version, DefaultConfiguration), cross)
}
- case class Java(dep: coursier.Dependency, cross: Boolean) extends Dep {
+ case class Java(dep: coursier.Dependency, cross: Boolean, force: Boolean) extends Dep {
def configure(attributes: coursier.Attributes): Dep = copy(dep = dep.copy(attributes = attributes))
}
object Java{
implicit def rw: RW[Java] = macroRW
- def apply(org: String, name: String, version: String, cross: Boolean): Dep = {
- Java(coursier.Dependency(coursier.Module(org, name), version), cross)
+ def apply(org: String, name: String, version: String, cross: Boolean, force: Boolean): Dep = {
+ Java(coursier.Dependency(coursier.Module(org, name), version, DefaultConfiguration), cross, force)
}
}
- implicit def default(dep: coursier.Dependency): Dep = new Java(dep, false)
- def apply(dep: coursier.Dependency, cross: Boolean) = Scala(dep, cross)
- case class Scala(dep: coursier.Dependency, cross: Boolean) extends Dep {
+ implicit def default(dep: coursier.Dependency): Dep = new Java(dep, false, false)
+ def apply(dep: coursier.Dependency, cross: Boolean) = Scala(dep, cross, false)
+ case class Scala(dep: coursier.Dependency, cross: Boolean, force: Boolean) extends Dep {
def configure(attributes: coursier.Attributes): Dep = copy(dep = dep.copy(attributes = attributes))
}
object Scala{
implicit def rw: RW[Scala] = macroRW
- def apply(org: String, name: String, version: String, cross: Boolean): Dep = {
- Scala(coursier.Dependency(coursier.Module(org, name), version), cross)
+ def apply(org: String, name: String, version: String, cross: Boolean, force: Boolean): Dep = {
+ Scala(coursier.Dependency(coursier.Module(org, name), version, DefaultConfiguration), cross, force)
}
}
- case class Point(dep: coursier.Dependency, cross: Boolean) extends Dep {
+ case class Point(dep: coursier.Dependency, cross: Boolean, force: Boolean) extends Dep {
def configure(attributes: coursier.Attributes): Dep = copy(dep = dep.copy(attributes = attributes))
}
object Point{
implicit def rw: RW[Point] = macroRW
- def apply(org: String, name: String, version: String, cross: Boolean): Dep = {
- Point(coursier.Dependency(coursier.Module(org, name), version), cross)
+ def apply(org: String, name: String, version: String, cross: Boolean, force: Boolean): Dep = {
+ Point(coursier.Dependency(coursier.Module(org, name), version, DefaultConfiguration), cross, force)
}
}
implicit def rw = RW.merge[Dep](
diff --git a/scalalib/src/mill/scalalib/GenIdea.scala b/scalalib/src/mill/scalalib/GenIdeaImpl.scala
index b118f29b..3e90f269 100644
--- a/scalalib/src/mill/scalalib/GenIdea.scala
+++ b/scalalib/src/mill/scalalib/GenIdeaImpl.scala
@@ -12,10 +12,10 @@ import mill.util.Strict.Agg
import scala.util.Try
-object GenIdeaModule extends ExternalModule {
+object GenIdea extends ExternalModule {
def idea(ev: Evaluator[Any]) = T.command{
- mill.scalalib.GenIdea(
+ mill.scalalib.GenIdeaImpl(
implicitly,
ev.rootModule,
ev.rootModule.millDiscover
@@ -25,7 +25,7 @@ object GenIdeaModule extends ExternalModule {
implicit def millScoptEvaluatorReads[T] = new mill.main.EvaluatorScopt[T]()
lazy val millDiscover = Discover[this.type]
}
-object GenIdea {
+object GenIdeaImpl {
def apply(ctx: Log with Home,
rootModule: BaseModule,
@@ -62,27 +62,31 @@ object GenIdea {
fetchMillModules: Boolean = true): Seq[(RelPath, scala.xml.Node)] = {
val modules = rootModule.millInternal.segmentsToModules.values
- .collect{ case x: scalalib.ScalaModule => (x.millModuleSegments, x)}
+ .collect{ case x: scalalib.JavaModule => (x.millModuleSegments, x)}
.toSeq
val buildLibraryPaths =
if (!fetchMillModules) Nil
else sys.props.get("MILL_BUILD_LIBRARIES") match {
- case Some(found) => Agg.from(found.split(',').map(Path(_)).distinct)
+ case Some(found) => found.split(',').map(Path(_)).distinct.toList
case None =>
- val repos = modules.foldLeft(Set.empty[Repository]) { _ ++ _._2.scalaWorker.repositories }
+ val repos = modules.foldLeft(Set.empty[Repository]) { _ ++ _._2.repositories }
val artifactNames = Seq("moduledefs", "core", "scalalib", "scalajslib")
val Result.Success(res) = scalalib.Lib.resolveDependencies(
repos.toList,
- "2.12.4",
+ Lib.depToDependency(_, "2.12.4", ""),
for(name <- artifactNames)
yield ivy"com.lihaoyi::mill-$name:${sys.props("MILL_VERSION")}"
)
- res.items.toSeq.map(_.path)
+ res.items.toList.map(_.path)
}
val resolved = for((path, mod) <- modules) yield {
- val allIvyDeps = T.task{mod.transitiveIvyDeps() ++ mod.scalaLibraryIvyDeps() ++ mod.compileIvyDeps()}
+ val scalaLibraryIvyDeps = mod match{
+ case x: ScalaModule => x.scalaLibraryIvyDeps
+ case _ => T.task{Nil}
+ }
+ val allIvyDeps = T.task{mod.transitiveIvyDeps() ++ scalaLibraryIvyDeps() ++ mod.compileIvyDeps()}
val externalDependencies = T.task{
mod.resolveDeps(allIvyDeps)() ++
Task.traverse(mod.transitiveModuleDeps)(_.unmanagedClasspath)().flatten
@@ -92,8 +96,10 @@ object GenIdea {
mod.resolveDeps(allIvyDeps, sources = true)()
}
- val scalacPluginsIvyDeps = T.task{mod.scalacPluginIvyDeps()}
- val scalacOptions = T.task{mod.scalacOptions()}
+ val (scalacPluginsIvyDeps, scalacOptions) = mod match{
+ case mod: ScalaModule => T.task{mod.scalacPluginIvyDeps()} -> T.task{mod.scalacOptions()}
+ case _ => T.task(Loose.Agg[Dep]()) -> T.task(Seq())
+ }
val scalacPluginDependencies = T.task{
mod.resolveDeps(scalacPluginsIvyDeps)()
}
@@ -103,18 +109,27 @@ object GenIdea {
val resolvedSp: Loose.Agg[PathRef] = evalOrElse(evaluator, scalacPluginDependencies, Loose.Agg.empty)
val scalacOpts: Seq[String] = evalOrElse(evaluator, scalacOptions, Seq())
- (path, resolvedCp.map(_.path).filter(_.ext == "jar") ++ resolvedSrcs.map(_.path), mod,
- resolvedSp.map(_.path).filter(_.ext == "jar"), scalacOpts)
+ (
+ path,
+ resolvedCp.map(_.path).filter(_.ext == "jar") ++ resolvedSrcs.map(_.path),
+ mod,
+ resolvedSp.map(_.path).filter(_.ext == "jar"),
+ scalacOpts
+ )
}
val moduleLabels = modules.map(_.swap).toMap
val allResolved = resolved.flatMap(_._2) ++ buildLibraryPaths
- val minResolvedLength = allResolved.map(_.segments.length).min
- val commonPrefix = allResolved.map(_.segments.take(minResolvedLength))
- .transpose
- .takeWhile(_.distinct.length == 1)
- .length
+ val commonPrefix =
+ if (allResolved.isEmpty) 0
+ else {
+ val minResolvedLength = allResolved.map(_.segments.length).min
+ allResolved.map(_.segments.take(minResolvedLength))
+ .transpose
+ .takeWhile(_.distinct.length == 1)
+ .length
+ }
// only resort to full long path names if the jar name is a duplicate
val pathShortLibNameDuplicate = allResolved
@@ -134,7 +149,7 @@ object GenIdea {
.toMap
val compilerSettings = resolved
- .foldLeft(Map[(Loose.Agg[Path], Seq[String]), Vector[ScalaModule]]()) {
+ .foldLeft(Map[(Loose.Agg[Path], Seq[String]), Vector[JavaModule]]()) {
(r, q) =>
val key = (q._4, q._5)
r + (key -> (r.getOrElse(key, Vector()) :+ q._3))
@@ -190,7 +205,10 @@ object GenIdea {
evaluator.outPath,
mod.compile.ctx.segments
)
- val Seq(scalaVersion: String) = evaluator.evaluate(Agg(mod.scalaVersion)).values
+ val scalaVersionOpt = mod match {
+ case x: ScalaModule => Some(evaluator.evaluate(Agg(x.scalaVersion)).values.head.asInstanceOf[String])
+ case _ => None
+ }
val generatedSourceOutPath = Evaluator.resolveDestPaths(
evaluator.outPath,
mod.generatedSources.ctx.segments
@@ -198,7 +216,7 @@ object GenIdea {
val elem = moduleXmlTemplate(
mod.millModuleBasePath.value,
- scalaVersion,
+ scalaVersionOpt,
Strict.Agg.from(resourcesPathRefs.map(_.path)),
Strict.Agg.from(normalSourcePaths),
Strict.Agg.from(generatedSourcePaths),
@@ -293,7 +311,7 @@ object GenIdea {
</component>
}
def moduleXmlTemplate(basePath: Path,
- scalaVersion: String,
+ scalaVersionOpt: Option[String],
resourcePaths: Strict.Agg[Path],
normalSourcePaths: Strict.Agg[Path],
generatedSourcePaths: Strict.Agg[Path],
@@ -326,7 +344,10 @@ object GenIdea {
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
- <orderEntry type="library" name={s"scala-sdk-$scalaVersion"} level="application" />
+ {
+ for(scalaVersion <- scalaVersionOpt.toSeq)
+ yield <orderEntry type="library" name={s"scala-sdk-$scalaVersion"} level="application" />
+ }
{
for(name <- libNames.toSeq.sorted)
@@ -340,7 +361,7 @@ object GenIdea {
</component>
</module>
}
- def scalaCompilerTemplate(settings: Map[(Loose.Agg[Path], Seq[String]), Seq[ScalaModule]]) = {
+ def scalaCompilerTemplate(settings: Map[(Loose.Agg[Path], Seq[String]), Seq[JavaModule]]) = {
<project version="4">
<component name="ScalaCompilerConfiguration">
diff --git a/scalalib/src/mill/scalalib/JavaModule.scala b/scalalib/src/mill/scalalib/JavaModule.scala
new file mode 100644
index 00000000..b8ae0fd4
--- /dev/null
+++ b/scalalib/src/mill/scalalib/JavaModule.scala
@@ -0,0 +1,284 @@
+package mill
+package scalalib
+
+
+import ammonite.ops._
+import coursier.Repository
+import mill.define.Task
+import mill.define.TaskModule
+import mill.eval.{PathRef, Result}
+import mill.modules.Jvm
+import mill.modules.Jvm.{createAssembly, createJar}
+import Lib._
+import mill.scalalib.publish.{Artifact, Scope}
+import mill.util.Loose.Agg
+
+/**
+ * Core configuration required to compile a single Scala compilation target
+ */
+trait JavaModule extends mill.Module with TaskModule { outer =>
+
+ def defaultCommandName() = "run"
+
+ def resolvePublishDependency: Task[Dep => publish.Dependency] = T.task{
+ Artifact.fromDepJava(_: Dep)
+ }
+ def resolveCoursierDependency: Task[Dep => coursier.Dependency] = T.task{
+ Lib.depToDependencyJava(_: Dep)
+ }
+
+ def mainClass: T[Option[String]] = None
+
+ def finalMainClassOpt: T[Either[String, String]] = T{
+ mainClass() match{
+ case Some(m) => Right(m)
+ case None => Left("No main class specified or found")
+ }
+ }
+
+ def finalMainClass: T[String] = T{
+ finalMainClassOpt() match {
+ case Right(main) => Result.Success(main)
+ case Left(msg) => Result.Failure(msg)
+ }
+ }
+
+ def ivyDeps = T{ Agg.empty[Dep] }
+ def compileIvyDeps = T{ Agg.empty[Dep] }
+ def runIvyDeps = T{ Agg.empty[Dep] }
+
+ def javacOptions = T{ Seq.empty[String] }
+
+ def moduleDeps = Seq.empty[JavaModule]
+
+
+ def transitiveModuleDeps: Seq[JavaModule] = {
+ Seq(this) ++ moduleDeps.flatMap(_.transitiveModuleDeps).distinct
+ }
+ def unmanagedClasspath = T{ Agg.empty[PathRef] }
+
+
+ def transitiveIvyDeps: T[Agg[Dep]] = T{
+ ivyDeps() ++ Task.traverse(moduleDeps)(_.transitiveIvyDeps)().flatten
+ }
+
+ def upstreamCompileOutput = T{
+ Task.traverse(moduleDeps)(_.compile)
+ }
+
+ def transitiveLocalClasspath: T[Agg[PathRef]] = T{
+ Task.traverse(moduleDeps)(m =>
+ T.task{m.localClasspath() ++ m.transitiveLocalClasspath()}
+ )().flatten
+ }
+
+ def mapDependencies(d: coursier.Dependency) = d
+
+ def resolveDeps(deps: Task[Agg[Dep]], sources: Boolean = false) = T.task{
+ resolveDependencies(
+ repositories,
+ resolveCoursierDependency().apply(_),
+ deps(),
+ sources,
+ mapDependencies = Some(mapDependencies)
+ )
+ }
+
+
+ def repositories: Seq[Repository] = ScalaWorkerModule.repositories
+
+ def platformSuffix = T{ "" }
+
+ private val Milestone213 = raw"""2.13.(\d+)-M(\d+)""".r
+
+ def prependShellScript: T[String] = T{
+ mainClass() match{
+ case None => ""
+ case Some(cls) =>
+ val isWin = scala.util.Properties.isWin
+ mill.modules.Jvm.launcherUniversalScript(
+ cls,
+ Agg("$0"), Agg("%~dpnx0"),
+ forkArgs()
+ )
+ }
+ }
+
+ def sources = T.sources{ millSourcePath / 'src }
+ def resources = T.sources{ millSourcePath / 'resources }
+ def generatedSources = T{ Seq.empty[PathRef] }
+ def allSources = T{ sources() ++ generatedSources() }
+
+ def allSourceFiles = T{
+ for {
+ root <- allSources()
+ if exists(root.path)
+ path <- ls.rec(root.path)
+ if path.isFile && (path.ext == "scala" || path.ext == "java")
+ } yield PathRef(path)
+ }
+
+ def compile: T[CompilationResult] = T{
+ Lib.compileJava(
+ allSourceFiles().map(_.path.toIO).toArray,
+ compileClasspath().map(_.path.toIO).toArray,
+ javacOptions(),
+ upstreamCompileOutput()
+ )
+ }
+ def localClasspath = T{
+ resources() ++ Agg(compile().classes)
+ }
+ def compileClasspath = T{
+ transitiveLocalClasspath() ++
+ resources() ++
+ unmanagedClasspath() ++
+ resolveDeps(T.task{compileIvyDeps() ++ transitiveIvyDeps()})()
+ }
+
+ def upstreamAssemblyClasspath = T{
+ transitiveLocalClasspath() ++
+ unmanagedClasspath() ++
+ resolveDeps(T.task{runIvyDeps() ++ transitiveIvyDeps()})()
+ }
+
+ def runClasspath = T{
+ localClasspath() ++
+ upstreamAssemblyClasspath()
+ }
+
+ /**
+ * Build the assembly for upstream dependencies separate from the current classpath
+ *
+ * This should allow much faster assembly creation in the common case where
+ * upstream dependencies do not change
+ */
+ def upstreamAssembly = T{
+ createAssembly(upstreamAssemblyClasspath().map(_.path), mainClass())
+ }
+
+ def assembly = T{
+ createAssembly(
+ Agg.from(localClasspath().map(_.path)),
+ mainClass(),
+ prependShellScript(),
+ Some(upstreamAssembly().path)
+ )
+ }
+
+
+ def jar = T{
+ createJar(
+ localClasspath().map(_.path).filter(exists),
+ mainClass()
+ )
+ }
+
+ def docJar = T[PathRef] {
+ val outDir = T.ctx().dest
+
+ val javadocDir = outDir / 'javadoc
+ mkdir(javadocDir)
+
+ val files = for{
+ ref <- allSources()
+ if exists(ref.path)
+ p <- ls.rec(ref.path)
+ if p.isFile
+ } yield p.toNIO.toString
+
+ val options = Seq("-d", javadocDir.toNIO.toString)
+
+ if (files.nonEmpty) Jvm.baseInteractiveSubprocess(
+ commandArgs = Seq(
+ "javadoc"
+ ) ++ options ++
+ Seq(
+ "-classpath",
+ compileClasspath()
+ .map(_.path)
+ .filter(_.ext != "pom")
+ .mkString(java.io.File.pathSeparator)
+ ) ++
+ files.map(_.toString),
+ envArgs = Map(),
+ workingDir = T.ctx().dest
+ )
+
+ createJar(Agg(javadocDir))(outDir)
+ }
+
+ def sourceJar = T {
+ createJar((allSources() ++ resources()).map(_.path).filter(exists))
+ }
+
+ def forkArgs = T{ Seq.empty[String] }
+
+ def forkEnv = T{ sys.env.toMap }
+
+ def launcher = T{
+ Result.Success(
+ Jvm.createLauncher(
+ finalMainClass(),
+ runClasspath().map(_.path),
+ forkArgs()
+ )
+ )
+ }
+
+ def ivyDepsTree(inverse: Boolean = false) = T.command {
+ val (flattened, resolution) = Lib.resolveDependenciesMetadata(
+ repositories, resolveCoursierDependency().apply(_), ivyDeps(), Some(mapDependencies)
+ )
+
+ println(coursier.util.Print.dependencyTree(flattened, resolution,
+ printExclusions = false, reverse = inverse))
+
+ Result.Success()
+ }
+
+ def runLocal(args: String*) = T.command {
+ Jvm.runLocal(
+ finalMainClass(),
+ runClasspath().map(_.path),
+ args
+ )
+ }
+
+ def run(args: String*) = T.command{
+ Jvm.interactiveSubprocess(
+ finalMainClass(),
+ runClasspath().map(_.path),
+ forkArgs(),
+ forkEnv(),
+ args,
+ workingDir = ammonite.ops.pwd
+ )
+ }
+
+
+ def runMainLocal(mainClass: String, args: String*) = T.command {
+ Jvm.runLocal(
+ mainClass,
+ runClasspath().map(_.path),
+ args
+ )
+ }
+
+ def runMain(mainClass: String, args: String*) = T.command{
+ Jvm.interactiveSubprocess(
+ mainClass,
+ runClasspath().map(_.path),
+ forkArgs(),
+ forkEnv(),
+ args,
+ workingDir = ammonite.ops.pwd
+ )
+ }
+
+ // publish artifact with name "mill_2.12.4" instead of "mill_2.12"
+
+ def artifactName: T[String] = millModuleSegments.parts.mkString("-")
+
+ def artifactId: T[String] = artifactName()
+} \ No newline at end of file
diff --git a/scalalib/src/mill/scalalib/Lib.scala b/scalalib/src/mill/scalalib/Lib.scala
index e6a7a255..7b4b5bdb 100644
--- a/scalalib/src/mill/scalalib/Lib.scala
+++ b/scalalib/src/mill/scalalib/Lib.scala
@@ -1,6 +1,9 @@
package mill
package scalalib
+import java.io.File
+import javax.tools.ToolProvider
+
import ammonite.ops._
import ammonite.util.Util
import coursier.{Cache, Dependency, Fetch, Repository, Resolution}
@@ -15,6 +18,28 @@ object CompilationResult {
case class CompilationResult(analysisFile: Path, classes: PathRef)
object Lib{
+ def compileJava(sources: Array[java.io.File],
+ classpath: Array[java.io.File],
+ javaOpts: Seq[String],
+ upstreamCompileOutput: Seq[CompilationResult])
+ (implicit ctx: mill.util.Ctx) = {
+ val javac = ToolProvider.getSystemJavaCompiler()
+
+ rm(ctx.dest / 'classes)
+ mkdir(ctx.dest / 'classes)
+ val cpArgs =
+ if(classpath.isEmpty) Seq()
+ else Seq("-cp", classpath.mkString(File.pathSeparator))
+
+ val args = Seq("-d", ctx.dest / 'classes) ++ cpArgs ++ javaOpts ++ sources
+
+ javac.run(
+ ctx.log.inStream, ctx.log.outputStream, ctx.log.errorStream,
+ args.map(_.toString):_*
+ )
+ if (ls(ctx.dest / 'classes).isEmpty) mill.eval.Result.Failure("Compilation Failed")
+ else mill.eval.Result.Success(CompilationResult(ctx.dest / 'zinc, PathRef(ctx.dest / 'classes)))
+ }
private val ReleaseVersion = raw"""(\d+)\.(\d+)\.(\d+)""".r
private val MinorSnapshotVersion = raw"""(\d+)\.(\d+)\.([1-9]\d*)-SNAPSHOT""".r
@@ -34,17 +59,23 @@ object Lib{
.toIO
}
- def depToDependency(dep: Dep, scalaVersion: String, platformSuffix: String = ""): Dependency =
+
+ def depToDependencyJava(dep: Dep, platformSuffix: String = ""): Dependency = {
dep match {
- case Dep.Java(dep, cross) =>
+ case Dep.Java(dep, cross, force) =>
dep.copy(
module = dep.module.copy(
name =
dep.module.name +
- (if (!cross) "" else platformSuffix)
+ (if (!cross) "" else platformSuffix)
)
)
- case Dep.Scala(dep, cross) =>
+ }
+ }
+ def depToDependency(dep: Dep, scalaVersion: String, platformSuffix: String = ""): Dependency =
+ dep match {
+ case d: Dep.Java => depToDependencyJava(dep)
+ case Dep.Scala(dep, cross, force) =>
dep.copy(
module = dep.module.copy(
name =
@@ -53,7 +84,7 @@ object Lib{
"_" + scalaBinaryVersion(scalaVersion)
)
)
- case Dep.Point(dep, cross) =>
+ case Dep.Point(dep, cross, force) =>
dep.copy(
module = dep.module.copy(
name =
@@ -65,7 +96,29 @@ object Lib{
}
+ def resolveDependenciesMetadata(repositories: Seq[Repository],
+ depToDependency: Dep => coursier.Dependency,
+ deps: TraversableOnce[Dep],
+ mapDependencies: Option[Dependency => Dependency] = None) = {
+ val depSeq = deps.toSeq
+ val flattened = depSeq.map(depToDependency)
+
+ val forceVersions = depSeq.filter(_.force)
+ .map(depToDependency)
+ .map(mapDependencies.getOrElse(identity[Dependency](_)))
+ .map{d => d.module -> d.version}
+ .toMap
+ val start = Resolution(
+ flattened.map(mapDependencies.getOrElse(identity[Dependency](_))).toSet,
+ forceVersions = forceVersions,
+ mapDependencies = mapDependencies
+ )
+
+ val fetch = Fetch.from(repositories, Cache.fetch())
+ val resolution = start.process.run(fetch).unsafePerformSync
+ (flattened, resolution)
+ }
/**
* Resolve dependencies using Coursier.
*
@@ -74,16 +127,14 @@ object Lib{
* `import $ivy` syntax.
*/
def resolveDependencies(repositories: Seq[Repository],
- scalaVersion: String,
+ depToDependency: Dep => coursier.Dependency,
deps: TraversableOnce[Dep],
- platformSuffix: String = "",
- sources: Boolean = false): Result[Agg[PathRef]] = {
+ sources: Boolean = false,
+ mapDependencies: Option[Dependency => Dependency] = None): Result[Agg[PathRef]] = {
- val flattened = deps.map(depToDependency(_, scalaVersion, platformSuffix)).toSet
- val start = Resolution(flattened)
-
- val fetch = Fetch.from(repositories, Cache.fetch())
- val resolution = start.process.run(fetch).unsafePerformSync
+ val (_, resolution) = resolveDependenciesMetadata(
+ repositories, depToDependency, deps, mapDependencies
+ )
val errs = resolution.metadataErrors
if(errs.nonEmpty) {
val header =
@@ -130,16 +181,17 @@ object Lib{
}
}
def scalaCompilerIvyDeps(scalaVersion: String) = Agg[Dep](
- ivy"org.scala-lang:scala-compiler:$scalaVersion",
- ivy"org.scala-lang:scala-reflect:$scalaVersion"
+ ivy"org.scala-lang:scala-compiler:$scalaVersion".forceVersion(),
+ ivy"org.scala-lang:scala-reflect:$scalaVersion".forceVersion()
)
def scalaRuntimeIvyDeps(scalaVersion: String) = Agg[Dep](
- ivy"org.scala-lang:scala-library:$scalaVersion"
+ ivy"org.scala-lang:scala-library:$scalaVersion".forceVersion()
)
def compilerBridgeIvyDep(scalaVersion: String) =
Dep.Point(
coursier.Dependency(coursier.Module("com.lihaoyi", "mill-bridge"), "0.1", transitive = false),
- cross = false
+ cross = false,
+ force = false
)
}
diff --git a/scalalib/src/mill/scalalib/PublishModule.scala b/scalalib/src/mill/scalalib/PublishModule.scala
index 3cc9fd30..2ab81269 100644
--- a/scalalib/src/mill/scalalib/PublishModule.scala
+++ b/scalalib/src/mill/scalalib/PublishModule.scala
@@ -9,22 +9,20 @@ import mill.util.Loose.Agg
/**
* Configuration necessary for publishing a Scala module to Maven Central or similar
*/
-trait PublishModule extends ScalaModule { outer =>
+trait PublishModule extends JavaModule { outer =>
import mill.scalalib.publish._
override def moduleDeps = Seq.empty[PublishModule]
def pomSettings: T[PomSettings]
def publishVersion: T[String]
- def artifactId: T[String] = T { s"${artifactName()}${artifactSuffix()}" }
+
def publishSelfDependency = T{
Artifact(pomSettings().organization, artifactId(), publishVersion()),
}
def publishXmlDeps = T.task{
- val ivyPomDeps = ivyDeps().map(
- Artifact.fromDep(_, scalaVersion(), Lib.scalaBinaryVersion(scalaVersion()))
- )
+ val ivyPomDeps = ivyDeps().map(resolvePublishDependency().apply(_))
val modulePomDeps = Task.sequence(moduleDeps.map(_.publishSelfDependency))()
ivyPomDeps ++ modulePomDeps.map(Dependency(_, Scope.Compile))
}
diff --git a/scalalib/src/mill/scalalib/ScalaModule.scala b/scalalib/src/mill/scalalib/ScalaModule.scala
index dc39a4a8..b98f248e 100644
--- a/scalalib/src/mill/scalalib/ScalaModule.scala
+++ b/scalalib/src/mill/scalalib/ScalaModule.scala
@@ -7,7 +7,7 @@ import mill.define.Task
import mill.define.TaskModule
import mill.eval.{PathRef, Result}
import mill.modules.Jvm
-import mill.modules.Jvm.{createAssembly, createJar, subprocess}
+import mill.modules.Jvm.{createJar, subprocess}
import Lib._
import mill.util.Loose.Agg
import mill.util.DummyInputStream
@@ -15,23 +15,29 @@ import mill.util.DummyInputStream
/**
* Core configuration required to compile a single Scala compilation target
*/
-trait ScalaModule extends mill.Module with TaskModule { outer =>
- def defaultCommandName() = "run"
+trait ScalaModule extends JavaModule { outer =>
+ def scalaWorker: ScalaWorkerModule = mill.scalalib.ScalaWorkerModule
+
trait Tests extends TestModule{
def scalaVersion = outer.scalaVersion()
override def repositories = outer.repositories
override def scalacPluginIvyDeps = outer.scalacPluginIvyDeps
override def scalacOptions = outer.scalacOptions
+ override def javacOptions = outer.javacOptions
override def scalaWorker = outer.scalaWorker
override def moduleDeps = Seq(outer)
}
def scalaVersion: T[String]
- def mainClass: T[Option[String]] = None
+ override def resolveCoursierDependency: Task[Dep => coursier.Dependency] = T.task{
+ Lib.depToDependency(_: Dep, scalaVersion(), platformSuffix())
+ }
- def scalaWorker: ScalaWorkerModule = mill.scalalib.ScalaWorkerModule
+ override def resolvePublishDependency: Task[Dep => publish.Dependency] = T.task{
+ publish.Artifact.fromDep(_: Dep, scalaVersion(), Lib.scalaBinaryVersion(scalaVersion()))
+ }
- def finalMainClassOpt: T[Either[String, String]] = T{
+ override def finalMainClassOpt: T[Either[String, String]] = T{
mainClass() match{
case Some(m) => Right(m)
case None =>
@@ -47,58 +53,12 @@ trait ScalaModule extends mill.Module with TaskModule { outer =>
}
}
- def finalMainClass: T[String] = T{
- finalMainClassOpt() match {
- case Right(main) => Result.Success(main)
- case Left(msg) => Result.Failure(msg)
- }
- }
- def ivyDeps = T{ Agg.empty[Dep] }
- def compileIvyDeps = T{ Agg.empty[Dep] }
def scalacPluginIvyDeps = T{ Agg.empty[Dep] }
- def runIvyDeps = T{ Agg.empty[Dep] }
def scalacOptions = T{ Seq.empty[String] }
- def javacOptions = T{ Seq.empty[String] }
-
- def moduleDeps = Seq.empty[ScalaModule]
-
-
- def transitiveModuleDeps: Seq[ScalaModule] = {
- Seq(this) ++ moduleDeps.flatMap(_.transitiveModuleDeps).distinct
- }
- def unmanagedClasspath = T{ Agg.empty[PathRef] }
-
-
- def transitiveIvyDeps: T[Agg[Dep]] = T{
- ivyDeps() ++ Task.traverse(moduleDeps)(_.transitiveIvyDeps)().flatten
- }
-
- def upstreamCompileOutput = T{
- Task.traverse(moduleDeps)(_.compile)
- }
-
- def transitiveLocalClasspath: T[Agg[PathRef]] = T{
- Task.traverse(moduleDeps)(m =>
- T.task{m.localClasspath() ++ m.transitiveLocalClasspath()}
- )().flatten
- }
-
- def resolveDeps(deps: Task[Agg[Dep]], sources: Boolean = false) = T.task{
- resolveDependencies(
- repositories,
- scalaVersion(),
- deps(),
- platformSuffix(),
- sources
- )
- }
-
- def repositories: Seq[Repository] = scalaWorker.repositories
-
- def platformSuffix = T{ "" }
+ override def repositories: Seq[Repository] = scalaWorker.repositories
private val Milestone213 = raw"""2.13.(\d+)-M(\d+)""".r
@@ -110,7 +70,7 @@ trait ScalaModule extends mill.Module with TaskModule { outer =>
resolveDependencies(
repositories,
- scalaVersion0,
+ Lib.depToDependency(_, scalaVersion0, platformSuffix()),
Seq(ivy"org.scala-sbt::compiler-bridge:1.1.0"),
sources = true
).map(_.find(_.path.last == s"compiler-bridge_${scalaBinaryVersion0}-1.1.0-sources.jar").map(_.path).get)
@@ -129,37 +89,20 @@ trait ScalaModule extends mill.Module with TaskModule { outer =>
T.task{scalaCompilerIvyDeps(scalaVersion()) ++ scalaRuntimeIvyDeps(scalaVersion())}
)()
}
-
-
- def prependShellScript: T[String] = T{
- mainClass() match{
- case None => ""
- case Some(cls) =>
- val isWin = scala.util.Properties.isWin
- mill.modules.Jvm.launcherShellScript(
- isWin,
- cls,
- Agg(if (isWin) "%~dp0%~nx0" else "$0"),
- forkArgs()
- )
- }
+ override def compileClasspath = T{
+ transitiveLocalClasspath() ++
+ resources() ++
+ unmanagedClasspath() ++
+ resolveDeps(T.task{compileIvyDeps() ++ scalaLibraryIvyDeps() ++ transitiveIvyDeps()})()
}
- def sources = T.sources{ millSourcePath / 'src }
- def resources = T.sources{ millSourcePath / 'resources }
- def generatedSources = T{ Seq.empty[PathRef] }
- def allSources = T{ sources() ++ generatedSources() }
-
- def allSourceFiles = T{
- for {
- root <- allSources()
- if exists(root.path)
- path <- ls.rec(root.path)
- if path.isFile && (path.ext == "scala" || path.ext == "java")
- } yield PathRef(path)
+ override def upstreamAssemblyClasspath = T{
+ transitiveLocalClasspath() ++
+ unmanagedClasspath() ++
+ resolveDeps(T.task{runIvyDeps() ++ scalaLibraryIvyDeps() ++ transitiveIvyDeps()})()
}
- def compile: T[CompilationResult] = T.persistent{
+ override def compile: T[CompilationResult] = T.persistent{
scalaWorker.worker().compileScala(
scalaVersion(),
allSourceFiles().map(_.path),
@@ -172,55 +115,8 @@ trait ScalaModule extends mill.Module with TaskModule { outer =>
upstreamCompileOutput()
)
}
- def localClasspath = T{
- resources() ++ Agg(compile().classes)
- }
- def compileClasspath = T{
- transitiveLocalClasspath() ++
- resources() ++
- unmanagedClasspath() ++
- resolveDeps(T.task{compileIvyDeps() ++ scalaLibraryIvyDeps() ++ transitiveIvyDeps()})()
- }
-
- def upstreamAssemblyClasspath = T{
- transitiveLocalClasspath() ++
- unmanagedClasspath() ++
- resolveDeps(T.task{runIvyDeps() ++ scalaLibraryIvyDeps() ++ transitiveIvyDeps()})()
- }
-
- def runClasspath = T{
- localClasspath() ++
- upstreamAssemblyClasspath()
- }
-
- /**
- * Build the assembly for upstream dependencies separate from the current classpath
- *
- * This should allow much faster assembly creation in the common case where
- * upstream dependencies do not change
- */
- def upstreamAssembly = T{
- createAssembly(upstreamAssemblyClasspath().map(_.path), mainClass())
- }
-
- def assembly = T{
- createAssembly(
- Agg.from(localClasspath().map(_.path)),
- mainClass(),
- prependShellScript(),
- Some(upstreamAssembly().path)
- )
- }
-
-
- def jar = T{
- createJar(
- localClasspath().map(_.path).filter(exists),
- mainClass()
- )
- }
- def docJar = T {
+ override def docJar = T {
val outDir = T.ctx().dest
val javadocDir = outDir / 'javadoc
@@ -233,7 +129,8 @@ trait ScalaModule extends mill.Module with TaskModule { outer =>
if p.isFile
} yield p.toNIO.toString
- val options = Seq("-d", javadocDir.toNIO.toString, "-usejavacp")
+ val pluginOptions = scalacPluginClasspath().map(pluginPathRef => s"-Xplugin:${pluginPathRef.path}")
+ val options = Seq("-d", javadocDir.toNIO.toString, "-usejavacp") ++ pluginOptions
if (files.nonEmpty) subprocess(
"scala.tools.nsc.ScalaDoc",
@@ -244,77 +141,6 @@ trait ScalaModule extends mill.Module with TaskModule { outer =>
createJar(Agg(javadocDir))(outDir)
}
- def sourceJar = T {
- createJar((allSources() ++ resources()).map(_.path).filter(exists))
- }
-
- def forkArgs = T{ Seq.empty[String] }
-
- def forkEnv = T{ sys.env.toMap }
-
- def launcher = T{
- Result.Success(
- Jvm.createLauncher(
- finalMainClass(),
- runClasspath().map(_.path),
- forkArgs()
- )
- )
- }
-
- def ivyDepsTree(inverse: Boolean = false) = T.command {
- import coursier.{Cache, Fetch, Resolution}
-
- val flattened = ivyDeps().map(depToDependency(_, scalaVersion(), platformSuffix())).toSeq
- val start = Resolution(flattened.toSet)
- val fetch = Fetch.from(repositories, Cache.fetch())
- val resolution = start.process.run(fetch).unsafePerformSync
-
- println(coursier.util.Print.dependencyTree(flattened, resolution,
- printExclusions = false, reverse = inverse))
-
- Result.Success()
- }
-
- def runLocal(args: String*) = T.command {
- Jvm.runLocal(
- finalMainClass(),
- runClasspath().map(_.path),
- args
- )
- }
-
- def run(args: String*) = T.command{
- Jvm.interactiveSubprocess(
- finalMainClass(),
- runClasspath().map(_.path),
- forkArgs(),
- forkEnv(),
- args,
- workingDir = ammonite.ops.pwd
- )
- }
-
-
- def runMainLocal(mainClass: String, args: String*) = T.command {
- Jvm.runLocal(
- mainClass,
- runClasspath().map(_.path),
- args
- )
- }
-
- def runMain(mainClass: String, args: String*) = T.command{
- Jvm.interactiveSubprocess(
- mainClass,
- runClasspath().map(_.path),
- forkArgs(),
- forkEnv(),
- args,
- workingDir = ammonite.ops.pwd
- )
- }
-
def console() = T.command{
if (T.ctx().log.inStream == DummyInputStream){
Result.Failure("repl needs to be run with the -i/--interactive flag")
@@ -330,15 +156,22 @@ trait ScalaModule extends mill.Module with TaskModule { outer =>
}
def ammoniteReplClasspath = T{
- resolveDeps(T.task{Agg(ivy"com.lihaoyi:::ammonite:1.1.0-7-33b728c")})()
+ localClasspath() ++
+ transitiveLocalClasspath() ++
+ unmanagedClasspath() ++
+ resolveDeps(T.task{
+ runIvyDeps() ++ scalaLibraryIvyDeps() ++ transitiveIvyDeps() ++
+ Agg(ivy"com.lihaoyi:::ammonite:1.1.0-12-f07633d")
+ })()
}
+
def repl() = T.command{
if (T.ctx().log.inStream == DummyInputStream){
Result.Failure("repl needs to be run with the -i/--interactive flag")
}else{
Jvm.interactiveSubprocess(
mainClass = "ammonite.Main",
- classPath = runClasspath().map(_.path) ++ ammoniteReplClasspath().map(_.path),
+ classPath = ammoniteReplClasspath().map(_.path),
mainArgs = Nil,
workingDir = pwd
)
@@ -354,9 +187,11 @@ trait ScalaModule extends mill.Module with TaskModule { outer =>
if (crossFullScalaVersion()) scalaVersion()
else Lib.scalaBinaryVersion(scalaVersion())
}
- def artifactName: T[String] = millModuleSegments.parts.mkString("-")
- def artifactSuffix: T[String] = T { s"_${artifactScalaVersion()}" }
+ def artifactSuffix: T[String] = s"_${artifactScalaVersion()}"
+
+ override def artifactId: T[String] = artifactName() + artifactSuffix()
+
}
diff --git a/scalalib/src/mill/scalalib/ScalaWorkerApi.scala b/scalalib/src/mill/scalalib/ScalaWorkerApi.scala
index 9739089a..f6500ae8 100644
--- a/scalalib/src/mill/scalalib/ScalaWorkerApi.scala
+++ b/scalalib/src/mill/scalalib/ScalaWorkerApi.scala
@@ -29,7 +29,7 @@ trait ScalaWorkerModule extends mill.Module{
} else {
resolveDependencies(
repositories,
- "2.12.4",
+ Lib.depToDependency(_, "2.12.4", ""),
Seq(ivy"com.lihaoyi::mill-scalaworker:${sys.props("MILL_VERSION")}")
).map(_.map(_.path))
}
@@ -48,7 +48,7 @@ trait ScalaWorkerModule extends mill.Module{
def compilerInterfaceClasspath = T{
resolveDependencies(
repositories,
- "2.12.4",
+ Lib.depToDependency(_, "2.12.4", ""),
Seq(ivy"org.scala-sbt:compiler-interface:1.1.0")
)
}
@@ -56,6 +56,7 @@ trait ScalaWorkerModule extends mill.Module{
}
trait ScalaWorkerApi {
+
def compileScala(scalaVersion: String,
sources: Agg[Path],
compilerBridgeSources: Path,
diff --git a/scalalib/src/mill/scalalib/publish/Ivy.scala b/scalalib/src/mill/scalalib/publish/Ivy.scala
index 3b271fa8..22e26ff6 100644
--- a/scalalib/src/mill/scalalib/publish/Ivy.scala
+++ b/scalalib/src/mill/scalalib/publish/Ivy.scala
@@ -42,10 +42,10 @@ object Ivy {
private def renderDependency(dep: Dependency) = {
if (dep.exclusions.isEmpty)
- <dependency org={dep.artifact.group} name={dep.artifact.id} rev={dep.artifact.version} conf={s"${dep.scope}->default(${dep.configuration.getOrElse("compile")})"} />
+ <dependency org={dep.artifact.group} name={dep.artifact.id} rev={dep.artifact.version} conf={s"${scopeToConf(dep.scope)}->${dep.configuration.getOrElse("default(compile)")}"} />
else
- <dependency org={dep.artifact.group} name={dep.artifact.id} rev={dep.artifact.version} conf={s"${dep.scope}->default(${dep.configuration.getOrElse("compile")})"}>
- {dep.exclusions.map(ex => <exclude org={ex._1} name={ex._2} matcher="exact"/>).toSeq}
+ <dependency org={dep.artifact.group} name={dep.artifact.id} rev={dep.artifact.version} conf={s"${scopeToConf(dep.scope)}->${dep.configuration.getOrElse("default(compile)")}"}>
+ {dep.exclusions.map(ex => <exclude org={ex._1} name={ex._2} matcher="exact"/>)}
</dependency>
}
diff --git a/scalalib/src/mill/scalalib/publish/Pom.scala b/scalalib/src/mill/scalalib/publish/Pom.scala
index 3c8ba4dc..84cf0632 100644
--- a/scalalib/src/mill/scalalib/publish/Pom.scala
+++ b/scalalib/src/mill/scalalib/publish/Pom.scala
@@ -108,7 +108,7 @@ object Pom {
<groupId>{ex._1}</groupId>
<artifactId>{ex._2}</artifactId>
</exclude>
- )}.toSeq
+ )}
</exclusions>
{scope}
</dependency>
diff --git a/scalalib/src/mill/scalalib/publish/settings.scala b/scalalib/src/mill/scalalib/publish/settings.scala
index 34f7e7ad..2cd92eb2 100644
--- a/scalalib/src/mill/scalalib/publish/settings.scala
+++ b/scalalib/src/mill/scalalib/publish/settings.scala
@@ -7,19 +7,23 @@ case class Artifact(group: String, id: String, version: String) {
}
object Artifact {
-
- def fromDep(dep: Dep,
- scalaFull: String,
- scalaBin: String): Dependency = {
+ def fromDepJava(dep: Dep) = {
dep match {
- case Dep.Java(dep, cross) =>
+ case Dep.Java(dep, cross, force) =>
Dependency(
Artifact(dep.module.organization, dep.module.name, dep.version),
Scope.Compile,
- if (dep.configuration == "" ) None else Some(dep.configuration),
+ if (dep.configuration == "") None else Some(dep.configuration),
dep.exclusions.toList
)
- case Dep.Scala(dep, cross) =>
+ }
+ }
+ def fromDep(dep: Dep,
+ scalaFull: String,
+ scalaBin: String): Dependency = {
+ dep match {
+ case d: Dep.Java => fromDepJava(d)
+ case Dep.Scala(dep, cross, force) =>
Dependency(
Artifact(
dep.module.organization,
@@ -30,7 +34,7 @@ object Artifact {
if (dep.configuration == "") None else Some(dep.configuration),
dep.exclusions.toList
)
- case Dep.Point(dep, cross) =>
+ case Dep.Point(dep, cross, force) =>
Dependency(
Artifact(
dep.module.organization,
diff --git a/scalalib/test/resources/hello-java/core/src/hello/Core.java b/scalalib/test/resources/hello-java/core/src/hello/Core.java
new file mode 100644
index 00000000..bef1a5a0
--- /dev/null
+++ b/scalalib/test/resources/hello-java/core/src/hello/Core.java
@@ -0,0 +1,8 @@
+package hello;
+
+public class Core{
+ public static String msg(){
+ return "Hello World";
+ }
+
+} \ No newline at end of file
diff --git a/scalalib/test/resources/hello-java/main/src/hello/Main.java b/scalalib/test/resources/hello-java/main/src/hello/Main.java
new file mode 100644
index 00000000..44b927bd
--- /dev/null
+++ b/scalalib/test/resources/hello-java/main/src/hello/Main.java
@@ -0,0 +1,7 @@
+package hello;
+
+public class Main{
+ public static void main(String[] args){
+ System.out.println(Core.msg() + " " + args[0]);
+ }
+} \ No newline at end of file
diff --git a/scalalib/test/resources/hello-world-macros/core/src/Main.scala b/scalalib/test/resources/hello-world-macros/core/src/Main.scala
new file mode 100644
index 00000000..20924a60
--- /dev/null
+++ b/scalalib/test/resources/hello-world-macros/core/src/Main.scala
@@ -0,0 +1,5 @@
+import monocle.macros._
+@Lenses case class Foo(bar: String)
+object Main extends App {
+ println(Foo.bar.get(Foo("bar")))
+}
diff --git a/scalalib/test/src/mill/scalalib/GenIdeaTests.scala b/scalalib/test/src/mill/scalalib/GenIdeaTests.scala
index 0f776802..51665867 100644
--- a/scalalib/test/src/mill/scalalib/GenIdeaTests.scala
+++ b/scalalib/test/src/mill/scalalib/GenIdeaTests.scala
@@ -23,7 +23,7 @@ object GenIdeaTests extends TestSuite {
'genIdeaTests - {
val pp = new scala.xml.PrettyPrinter(999, 4)
- val layout = GenIdea.xmlFileLayout(
+ val layout = GenIdeaImpl.xmlFileLayout(
helloWorldEvaluator.evaluator,
HelloWorld,
("JDK_1_8", "1.8 (1)"), fetchMillModules = false)
diff --git a/scalalib/test/src/mill/scalalib/HelloJavaTests.scala b/scalalib/test/src/mill/scalalib/HelloJavaTests.scala
new file mode 100644
index 00000000..7794d5a5
--- /dev/null
+++ b/scalalib/test/src/mill/scalalib/HelloJavaTests.scala
@@ -0,0 +1,60 @@
+package mill.scalalib
+
+
+import ammonite.ops.{%, %%, cp, ls, mkdir, pwd, rm, up}
+import ammonite.ops.ImplicitWd._
+import mill.util.{TestEvaluator, TestUtil}
+import utest._
+import utest.framework.TestPath
+
+
+object HelloJavaTests extends TestSuite {
+
+ object HelloJava extends TestUtil.BaseModule{
+ def millSourcePath = TestUtil.getSrcPathBase() / millOuterCtx.enclosing.split('.')
+ object core extends JavaModule
+ object main extends JavaModule{
+ def moduleDeps = Seq(core)
+ }
+ }
+ val resourcePath = pwd / 'scalalib / 'test / 'resources / "hello-java"
+
+ def init()(implicit tp: TestPath) = {
+ val eval = new TestEvaluator(HelloJava)
+ rm(HelloJava.millSourcePath)
+ rm(eval.outPath)
+ mkdir(HelloJava.millSourcePath / up)
+ cp(resourcePath, HelloJava.millSourcePath)
+ eval
+ }
+ def tests: Tests = Tests {
+ 'scalaVersion - {
+ val eval = init()
+
+ val Right((res1, n1)) = eval.apply(HelloJava.core.compile)
+ val Right((res2, 0)) = eval.apply(HelloJava.core.compile)
+ val Right((res3, n2)) = eval.apply(HelloJava.main.compile)
+
+ assert(
+ res1 == res2,
+ n1 != 0,
+ n2 != 0,
+ ls.rec(res1.classes.path).exists(_.last == "Core.class"),
+ !ls.rec(res1.classes.path).exists(_.last == "Main.class"),
+ ls.rec(res3.classes.path).exists(_.last == "Main.class"),
+ !ls.rec(res3.classes.path).exists(_.last == "Core.class")
+ )
+ }
+ 'docJar - {
+ val eval = init()
+
+ val Right((ref1, _)) = eval.apply(HelloJava.core.docJar)
+ val Right((ref2, _)) = eval.apply(HelloJava.main.docJar)
+
+ assert(
+ %%("jar", "tf", ref1.path).out.lines.contains("hello/Core.html"),
+ %%("jar", "tf", ref2.path).out.lines.contains("hello/Main.html")
+ )
+ }
+ }
+}
diff --git a/scalalib/test/src/mill/scalalib/HelloWorldTests.scala b/scalalib/test/src/mill/scalalib/HelloWorldTests.scala
index 74078221..10f936f0 100644
--- a/scalalib/test/src/mill/scalalib/HelloWorldTests.scala
+++ b/scalalib/test/src/mill/scalalib/HelloWorldTests.scala
@@ -98,6 +98,38 @@ object HelloWorldTests extends TestSuite {
override def ivyDeps = Agg(ivy"com.lihaoyi::sourcecode:0.1.4")
}
}
+
+ object HelloWorldTypeLevel extends HelloBase{
+ object foo extends ScalaModule {
+ def scalaVersion = "2.11.8"
+ override def mapDependencies(d: coursier.Dependency) = {
+ val artifacts = Set("scala-library", "scala-compiler", "scala-reflect")
+ if (d.module.organization != "org.scala-lang" || !artifacts(d.module.name)) d
+ else d.copy(module = d.module.copy(organization = "org.typelevel"))
+ }
+
+ def ivyDeps = Agg(
+ ivy"com.github.julien-truffaut::monocle-macro::1.4.0"
+ )
+ def scalacPluginIvyDeps = super.scalacPluginIvyDeps() ++ Agg(
+ ivy"org.scalamacros:::paradise:2.1.0"
+ )
+ }
+ }
+
+ object HelloWorldMacros extends HelloBase{
+ object core extends ScalaModule {
+ def scalaVersion = "2.12.4"
+
+ def ivyDeps = Agg(
+ ivy"com.github.julien-truffaut::monocle-macro::1.4.0"
+ )
+ def scalacPluginIvyDeps = super.scalacPluginIvyDeps() ++ Agg(
+ ivy"org.scalamacros:::paradise:2.1.0"
+ )
+ }
+ }
+
val resourcePath = pwd / 'scalalib / 'test / 'resources / "hello-world"
def jarMainClass(jar: JarFile): Option[String] = {
@@ -438,7 +470,42 @@ object HelloWorldTests extends TestSuite {
!result2.exists(_.path.last == "sourcecode_2.12-0.1.3.jar")
)
}
- }
-
+ 'typeLevel - workspaceTest(HelloWorldTypeLevel){ eval =>
+ val classPathsToCheck = Seq(
+ HelloWorldTypeLevel.foo.runClasspath,
+ HelloWorldTypeLevel.foo.ammoniteReplClasspath,
+ HelloWorldTypeLevel.foo.compileClasspath
+ )
+ for(cp <- classPathsToCheck){
+ val Right((result, _)) = eval.apply(cp)
+ assert(
+ // Make sure every relevant piece org.scala-lang has been substituted for org.typelevel
+ !result.map(_.toString).exists(x =>
+ x.contains("scala-lang") &&
+ (x.contains("scala-library") || x.contains("scala-compiler") || x.contains("scala-reflect"))
+ ),
+ result.map(_.toString).exists(x => x.contains("typelevel") && x.contains("scala-library"))
+ )
+ }
+ }
+ 'macros - {
+ // make sure macros are applied when compiling/running
+ 'runMain - workspaceTest(
+ HelloWorldMacros,
+ resourcePath = pwd / 'scalalib / 'test / 'resources / "hello-world-macros"
+ ){ eval =>
+ val Right((_, evalCount)) = eval.apply(HelloWorldMacros.core.runMain("Main"))
+ assert(evalCount > 0)
+ }
+ // make sure macros are applied when compiling during scaladoc generation
+ 'docJar - workspaceTest(
+ HelloWorldMacros,
+ resourcePath = pwd / 'scalalib / 'test / 'resources / "hello-world-macros"
+ ){ eval =>
+ val Right((_, evalCount)) = eval.apply(HelloWorldMacros.core.docJar)
+ assert(evalCount > 0)
+ }
+ }
+ }
}
diff --git a/scalalib/test/src/mill/scalalib/ResolveDepsTests.scala b/scalalib/test/src/mill/scalalib/ResolveDepsTests.scala
index b1fcec3e..4240478c 100644
--- a/scalalib/test/src/mill/scalalib/ResolveDepsTests.scala
+++ b/scalalib/test/src/mill/scalalib/ResolveDepsTests.scala
@@ -10,7 +10,11 @@ import utest._
object ResolveDepsTests extends TestSuite {
val repos = Seq(Cache.ivy2Local, MavenRepository("https://repo1.maven.org/maven2"))
- def evalDeps(deps: Agg[Dep]): Result[Agg[PathRef]] = Lib.resolveDependencies(repos, "2.12.4", deps)
+ def evalDeps(deps: Agg[Dep]): Result[Agg[PathRef]] = Lib.resolveDependencies(
+ repos,
+ Lib.depToDependency(_, "2.12.4", ""),
+ deps
+ )
val tests = Tests {
'resolveValidDeps - {
@@ -26,6 +30,14 @@ object ResolveDepsTests extends TestSuite {
assert(paths.items.next.path.toString.contains("natives-macos"))
}
+ 'resolveTransitiveRuntimeDeps - {
+ val deps = Agg(ivy"org.mockito:mockito-core:2.7.22")
+ val Success(paths) = evalDeps(deps)
+ assert(paths.nonEmpty)
+ assert(paths.exists(_.path.toString.contains("objenesis")))
+ assert(paths.exists(_.path.toString.contains("byte-buddy")))
+ }
+
'excludeTransitiveDeps - {
val deps = Agg(ivy"com.lihaoyi::pprint:0.5.3".exclude("com.lihaoyi" -> "fansi_2.12"))
val Success(paths) = evalDeps(deps)
diff --git a/scalalib/test/src/mill/scalalib/publish/IvyTests.scala b/scalalib/test/src/mill/scalalib/publish/IvyTests.scala
index 0f275dd9..d187f969 100644
--- a/scalalib/test/src/mill/scalalib/publish/IvyTests.scala
+++ b/scalalib/test/src/mill/scalalib/publish/IvyTests.scala
@@ -26,9 +26,9 @@ object IvyTests extends TestSuite {
'topLevel - {
val info = singleNode(fullIvy \ "info")
assert(
- singleAttr(info, "organisation") == artifact.group
- , singleAttr(info, "module") == artifact.id
- , singleAttr(info, "revision") == artifact.version
+ singleAttr(info, "organisation") == artifact.group,
+ singleAttr(info, "module") == artifact.id,
+ singleAttr(info, "revision") == artifact.version
)
}
@@ -40,10 +40,10 @@ object IvyTests extends TestSuite {
dependencies.zipWithIndex.foreach { case (dep, index) =>
assert(
- singleAttr(dep, "org") == ivyDeps(index).artifact.group
- , singleAttr(dep, "name") == ivyDeps(index).artifact.id
- , singleAttr(dep, "rev") == ivyDeps(index).artifact.version
- , (dep \ "exclude").zipWithIndex forall { case (exclude, j) =>
+ singleAttr(dep, "org") == ivyDeps(index).artifact.group,
+ singleAttr(dep, "name") == ivyDeps(index).artifact.id,
+ singleAttr(dep, "rev") == ivyDeps(index).artifact.version,
+ (dep \ "exclude").zipWithIndex forall { case (exclude, j) =>
singleAttr(exclude, "org") == ivyDeps(index).exclusions(j)._1 &&
singleAttr(exclude, "name") == ivyDeps(index).exclusions(j)._2
}
diff --git a/scalaworker/src/mill/scalaworker/ScalaWorker.scala b/scalaworker/src/mill/scalaworker/ScalaWorker.scala
index 0411af92..9a4c317c 100644
--- a/scalaworker/src/mill/scalaworker/ScalaWorker.scala
+++ b/scalaworker/src/mill/scalaworker/ScalaWorker.scala
@@ -5,6 +5,7 @@ import java.lang.annotation.Annotation
import java.net.URLClassLoader
import java.util.Optional
import java.util.zip.ZipInputStream
+import javax.tools.ToolProvider
import ammonite.ops.{Path, exists, ls, mkdir, rm, up}
import ammonite.util.Colors
@@ -18,9 +19,14 @@ import mill.scalalib.Lib.grepJar
import mill.scalalib.TestRunner.Result
import mill.util.{Ctx, PrintLogger}
import sbt.internal.inc._
+import sbt.internal.inc.classfile.Analyze
+import sbt.internal.inc.classpath.ClasspathUtilities
+import sbt.internal.inc.javac.JavaCompiler
import sbt.internal.util.{ConsoleOut, MainAppender}
+import sbt.io.PathFinder
import sbt.testing._
-import sbt.util.LogExchange
+import sbt.util.{InterfaceUtil, LogExchange}
+import xsbti.AnalysisCallback
import scala.collection.mutable
@@ -131,6 +137,7 @@ class ScalaWorker(ctx0: mill.util.Ctx,
.getOrElse(Seq.empty[String])
}
+
def compileScala(scalaVersion: String,
sources: Agg[Path],
compilerBridgeSources: Path,