mirror of
https://github.com/enso-org/enso.git
synced 2025-01-03 17:14:33 +03:00
Jupyter bindings (#335)
This commit is contained in:
parent
6078b54f50
commit
51d66cdef6
@ -148,6 +148,20 @@ JAVA_HOME=<PATH_TO_GRAAL_HOME> ./enso.jar <CLI_ARGS>
|
||||
If you decide not to use the default launcher script, make sure to pass
|
||||
the `-XX:-UseJVMCIClassLoader` option to the `java` command.
|
||||
|
||||
#### Installing the Jupyter kernel
|
||||
Enso has a higly experimental and not-actively-maintained Jupyer Kernel.
|
||||
To run it:
|
||||
|
||||
1. Build (or download from the CI server) the CLI Fat Jar.
|
||||
2. Fill in the `engine/language-server/jupyter-kernel/enso/kernel.json`
|
||||
file, providing correct paths to the `enso.jar` distribution
|
||||
and GraalVM JAVA_HOME.
|
||||
3. Run:
|
||||
```
|
||||
jupyter kernelspec install <ROOT_OF_THIS_REPO>/engine/language-server/jupyter-kernel/enso
|
||||
```
|
||||
Congratulations, your Jupyter Kernel should now be installed and ready to use.
|
||||
|
||||
#### Passing Debug Options
|
||||
GraalVM provides some useful debugging options, including the ability to output
|
||||
the compilation graph during JIT optimisation, and the ASM generated by the JIT.
|
||||
|
@ -53,7 +53,7 @@ jobs:
|
||||
testResultsFormat: 'JUnit'
|
||||
testResultsFiles: '**/TEST-*.xml'
|
||||
- script: |
|
||||
sbt runtime/assembly
|
||||
sbt language_server/assembly
|
||||
displayName: Build the Uberjar
|
||||
continueOnError: true
|
||||
- task: CopyFiles@2
|
||||
|
71
build.sbt
71
build.sbt
@ -55,7 +55,6 @@ scalacOptions in ThisBuild ++= Seq(
|
||||
"-Xlint:unsound-match", // Pattern match may not be typesafe.
|
||||
"-Xmacro-settings:-logging@org.enso", // Disable the debug logging globally.
|
||||
"-Yno-adapted-args", // Do not adapt an argument list (either by inserting () or creating a tuple) to match the receiver.
|
||||
"-Ypartial-unification", // Enable partial unification (which is enabled by default in Scala 2.13).
|
||||
"-Ypartial-unification", // Enable partial unification in type constructor inference
|
||||
"-Ywarn-dead-code", // Warn when dead code is identified.
|
||||
"-Ywarn-extra-implicit", // Warn when more than one implicit parameter section is defined.
|
||||
@ -311,19 +310,6 @@ val truffleRunOptionsSettings = Seq(
|
||||
|
||||
lazy val runtime = (project in file("engine/runtime"))
|
||||
.settings(
|
||||
mainClass in (Compile, run) := Some("org.enso.interpreter.Main"),
|
||||
mainClass in assembly := (Compile / run / mainClass).value,
|
||||
assemblyJarName in assembly := "enso.jar",
|
||||
test in assembly := {},
|
||||
assemblyOutputPath in assembly := file("enso.jar"),
|
||||
assemblyOption in assembly := (assemblyOption in assembly).value.copy(
|
||||
prependShellScript = Some(
|
||||
defaultUniversalScript(
|
||||
shebang = false,
|
||||
javaOpts = truffleRunOptions
|
||||
)
|
||||
)
|
||||
),
|
||||
version := "0.1",
|
||||
commands += WithDebugCommand.withDebug,
|
||||
inConfig(Compile)(truffleRunOptionsSettings),
|
||||
@ -344,8 +330,7 @@ lazy val runtime = (project in file("engine/runtime"))
|
||||
"org.scalacheck" %% "scalacheck" % "1.14.0" % Test,
|
||||
"org.scalactic" %% "scalactic" % "3.0.8" % Test,
|
||||
"org.scalatest" %% "scalatest" % "3.2.0-SNAP10" % Test,
|
||||
"org.typelevel" %% "cats-core" % "2.0.0-M4",
|
||||
"commons-cli" % "commons-cli" % "1.4"
|
||||
"org.typelevel" %% "cats-core" % "2.0.0-M4"
|
||||
),
|
||||
libraryDependencies ++= jmh
|
||||
)
|
||||
@ -360,20 +345,6 @@ lazy val runtime = (project in file("engine/runtime"))
|
||||
.dependsOn(Def.task { (Compile / sourceManaged).value.mkdirs })
|
||||
.value
|
||||
)
|
||||
.settings(
|
||||
buildNativeImage := Def
|
||||
.task {
|
||||
val javaHome = System.getProperty("java.home")
|
||||
val nativeImagePath = s"$javaHome/bin/native-image"
|
||||
val classPath = (Runtime / fullClasspath).value.files.mkString(":")
|
||||
val resourcesGlobOpt = "-H:IncludeResources=.*Main.enso$"
|
||||
val cmd =
|
||||
s"$nativeImagePath $resourcesGlobOpt --macro:truffle --no-fallback --initialize-at-build-time -cp $classPath ${(Compile / mainClass).value.get} enso"
|
||||
cmd !
|
||||
}
|
||||
.dependsOn(Compile / compile)
|
||||
.value
|
||||
)
|
||||
.configs(Benchmark)
|
||||
.settings(
|
||||
logBuffered := false,
|
||||
@ -394,3 +365,43 @@ lazy val runtime = (project in file("engine/runtime"))
|
||||
)
|
||||
.dependsOn(pkg)
|
||||
.dependsOn(syntax)
|
||||
|
||||
lazy val language_server = project
|
||||
.in(file("engine/language-server"))
|
||||
.settings(
|
||||
mainClass in (Compile, run) := Some("org.enso.languageserver.Main"),
|
||||
mainClass in assembly := (Compile / run / mainClass).value,
|
||||
assemblyJarName in assembly := "enso.jar",
|
||||
test in assembly := {},
|
||||
assemblyOutputPath in assembly := file("enso.jar"),
|
||||
assemblyOption in assembly := (assemblyOption in assembly).value.copy(
|
||||
prependShellScript = Some(
|
||||
defaultUniversalScript(
|
||||
shebang = false,
|
||||
javaOpts = truffleRunOptions
|
||||
)
|
||||
)
|
||||
),
|
||||
inConfig(Compile)(truffleRunOptionsSettings),
|
||||
libraryDependencies ++= Seq(
|
||||
"org.graalvm.sdk" % "polyglot-tck" % graalVersion % "provided",
|
||||
"commons-cli" % "commons-cli" % "1.4",
|
||||
"io.github.spencerpark" % "jupyter-jvm-basekernel" % "2.3.0"
|
||||
)
|
||||
)
|
||||
.settings(
|
||||
buildNativeImage := Def
|
||||
.task {
|
||||
val javaHome = System.getProperty("java.home")
|
||||
val nativeImagePath = s"$javaHome/bin/native-image"
|
||||
val classPath = (Runtime / fullClasspath).value.files.mkString(":")
|
||||
val resourcesGlobOpt = "-H:IncludeResources=.*Main.enso$"
|
||||
val cmd =
|
||||
s"$nativeImagePath $resourcesGlobOpt --macro:truffle --no-fallback --initialize-at-build-time -cp $classPath ${(Compile / mainClass).value.get} enso"
|
||||
cmd !
|
||||
}
|
||||
.dependsOn(Compile / compile)
|
||||
.value
|
||||
)
|
||||
.dependsOn(runtime)
|
||||
.dependsOn(pkg)
|
||||
|
15
engine/language-server/jupyter-kernel/enso/kernel.json
Normal file
15
engine/language-server/jupyter-kernel/enso/kernel.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"argv": [
|
||||
"java",
|
||||
"-XX:-UseJVMCIClassLoader",
|
||||
"-jar",
|
||||
"<ABSOLUTE_PATH_TO_ENSO_JAR>",
|
||||
"--jupyter-kernel",
|
||||
"{connection_file}"
|
||||
],
|
||||
"display_name": "Enso",
|
||||
"language": "Enso",
|
||||
"env": {
|
||||
"JAVA_HOME": "<GRAAL_VM_JAVA_HOME>"
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package org.enso.languageserver
|
||||
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
|
||||
import org.enso.interpreter.Constants
|
||||
import org.enso.interpreter.runtime.RuntimeOptions
|
||||
import org.graalvm.polyglot.Context
|
||||
|
||||
/**
|
||||
* Utility class for creating Graal polyglot contexts.
|
||||
*/
|
||||
class ContextFactory {
|
||||
|
||||
/**
|
||||
* Creates a new Graal polyglot context.
|
||||
*
|
||||
* @param packagesPath Enso packages path
|
||||
* @param in the input stream for standard in
|
||||
* @param out the output stream for standard out
|
||||
* @return configured Context instance
|
||||
*/
|
||||
def create(
|
||||
packagesPath: String = "",
|
||||
in: InputStream = System.in,
|
||||
out: OutputStream = System.out
|
||||
): Context =
|
||||
Context
|
||||
.newBuilder(Constants.LANGUAGE_ID)
|
||||
.allowExperimentalOptions(true)
|
||||
.allowAllAccess(true)
|
||||
.option(RuntimeOptions.getPackagesPathOption, packagesPath)
|
||||
.out(out)
|
||||
.in(in)
|
||||
.build
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package org.enso.languageserver
|
||||
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Paths
|
||||
import java.util.logging.Level
|
||||
|
||||
import io.github.spencerpark.jupyter.channels.JupyterConnection
|
||||
import io.github.spencerpark.jupyter.channels.JupyterSocket
|
||||
import io.github.spencerpark.jupyter.kernel.BaseKernel
|
||||
import io.github.spencerpark.jupyter.kernel.KernelConnectionProperties
|
||||
import io.github.spencerpark.jupyter.kernel.LanguageInfo
|
||||
import io.github.spencerpark.jupyter.kernel.display.DisplayData
|
||||
import org.enso.interpreter.Constants
|
||||
import org.graalvm.polyglot.Context
|
||||
|
||||
/**
|
||||
* A wrapper for Enso interpreter for use by Jupyter
|
||||
*/
|
||||
class JupyterKernel extends BaseKernel {
|
||||
private val context: Context =
|
||||
new ContextFactory().create("", getIO.in, getIO.out)
|
||||
|
||||
/**
|
||||
* Evaluates Enso code in the context of Jupyter request
|
||||
*
|
||||
* @param expr the expression to execute
|
||||
* @return the Jupyter-friendly representation of the result of executing `expr`
|
||||
*/
|
||||
override def eval(expr: String) =
|
||||
new DisplayData(context.eval(Constants.LANGUAGE_ID, expr).toString)
|
||||
|
||||
/**
|
||||
* Basic language information to display in Jupyter
|
||||
* @return the basic language information object
|
||||
*/
|
||||
override def getLanguageInfo: LanguageInfo =
|
||||
new LanguageInfo.Builder(Constants.LANGUAGE_ID)
|
||||
.version(Constants.LANGUAGE_VERSION)
|
||||
.build
|
||||
|
||||
/**
|
||||
* Starts the Jupyter kernel server
|
||||
* @param connectionFileStr filepath of the Jupyter connection file
|
||||
*/
|
||||
def run(connectionFileStr: String): Unit = {
|
||||
val connectionFile = Paths.get(connectionFileStr)
|
||||
|
||||
if (!Files.isRegularFile(connectionFile))
|
||||
throw new IllegalArgumentException(
|
||||
"Connection file '" + connectionFile + "' isn't a file."
|
||||
)
|
||||
|
||||
val contents = new String(Files.readAllBytes(connectionFile))
|
||||
JupyterSocket.JUPYTER_LOGGER.setLevel(Level.WARNING)
|
||||
val connProps = KernelConnectionProperties.parse(contents)
|
||||
val connection = new JupyterConnection(connProps)
|
||||
|
||||
becomeHandlerForConnection(connection)
|
||||
connection.connect()
|
||||
connection.waitUntilClose()
|
||||
}
|
||||
}
|
@ -0,0 +1,143 @@
|
||||
package org.enso.languageserver
|
||||
|
||||
import org.apache.commons.cli._
|
||||
import org.enso.interpreter.Constants
|
||||
import org.enso.pkg.Package
|
||||
import org.graalvm.polyglot.Source
|
||||
import java.io.File
|
||||
import scala.util.Try
|
||||
|
||||
/** The main CLI entry point class. */
|
||||
object Main {
|
||||
|
||||
private val RUN_OPTION = "run"
|
||||
private val HELP_OPTION = "help"
|
||||
private val NEW_OPTION = "new"
|
||||
private val JUPYTER_OPTION = "jupyter-kernel"
|
||||
|
||||
/**
|
||||
* Builds the [[Options]] object representing the CLI syntax.
|
||||
*
|
||||
* @return an [[Options]] object representing the CLI syntax
|
||||
*/
|
||||
private def buildOptions = {
|
||||
val help = Option
|
||||
.builder("h")
|
||||
.longOpt(HELP_OPTION)
|
||||
.desc("Displays this message.")
|
||||
.build
|
||||
val run = Option.builder
|
||||
.hasArg(true)
|
||||
.numberOfArgs(1)
|
||||
.argName("file")
|
||||
.longOpt(RUN_OPTION)
|
||||
.desc("Runs a specified Enso file.")
|
||||
.build
|
||||
val newOpt = Option.builder
|
||||
.hasArg(true)
|
||||
.numberOfArgs(1)
|
||||
.argName("path")
|
||||
.longOpt(NEW_OPTION)
|
||||
.desc("Creates a new Enso project.")
|
||||
.build
|
||||
val jupyterOption = Option.builder
|
||||
.hasArg(true)
|
||||
.numberOfArgs(1)
|
||||
.argName("connection file")
|
||||
.longOpt(JUPYTER_OPTION)
|
||||
.desc("Runs Enso Jupyter Kernel.")
|
||||
.build
|
||||
val options = new Options
|
||||
options
|
||||
.addOption(help)
|
||||
.addOption(run)
|
||||
.addOption(newOpt)
|
||||
.addOption(jupyterOption)
|
||||
options
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the help message to the standard output.
|
||||
*
|
||||
* @param options object representing the CLI syntax
|
||||
*/
|
||||
private def printHelp(options: Options): Unit =
|
||||
new HelpFormatter().printHelp(Constants.LANGUAGE_ID, options)
|
||||
|
||||
/** Terminates the process with a failure exit code. */
|
||||
private def exitFail(): Unit = System.exit(1)
|
||||
|
||||
/** Terminates the process with a success exit code. */
|
||||
private def exitSuccess(): Unit = System.exit(0)
|
||||
|
||||
/**
|
||||
* Handles the `--new` CLI option.
|
||||
*
|
||||
* @param path root path of the newly created project
|
||||
*/
|
||||
private def createNew(path: String) {
|
||||
Package.getOrCreate(new File(path))
|
||||
exitSuccess()
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the `--run` CLI option.
|
||||
*
|
||||
* @param path path of the project or file to execute
|
||||
*/
|
||||
private def run(path: String): Unit = {
|
||||
val file = new File(path)
|
||||
if (!file.exists) {
|
||||
System.out.println("File " + file + " does not exist.")
|
||||
exitFail()
|
||||
}
|
||||
val projectMode = file.isDirectory
|
||||
val packagePath =
|
||||
if (projectMode) file.getAbsolutePath
|
||||
else ""
|
||||
var mainLocation = file
|
||||
if (projectMode) {
|
||||
val pkg = Package.fromDirectory(file)
|
||||
val main = pkg.map(_.mainFile)
|
||||
if (!main.exists(_.exists)) {
|
||||
println("Main file does not exist.")
|
||||
exitFail()
|
||||
}
|
||||
mainLocation = main.get
|
||||
}
|
||||
val context = new ContextFactory().create(packagePath)
|
||||
val source = Source.newBuilder(Constants.LANGUAGE_ID, mainLocation).build
|
||||
context.eval(source)
|
||||
exitSuccess()
|
||||
}
|
||||
|
||||
/**
|
||||
* Main entry point for the CLI program.
|
||||
*
|
||||
* @param args the command line arguments
|
||||
*/
|
||||
def main(args: Array[String]): Unit = {
|
||||
val options = buildOptions
|
||||
val parser = new DefaultParser
|
||||
val line: CommandLine = Try(parser.parse(options, args)).getOrElse {
|
||||
printHelp(options)
|
||||
exitFail()
|
||||
return
|
||||
}
|
||||
if (line.hasOption(HELP_OPTION)) {
|
||||
printHelp(options)
|
||||
exitSuccess()
|
||||
}
|
||||
if (line.hasOption(NEW_OPTION)) {
|
||||
createNew(line.getOptionValue(NEW_OPTION))
|
||||
}
|
||||
if (line.hasOption(RUN_OPTION)) {
|
||||
run(line.getOptionValue(RUN_OPTION))
|
||||
}
|
||||
if (line.hasOption(JUPYTER_OPTION)) {
|
||||
new JupyterKernel().run(line.getOptionValue(JUPYTER_OPTION))
|
||||
}
|
||||
printHelp(options)
|
||||
exitFail()
|
||||
}
|
||||
}
|
@ -1,150 +0,0 @@
|
||||
package org.enso.interpreter;
|
||||
|
||||
import org.apache.commons.cli.*;
|
||||
import org.enso.interpreter.runtime.RuntimeOptions;
|
||||
import org.enso.interpreter.util.ScalaConversions;
|
||||
import org.enso.pkg.Package;
|
||||
import org.graalvm.polyglot.Context;
|
||||
import org.graalvm.polyglot.Source;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
|
||||
/** The main CLI entry point class. */
|
||||
public class Main {
|
||||
private static final String RUN_OPTION = "run";
|
||||
private static final String HELP_OPTION = "help";
|
||||
private static final String NEW_OPTION = "new";
|
||||
|
||||
/**
|
||||
* Builds the {@link Options} object representing the CLI syntax.
|
||||
*
|
||||
* @return an {@link Options} object representing the CLI syntax
|
||||
*/
|
||||
private static Options buildOptions() {
|
||||
Option help = Option.builder("h").longOpt(HELP_OPTION).desc("Displays this message.").build();
|
||||
Option run =
|
||||
Option.builder()
|
||||
.hasArg(true)
|
||||
.numberOfArgs(1)
|
||||
.argName("file")
|
||||
.longOpt(RUN_OPTION)
|
||||
.desc("Runs a specified Enso file.")
|
||||
.build();
|
||||
|
||||
Option newOpt =
|
||||
Option.builder()
|
||||
.hasArg(true)
|
||||
.numberOfArgs(1)
|
||||
.argName("path")
|
||||
.longOpt(NEW_OPTION)
|
||||
.desc("Creates a new Enso project.")
|
||||
.build();
|
||||
|
||||
Options options = new Options();
|
||||
options.addOption(help).addOption(run).addOption(newOpt);
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the help message to the standard output.
|
||||
*
|
||||
* @param options object representing the CLI syntax
|
||||
*/
|
||||
private static void printHelp(Options options) {
|
||||
new HelpFormatter().printHelp(Constants.LANGUAGE_ID, options);
|
||||
}
|
||||
|
||||
/** Terminates the process with a failure exit code. */
|
||||
private static void exitFail() {
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
/** Terminates the process with a success exit code. */
|
||||
private static void exitSuccess() {
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the {@code --new} CLI option.
|
||||
*
|
||||
* @param path root path of the newly created project
|
||||
*/
|
||||
private static void createNew(String path) {
|
||||
Package.getOrCreate(new File(path));
|
||||
exitSuccess();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the {@code --run} CLI option.
|
||||
*
|
||||
* @param path path of the project or file to execute
|
||||
* @throws IOException when source code cannot be parsed
|
||||
*/
|
||||
private static void run(String path) throws IOException {
|
||||
File file = new File(path);
|
||||
|
||||
if (!file.exists()) {
|
||||
System.out.println("File " + file + " does not exist.");
|
||||
exitFail();
|
||||
}
|
||||
|
||||
boolean projectMode = file.isDirectory();
|
||||
String packagePath = projectMode ? file.getAbsolutePath() : "";
|
||||
File mainLocation = file;
|
||||
if (projectMode) {
|
||||
Optional<Package> pkg = ScalaConversions.asJava(Package.fromDirectory(file));
|
||||
Optional<File> main = pkg.map(Package::mainFile);
|
||||
if (!main.isPresent() || !main.get().exists()) {
|
||||
System.out.println("Main file does not exist.");
|
||||
exitFail();
|
||||
}
|
||||
mainLocation = main.get();
|
||||
}
|
||||
|
||||
Context context =
|
||||
Context.newBuilder(Constants.LANGUAGE_ID)
|
||||
.allowExperimentalOptions(true)
|
||||
.allowAllAccess(true)
|
||||
.option(RuntimeOptions.getPackagesPathOption(), packagePath)
|
||||
.out(System.out)
|
||||
.in(System.in)
|
||||
.build();
|
||||
Source source = Source.newBuilder(Constants.LANGUAGE_ID, mainLocation).build();
|
||||
context.eval(source);
|
||||
exitSuccess();
|
||||
}
|
||||
|
||||
/**
|
||||
* Main entry point for the CLI program.
|
||||
*
|
||||
* @param args the command line arguments
|
||||
*/
|
||||
public static void main(String[] args) throws IOException {
|
||||
Options options = buildOptions();
|
||||
CommandLineParser parser = new DefaultParser();
|
||||
CommandLine line;
|
||||
try {
|
||||
line = parser.parse(options, args);
|
||||
} catch (ParseException e) {
|
||||
printHelp(options);
|
||||
exitFail();
|
||||
return;
|
||||
}
|
||||
if (line.hasOption(HELP_OPTION)) {
|
||||
printHelp(options);
|
||||
exitSuccess();
|
||||
return;
|
||||
}
|
||||
if (line.hasOption(NEW_OPTION)) {
|
||||
createNew(line.getOptionValue(NEW_OPTION));
|
||||
}
|
||||
if (line.hasOption(RUN_OPTION)) {
|
||||
run(line.getOptionValue(RUN_OPTION));
|
||||
}
|
||||
|
||||
printHelp(options);
|
||||
exitFail();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user