Skip to content

Add additional packaging.graalvm* directives#4225

Merged
Gedochao merged 2 commits intoVirtusLab:mainfrom
zrhmn:main
Apr 15, 2026
Merged

Add additional packaging.graalvm* directives#4225
Gedochao merged 2 commits intoVirtusLab:mainfrom
zrhmn:main

Conversation

@zrhmn
Copy link
Copy Markdown
Contributor

@zrhmn zrhmn commented Apr 13, 2026

Fixes #4224.

  • Add directive packaging.graalvmVersion (string)
  • Add directive packaging.graalvmJavaVersion (int)
  • Validate (valid int and >0) of graalvmJavaVersion
  • Update docs to reflect the changes (as well as a previously missed doc for graalvmJvmId)
  • Also updated the docs to reflect the "default" GraalVM versions and IDs -- I did not think it mattered too much, but I thought it was best for the sake of consistency.
  • Add unit tests for the newly added directives.

Checklist

  • tested the solution locally and it works
  • ran the code formatter (scala-cli fmt .)
  • ran scalafix (./mill -i __.fix)
  • ran reference docs auto-generation (./mill -i 'generate-reference-doc[]'.run)

How much have your relied on LLM-based tools in this contribution?

Moderately. In a previous PR #4223 I got familiarized with the preprocessing.directives.Packaging case class as well as relevant unit tests with the help of GitHub Copilot (using Claude Sonnet 4.6), so I did not need much AI assistance this time around. I did trip on the fact that graalvmJvmVersion arg of NativeImageOptions was supposed to be an Option[Int] while the arg I was getting from the Packaging args was an Option[String]. I tried using a simple .flatMap(_.toIntOption) but then realized if a bad (non-int) graalvmJvmVersion is passed, it would be replaced silently. So I used AI help to figure out how to weave-in validation into the buildOptions method. A hint was all it took; it was easy enough to follow existing patterns.

How was the solution tested?

Unit tests included.

Additionally, I created a test project with the following lines:

//> using packaging.packageType graalvm
//> using packaging.graalvmVersion 25.0.2
//> using packaging.graalvmJavaVersion 25
//> using packaging.output "sctest02"
//> using packaging.graalvmArgs --no-fallback

And ran with

../scala-cli/out/cli/3.3.7/standaloneLauncher.dest/launcher --power package .

I saw the version GraalVM CE 25.0.2+10.1 in the build output, which verified that graalvmVersion and graalvmJavaVersion were being correctly compiled into a valid JVM ID by the just-compiled launcher.

I also tried with an invalid graalvmJavaVersion and saw the expected error output:

[error] ./build.scala:10:40
[error] Malformed graalvm-java-version "notint", expected a positive integer
[error] //> using packaging.graalvmJavaVersion foo
[error]

verifying that integer validation of graalvmJavaVersion was working as intended.

Additional notes

I think enhancements could be made to NativeImageOptions.

Currently, passing just a whole JVM ID (e.g. graalvm-community:25.0.2) works smoothly. If the JVM ID is not passed, and instead a Java version (e.g. 25) along with a JVM version (e.g. 25.0.2) is passed, then NativeImageOptions uses that to construct a JVM ID (in case of e.g. it would be graalvm-java25:25.0.2). If both a JVM ID as well as the Java version + JVM version pair are passed, then the latter get ignored entirely. This is all expected behavior.

If user mistakenly passes an unmatched Java version and JVM version pair (e.g. graalvm-version=24.0.2 graalvm-java-version=22), NativeImageOptions naively compiles that to an invalid JVM ID (in case of e.g. graalvm-java22:24.0.2). This, too, may be considered expected behavior and an obvious user error.

However, something that IMO could be more common and not an obvious user error, would be that user passes a JVM version but neglects to pass a Java version or vice versa (e.g. user passes graalvm-version=24.0.2, resulting in graalvm-jvm-id=graalvm-java17:24.0.2 or user passes graalvm-java-version=24, resulting in graalvm-jvm-id=graalvm-java24:17.0.9).

So for the user, passing a JVM ID directly may be the recommended action, rendering the separate Java version and JVM version parameters useless and superficial in practice. Alternatively, we can have NativeImageOptions resolve JVM version + Java version pair in a more intelligent, slightly better way. Perhaps, if a JVM version is provided without a Java version, the Java version can be determined from the first part of the JVM version (e.g. for JVM version 24.0.2 assume the Java version will always be 24). If Java version is provided without a JVM version, the latest JVM version for the given Java version could automatically be determined somehow. An error could be shown if the JVM version and Java version are both given but do not match. etc. etc.

This is just something I was considering when making the changes in this PR. For now I think the changes in the PR are fine and in-line with the currently expected behavior. I would welcome comments on the suggested improvements to NativeImageOptions and consider making the changes in the future if I can get folks onboard with my thinking.

@Gedochao Gedochao self-requested a review April 13, 2026 17:30
Comment thread modules/build/src/test/scala/scala/build/tests/PackagingUsingDirectiveTests.scala Outdated
@Gedochao Gedochao requested review from tgodzik and zielinsky April 13, 2026 19:45
@zrhmn zrhmn requested a review from Gedochao April 14, 2026 13:17
Copy link
Copy Markdown
Contributor

@Gedochao Gedochao left a comment

Choose a reason for hiding this comment

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

LGTM, thanks!

@Gedochao Gedochao merged commit 8d20222 into VirtusLab:main Apr 15, 2026
135 of 138 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add missing packaging.graalvm* directives

2 participants