Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make java home configurable per-module #3716

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
1 change: 1 addition & 0 deletions build.mill
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
6 changes: 3 additions & 3 deletions contrib/jmh/src/mill/contrib/jmh/JmhModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
)
}

Expand Down Expand Up @@ -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"

Expand All @@ -90,7 +90,7 @@ trait JmhModule extends JavaModule {
resourcesDir.toString,
"default"
),
jvmArgs = javacOpts
javaHome = zincWorker().javaHome().map(_.path)
)

(sourcesDir, resourcesDir)
Expand Down
7 changes: 6 additions & 1 deletion example/thirdparty/commons-io/build.mill
Original file line number Diff line number Diff line change
@@ -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(
Expand Down Expand Up @@ -69,4 +74,4 @@ PathUtilsContentEqualsBenchmark.testCurrent_fileContentEquals_Blackhole ss
PathUtilsContentEqualsBenchmark.testProposal_contentEquals ss 5 ...
PathUtilsContentEqualsBenchmark.testProposal_contentEquals_Blackhole ss 5 ...

*/
*/
5 changes: 5 additions & 0 deletions example/thirdparty/mockito/build.mill
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package build
import mill._, javalib._
import mill.define.ModuleRef

object libraries{

Expand Down Expand Up @@ -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]
Expand Down
11 changes: 10 additions & 1 deletion main/package.mill
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a comment here explaining why it is excluded, for future reference

),
build.Deps.jline
)
}

object define extends build.MillStableScalaModule {
Expand Down
38 changes: 38 additions & 0 deletions main/util/src/mill/util/CoursierSupport.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@ 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
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._
Expand Down Expand Up @@ -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],
Expand Down
52 changes: 42 additions & 10 deletions main/util/src/mill/util/Jvm.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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}")
Expand All @@ -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"))
Expand Down Expand Up @@ -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,
Expand All @@ -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
)
}

Expand Down Expand Up @@ -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 =
Expand All @@ -193,7 +202,7 @@ object Jvm extends CoursierSupport {
Seq(mainClass)
} else Seq.empty
val args =
Vector(javaExe) ++
Vector(javaExe(javaHome)) ++
jvmArgs ++
cpArgument ++
mainClassArgument ++
Expand All @@ -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.
*/
Expand Down
9 changes: 9 additions & 0 deletions scalalib/src/mill/scalalib/CoursierModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
10 changes: 7 additions & 3 deletions scalalib/src/mill/scalalib/JavaModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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()))
}
}
9 changes: 6 additions & 3 deletions scalalib/src/mill/scalalib/RunModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,8 @@ trait RunModule extends WithZincWorker {
runClasspath().map(_.path),
forkArgs(),
forkEnv(),
runUseArgsFile()
runUseArgsFile(),
zincWorker().javaHome().map(_.path)
)
}

Expand Down Expand Up @@ -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(
Expand All @@ -283,7 +285,8 @@ object RunModule {
case Some(b) => b
case None => useCpPassingJar0
},
runBackgroundLogToConsole = runBackgroundLogToConsole
runBackgroundLogToConsole = runBackgroundLogToConsole,
javaHome
)
}
}
Expand Down
2 changes: 1 addition & 1 deletion scalalib/src/mill/scalalib/ScalaModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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())
)
))
}
Expand Down
3 changes: 2 additions & 1 deletion scalalib/src/mill/scalalib/TestModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,8 @@ trait TestModule
forkEnv(),
testSandboxWorkingDir(),
forkWorkingDir(),
testReportXml()
testReportXml(),
zincWorker().javaHome().map(_.path)
)
}

Expand Down
6 changes: 4 additions & 2 deletions scalalib/src/mill/scalalib/TestModuleUtil.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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")
Expand Down
Loading
Loading