diff --git a/build.mill b/build.mill index 9c2c741026a..a7f949c09fd 100644 --- a/build.mill +++ b/build.mill @@ -123,6 +123,7 @@ object Deps { val coursier = ivy"io.get-coursier::coursier:2.1.14" val coursierInterface = ivy"io.get-coursier:interface:1.0.19" + val coursierJvm = ivy"io.get-coursier::coursier-jvm:2.1.13" val cask = ivy"com.lihaoyi::cask:0.9.4" val castor = ivy"com.lihaoyi::castor:0.3.0" diff --git a/contrib/jmh/src/mill/contrib/jmh/JmhModule.scala b/contrib/jmh/src/mill/contrib/jmh/JmhModule.scala index 27f71e6b81d..01d8f436dad 100644 --- a/contrib/jmh/src/mill/contrib/jmh/JmhModule.scala +++ b/contrib/jmh/src/mill/contrib/jmh/JmhModule.scala @@ -43,7 +43,8 @@ trait JmhModule extends JavaModule { classPath = (runClasspath() ++ generatorDeps()).map(_.path) ++ Seq(compileGeneratedSources().path, resources), mainArgs = args, - workingDir = T.ctx().dest + workingDir = T.ctx().dest, + javaHome = zincWorker().javaHome().map(_.path) ) } @@ -72,7 +73,6 @@ trait JmhModule extends JavaModule { def generateBenchmarkSources = Task { val dest = T.ctx().dest - val javacOpts = javacOptions().toSeq val sourcesDir = dest / "jmh_sources" val resourcesDir = dest / "jmh_resources" @@ -90,7 +90,7 @@ trait JmhModule extends JavaModule { resourcesDir.toString, "default" ), - jvmArgs = javacOpts + javaHome = zincWorker().javaHome().map(_.path) ) (sourcesDir, resourcesDir) diff --git a/example/thirdparty/commons-io/build.mill b/example/thirdparty/commons-io/build.mill index 66d635da957..1d44f8a7d30 100644 --- a/example/thirdparty/commons-io/build.mill +++ b/example/thirdparty/commons-io/build.mill @@ -1,9 +1,14 @@ package build import mill._, javalib._, publish._ +import mill.define.ModuleRef import $ivy.`com.lihaoyi::mill-contrib-jmh:$MILL_VERSION` import contrib.jmh.JmhModule +object ZincWorkerJava11 extends ZincWorkerModule.ForJvm("temurin:1.11.0.24") + object `package` extends RootModule with PublishModule with MavenModule { + override def zincWorker = ModuleRef(ZincWorkerJava11) + def javacOptions = Seq("-encoding", "UTF-8") def publishVersion = "2.17.0-SNAPSHOT" def pomSettings = PomSettings( @@ -69,4 +74,4 @@ PathUtilsContentEqualsBenchmark.testCurrent_fileContentEquals_Blackhole ss PathUtilsContentEqualsBenchmark.testProposal_contentEquals ss 5 ... PathUtilsContentEqualsBenchmark.testProposal_contentEquals_Blackhole ss 5 ... -*/ \ No newline at end of file +*/ diff --git a/example/thirdparty/mockito/build.mill b/example/thirdparty/mockito/build.mill index ad0fbf5e3b1..ea1ec445fb4 100644 --- a/example/thirdparty/mockito/build.mill +++ b/example/thirdparty/mockito/build.mill @@ -1,5 +1,6 @@ package build import mill._, javalib._ +import mill.define.ModuleRef object libraries{ @@ -36,7 +37,11 @@ object libraries{ val groovy = ivy"org.codehaus.groovy:groovy:3.0.22" } +object ZincWorkerJava11 extends ZincWorkerModule.ForJvm("temurin:1.11.0.24") + trait MockitoModule extends MavenModule{ + override def zincWorker = ModuleRef(ZincWorkerJava11) + def javacOptions = Seq("-encoding", "UTF-8") def testModuleDeps: Seq[JavaModule] = Nil def testIvyDeps: T[Agg[Dep]] = Agg.empty[Dep] def testRuntimeIvyDeps: T[Agg[Dep]] = Agg.empty[Dep] diff --git a/main/package.mill b/main/package.mill index 083d84bce05..f2535efba3e 100644 --- a/main/package.mill +++ b/main/package.mill @@ -100,7 +100,16 @@ object `package` extends RootModule with build.MillStableScalaModule with BuildI object util extends build.MillStableScalaModule { def moduleDeps = Seq(api, client) - def ivyDeps = Agg(build.Deps.coursier, build.Deps.jline) + def ivyDeps = Agg( + build.Deps.coursier, + build.Deps.coursierJvm.exclude( + // coursier-jvm has an unused dependency on argonaut-shapeless and + // shapeless causes issues with the assembly task when the locale + // is not set properly https://github.com/sbt/sbt-assembly/issues/496 + "com.github.alexarchambault" -> "argonaut-shapeless_6.3_2.13", + ), + build.Deps.jline + ) } object define extends build.MillStableScalaModule { diff --git a/main/util/src/mill/util/CoursierSupport.scala b/main/util/src/mill/util/CoursierSupport.scala index ba09b7bfb0b..6049f341aff 100644 --- a/main/util/src/mill/util/CoursierSupport.scala +++ b/main/util/src/mill/util/CoursierSupport.scala @@ -5,6 +5,7 @@ import coursier.error.FetchError.DownloadingArtifacts import coursier.error.ResolutionError.CantDownloadModule import coursier.params.ResolutionParams import coursier.parse.RepositoryParser +import coursier.jvm.{JvmCache, JvmIndex, JavaHome} import coursier.util.Task import coursier.{Artifacts, Classifier, Dependency, Repository, Resolution, Resolve, Type} import mill.api.Loose.Agg @@ -12,6 +13,8 @@ import mill.api.{Ctx, PathRef, Result} import scala.collection.mutable import scala.util.chaining.scalaUtilChainingOps +import coursier.cache.ArchiveCache +import scala.util.control.NonFatal trait CoursierSupport { import CoursierSupport._ @@ -164,6 +167,41 @@ trait CoursierSupport { (deps0, res.getOrThrow) } + def jvmIndex( + ctx: Option[mill.api.Ctx.Log] = None, + coursierCacheCustomizer: Option[FileCache[Task] => FileCache[Task]] = None + ): Result[JvmIndex] = { + val coursierCache0 = coursierCache(ctx, coursierCacheCustomizer) + JvmIndex.load().unsafeRun()(coursierCache0.ec) + } + + /** + * Resolve java home using Coursier. + * + * The id string has format "$DISTRIBUTION:$VERSION". e.g. graalvm-community:23.0.0 + */ + def resolveJavaHome( + id: String, + ctx: Option[mill.api.Ctx.Log] = None, + coursierCacheCustomizer: Option[FileCache[Task] => FileCache[Task]] = None + ): Result[os.Path] = { + val coursierCache0 = coursierCache(ctx, coursierCacheCustomizer) + val jvmCache = JvmCache() + .withArchiveCache( + ArchiveCache().withCache(coursierCache0) + ) + .withIndex(JvmIndex.load()) + val javaHome = JavaHome() + .withCache(jvmCache) + try { + val file = javaHome.get(id).unsafeRun()(coursierCache0.ec) + Result.Success(os.Path(file)) + } catch { + case NonFatal(error) => + Result.Exception(error, new Result.OuterStack((new Exception).getStackTrace)) + } + } + def resolveDependenciesMetadataSafe( repositories: Seq[Repository], deps: IterableOnce[Dependency], diff --git a/main/util/src/mill/util/Jvm.scala b/main/util/src/mill/util/Jvm.scala index aaff3514375..5767a9b7709 100644 --- a/main/util/src/mill/util/Jvm.scala +++ b/main/util/src/mill/util/Jvm.scala @@ -25,11 +25,12 @@ object Jvm extends CoursierSupport { mainArgs: Seq[String] = Seq.empty, workingDir: os.Path = null, streamOut: Boolean = true, - check: Boolean = true + check: Boolean = true, + javaHome: Option[os.Path] = None )(implicit ctx: Ctx): CommandResult = { val commandArgs = - Vector(javaExe) ++ + Vector(javaExe(javaHome)) ++ jvmArgs ++ Vector("-cp", classPath.iterator.mkString(java.io.File.pathSeparator), mainClass) ++ mainArgs @@ -65,9 +66,10 @@ object Jvm extends CoursierSupport { /** * Resolves a tool to a path under the currently used JDK (if known). */ - def jdkTool(toolName: String): String = { - sys.props - .get("java.home") + def jdkTool(toolName: String, javaHome: Option[os.Path]): String = { + javaHome + .map(_.toString()) + .orElse(sys.props.get("java.home")) .map(h => if (isWin) new File(h, s"bin\\${toolName}.exe") else new File(h, s"bin/${toolName}") @@ -77,7 +79,11 @@ object Jvm extends CoursierSupport { } - def javaExe: String = jdkTool("java") + def jdkTool(toolName: String): String = jdkTool(toolName, None) + + def javaExe(javaHome: Option[os.Path]): String = jdkTool("java", javaHome) + + def javaExe: String = javaExe(None) def defaultBackgroundOutputs(outputDir: os.Path): Option[(ProcessOutput, ProcessOutput)] = Some((outputDir / "stdout.log", outputDir / "stderr.log")) @@ -107,7 +113,8 @@ object Jvm extends CoursierSupport { workingDir: os.Path = null, background: Boolean = false, useCpPassingJar: Boolean = false, - runBackgroundLogToConsole: Boolean = false + runBackgroundLogToConsole: Boolean = false, + javaHome: Option[os.Path] = None )(implicit ctx: Ctx): Unit = { runSubprocessWithBackgroundOutputs( mainClass, @@ -119,7 +126,8 @@ object Jvm extends CoursierSupport { if (!background) None else if (runBackgroundLogToConsole) Some((os.Inherit, os.Inherit)) else Jvm.defaultBackgroundOutputs(ctx.dest), - useCpPassingJar + useCpPassingJar, + javaHome ) } @@ -169,7 +177,8 @@ object Jvm extends CoursierSupport { mainArgs: Seq[String] = Seq.empty, workingDir: os.Path = null, backgroundOutputs: Option[Tuple2[ProcessOutput, ProcessOutput]] = None, - useCpPassingJar: Boolean = false + useCpPassingJar: Boolean = false, + javaHome: Option[os.Path] = None )(implicit ctx: Ctx): Unit = { val cp = @@ -193,7 +202,7 @@ object Jvm extends CoursierSupport { Seq(mainClass) } else Seq.empty val args = - Vector(javaExe) ++ + Vector(javaExe(javaHome)) ++ jvmArgs ++ cpArgument ++ mainClassArgument ++ @@ -207,6 +216,29 @@ object Jvm extends CoursierSupport { runSubprocess(args, envArgs, workingDir) } + // bincompat shim + def runSubprocessWithBackgroundOutputs( + mainClass: String, + classPath: Agg[os.Path], + jvmArgs: Seq[String], + envArgs: Map[String, String], + mainArgs: Seq[String], + workingDir: os.Path, + backgroundOutputs: Option[Tuple2[ProcessOutput, ProcessOutput]], + useCpPassingJar: Boolean + )(implicit ctx: Ctx): Unit = + runSubprocessWithBackgroundOutputs( + mainClass, + classPath, + jvmArgs, + envArgs, + mainArgs, + workingDir, + backgroundOutputs, + useCpPassingJar, + None + )(ctx) + /** * Runs a generic subprocess and waits for it to terminate. */ diff --git a/scalalib/src/mill/scalalib/CoursierModule.scala b/scalalib/src/mill/scalalib/CoursierModule.scala index a952acb62eb..5c8b0516a05 100644 --- a/scalalib/src/mill/scalalib/CoursierModule.scala +++ b/scalalib/src/mill/scalalib/CoursierModule.scala @@ -32,6 +32,15 @@ trait CoursierModule extends mill.Module { Lib.depToDependencyJava(_: Dep) } + def resolveJavaHome(id: String): Task[PathRef] = Task.Anon { + val path = mill.util.Jvm.resolveJavaHome( + id = id, + coursierCacheCustomizer = coursierCacheCustomizer(), + ctx = Some(implicitly[mill.api.Ctx.Log]) + ).getOrThrow + PathRef(path, quick = true) + } + def defaultResolver: Task[CoursierModule.Resolver] = Task.Anon { new CoursierModule.Resolver( repositories = repositoriesTask(), diff --git a/scalalib/src/mill/scalalib/JavaModule.scala b/scalalib/src/mill/scalalib/JavaModule.scala index e7da0fb7760..2a8ce4c4492 100644 --- a/scalalib/src/mill/scalalib/JavaModule.scala +++ b/scalalib/src/mill/scalalib/JavaModule.scala @@ -1071,14 +1071,18 @@ trait JavaModule ) @internal - def bspJvmBuildTarget: JvmBuildTarget = + def bspJvmBuildTarget: Task[JvmBuildTarget] = Task.Anon { JvmBuildTarget( - javaHome = Option(System.getProperty("java.home")).map(p => BspUri(os.Path(p))), + javaHome = zincWorker() + .javaHome() + .map(p => BspUri(p.path)) + .orElse(Option(System.getProperty("java.home")).map(p => BspUri(os.Path(p)))), javaVersion = Option(System.getProperty("java.version")) ) + } @internal override def bspBuildTargetData: Task[Option[(String, AnyRef)]] = Task.Anon { - Some((JvmBuildTarget.dataKind, bspJvmBuildTarget)) + Some((JvmBuildTarget.dataKind, bspJvmBuildTarget())) } } diff --git a/scalalib/src/mill/scalalib/RunModule.scala b/scalalib/src/mill/scalalib/RunModule.scala index b8212f5a2d4..f9a24b2b1fa 100644 --- a/scalalib/src/mill/scalalib/RunModule.scala +++ b/scalalib/src/mill/scalalib/RunModule.scala @@ -135,7 +135,8 @@ trait RunModule extends WithZincWorker { runClasspath().map(_.path), forkArgs(), forkEnv(), - runUseArgsFile() + runUseArgsFile(), + zincWorker().javaHome().map(_.path) ) } @@ -257,7 +258,8 @@ object RunModule { runClasspath: Seq[os.Path], forkArgs0: Seq[String], forkEnv0: Map[String, String], - useCpPassingJar0: Boolean + useCpPassingJar0: Boolean, + javaHome: Option[os.Path] ) extends Runner { def run( @@ -283,7 +285,8 @@ object RunModule { case Some(b) => b case None => useCpPassingJar0 }, - runBackgroundLogToConsole = runBackgroundLogToConsole + runBackgroundLogToConsole = runBackgroundLogToConsole, + javaHome ) } } diff --git a/scalalib/src/mill/scalalib/ScalaModule.scala b/scalalib/src/mill/scalalib/ScalaModule.scala index c91ef4add7b..7da35a4c560 100644 --- a/scalalib/src/mill/scalalib/ScalaModule.scala +++ b/scalalib/src/mill/scalalib/ScalaModule.scala @@ -592,7 +592,7 @@ trait ScalaModule extends JavaModule with TestModule.ScalaModuleBase { outer => scalaBinaryVersion = ZincWorkerUtil.scalaBinaryVersion(scalaVersion()), platform = ScalaPlatform.JVM, jars = scalaCompilerClasspath().map(_.path.toNIO.toUri.toString).iterator.toSeq, - jvmBuildTarget = Some(bspJvmBuildTarget) + jvmBuildTarget = Some(bspJvmBuildTarget()) ) )) } diff --git a/scalalib/src/mill/scalalib/TestModule.scala b/scalalib/src/mill/scalalib/TestModule.scala index 8b6d1d4185d..f1fd922bd6f 100644 --- a/scalalib/src/mill/scalalib/TestModule.scala +++ b/scalalib/src/mill/scalalib/TestModule.scala @@ -200,7 +200,8 @@ trait TestModule forkEnv(), testSandboxWorkingDir(), forkWorkingDir(), - testReportXml() + testReportXml(), + zincWorker().javaHome().map(_.path) ) } diff --git a/scalalib/src/mill/scalalib/TestModuleUtil.scala b/scalalib/src/mill/scalalib/TestModuleUtil.scala index b7a29bcbdcc..a65624122e1 100644 --- a/scalalib/src/mill/scalalib/TestModuleUtil.scala +++ b/scalalib/src/mill/scalalib/TestModuleUtil.scala @@ -28,7 +28,8 @@ private[scalalib] object TestModuleUtil { forkEnv: Map[String, String], testSandboxWorkingDir: Boolean, forkWorkingDir: os.Path, - testReportXml: Option[String] + testReportXml: Option[String], + javaHome: Option[os.Path] )(implicit ctx: mill.api.Ctx) = { val (jvmArgs, props: Map[String, String]) = loadArgsAndProps(useArgsFile, forkArgs) @@ -72,7 +73,8 @@ private[scalalib] object TestModuleUtil { envArgs = forkEnv ++ resourceEnv, mainArgs = Seq(testRunnerClasspathArg, argsFile.toString), workingDir = if (testSandboxWorkingDir) sandbox else forkWorkingDir, - useCpPassingJar = useArgsFile + useCpPassingJar = useArgsFile, + javaHome = javaHome ) if (!os.exists(outputPath)) Left(s"Test reporting Failed: ${outputPath} does not exist") diff --git a/scalalib/src/mill/scalalib/ZincWorkerModule.scala b/scalalib/src/mill/scalalib/ZincWorkerModule.scala index eb2ebb6f6b1..c6619d70ef1 100644 --- a/scalalib/src/mill/scalalib/ZincWorkerModule.scala +++ b/scalalib/src/mill/scalalib/ZincWorkerModule.scala @@ -15,6 +15,12 @@ import mill.util.Util.millProjectModule */ object ZincWorkerModule extends ExternalModule with ZincWorkerModule with CoursierModule { lazy val millDiscover = Discover[this.type] + + abstract class ForJvm(jvmId: String) extends ZincWorkerModule with CoursierModule { + override def javaHome: T[Option[PathRef]] = Task { + Some(resolveJavaHome(jvmId)()) + } + } } /** @@ -44,6 +50,16 @@ trait ZincWorkerModule extends mill.Module with OfflineSupportModule { self: Cou def zincLogDebug: T[Boolean] = Task.Input(T.ctx().log.debugEnabled) + /** + * Optional custom Java Home for the ZincWorker to use + * + * If this value is None, then the ZincWorker uses the same Java used to run + * the current mill instance. + */ + def javaHome: T[Option[PathRef]] = Task { + None + } + def worker: Worker[ZincWorkerApi] = Task.Worker { val jobs = T.ctx() match { case j: Ctx.Jobs => j.jobs @@ -66,7 +82,8 @@ trait ZincWorkerModule extends mill.Module with OfflineSupportModule { self: Cou classOf[(Agg[PathRef], String) => PathRef], // compilerJarNameGrep classOf[KeyedLockedCache[_]], // compilerCache classOf[Boolean], // compileToJar - classOf[Boolean] // zincLogDebug + classOf[Boolean], // zincLogDebug + classOf[Option[PathRef]] // javaHome ) .newInstance( Left(( @@ -83,7 +100,8 @@ trait ZincWorkerModule extends mill.Module with OfflineSupportModule { self: Cou ZincWorkerUtil.grepJar(_, "scala-compiler", _, sources = false), new FixSizedCache(jobs), java.lang.Boolean.FALSE, - java.lang.Boolean.valueOf(zincLogDebug()) + java.lang.Boolean.valueOf(zincLogDebug()), + javaHome() ) instance.asInstanceOf[ZincWorkerApi] } diff --git a/scalalib/test/resources/hello-java/core/src/Core.java b/scalalib/test/resources/hello-java/core/src/Core.java index 3ecb1f61032..313d7711eee 100644 --- a/scalalib/test/resources/hello-java/core/src/Core.java +++ b/scalalib/test/resources/hello-java/core/src/Core.java @@ -1,7 +1,18 @@ package hello; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.Path; + public class Core{ public static String msg(){ return "Hello World"; } -} \ No newline at end of file + + public static void main(String[] args) throws IOException { + Path path = Paths.get(args[0]); + String version = System.getProperty("java.version"); + Files.write(path, version.getBytes()); + } +} diff --git a/scalalib/test/resources/hello-java/core/test/src/MyCoreTests.java b/scalalib/test/resources/hello-java/core/test/src/MyCoreTests.java index 38bebaebe46..e11c78c8fd1 100644 --- a/scalalib/test/resources/hello-java/core/test/src/MyCoreTests.java +++ b/scalalib/test/resources/hello-java/core/test/src/MyCoreTests.java @@ -1,6 +1,7 @@ package hello; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import org.junit.Test; public class MyCoreTests { @@ -12,4 +13,22 @@ public void msgTest() { public void lengthTest() { assertEquals(Core.msg().length(), 11); } -} \ No newline at end of file + + @Test + public void java11Test() { + String version = System.getProperty("java.version"); + int dot = version.indexOf("."); + assertNotEquals(dot, -1); + System.out.println(version); + assertEquals(version.substring(0, dot), "11"); + } + + @Test + public void java17Test() { + String version = System.getProperty("java.version"); + int dot = version.indexOf("."); + assertNotEquals(dot, -1); + System.out.println(version); + assertEquals(version.substring(0, dot), "17"); + } +} diff --git a/scalalib/test/src/mill/scalalib/HelloJavaTests.scala b/scalalib/test/src/mill/scalalib/HelloJavaTests.scala index 7c8a0461087..549a9d08a3d 100644 --- a/scalalib/test/src/mill/scalalib/HelloJavaTests.scala +++ b/scalalib/test/src/mill/scalalib/HelloJavaTests.scala @@ -5,7 +5,7 @@ import mill.api.Result import mill.testkit.UnitTester import mill.testkit.TestBaseModule import utest._ -import utest.framework.TestPath +import mill.define.ModuleRef object HelloJavaTests extends TestSuite { @@ -25,6 +25,26 @@ object HelloJavaTests extends TestSuite { } } } + + object HelloJavaJavaHome11Override extends TestBaseModule { + object ZincWorkerJava11 extends ZincWorkerModule.ForJvm("temurin:1.11.0.24") + object core extends JavaModule { + override def zincWorker: ModuleRef[ZincWorkerModule] = ModuleRef(ZincWorkerJava11) + override def docJarUseArgsFile = false + object test extends JavaTests with TestModule.Junit4 + } + } + + object HelloJavaJavaHome17Override extends TestBaseModule { + object ZincWorkerJava17 extends ZincWorkerModule.ForJvm("temurin:1.17.0.9") + + object core extends JavaModule { + override def zincWorker: ModuleRef[ZincWorkerModule] = ModuleRef(ZincWorkerJava17) + override def docJarUseArgsFile = false + object test extends JavaTests with TestModule.Junit4 + } + } + val resourcePath = os.Path(sys.env("MILL_TEST_RESOURCE_DIR")) / "hello-java" def testEval() = UnitTester(HelloJava, resourcePath) @@ -47,6 +67,86 @@ object HelloJavaTests extends TestSuite { !os.walk(result3.value.classes.path).exists(_.last == "Core.class") ) } + + test("javaHome") { + test("compile11") { + val eval = UnitTester(HelloJavaJavaHome11Override, resourcePath) + + val Right(result) = eval.apply(HelloJavaJavaHome11Override.core.compile) + + val coreClassFile = os.walk(result.value.classes.path).find(_.last == "Core.class") + + assert( + coreClassFile.isDefined, + + // The first eight bytes are magic numbers followed by two bytes for major version and two bytes for minor version + // We are overriding to java 11 which corresponds to class file version 55 + os.read.bytes(coreClassFile.get, 4, 4).toSeq == Seq[Byte](0, 0, 0, 55) + ) + } + test("compile17") { + val eval = UnitTester(HelloJavaJavaHome17Override, resourcePath) + + val Right(result) = eval.apply(HelloJavaJavaHome17Override.core.compile) + + val coreClassFile = os.walk(result.value.classes.path).find(_.last == "Core.class") + + assert( + coreClassFile.isDefined, + + // The first eight bytes are magic numbers followed by two bytes for major version and two bytes for minor version + // We are overriding to java 17 which corresponds to class file version 67 + os.read.bytes(coreClassFile.get, 4, 4).toSeq == Seq[Byte](0, 0, 0, 61) + ) + } + test("run11") { + val eval = UnitTester(HelloJavaJavaHome11Override, resourcePath) + + val path = eval.evaluator.workspace / "java.version" + val Right(_) = eval.apply(HelloJavaJavaHome11Override.core.run(Task.Anon(Args(path)))) + + assert( + os.read(path).startsWith("11.") + ) + } + test("run17") { + val eval = UnitTester(HelloJavaJavaHome17Override, resourcePath) + + val path = eval.evaluator.workspace / "java.version" + val Right(_) = eval.apply(HelloJavaJavaHome17Override.core.run(Task.Anon(Args(path)))) + + assert( + os.read(path).startsWith("17.") + ) + } + test("test11") { + val eval = UnitTester(HelloJavaJavaHome11Override, resourcePath) + + val Left(Result.Failure(ref1, Some(v1))) = + eval.apply(HelloJavaJavaHome11Override.core.test.test()) + + assert( + v1._2(0).fullyQualifiedName == "hello.MyCoreTests.java11Test", + v1._2(0).status == "Success", + v1._2(1).fullyQualifiedName == "hello.MyCoreTests.java17Test", + v1._2(1).status == "Failure" + ) + } + test("test17") { + val eval = UnitTester(HelloJavaJavaHome17Override, resourcePath) + + val Left(Result.Failure(ref1, Some(v1))) = + eval.apply(HelloJavaJavaHome17Override.core.test.test()) + + assert( + v1._2(0).fullyQualifiedName == "hello.MyCoreTests.java11Test", + v1._2(0).status == "Failure", + v1._2(1).fullyQualifiedName == "hello.MyCoreTests.java17Test", + v1._2(1).status == "Success" + ) + } + } + test("semanticDbData") { val expectedFile1 = os.rel / "META-INF/semanticdb/core/src/Core.java.semanticdb" @@ -152,10 +252,12 @@ object HelloJavaTests extends TestSuite { val Left(Result.Failure(ref1, Some(v1))) = eval.apply(HelloJava.core.test.test()) assert( - v1._2(0).fullyQualifiedName == "hello.MyCoreTests.lengthTest", - v1._2(0).status == "Success", - v1._2(1).fullyQualifiedName == "hello.MyCoreTests.msgTest", - v1._2(1).status == "Failure" + v1._2(0).fullyQualifiedName == "hello.MyCoreTests.java11Test", + v1._2(1).fullyQualifiedName == "hello.MyCoreTests.java17Test", + v1._2(2).fullyQualifiedName == "hello.MyCoreTests.lengthTest", + v1._2(2).status == "Success", + v1._2(3).fullyQualifiedName == "hello.MyCoreTests.msgTest", + v1._2(3).status == "Failure" ) val Right(result2) = eval.apply(HelloJava.app.test.test()) diff --git a/scalalib/worker/src/mill/scalalib/worker/ZincWorkerImpl.scala b/scalalib/worker/src/mill/scalalib/worker/ZincWorkerImpl.scala index f8bb3ccdea5..a652304621f 100644 --- a/scalalib/worker/src/mill/scalalib/worker/ZincWorkerImpl.scala +++ b/scalalib/worker/src/mill/scalalib/worker/ZincWorkerImpl.scala @@ -62,7 +62,8 @@ class ZincWorkerImpl( compilerJarNameGrep: (Agg[PathRef], String) => PathRef, compilerCache: KeyedLockedCache[Compilers], compileToJar: Boolean, - zincLogDebug: Boolean + zincLogDebug: Boolean, + javaHome: Option[PathRef] ) extends ZincWorkerApi with AutoCloseable { private val zincLogLevel = if (zincLogDebug) sbt.util.Level.Debug else sbt.util.Level.Info private[this] val ic = new sbt.internal.inc.IncrementalCompilerImpl() @@ -113,12 +114,13 @@ class ZincWorkerImpl( } private def getLocalOrCreateJavaTools(javacRuntimeOptions: Seq[String]): JavaTools = { + val javaHome = this.javaHome.map(_.path.toNIO) val (javaCompiler, javaDoc) = // Local java compilers don't accept -J flags so when we put this together if we detect // any javacOptions starting with -J we ensure we have a non-local Java compiler which // can handle them. - if (javacRuntimeOptions.exists(filterJavacRuntimeOptions)) { - (javac.JavaCompiler.fork(None), javac.Javadoc.fork(None)) + if (javacRuntimeOptions.exists(filterJavacRuntimeOptions) || javaHome.isDefined) { + (javac.JavaCompiler.fork(javaHome), javac.Javadoc.fork(javaHome)) } else { val compiler = javac.JavaCompiler.local.getOrElse(javac.JavaCompiler.fork(None))