Skip to content

Commit

Permalink
Instructions to build Enso with Espresso for GraalVM for JDK21 (#8641)
Browse files Browse the repository at this point in the history
  • Loading branch information
JaroslavTulach committed Jan 5, 2024
1 parent 8396bfa commit 542357a
Show file tree
Hide file tree
Showing 6 changed files with 305 additions and 218 deletions.
38 changes: 31 additions & 7 deletions docs/infrastructure/native-image.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,16 +228,41 @@ to allow use of some library functions (like `IO.println`) in the _Native Image_
built runner.

The support can be enabled by setting environment variable `ENSO_JAVA=espresso`
and making sure Espresso is installed in GraalVM executing the Enso engine -
e.g. by running `graalvm/bin/gu install espresso`. Then execute:
and making sure Espresso is installed in the Enso engine `component` directory:

```bash
enso$ built-distribution/enso-engine-*/enso-*/component/
```

e.g. next to `js-language-*.jar` and other JARs. Download following these two
JARs (tested for version 23.1.1) and copy them into the directory:

```bash
enso$ ls built-distribution/enso-engine-*/enso-*/component/espresso-*
built-distribution/enso-engine-0.0.0-dev-linux-amd64/enso-0.0.0-dev/component/espresso-language-23.1.1.jar
built-distribution/enso-engine-0.0.0-dev-linux-amd64/enso-0.0.0-dev/component/espresso-libs-resources-linux-amd64-23.1.1.jar
built-distribution/enso-engine-0.0.0-dev-linux-amd64/enso-0.0.0-dev/component/espresso-runtime-resources-linux-amd64-23.1.1.jar
```

the libraries can be found at
[Maven Central](https://repo1.maven.org/maven2/org/graalvm/espresso/). Version
`23.1.1` is known to work.

Alternatively just build the Enso code with `ENSO_JAVA=espresso` specified

```bash
enso$ ENSO_JAVA=espresso sbt --java-home /graalvm buildEngineDistribution
```

Then you can verify the support works:

```bash
$ cat >hello.enso
import Standard.Base.IO

main = IO.println <| "Hello World!"

$ ENSO_JAVA=espresso ./enso-x.y.z-dev/bin/enso --run hello.enso
$ ENSO_JAVA=espresso ./built-distribution/enso-engine-*/enso-*/bin/enso --run hello.enso
```

Unless you see a warning containing _"No language for id java found."_ your code
Expand All @@ -250,13 +275,12 @@ $ JAVA_OPTS=-agentlib:jdwp=transport=dt_socket,address=5005 ENSO_JAVA=espresso e
```

Espresso support works also with
[native image support](#engine-runner-configuration). Just make sure Espresso is
installed in your GraalVM (via `gu install espresso`) and then rebuild the
`runner` executable:
[native image support](#engine-runner-configuration). Just make sure
`ENSO_JAVA=espresso` is specified when building the `runner` executable:

```bash
enso$ rm runner
enso$ sbt --java-home /graalvm
enso$ ENSO_JAVA=espresso sbt --java-home /graalvm
sbt> engine-runner/buildNativeImage
```

Expand Down
210 changes: 210 additions & 0 deletions engine/runner/src/main/java/org/enso/runner/ContextFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
package org.enso.runner;

import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Map;
import org.enso.logger.Converter;
import org.enso.logger.JulHandler;
import org.enso.logger.LoggerSetup;
import org.enso.polyglot.HostAccessFactory;
import org.enso.polyglot.LanguageInfo;
import org.enso.polyglot.PolyglotContext;
import org.enso.polyglot.RuntimeOptions;
import org.enso.polyglot.debugger.DebugServerInfo;
import org.enso.polyglot.debugger.DebuggerSessionManagerEndpoint;
import org.graalvm.polyglot.Context;
import org.slf4j.event.Level;

/**
* Builder to create a new Graal polyglot context.
*
* @param projectRoot root of the project the interpreter is being run in (or empty if ran outside
* of any projects)
* @param in the input stream for standard in
* @param out the output stream for standard out
* @param repl the Repl manager to use for this context
* @param logLevel the log level for this context
* @param enableIrCaches whether or not IR caching should be enabled
* @param disablePrivateCheck If `private` keyword should be disabled.
* @param strictErrors whether or not to use strict errors
* @param useGlobalIrCacheLocation whether or not to use the global IR cache location
* @param options additional options for the Context
* @param executionEnvironment optional name of the execution environment to use during execution
* @param warningsLimit maximal number of warnings reported to the user
*/
final class ContextFactory {
private String projectRoot;
private InputStream in;
private OutputStream out;
private Repl repl;
private Level logLevel;
private boolean logMasking;
private boolean enableIrCaches;
private boolean disablePrivateCheck;
private boolean strictErrors;
private boolean useGlobalIrCacheLocation = true;
private boolean enableAutoParallelism;
private String executionEnvironment;
private int warningsLimit = 100;
private java.util.Map<String, String> options = java.util.Collections.emptyMap();

private ContextFactory() {}

public static ContextFactory create() {
return new ContextFactory();
}

public ContextFactory projectRoot(String projectRoot) {
this.projectRoot = projectRoot;
return this;
}

public ContextFactory in(InputStream in) {
this.in = in;
return this;
}

public ContextFactory out(OutputStream out) {
this.out = out;
return this;
}

public ContextFactory repl(Repl repl) {
this.repl = repl;
return this;
}

public ContextFactory logLevel(Level logLevel) {
this.logLevel = logLevel;
return this;
}

public ContextFactory logMasking(boolean logMasking) {
this.logMasking = logMasking;
return this;
}

public ContextFactory enableIrCaches(boolean enableIrCaches) {
this.enableIrCaches = enableIrCaches;
return this;
}

public ContextFactory disablePrivateCheck(boolean disablePrivateCheck) {
this.disablePrivateCheck = disablePrivateCheck;
return this;
}

public ContextFactory strictErrors(boolean strictErrors) {
this.strictErrors = strictErrors;
return this;
}

public ContextFactory useGlobalIrCacheLocation(boolean useGlobalIrCacheLocation) {
this.useGlobalIrCacheLocation = useGlobalIrCacheLocation;
return this;
}

public ContextFactory enableAutoParallelism(boolean enableAutoParallelism) {
this.enableAutoParallelism = enableAutoParallelism;
return this;
}

public ContextFactory executionEnvironment(String executionEnvironment) {
this.executionEnvironment = executionEnvironment;
return this;
}

public ContextFactory warningsLimit(int warningsLimit) {
this.warningsLimit = warningsLimit;
return this;
}

public ContextFactory options(Map<String, String> options) {
this.options = options;
return this;
}

PolyglotContext build() {
if (executionEnvironment != null) {
options.put("enso.ExecutionEnvironment", executionEnvironment);
}
var julLogLevel = Converter.toJavaLevel(logLevel);
var logLevelName = julLogLevel.getName();
var builder =
Context.newBuilder()
.allowExperimentalOptions(true)
.allowAllAccess(true)
.allowHostAccess(new HostAccessFactory().allWithTypeMapping())
.option(RuntimeOptions.PROJECT_ROOT, projectRoot)
.option(RuntimeOptions.STRICT_ERRORS, Boolean.toString(strictErrors))
.option(RuntimeOptions.WAIT_FOR_PENDING_SERIALIZATION_JOBS, "true")
.option(
RuntimeOptions.USE_GLOBAL_IR_CACHE_LOCATION,
Boolean.toString(useGlobalIrCacheLocation))
.option(RuntimeOptions.DISABLE_IR_CACHES, Boolean.toString(!enableIrCaches))
.option(RuntimeOptions.DISABLE_PRIVATE_CHECK, Boolean.toString(disablePrivateCheck))
.option(DebugServerInfo.ENABLE_OPTION, "true")
.option(RuntimeOptions.LOG_MASKING, Boolean.toString(logMasking))
.options(options)
.option(RuntimeOptions.ENABLE_AUTO_PARALLELISM, Boolean.toString(enableAutoParallelism))
.option(RuntimeOptions.WARNINGS_LIMIT, Integer.toString(warningsLimit))
.option("js.foreign-object-prototype", "true")
.out(out)
.in(in)
.serverTransport(
(uri, peer) ->
DebugServerInfo.URI.equals(uri.toString())
? new DebuggerSessionManagerEndpoint(repl, peer)
: null);

builder.option(RuntimeOptions.LOG_LEVEL, logLevelName);
var logHandler = JulHandler.get();
var logLevels = LoggerSetup.get().getConfig().getLoggers();
if (logLevels.hasEnsoLoggers()) {
logLevels
.entrySet()
.forEach(
(entry) ->
builder.option(
"log." + LanguageInfo.ID + "." + entry.getKey() + ".level",
Converter.toJavaLevel(entry.getValue()).getName()));
}
builder.logHandler(logHandler);

var graalpy =
new File(
new File(new File(new File(new File(projectRoot), "polyglot"), "python"), "bin"),
"graalpy");
if (graalpy.exists()) {
builder.option("python.Executable", graalpy.getAbsolutePath());
}
if (ENGINE_HAS_JAVA) {
var javaHome = System.getProperty("java.home");
if (javaHome == null) {
javaHome = System.getenv("JAVA_HOME");
}
if (javaHome == null) {
throw new IllegalStateException("Specify JAVA_HOME environment property");
}
builder
.option("java.ExposeNativeJavaVM", "true")
.option("java.Polyglot", "true")
.option("java.UseBindingsLoader", "true")
.option("java.JavaHome", javaHome)
.allowCreateThread(true);
}
return new PolyglotContext(builder.build());
}

/**
* Checks whether the polyglot engine has Espresso. Recorded as static constant to be remembered
* in AOT mode.
*/
private static final boolean ENGINE_HAS_JAVA;

static {
var modules = ModuleLayer.boot().modules().stream();
ENGINE_HAS_JAVA = modules.anyMatch(m -> "org.graalvm.espresso".equals(m.getName()));
}
}
Loading

0 comments on commit 542357a

Please sign in to comment.