Skip to content

Consistency of --jvm CLI options: allowing optional JVM path #13225

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

Merged
merged 5 commits into from
Jun 6, 2025

Conversation

JaroslavTulach
Copy link
Member

@JaroslavTulach JaroslavTulach commented Jun 5, 2025

Pull Request Description

Fixing broken sbt command by adding support for optional parameter to --jvm argument:

sbt:enso> runProjectManagerDistribution --debug

was broken by incorrect assumption that --jvm flags may take arguments. The enso one did accept argument, the project-manager one didn't. This PR fixes that by giving the project-manager --jvm flag an optional path attribute.

Important Notes

Checklist

Please ensure that the following checklist has been satisfied before submitting the PR:

  • The documentation has been updated, if necessary.
  • All code follows the
    Scala,
    Java,
  • Unit tests have been written where possible.

@JaroslavTulach JaroslavTulach self-assigned this Jun 5, 2025
@JaroslavTulach JaroslavTulach changed the title Consistency of --jvm arguments: allowing optional JVM path Consistency of --jvm CLI options: allowing optional JVM path Jun 5, 2025
Copy link

github-actions bot commented Jun 5, 2025

✨ GUI Checks Results

Summary

  • 🧹 Prettier: ✅ Passed
  • 🧰 GUI Checks: ❌ Failed
  • 📚 Storybook: ❌ Failed

⚠️ Action Required: Please review the failed checks and fix them before merging.

See individual check results for more details.

JaroslavTulach referenced this pull request Jun 5, 2025
This change makes Native Image build opt-out rather than opt-in. To use JVM mode, IDE has to start with `--jvm` or `ENSO_LAUNCHER=shell`.

An alternative approach to #12501. Closes #12483.
Likely also addresses #12303.
@JaroslavTulach JaroslavTulach mentioned this pull request Jun 5, 2025
2 tasks
val requiresJVMRunner =
ensoLauncher.exists(_.equals("shell")) || runSettings.jvmMode
if (requiresJVMRunner) {
if (runSettings.jvm.isDefined) {
Copy link
Member Author

Choose a reason for hiding this comment

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

Is there a recommended way to (unit) test this logic?

Copy link
Member

Choose a reason for hiding this comment

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

I don't think we are testing this at all. Make sure you try manually launching project-manager.

if (requiresJVMRunner) {
if (runSettings.jvm.isDefined) {
val javaHome = runSettings.jvm.get
val javaExec = if (javaHome.isDefined) {
Copy link
Member Author

Choose a reason for hiding this comment

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

  • I wrote this code in a way I can read it later.
  • I assume there is some fancy way of using Option.map.orElse instead
  • reviewers feel free to make such a local stylistic changes directly
  • you have write access to the code in this PR


object Runner {

private val LAUNCHER_ENV_NAME = "ENSO_LAUNCHER"
Copy link
Member Author

Choose a reason for hiding this comment

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

I don't think there is any need to support ENSO_LAUNCHER=shell when there is --jvm option in project-manager. Removing the ENSO_LAUNCHER check.

Copy link
Member

@Akirathan Akirathan left a comment

Choose a reason for hiding this comment

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

Unification of --jvm argument is good.

  1. Make sure you manually test it, I am afraid there are no unit tests for this.
  2. Please choose any other type than Option[Option[Path]].

@JaroslavTulach JaroslavTulach force-pushed the wip/jtulach/DebugProjectManagerDistribution branch from b3251dc to b491672 Compare June 5, 2025 17:17
@JaroslavTulach JaroslavTulach added the CI: Ready to merge This PR is eligible for automatic merge label Jun 5, 2025
@enso-bot enso-bot bot mentioned this pull request Jun 6, 2025
3 tasks
@JaroslavTulach
Copy link
Member Author

JaroslavTulach commented Jun 6, 2025

@JaroslavTulach JaroslavTulach force-pushed the wip/jtulach/DebugProjectManagerDistribution branch from 2ea881b to 8728e1a Compare June 6, 2025 04:29
private def jvmMode =
Opts.flag(
private def jvm =
Opts.optionalParameter[Path](
Copy link
Member Author

@JaroslavTulach JaroslavTulach Jun 6, 2025

Choose a reason for hiding this comment

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

  • I have to admit I am not sure at all how to represent this optional option with optional parameter in this system?
  • in 8728e1a I at least fixed the compilation
  • but using Option(jvm) at various places is unlikely correct, right @radeusgd?

additionalArgs
) => (config: Config) =>
Launcher(config).runRun(
path = path,
versionOverride = versionOverride,
useSystemJVM = systemJVMOverride,
jvmOpts = jvmOpts,
jvmMode = jvmMode,
jvm = Option(jvm),
Copy link
Member Author

@JaroslavTulach JaroslavTulach Jun 6, 2025

Choose a reason for hiding this comment

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

  • Assuming jvm is never null, this will always produce Some and that's not what we want.
  • It should be None if the --jvm option wasn't specified at all
  • How does one represent that with Opts factory methods, @radeusgd?

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure if I understand what you want to achieve.

Do I understand correctly that you want to support 2 scenarios?

  1. --jvm with nothing after it - to enable JVM mode but no path to the JVM
  2. --jvm <path> with a path to the JVM that should be used

Copy link
Contributor

Choose a reason for hiding this comment

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

If the requirements above are correct, then I'm afraid that the framework does not support such a duality at all.

The problem is the framework was created with some other assumptions in mind that contradict the approach above. One of the features we wanted in the framework was that the flags and options can appear in mostly any order relative to positional arguments.

Thus we either have flags that do not expect any 'values' after them, or parameters that expect 1 'value' for it. Thus, if I encounter --foo bar - depending on if --foo is a flag or a parameter, I can determine if bar should be treated as the 'value' for parameter foo or if bar is just a positional argument following --foo.

Of course I can imagine we could take some other approach to this, but these are assumptions of this framework and I think it may be unlikely that it may be easy to change them.

Copy link
Contributor

Choose a reason for hiding this comment

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

So IF my assumptions about what you want from this parameter/flag are correct, I would suggest to try some other approach for selecting the JVM to use. As this flag is doing 2 things at once (one could argue violating SRP).

I'd suggest to keep a boolean flag --jvm that either is there or isn't present.

Note that we already have a flag --use-system-jvm in the same launcher (in fact it's next to your --jvm option). Perhaps that + setting of JAVA_HOME and PATH could be sufficient.

If not, I'd suggest to add --use-custom-jvm=<path> parameter that would override the default selection.

Copy link
Contributor

Choose a reason for hiding this comment

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

Unless I misunderstood your requirements, and you are okay for <path> argument of --jvm to be required - i.e. you either:

  1. do not specify --jvm at all
  2. specify --jvm some-path always with some path.

In that case I think you should have it almost ready - the optionalParameter yields results in form of Option[A] so isn't your jvm already an Option[Path]? Then the Option(jvm) should not be needed. Or is the type something else?

Copy link
Member Author

@JaroslavTulach JaroslavTulach Jun 8, 2025

Choose a reason for hiding this comment

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

suggest to try some other approach

With ...

The primary goal of this PR is to fix sbt "runProjectManagerDistribution --debug"

... being achieved by giving the --jvm flag in project-manager and enso the same meaning and behavior. It is not really possible to try some other approach in ensoup. That wouldn't bring the needed consistency.

But let's ask a question:

  • why do we have the --jvm flag at all?
  • it has been added by Make Native Image opt-out #12515, but
  • why do we need the flag at all?
  • unless I am mistaken ensoup can pass any options (specified after --) to enso, right Radek?
  • then we could just delete the special support and use ensoup run -- --jvm....
  • what do you think @hubertp, @radeusgd?
  • vote +1 for me to open a new PR that removes the ensoup --jvm option/flag/parameter altogether

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, passing some custom options can be done by passing them after --. I think due to the non-standard (from perspective of ensoup's CLI) behaviour of --jvm, passing it after -- may be the preferred solution.

On the other hand let's not that part of ensoup's job is also selecting which JVM to use to run the engine. SO I feel like we are kind of having 2 places that now do a similar thing here. This duplication bothers me a bit, but it's a completely separate issue.

So as for argument passing - yeah I'd vote for passing --jvm after -- and thus we avoid the issues with CLI parsing.

But still slightly worried that we have 2 components selecting JVMs - right now ensoup will select some JVM for the engine and then the engine may actually select another one. Messy.

Copy link
Member Author

Choose a reason for hiding this comment

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

passing it after -- may be the preferred solution.

I can try to open a PR removing this ensoup --jvm altogether. Is that OKeyish, @hubertp?

part of ensoup's job is also selecting which JVM to use to run the engine.

  • can you point me to the specification?
  • it may need updating - it was written prior enso being a native executable

Copy link
Contributor

Choose a reason for hiding this comment

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

Some docs:
https://github.com/enso-org/enso/blob/develop/docs/distribution/launcher.md#enso-and-graal-version-management
https://github.com/enso-org/enso/blob/develop/docs/distribution/launcher-cli.md#--use-system-jvm

Code that manages GraalVM versions and finds/installs the JVM for a given engine version:
https://github.com/enso-org/enso/blob/develop/lib/scala/runtime-version-manager/src/main/java/org/enso/runtimeversionmanager/components/GraalVersionManager.java
https://github.com/enso-org/enso/blob/develop/lib/scala/runtime-version-manager/src/main/scala/org/enso/runtimeversionmanager/components/RuntimeVersionManager.scala#L145-L168

Building engine manifest that specifies the JVM used with it:

buildEngineManifest(
template = file("distribution/manifest.template.yaml"),
destination = distributionRoot / "manifest.yaml",
graalVersion = graalVersion,
javaVersion = javaVersion
)

The representation of the Manifest file in code (and its parsing via YamlDecoder):
https://github.com/enso-org/enso/blob/develop/lib/scala/runtime-version-manager/src/main/scala/org/enso/runtimeversionmanager/components/Manifest.scala#L39


For each engine release we create a manifest.yaml artifact that stores the JVM version needed to run that engine.
ensoup, when installing a given release, downloads this manifest first and it will then install the required GraalVM version if it is not yet installed. All JVMs used for Enso are kept in $ENSO_DATA_DIRECTORY/runtime (see https://github.com/enso-org/enso/blob/a1aa84c0285de4b86168ace233d6d5b50693311c/docs/distribution/distribution.md#installed-enso-distribution-layout).

Perhaps this logic should be updated if the requirements are changed.

Perhaps if no JVM is needed as its running native (but --jvm flag still needs a JVM, right?) the JVM version in manifest.yaml could be set to null. Proper launcher upgrade (breaking change) would be needed for that though as currently null is not a valid value and the logic would break. Ideally it should be done in a backwards compatible way (e.g. by creating a new launcher version and bumping minimum-launcher-version so that a newer launcher is used for engines that rely on the new logic).

Copy link
Member Author

Choose a reason for hiding this comment

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

  • GraalVM will still be needed even with enso native executable
  • first of all --jvm needs the GraalVM
  • the GraalVM will be even more needed with Using Channel to load Java classes #13238
    • unless we create an native only mode
    • that would prevent automatic delegation to HotSpot JVM

Some docs:
https://github.com/enso-org/enso/blob/develop/docs/distribution/launcher.md#enso-and-graal-version-management
https://github.com/enso-org/enso/blob/develop/docs/distribution/launcher-cli.md#--use-system-jvm

OK. I believe the following flag can be removed...

--use-system-jvm
Tells the launcher to use the default JVM (based on JAVA_HOME) instead of the managed one. Will not work if the set-up JVM version is not GraalVM.
GraalVM Override
While the launcher manages its own installation of GraalVM to ensure that the right JVM version is used to launch each version of Enso, the user can override this mechanism to use the installed system JVM instead. This is an advanced feature and should rarely be used.

...such an advanced feature deserves to be complicated - e.g. ensoup run -- --jvm path_to_JDK would be good enough replacement...

@JaroslavTulach JaroslavTulach removed the CI: Ready to merge This PR is eligible for automatic merge label Jun 6, 2025
@JaroslavTulach JaroslavTulach merged commit 2f27473 into develop Jun 6, 2025
81 of 83 checks passed
@JaroslavTulach JaroslavTulach deleted the wip/jtulach/DebugProjectManagerDistribution branch June 6, 2025 17:24
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.

5 participants