-
-
Notifications
You must be signed in to change notification settings - Fork 358
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 JVM version configurable per-module #3716
Conversation
I think I'm running into the same issue as this sbt/sbt-assembly#496.
|
At a first skim this looks great. I didnt know coursier could fetch JVMs or that it was so easy to integrate Is it possible to configure a module to point it at a JVM not listed in coursier's index? I imagine there would be some use cases for that The cross module stuff is lazily initialized then cached, so it should be OK to perform heavy downloads and initialization, but is it possible to move the download into a task? That would help benefit from parallelization and log handling and error reporting and all that @alexarchambault should probably review this as well, since he knows more about coursier than i do. One question is whether coursier JVM downloads are safe to run in parallel, or if they need to be locked/retries in mill and/or made safe upstream |
yes this is possible. I added
How do you make the cross value for a cross module the output of a task? I'm not sure how, since it needs to be invoked outside of a |
Ah I see, you mean the index itself, and not the JVMs that the index references. I think what we should do here is avoid using a Cross module to list out the various object myZincWorker = new ZincWorkerModule.ForJvm("graalvm-community:23.0.0")
object core extends JavaModule {
override def zincWorker = ModuleRef(myZincWorker)
object test extends JavaTests with TestModule.Junit4
} That is a tiny bit more boilerplate in the build file, but avoids the need for Mill to know about the contents of the coursier index entirely, and avoids having a huge cross module inside We still want to provide a way to download the index and list out its entries for debugging purposes, but that can be a normal task or command since it won't change the shape of the moduletree/taskgraph |
@alexarchambault one question about https://github.com/coursier/jvm-index: do we offer any guarantees on that? e.g. does Coursier need to fetch it from Github every time? I'm concerned about having an operational dependency here, e.g. I don't want people's Mill builds to start failing if Github has an outage (that's also why we moved the Mill assembly jar from Github to Maven Central in 0.11.x) To avoid a dependency on Github, maybe we can publish the index to maven central somewhere, since that's already a piece of infrastructure we are bound to? Or we can bundle snapshots of it with Mill when we make releases |
Come to think of it, if the index is bundled with Mill, then having it used to initialize the Cross module becomes not an issue at all |
It's fetched by coursier just like a snapshot artifact. So it lives in the coursier cache ( |
It used to be pushed to Maven Central too, some time ago. This can always be re-instated (coursier/jvm-index#277). |
It's up to you, but beware that it's getting more and more heavyweight (~1.8 MB uncompressed, 110 kB gzipped) |
@albertpchen It looks like you inlined quite some code from coursier-jvm. Is it because of the shapeless issue you mentioned? It seems it's pulled by coursier-jvm by mistake (or not to break bin compat, I don't recall). You can safely exclude it manually when you pull it in Mill. |
@alexarchambault I think 110kb is fine to bundle, the other issue is that it would fall stale relatively wuickly as new versions are added I glanced through the indices, and it seems like a lot of the download paths are computed based on the artifact version. Could the logic to compute these be bundled into Mill, rather than bundling the generated JSON? That would have the effect that new versions of known distirbutions would automatically be supported, and we would only need to make a new release if new distributions are added or the URL pattern for a distribution changes (both of which should be much rarer than existing distirbutions adding new versions). We could still bundle a list of known good versions, for debugging and error reporting purposes, but it would mean we could support unknown good versions as well |
Somehow, except there are often subtleties. In |
Yes, but that is accounted for by the Scala code in coursier/jvm-index right? So if we bundle the few kb of classfiles we get all these special cases included, as well as support for any future versions (unless they add additional special cases) |
What do you mean? Right now, the index basically look like this:
There's some logic in the coursier-jvm module, so that the superfluous |
But we can probably "compress" that a bit, with a pattern and "build" version listings (in the snippet above, |
@alexarchambault i mean the scala code in https://github.com/coursier/jvm-index/tree/master/src; isnt that what generates the json? |
It does, yeah |
Could that coxe be bundled with Mill to generate the download URL from a jdk version that gets dynamically passed in? I see some parts of it are passing github tokens around, but its unclear to me how much of the logic needs to talk to github vs how much is just mangling strings locally |
…mple to use custom java home
db513dd
to
4a08b37
Compare
yeah thanks for the tip. I've updated the PR |
main/package.mill
Outdated
def ivyDeps = Agg( | ||
build.Deps.coursier, | ||
build.Deps.coursierJvm.exclude( | ||
"com.github.alexarchambault" -> "argonaut-shapeless_6.3_2.13", |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that bumping coursier to 2.1.15
or higher should make that exclude unnecessary (trying that in #3943)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems you did it already in this PR, so the exclude can be removed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The exclude can be removed here
|
||
// 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 23 which corresponds to class file version 67 | ||
os.read.bytes(coreClassFile.get, 4, 4).toSeq == Seq[Byte](0, 0, 0, 67) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's also add a test to exercise .test
and .run
and verify that System.getProperty("java.version")
returns the expected value. We should also exercise this for two different java versions, to verify that it's not just picking up the environmental default
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should overriding the java version also affect .launcher
and .assembly
?
-
launcher
is explicitly not meant to be portable: it's meant to be something you run on the same machine where it was built, so any JVM you installed would still be present -
assembly
is meant to be portable, e.g. run on a different machine from which it was built, so we probably can't rely on the configured java home for that
Took a pass at cleaning up the PR, I think it's in pretty good state now. @alexarchambault and @lefou can take another review pass. One existing limitation is that setting the JVM version does not apply to Scala module compilation, as AFAICT Zinc does not support setting the javaHome for compiling Scala sbt/zinc#1498. We may need to manually fork and manage our own JVM subprocesses to make that work, which I think we can leave to a follow up |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good from an quick scan. One concern about bin compat; see below.
@@ -1126,14 +1130,18 @@ trait JavaModule | |||
) | |||
|
|||
@internal | |||
def bspJvmBuildTarget: JvmBuildTarget = | |||
def bspJvmBuildTarget: Task[JvmBuildTarget] = Task.Anon { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think changing this signature might produce trouble, since plugins extending JavaModule
in trait
s could break.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll leave it in place and add a new method
build.mill
Outdated
@@ -118,6 +118,7 @@ object Deps { | |||
|
|||
val coursier = ivy"io.get-coursier::coursier:2.1.16" | |||
val coursierInterface = ivy"io.get-coursier:interface:1.0.22" | |||
val coursierJvm = ivy"io.get-coursier::coursier-jvm:2.1.15" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is out-of-sync with the coursier version just above. Maybe the version could be put in a coursierVersion
variable.
main/package.mill
Outdated
def ivyDeps = Agg( | ||
build.Deps.coursier, | ||
build.Deps.coursierJvm.exclude( | ||
"com.github.alexarchambault" -> "argonaut-shapeless_6.3_2.13", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The exclude can be removed here
def jvmIndex0( | ||
ctx: Option[mill.api.Ctx.Log] = None, | ||
coursierCacheCustomizer: Option[FileCache[Task] => FileCache[Task]] = None, | ||
jvmIndexVersion: String = "release.latest" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be latest.release
jvmIndexVersion: String = "release.latest" | |
jvmIndexVersion: String = "latest.release" |
cache = coursierCache0, // the coursier.cache.Cache instance to use | ||
repositories = Resolve().repositories, // repositories to use | ||
indexChannel = JvmChannel.module( | ||
JvmChannel.centralModule(JvmChannel.defaultOs(), JvmChannel.defaultArchitecture()), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The default values don't need to be explicitly passed here
JvmChannel.centralModule(JvmChannel.defaultOs(), JvmChannel.defaultArchitecture()), | |
JvmChannel.centralModule(), |
), // use new indices published to Maven Central | ||
os = None, // use defaults | ||
arch = None // use defaults |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These values can be removed (coursier/coursier#3160 undeprecates the JvmIndex.load
overload in the next coursier release)
), // use new indices published to Maven Central | |
os = None, // use defaults | |
arch = None // use defaults | |
) // use new indices published to Maven Central |
main/util/src/mill/util/Jvm.scala
Outdated
mainArgs, | ||
workingDir, | ||
streamOut, | ||
true, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like this should be check
true, | |
check, |
if (process.exitCode == 0) { | ||
val a = process.out.lines() | ||
a | ||
} else { | ||
throw new Exception( | ||
"Discover main classes subprocess failed (exit code " + process.exitCode + ")" | ||
) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems this requires passing check = false
to callSubprocess
just above (process.exitCode
will be 0
in all cases else)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll go with check = true
and remove the special handling instead. This should "never" fail, and if it does better to have a verbose stack trace than an anemic custom error
trait ZincWorkerModule extends mill.Module with OfflineSupportModule with CoursierModule { | ||
def jvmId: mill.define.Target[String] = Task[String] { "" } | ||
|
||
def jvmIndexVersion: mill.define.Target[String] = "0.0.4-70-51469f" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this version should be managed from build.sc
, so that we can update it from there
Thanks for your contribution @albertpchen! I'll pay out half the bounty for this PR since we split the work, email me your international bank transfer details to [email protected] and I will transfer the funds |
) | ||
|
||
@internal | ||
def bspJvmBuildTargetTask: Task[JvmBuildTarget] = Task.Anon { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This new impl will miss any customizations. It should instead reuse the value of deprecated method.
FYI, I just expanded the coursier/jvm-index README |
Fixes #3480.
Changes:
adds
coursier-jvm
as a dependency to theutil
module, this library is for fetching JVMs using coursier and the coursier jvm-indexadd a
def javaHome: Task[Option[PathRef]]
task toZincWorkerModule
. This defaults toNone
, which is the existing behavior of using mill's java home.Users who want to use a custom JVM need to define a custom
ZincWorkerModule
and overridedef jvmId
, optionallydef jvmIndexVersion
updates the
mockito
andcommons-io
examples to use the new configuration options. Now these examples should run even when mill itself is not running on java 11.This also required adding
-encoding utf-8
to the projects' javac options.update the
run
andtest
tasks to use thezincWorker
's java home if a different one specifiedNotes:
JavaModule#compile
forks a new JVM for each compilation to support custom JVM versions, andScalaModule#compile
does not support custom JVM versions at all. This would require a deeper refactoring ofZincWorkerModule
to fix