diff options
author | Li Haoyi <haoyi.sg@gmail.com> | 2018-01-21 23:35:52 -0800 |
---|---|---|
committer | Li Haoyi <haoyi.sg@gmail.com> | 2018-01-21 23:35:52 -0800 |
commit | 4a0658da074bc7b7df0c5bdff90e2c6bb1977b15 (patch) | |
tree | 59d077d40a7e96b419161f5b10cb17d326a51f9f | |
parent | 3e80ade3249bc83b564d7b5bd31853c8affd555d (diff) | |
download | mill-4a0658da074bc7b7df0c5bdff90e2c6bb1977b15.tar.gz mill-4a0658da074bc7b7df0c5bdff90e2c6bb1977b15.tar.bz2 mill-4a0658da074bc7b7df0c5bdff90e2c6bb1977b15.zip |
- Make `forkTest` and `forkRun` the default, renaming `test` and `run` to `testLocal` and `runLocal`
- Support passing `forkEnv` parameters to `test` and `run`, necessary to get Ammonite working
- Standardize signatures of `Jvm.interactiveSubprocess`/`Jvm.subprocess`
- `Jvm.inprocess` is now `Jvm.runLocal`
- Swap `TestModule.testLocal` over to using `Jvm.runLocal`, for consistency with everything else
-rwxr-xr-x | build.sc | 1 | ||||
-rw-r--r-- | core/src/mill/define/Module.scala | 3 | ||||
-rw-r--r-- | core/src/mill/main/Resolve.scala | 2 | ||||
-rw-r--r-- | core/src/mill/modules/Jvm.scala | 37 | ||||
-rw-r--r-- | integration/test/resources/ammonite/build.sc | 13 | ||||
-rw-r--r-- | scalalib/src/mill/scalalib/ScalaModule.scala | 65 | ||||
-rw-r--r-- | scalalib/test/src/mill/scalalib/HelloWorldTests.scala | 8 |
7 files changed, 86 insertions, 43 deletions
@@ -30,7 +30,6 @@ trait MillModule extends ScalaModule{ outer => val test = new Tests(implicitly) class Tests(ctx0: mill.define.Ctx) extends mill.Module()(ctx0) with super.Tests{ - def defaultCommandName() = "forkTest" def forkArgs = T{ testArgs() } def moduleDeps = if (this == core.test) Seq(core) diff --git a/core/src/mill/define/Module.scala b/core/src/mill/define/Module.scala index 1be75e1d..222bb7ec 100644 --- a/core/src/mill/define/Module.scala +++ b/core/src/mill/define/Module.scala @@ -76,12 +76,13 @@ object Module{ // another top-level concrete `object`. This is fine for now, since Mill's Ammonite // script/REPL runner always wraps user code in a wrapper object/trait def reflectNestedObjects[T: ClassTag] = { - reflect[T] ++ + (reflect[T] ++ outer .getClass .getClasses .filter(implicitly[ClassTag[T]].runtimeClass isAssignableFrom _) .flatMap(c => c.getFields.find(_.getName == "MODULE$").map(_.get(c).asInstanceOf[T])) + ).distinct } } } diff --git a/core/src/mill/main/Resolve.scala b/core/src/mill/main/Resolve.scala index d836a1a1..b01391d0 100644 --- a/core/src/mill/main/Resolve.scala +++ b/core/src/mill/main/Resolve.scala @@ -46,7 +46,7 @@ object Resolve { val command = invokeCommand(obj, last).headOption - command orElse target orElse runDefault.headOption.flatten match{ + command orElse target orElse runDefault.flatten.headOption match{ case None => Left("Cannot resolve task " + Segments((Segment.Label(last) :: revSelectorsSoFar).reverse:_*).render ) diff --git a/core/src/mill/modules/Jvm.scala b/core/src/mill/modules/Jvm.scala index fdd928d7..7f2ca4dd 100644 --- a/core/src/mill/modules/Jvm.scala +++ b/core/src/mill/modules/Jvm.scala @@ -33,17 +33,26 @@ object Jvm { def interactiveSubprocess(mainClass: String, classPath: Agg[Path], - options: Seq[String] = Seq.empty): Unit = { + jvmArgs: Seq[String] = Seq.empty, + envArgs: Map[String, String] = Map.empty, + mainArgs: Seq[String] = Seq.empty, + workingDir: Path = null): Unit = { import ammonite.ops.ImplicitWd._ - %("java", "-cp", classPath.mkString(":"), mainClass, options) + val commandArgs = + Vector("java") ++ + jvmArgs ++ + Vector("-cp", classPath.mkString(":"), mainClass) ++ + mainArgs + + %.copy(envArgs = envArgs)(commandArgs)(workingDir) } - def inprocess(mainClass: String, - classPath: Agg[Path], - options: Seq[String] = Seq.empty) - (implicit ctx: Ctx): Unit = { + def runLocal(mainClass: String, + classPath: Agg[Path], + mainArgs: Seq[String] = Seq.empty) + (implicit ctx: Ctx): Unit = { inprocess(classPath, classLoaderOverrideSbtTesting = false, cl => { - getMainMethod(mainClass, cl).invoke(null, options.toArray) + getMainMethod(mainClass, cl).invoke(null, mainArgs.toArray) }) } @@ -95,27 +104,29 @@ object Jvm { def subprocess(mainClass: String, classPath: Agg[Path], - jvmOptions: Seq[String] = Seq.empty, - options: Seq[String] = Seq.empty, + jvmArgs: Seq[String] = Seq.empty, + envArgs: Map[String, String] = Map.empty, + mainArgs: Seq[String] = Seq.empty, workingDir: Path = null) (implicit ctx: Ctx) = { val commandArgs = Vector("java") ++ - jvmOptions ++ + jvmArgs ++ Vector("-cp", classPath.mkString(":"), mainClass) ++ - options + mainArgs val workingDir1 = Option(workingDir).getOrElse(ctx.dest) mkdir(workingDir1) - val proc = + val builder = new java.lang.ProcessBuilder() .directory(workingDir1.toIO) .command(commandArgs:_*) .redirectOutput(ProcessBuilder.Redirect.PIPE) .redirectError(ProcessBuilder.Redirect.PIPE) - .start() + for((k, v) <- envArgs) builder.environment().put(k, v) + val proc = builder.start() val stdout = proc.getInputStream val stderr = proc.getErrorStream val sources = Seq( diff --git a/integration/test/resources/ammonite/build.sc b/integration/test/resources/ammonite/build.sc index cb8d32ab..ebc264d4 100644 --- a/integration/test/resources/ammonite/build.sc +++ b/integration/test/resources/ammonite/build.sc @@ -26,7 +26,7 @@ object terminal extends Cross[TerminalModule](binCrossScalaVersions:_*) class TerminalModule(val crossScalaVersion: String) extends AmmModule{ def ivyDeps = Agg( ivy"com.lihaoyi::sourcecode:0.1.3", - ivy"com.lihaoyi::fansi:0.2.3" + ivy"com.lihaoyi::fansi:0.2.4" ) def compileIvyDeps = Agg( ivy"org.scala-lang:scala-reflect:$crossScalaVersion" @@ -141,9 +141,10 @@ class ShellModule(val crossScalaVersion: String) extends AmmModule{ def moduleDeps = Seq(ops(), amm()) object test extends Tests{ def moduleDeps = super.moduleDeps ++ Seq(amm.repl().test) - // (test in Test) := (test in Test).dependsOn(packageBin in Compile).value, - // (run in Test) := (run in Test).dependsOn(packageBin in Compile).evaluated, - // (testOnly in Test) := (testOnly in Test).dependsOn(packageBin in Compile).evaluated + def forkEnv = super.forkEnv() ++ Seq( + "AMMONITE_TEST_SHELL" -> shell().jar().path.toString, + "AMMONITE_TEST_ASSEMBLY" -> amm().assembly().path.toString + ) } } object integration extends Cross[IntegrationModule](fullCrossScalaVersions:_*) @@ -155,6 +156,10 @@ class IntegrationModule(val crossScalaVersion: String) extends AmmModule{ // (console in Test) := (console in Test).dependsOn(integrationTasks:_*).value, // initialCommands in (Test, console) := "ammonite.integration.Main.main(null)" object test extends Tests { + def forkEnv = super.forkEnv() ++ Seq( + "AMMONITE_TEST_SHELL" -> shell().jar().path.toString, + "AMMONITE_TEST_ASSEMBLY" -> amm().assembly().path.toString + ) } } diff --git a/scalalib/src/mill/scalalib/ScalaModule.scala b/scalalib/src/mill/scalalib/ScalaModule.scala index 975a5972..87bf119c 100644 --- a/scalalib/src/mill/scalalib/ScalaModule.scala +++ b/scalalib/src/mill/scalalib/ScalaModule.scala @@ -7,7 +7,7 @@ import mill.define.{Cross, Task} import mill.define.TaskModule import mill.eval.{PathRef, Result} import mill.modules.Jvm -import mill.modules.Jvm.{createAssembly, createJar, interactiveSubprocess, subprocess, inprocess} +import mill.modules.Jvm.{createAssembly, createJar, interactiveSubprocess, subprocess, runLocal} import Lib._ import mill.define.Cross.Resolver import mill.util.Loose.Agg @@ -205,7 +205,7 @@ trait ScalaModule extends mill.Module with TaskModule { outer => if (files.nonEmpty) subprocess( "scala.tools.nsc.ScalaDoc", compileDepClasspath().filter(_.path.ext != "pom").map(_.path), - options = (files ++ options).toSeq + mainArgs = (files ++ options).toSeq ) createJar(Agg(javadocDir))(outDir / "javadoc.jar") @@ -217,38 +217,51 @@ trait ScalaModule extends mill.Module with TaskModule { outer => def forkArgs = T{ Seq.empty[String] } + def forkEnv = T{ sys.env.toMap } - def run(args: String*) = T.command { - inprocess( + def runLocal(args: String*) = T.command { + Jvm.runLocal( mainClass().getOrElse(throw new RuntimeException("No mainClass provided!")), runClasspath().map(_.path), - args) + args + ) } - def forkRun(args: String*) = T.command{ - subprocess( + def run(args: String*) = T.command{ + Jvm.interactiveSubprocess( mainClass().getOrElse(throw new RuntimeException("No mainClass provided!")), 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{ - subprocess( + Jvm.interactiveSubprocess( mainClass, runClasspath().map(_.path), forkArgs(), + forkEnv(), args, workingDir = ammonite.ops.pwd ) } def console() = T.command{ - interactiveSubprocess( + Jvm.interactiveSubprocess( mainClass = "scala.tools.nsc.MainGenericRunner", classPath = runClasspath().map(_.path), - options = Seq("-usejavacp") + mainArgs = Seq("-usejavacp") ) } @@ -282,15 +295,16 @@ trait TestModule extends ScalaModule with TaskModule { def forkWorkingDir = ammonite.ops.pwd - def forkTest(args: String*) = T.command{ + def test(args: String*) = T.command{ mkdir(T.ctx().dest) val outputPath = T.ctx().dest/"out.json" Jvm.subprocess( mainClass = "mill.scalalib.TestRunner", classPath = Jvm.gatherClassloaderJars(), - jvmOptions = forkArgs(), - options = Seq( + jvmArgs = forkArgs(), + envArgs = forkEnv(), + mainArgs = Seq( testFramework(), runClasspath().map(_.path).mkString(" "), Seq(compile().classes.path).mkString(" "), @@ -306,13 +320,26 @@ trait TestModule extends ScalaModule with TaskModule { TestModule.handleResults(doneMsg, results) } - def test(args: String*) = T.command{ - val (doneMsg, results) = TestRunner( - testFramework(), - runClasspath().map(_.path), - Agg(compile().classes.path), - args + def testLocal(args: String*) = T.command{ + mkdir(T.ctx().dest) + val outputPath = T.ctx().dest/"out.json" + + Jvm.runLocal( + mainClass = "mill.scalalib.TestRunner", + classPath = Jvm.gatherClassloaderJars(), + mainArgs = Seq( + testFramework(), + runClasspath().map(_.path).mkString(" "), + Seq(compile().classes.path).mkString(" "), + args.mkString(" "), + outputPath.toString, + T.ctx().log.colored.toString + ) ) + + val jsonOutput = upickle.json.read(outputPath.toIO) + val (doneMsg, results) = upickle.default.readJs[(String, Seq[TestRunner.Result])](jsonOutput) TestModule.handleResults(doneMsg, results) + } }
\ No newline at end of file diff --git a/scalalib/test/src/mill/scalalib/HelloWorldTests.scala b/scalalib/test/src/mill/scalalib/HelloWorldTests.scala index 8ac45bf3..1c22c578 100644 --- a/scalalib/test/src/mill/scalalib/HelloWorldTests.scala +++ b/scalalib/test/src/mill/scalalib/HelloWorldTests.scala @@ -267,7 +267,7 @@ object HelloWorldTests extends TestSuite { 'runIfMainClassProvided - { val runResult = basePath / 'out / 'run / 'dest / "hello-mill" val Right((_, evalCount)) = helloWorldWithMainEvaluator( - HelloWorldWithMain.forkRun(runResult.toString) + HelloWorldWithMain.run(runResult.toString) ) assert(evalCount > 0) @@ -279,7 +279,7 @@ object HelloWorldTests extends TestSuite { ) } 'notRunWithoutMainClass - { - val Left(Result.Exception(err, _)) = helloWorldEvaluator(HelloWorld.forkRun()) + val Left(Result.Exception(err, _)) = helloWorldEvaluator(HelloWorld.run()) assert( err.isInstanceOf[RuntimeException] @@ -290,7 +290,7 @@ object HelloWorldTests extends TestSuite { 'runIfMainClassProvided - { val runResult = basePath / 'out / 'run / 'dest / "hello-mill" val Right((_, evalCount)) = helloWorldWithMainEvaluator( - HelloWorldWithMain.run(runResult.toString) + HelloWorldWithMain.runLocal(runResult.toString) ) assert(evalCount > 0) @@ -302,7 +302,7 @@ object HelloWorldTests extends TestSuite { ) } 'notRunWithoutMainClass - { - val Left(Result.Exception(err, _)) = helloWorldEvaluator(HelloWorld.run()) + val Left(Result.Exception(err, _)) = helloWorldEvaluator(HelloWorld.runLocal()) assert( err.isInstanceOf[RuntimeException] |