mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 02:01:47 +03:00
Run unit tests with truffle-compiler (#8467)
This commit is contained in:
parent
b2a1cd55d2
commit
21d164ec3e
439
build.sbt
439
build.sbt
@ -12,9 +12,14 @@ import src.main.scala.licenses.{
|
||||
DistributionDescription,
|
||||
SBTDistributionComponent
|
||||
}
|
||||
import com.sandinh.javamodule.moduleinfo.JpmsModule
|
||||
import sbt.librarymanagement.DependencyFilter
|
||||
|
||||
// This import is unnecessary, but bit adds a proper code completion features
|
||||
// to IntelliJ.
|
||||
import JPMSPlugin.autoImport.*
|
||||
|
||||
import java.io.File
|
||||
import scala.collection.mutable.ListBuffer
|
||||
|
||||
// ============================================================================
|
||||
// === Global Configuration ===================================================
|
||||
@ -295,8 +300,6 @@ lazy val enso = (project in file("."))
|
||||
`runtime-instrument-id-execution`,
|
||||
`runtime-instrument-repl-debugger`,
|
||||
`runtime-instrument-runtime-server`,
|
||||
`runtime-with-instruments`,
|
||||
`runtime-with-polyglot`,
|
||||
`runtime-version-manager`,
|
||||
`runtime-version-manager-test`,
|
||||
editions,
|
||||
@ -519,6 +522,8 @@ lazy val componentModulesPaths =
|
||||
)
|
||||
}
|
||||
|
||||
lazy val compileModuleInfo = taskKey[Unit]("Compiles `module-info.java`")
|
||||
|
||||
// ============================================================================
|
||||
// === Internal Libraries =====================================================
|
||||
// ============================================================================
|
||||
@ -879,6 +884,7 @@ lazy val `refactoring-utils` = project
|
||||
.dependsOn(testkit % Test)
|
||||
|
||||
lazy val `project-manager` = (project in file("lib/scala/project-manager"))
|
||||
.enablePlugins(JPMSPlugin)
|
||||
.settings(
|
||||
(Compile / mainClass) := Some("org.enso.projectmanager.boot.ProjectManager")
|
||||
)
|
||||
@ -907,6 +913,8 @@ lazy val `project-manager` = (project in file("lib/scala/project-manager"))
|
||||
"org.typelevel" %% "kind-projector" % kindProjectorVersion cross CrossVersion.full
|
||||
)
|
||||
)
|
||||
/** Fat jar assembly settings
|
||||
*/
|
||||
.settings(
|
||||
assembly / assemblyJarName := "project-manager.jar",
|
||||
assembly / test := {},
|
||||
@ -934,26 +942,42 @@ lazy val `project-manager` = (project in file("lib/scala/project-manager"))
|
||||
case "application.conf" => MergeStrategy.concat
|
||||
case "reference.conf" => MergeStrategy.concat
|
||||
case _ => MergeStrategy.first
|
||||
}
|
||||
)
|
||||
/** JPMS related settings for tests
|
||||
*/
|
||||
.settings(
|
||||
Test / fork := true,
|
||||
// These dependencies are here so that we can use them in `--module-path` later on.
|
||||
libraryDependencies ++= {
|
||||
val necessaryModules =
|
||||
GraalVM.modules.map(_.withConfigurations(Some(Test.name))) ++
|
||||
GraalVM.langsPkgs.map(_.withConfigurations(Some(Test.name)))
|
||||
necessaryModules
|
||||
},
|
||||
Test / javaOptions ++=
|
||||
Seq(
|
||||
"-Dpolyglot.engine.WarnInterpreterOnly=false",
|
||||
"-Dpolyglotimpl.DisableClassPathIsolation=true",
|
||||
s"-Dconfig.file=${sourceDirectory.value}/test/resources/application.conf",
|
||||
"-Dslf4j.provider=ch.qos.logback.classic.spi.LogbackServiceProvider"
|
||||
Test / addModules := Seq(
|
||||
(`runtime-fat-jar` / javaModuleName).value
|
||||
),
|
||||
// Append enso language on the class-path
|
||||
Test / unmanagedClasspath :=
|
||||
(LocalProject(
|
||||
"runtime-with-instruments"
|
||||
) / Compile / fullClasspath).value,
|
||||
// In project-manager tests, we test installing projects and for that, we need
|
||||
// to launch engine-runner properly. For that, we need all the JARs that we
|
||||
// normally use in engine distribution. That is why there is dependency on
|
||||
// `buildEngineDistributionNoIndex`.
|
||||
(Test / test) := (Test / test)
|
||||
.dependsOn(buildEngineDistributionNoIndex)
|
||||
.value,
|
||||
Test / modulePath := {
|
||||
val updateReport = (Test / update).value
|
||||
val requiredModIds =
|
||||
GraalVM.modules ++ GraalVM.langsPkgs ++ logbackPkg ++ Seq(
|
||||
"org.slf4j" % "slf4j-api" % slf4jVersion
|
||||
)
|
||||
val requiredMods = JPMSUtils.filterModulesFromUpdate(
|
||||
updateReport,
|
||||
requiredModIds,
|
||||
streams.value.log,
|
||||
shouldContainAll = true
|
||||
)
|
||||
val runtimeMod =
|
||||
(`runtime-fat-jar` / Compile / productDirectories).value.head
|
||||
|
||||
requiredMods ++ Seq(runtimeMod)
|
||||
},
|
||||
Test / javaOptions ++= testLogProviderOptions
|
||||
)
|
||||
.settings(
|
||||
rebuildNativeImage := NativeImage
|
||||
.buildNativeImage(
|
||||
"project-manager",
|
||||
@ -1201,7 +1225,7 @@ lazy val `polyglot-api` = project
|
||||
// Append enso language on the class-path
|
||||
Test / unmanagedClasspath :=
|
||||
(LocalProject(
|
||||
"runtime-with-instruments"
|
||||
"runtime-fat-jar"
|
||||
) / Compile / fullClasspath).value,
|
||||
libraryDependencies ++= Seq(
|
||||
"org.graalvm.sdk" % "polyglot-tck" % graalMavenPackagesVersion % "provided",
|
||||
@ -1220,6 +1244,7 @@ lazy val `polyglot-api` = project
|
||||
.dependsOn(testkit % Test)
|
||||
|
||||
lazy val `language-server` = (project in file("engine/language-server"))
|
||||
.enablePlugins(JPMSPlugin)
|
||||
.settings(
|
||||
commands += WithDebugCommand.withDebug,
|
||||
frgaalJavaCompilerSetting,
|
||||
@ -1237,7 +1262,11 @@ lazy val `language-server` = (project in file("engine/language-server"))
|
||||
"org.scalatest" %% "scalatest" % scalatestVersion % Test,
|
||||
"org.scalacheck" %% "scalacheck" % scalacheckVersion % Test,
|
||||
"org.graalvm.sdk" % "polyglot-tck" % graalMavenPackagesVersion % "provided",
|
||||
"org.eclipse.jgit" % "org.eclipse.jgit" % jgitVersion
|
||||
"org.eclipse.jgit" % "org.eclipse.jgit" % jgitVersion,
|
||||
"org.bouncycastle" % "bcutil-jdk18on" % "1.76" % Test,
|
||||
"org.bouncycastle" % "bcpkix-jdk18on" % "1.76" % Test,
|
||||
"org.bouncycastle" % "bcprov-jdk18on" % "1.76" % Test,
|
||||
"org.apache.tika" % "tika-core" % tikaVersion % Test
|
||||
),
|
||||
Test / testOptions += Tests
|
||||
.Argument(TestFrameworks.ScalaCheck, "-minSuccessfulTests", "1000"),
|
||||
@ -1256,17 +1285,93 @@ lazy val `language-server` = (project in file("engine/language-server"))
|
||||
)
|
||||
)
|
||||
.settings(
|
||||
// These settings are needed by language-server tests that create a runtime context.
|
||||
Test / fork := true,
|
||||
Test / javaOptions ++= testLogProviderOptions ++ Seq(
|
||||
"-Dpolyglot.engine.WarnInterpreterOnly=false",
|
||||
"-Dpolyglotimpl.DisableClassPathIsolation=true"
|
||||
// These dependencies are here so that we can use them in `--module-path` later on.
|
||||
libraryDependencies ++= {
|
||||
val necessaryModules =
|
||||
GraalVM.modules.map(_.withConfigurations(Some(Test.name))) ++
|
||||
GraalVM.langsPkgs.map(_.withConfigurations(Some(Test.name)))
|
||||
necessaryModules
|
||||
},
|
||||
Test / addModules := Seq(
|
||||
(`runtime-fat-jar` / javaModuleName).value
|
||||
),
|
||||
// Append enso language on the class-path
|
||||
Test / unmanagedClasspath :=
|
||||
Test / modulePath := {
|
||||
val updateReport = (Test / update).value
|
||||
// org.bouncycastle is a module required by `org.enso.runtime` module.
|
||||
val requiredModIds =
|
||||
GraalVM.modules ++ GraalVM.langsPkgs ++ logbackPkg ++ Seq(
|
||||
"org.slf4j" % "slf4j-api" % slf4jVersion,
|
||||
"org.bouncycastle" % "bcutil-jdk18on" % "1.76",
|
||||
"org.bouncycastle" % "bcpkix-jdk18on" % "1.76",
|
||||
"org.bouncycastle" % "bcprov-jdk18on" % "1.76"
|
||||
)
|
||||
val requiredMods = JPMSUtils.filterModulesFromUpdate(
|
||||
updateReport,
|
||||
requiredModIds,
|
||||
streams.value.log,
|
||||
shouldContainAll = true
|
||||
)
|
||||
val runtimeMod =
|
||||
(`runtime-fat-jar` / Compile / productDirectories).value.head
|
||||
requiredMods ++ Seq(runtimeMod)
|
||||
},
|
||||
Test / javaOptions ++= testLogProviderOptions,
|
||||
Test / patchModules := {
|
||||
|
||||
/** All these modules will be in --patch-module cmdline option to java, which means that
|
||||
* for the JVM, it will appear that all the classes contained in these sbt projects are contained
|
||||
* in the `org.enso.runtime` module. In this way, we do not have to assembly the `runtime.jar`
|
||||
* fat jar.
|
||||
*/
|
||||
val modulesToPatchIntoRuntime: Seq[File] =
|
||||
(LocalProject(
|
||||
"runtime-with-instruments"
|
||||
) / Compile / fullClasspath).value,
|
||||
"runtime-instrument-common"
|
||||
) / Compile / productDirectories).value ++
|
||||
(LocalProject(
|
||||
"runtime-instrument-id-execution"
|
||||
) / Compile / productDirectories).value ++
|
||||
(LocalProject(
|
||||
"runtime-instrument-repl-debugger"
|
||||
) / Compile / productDirectories).value ++
|
||||
(LocalProject(
|
||||
"runtime-instrument-runtime-server"
|
||||
) / Compile / productDirectories).value ++
|
||||
(LocalProject(
|
||||
"runtime-language-epb"
|
||||
) / Compile / productDirectories).value ++
|
||||
(LocalProject(
|
||||
"runtime-compiler"
|
||||
) / Compile / productDirectories).value ++
|
||||
(LocalProject("runtime-parser") / Compile / productDirectories).value ++
|
||||
(LocalProject(
|
||||
"interpreter-dsl"
|
||||
) / Compile / productDirectories).value ++
|
||||
// We have to patch the `runtime` project as well, as it contains BuiltinTypes.metadata in
|
||||
// runtime/target/classes/META-INF directory
|
||||
(LocalProject("runtime") / Compile / productDirectories).value ++
|
||||
(LocalProject(
|
||||
"syntax-rust-definition"
|
||||
) / Compile / productDirectories).value
|
||||
val extraModsToPatch = JPMSUtils.filterModulesFromUpdate(
|
||||
(Test / update).value,
|
||||
Seq(
|
||||
"org.apache.tika" % "tika-core" % tikaVersion
|
||||
),
|
||||
streams.value.log,
|
||||
shouldContainAll = true
|
||||
)
|
||||
Map(
|
||||
(`runtime-fat-jar` / javaModuleName).value -> (modulesToPatchIntoRuntime ++ extraModsToPatch)
|
||||
)
|
||||
},
|
||||
Test / addReads := {
|
||||
Map(
|
||||
(`runtime-fat-jar` / javaModuleName).value -> Seq("ALL-UNNAMED")
|
||||
)
|
||||
}
|
||||
)
|
||||
.settings(
|
||||
Test / compile := (Test / compile)
|
||||
.dependsOn(LocalProject("enso") / updateLibraryManifests)
|
||||
.value,
|
||||
@ -1388,8 +1493,37 @@ lazy val `runtime-language-epb` =
|
||||
)
|
||||
)
|
||||
|
||||
/** `runtime-test-instruments` project contains Truffle instruments that are used solely for testing.
|
||||
* It is compiled into an explicit Java module. Note that this project cannot have compile-time dependency on `runtime`
|
||||
* project, so if you need access to classes from `runtime`, you need to use reflection.
|
||||
*/
|
||||
lazy val `runtime-test-instruments` =
|
||||
(project in file("engine/runtime-test-instruments"))
|
||||
.enablePlugins(JPMSPlugin)
|
||||
.settings(
|
||||
inConfig(Compile)(truffleRunOptionsSettings),
|
||||
truffleDslSuppressWarnsSetting,
|
||||
instrumentationSettings,
|
||||
javaModuleName := "org.enso.runtime.test",
|
||||
modulePath := {
|
||||
JPMSUtils.filterModulesFromUpdate(
|
||||
update.value,
|
||||
GraalVM.modules ++ Seq(
|
||||
"org.netbeans.api" % "org-openide-util-lookup" % netbeansApiVersion
|
||||
),
|
||||
streams.value.log,
|
||||
shouldContainAll = true
|
||||
)
|
||||
},
|
||||
libraryDependencies ++= GraalVM.modules,
|
||||
libraryDependencies ++= Seq(
|
||||
"org.netbeans.api" % "org-openide-util-lookup" % netbeansApiVersion % "provided"
|
||||
)
|
||||
)
|
||||
|
||||
lazy val runtime = (project in file("engine/runtime"))
|
||||
.configs(Benchmark)
|
||||
.enablePlugins(JPMSPlugin)
|
||||
.settings(
|
||||
frgaalJavaCompilerSetting,
|
||||
truffleDslSuppressWarnsSetting,
|
||||
@ -1426,7 +1560,8 @@ lazy val runtime = (project in file("engine/runtime"))
|
||||
"junit" % "junit" % junitVersion % Test,
|
||||
"com.github.sbt" % "junit-interface" % junitIfVersion % Test,
|
||||
"org.hamcrest" % "hamcrest-all" % hamcrestVersion % Test,
|
||||
"org.slf4j" % "slf4j-nop" % slf4jVersion % Benchmark
|
||||
"org.slf4j" % "slf4j-nop" % slf4jVersion % Benchmark,
|
||||
"org.slf4j" % "slf4j-api" % slf4jVersion % Test
|
||||
),
|
||||
// Add all GraalVM packages with Runtime scope - we don't need them for compilation,
|
||||
// just provide them at runtime (in module-path).
|
||||
@ -1438,10 +1573,87 @@ lazy val runtime = (project in file("engine/runtime"))
|
||||
val tools =
|
||||
GraalVM.toolsPkgs.map(_.withConfigurations(Some(Runtime.name)))
|
||||
necessaryModules ++ langs ++ tools
|
||||
},
|
||||
Test / javaOptions ++= testLogProviderOptions ++ Seq(
|
||||
"-Dpolyglotimpl.DisableClassPathIsolation=true"
|
||||
}
|
||||
)
|
||||
.settings(
|
||||
Test / unmanagedClasspath := (LocalProject(
|
||||
"runtime-fat-jar"
|
||||
) / Compile / exportedProducts).value,
|
||||
Test / addModules := Seq(
|
||||
(`runtime-test-instruments` / javaModuleName).value,
|
||||
(`runtime-fat-jar` / javaModuleName).value
|
||||
),
|
||||
Test / modulePath := {
|
||||
val updateReport = (Test / update).value
|
||||
val requiredModIds =
|
||||
GraalVM.modules ++ GraalVM.langsPkgs ++ GraalVM.insightPkgs ++ logbackPkg ++ Seq(
|
||||
"org.slf4j" % "slf4j-api" % slf4jVersion,
|
||||
"org.netbeans.api" % "org-openide-util-lookup" % netbeansApiVersion
|
||||
)
|
||||
val requiredMods = JPMSUtils.filterModulesFromUpdate(
|
||||
updateReport,
|
||||
requiredModIds,
|
||||
streams.value.log,
|
||||
shouldContainAll = true
|
||||
)
|
||||
val runtimeTestInstrumentsMod =
|
||||
(`runtime-test-instruments` / Compile / exportedProducts).value.head.data
|
||||
val runtimeMod =
|
||||
(`runtime-fat-jar` / Compile / exportedProducts).value.head.data
|
||||
requiredMods ++
|
||||
Seq(runtimeTestInstrumentsMod) ++
|
||||
Seq(runtimeMod)
|
||||
},
|
||||
Test / patchModules := {
|
||||
|
||||
/** All these modules will be in --patch-module cmdline option to java, which means that
|
||||
* for the JVM, it will appear that all the classes contained in these sbt projects are contained
|
||||
* in the `org.enso.runtime` module. In this way, we do not have to assembly the `runtime.jar`
|
||||
* fat jar.
|
||||
*/
|
||||
val modulesToPatchIntoRuntime: Seq[File] =
|
||||
(LocalProject(
|
||||
"runtime-instrument-common"
|
||||
) / Compile / productDirectories).value ++
|
||||
(LocalProject(
|
||||
"runtime-instrument-id-execution"
|
||||
) / Compile / productDirectories).value ++
|
||||
(LocalProject(
|
||||
"runtime-instrument-repl-debugger"
|
||||
) / Compile / productDirectories).value ++
|
||||
(LocalProject(
|
||||
"runtime-instrument-runtime-server"
|
||||
) / Compile / productDirectories).value ++
|
||||
(LocalProject(
|
||||
"runtime-language-epb"
|
||||
) / Compile / productDirectories).value ++
|
||||
(LocalProject(
|
||||
"runtime-compiler"
|
||||
) / Compile / productDirectories).value ++
|
||||
(LocalProject("refactoring-utils") / Compile / productDirectories).value
|
||||
// Patch test-classes into the runtime module. This is standard way to deal with the
|
||||
// split package problem in unit tests. For example, Maven's surefire plugin does this.
|
||||
val testClassesDir = (Test / productDirectories).value.head
|
||||
Map(
|
||||
(`runtime-fat-jar` / javaModuleName).value -> (modulesToPatchIntoRuntime ++ Seq(
|
||||
testClassesDir
|
||||
))
|
||||
)
|
||||
},
|
||||
Test / addReads := {
|
||||
val runtimeModName = (`runtime-fat-jar` / javaModuleName).value
|
||||
val testInstrumentsModName =
|
||||
(`runtime-test-instruments` / javaModuleName).value
|
||||
Map(
|
||||
// We patched the test-classes into the runtime module. These classes access some stuff from
|
||||
// unnamed module. Thus, let's add ALL-UNNAMED.
|
||||
runtimeModName -> Seq("ALL-UNNAMED", testInstrumentsModName),
|
||||
testInstrumentsModName -> Seq(runtimeModName)
|
||||
)
|
||||
},
|
||||
Test / javaOptions ++= testLogProviderOptions
|
||||
)
|
||||
.settings(
|
||||
Test / fork := true,
|
||||
Test / envVars ++= distributionEnvironmentOverrides ++ Map(
|
||||
"ENSO_TEST_DISABLE_IR_CACHE" -> "false"
|
||||
@ -1479,7 +1691,7 @@ lazy val runtime = (project in file("engine/runtime"))
|
||||
// runtime.jar fat jar needs to be assembled as it is used in the
|
||||
// benchmarks. This dependency is here so that `runtime/bench` works
|
||||
// after clean build.
|
||||
LocalProject("runtime-with-instruments") / assembly
|
||||
LocalProject("runtime-fat-jar") / assembly
|
||||
)
|
||||
.value,
|
||||
benchOnly := Def.inputTaskDyn {
|
||||
@ -1519,6 +1731,7 @@ lazy val runtime = (project in file("engine/runtime"))
|
||||
.dependsOn(`connected-lock-manager`)
|
||||
.dependsOn(testkit % Test)
|
||||
.dependsOn(`logging-service-logback` % "test->test")
|
||||
.dependsOn(`runtime-test-instruments` % "test->compile")
|
||||
|
||||
lazy val `runtime-parser` =
|
||||
(project in file("engine/runtime-parser"))
|
||||
@ -1583,7 +1796,9 @@ lazy val `runtime-instrument-common` =
|
||||
)
|
||||
.dependsOn(`refactoring-utils`)
|
||||
.dependsOn(
|
||||
runtime % "compile->compile;test->test;runtime->runtime;bench->bench"
|
||||
LocalProject(
|
||||
"runtime"
|
||||
) % "compile->compile;test->test;runtime->runtime;bench->bench"
|
||||
)
|
||||
|
||||
lazy val `runtime-instrument-id-execution` =
|
||||
@ -1593,7 +1808,7 @@ lazy val `runtime-instrument-id-execution` =
|
||||
inConfig(Compile)(truffleRunOptionsSettings),
|
||||
instrumentationSettings
|
||||
)
|
||||
.dependsOn(runtime)
|
||||
.dependsOn(LocalProject("runtime"))
|
||||
.dependsOn(`runtime-instrument-common`)
|
||||
|
||||
lazy val `runtime-instrument-repl-debugger` =
|
||||
@ -1602,7 +1817,7 @@ lazy val `runtime-instrument-repl-debugger` =
|
||||
inConfig(Compile)(truffleRunOptionsSettings),
|
||||
instrumentationSettings
|
||||
)
|
||||
.dependsOn(runtime)
|
||||
.dependsOn(LocalProject("runtime"))
|
||||
.dependsOn(`runtime-instrument-common`)
|
||||
|
||||
lazy val `runtime-instrument-runtime-server` =
|
||||
@ -1611,50 +1826,21 @@ lazy val `runtime-instrument-runtime-server` =
|
||||
inConfig(Compile)(truffleRunOptionsSettings),
|
||||
instrumentationSettings
|
||||
)
|
||||
.dependsOn(runtime)
|
||||
.dependsOn(LocalProject("runtime"))
|
||||
.dependsOn(`runtime-instrument-common`)
|
||||
|
||||
lazy val `runtime-with-instruments` =
|
||||
(project in file("engine/runtime-with-instruments"))
|
||||
.configs(Benchmark)
|
||||
/** A "meta" project that exists solely to provide logic for assembling the `runtime.jar` fat Jar.
|
||||
* We do not want to put this task into any other existing project, as it internally copies some
|
||||
* classes from other projects into the `classes` directory, therefore, pollutes the build.
|
||||
* There is only one Java source in this project - `module-info.java`. During the assembling of the
|
||||
* fat jar, all the classes from the dependent projects are copied into the `classes` directory of
|
||||
* this project and then, a custom task is invoked to compile the `module-info.java`.
|
||||
*/
|
||||
lazy val `runtime-fat-jar` =
|
||||
(project in file("engine/runtime-fat-jar"))
|
||||
.enablePlugins(JPMSPlugin)
|
||||
.settings(
|
||||
frgaalJavaCompilerSetting,
|
||||
inConfig(Compile)(truffleRunOptionsSettings),
|
||||
commands += WithDebugCommand.withDebug,
|
||||
Test / javaOptions ++= testLogProviderOptions ++ Seq(
|
||||
"-Dpolyglotimpl.DisableClassPathIsolation=true"
|
||||
),
|
||||
Test / fork := true,
|
||||
Test / envVars ++= distributionEnvironmentOverrides ++ Map(
|
||||
"ENSO_TEST_DISABLE_IR_CACHE" -> "false"
|
||||
),
|
||||
libraryDependencies ++= Seq(
|
||||
"org.scalatest" %% "scalatest" % scalatestVersion % Test,
|
||||
"org.graalvm.truffle" % "truffle-api" % graalMavenPackagesVersion % Test,
|
||||
"org.graalvm.truffle" % "truffle-dsl-processor" % graalMavenPackagesVersion % Test,
|
||||
"org.slf4j" % "slf4j-nop" % slf4jVersion % Benchmark
|
||||
),
|
||||
// Add all GraalVM packages with Runtime scope - we don't need them for compilation,
|
||||
// just provide them at runtime (in module-path).
|
||||
libraryDependencies ++= {
|
||||
val necessaryModules =
|
||||
GraalVM.modules.map(_.withConfigurations(Some(Runtime.name)))
|
||||
val langs =
|
||||
GraalVM.langsPkgs.map(_.withConfigurations(Some(Runtime.name)))
|
||||
necessaryModules ++ langs
|
||||
},
|
||||
// Note [Unmanaged Classpath]
|
||||
Test / unmanagedClasspath += (baseDirectory.value / ".." / ".." / "app" / "gui" / "view" / "graph-editor" / "src" / "builtin" / "visualization" / "native" / "inc"),
|
||||
// Filter module-info.java from the compilation
|
||||
excludeFilter := excludeFilter.value || "module-info.java",
|
||||
moduleInfos := Seq(
|
||||
JpmsModule("org.enso.runtime")
|
||||
)
|
||||
)
|
||||
/** Assembling Uber Jar */
|
||||
.settings(
|
||||
assembly := assembly
|
||||
.dependsOn(
|
||||
Compile / compileModuleInfo := {
|
||||
JPMSUtils.compileModuleInfo(
|
||||
copyDepsFilter = ScopeFilter(
|
||||
inProjects(
|
||||
@ -1669,7 +1855,35 @@ lazy val `runtime-with-instruments` =
|
||||
),
|
||||
modulePath = JPMSUtils.componentModules
|
||||
)
|
||||
}
|
||||
.dependsOn(Compile / compile)
|
||||
.value,
|
||||
// Filter module-info.java from the compilation
|
||||
excludeFilter := excludeFilter.value || "module-info.java",
|
||||
javaModuleName := "org.enso.runtime",
|
||||
compileOrder := CompileOrder.JavaThenScala
|
||||
)
|
||||
/** The following libraryDependencies are provided in Runtime scope.
|
||||
* Later, we will collect them into --module-path option.
|
||||
* We don't collect them in Compile scope as it does not even make sense
|
||||
* to run `compile` task in this project.
|
||||
*/
|
||||
.settings(
|
||||
libraryDependencies ++= {
|
||||
val graalMods =
|
||||
GraalVM.modules.map(_.withConfigurations(Some(Runtime.name)))
|
||||
val langMods =
|
||||
GraalVM.langsPkgs.map(_.withConfigurations(Some(Runtime.name)))
|
||||
val logbackMods =
|
||||
logbackPkg.map(_.withConfigurations(Some(Runtime.name)))
|
||||
graalMods ++ langMods ++ logbackMods
|
||||
}
|
||||
)
|
||||
/** Assembling Uber Jar */
|
||||
.settings(
|
||||
assembly := assembly
|
||||
.dependsOn(Compile / compile)
|
||||
.dependsOn(Compile / compileModuleInfo)
|
||||
.value,
|
||||
assembly / assemblyJarName := "runtime.jar",
|
||||
assembly / test := {},
|
||||
@ -1704,61 +1918,12 @@ lazy val `runtime-with-instruments` =
|
||||
case _ => MergeStrategy.first
|
||||
}
|
||||
)
|
||||
/** Benchmark settings */
|
||||
.settings(
|
||||
inConfig(Benchmark)(Defaults.testSettings),
|
||||
Benchmark / javacOptions --= Seq(
|
||||
"-source",
|
||||
frgaalSourceLevel,
|
||||
"--enable-preview"
|
||||
),
|
||||
(Benchmark / javaOptions) :=
|
||||
(LocalProject("std-benchmarks") / Benchmark / run / javaOptions).value
|
||||
)
|
||||
.dependsOn(runtime % "compile->compile;test->test;runtime->runtime")
|
||||
.dependsOn(`runtime-instrument-common`)
|
||||
.dependsOn(`runtime-instrument-id-execution`)
|
||||
.dependsOn(`runtime-instrument-repl-debugger`)
|
||||
.dependsOn(`runtime-instrument-runtime-server`)
|
||||
.dependsOn(`runtime-language-epb`)
|
||||
.dependsOn(`logging-service-logback` % "test->test")
|
||||
|
||||
/* runtime-with-polyglot
|
||||
* ~~~~~~~~~~~~~~~~~~~~~
|
||||
* Unlike `runtime`, this project includes the truffle language JARs on the
|
||||
* class-path.
|
||||
*/
|
||||
|
||||
lazy val `runtime-with-polyglot` =
|
||||
(project in file("engine/runtime-with-polyglot"))
|
||||
.configs(Benchmark)
|
||||
.settings(
|
||||
frgaalJavaCompilerSetting,
|
||||
inConfig(Compile)(truffleRunOptionsSettings),
|
||||
inConfig(Benchmark)(Defaults.testSettings),
|
||||
commands += WithDebugCommand.withDebug,
|
||||
Benchmark / javacOptions --= Seq(
|
||||
"-source",
|
||||
frgaalSourceLevel,
|
||||
"--enable-preview"
|
||||
),
|
||||
Test / javaOptions ++= testLogProviderOptions ++ Seq(
|
||||
"-Dpolyglotimpl.DisableClassPathIsolation=true"
|
||||
),
|
||||
Test / fork := true,
|
||||
Test / envVars ++= distributionEnvironmentOverrides ++ Map(
|
||||
"ENSO_TEST_DISABLE_IR_CACHE" -> "false"
|
||||
),
|
||||
libraryDependencies ++= GraalVM.langsPkgs ++ Seq(
|
||||
"org.graalvm.polyglot" % "polyglot" % graalMavenPackagesVersion % "provided",
|
||||
"org.graalvm.tools" % "insight-tool" % graalMavenPackagesVersion % "provided",
|
||||
"org.scalatest" %% "scalatest" % scalatestVersion % Test
|
||||
),
|
||||
(Benchmark / javaOptions) :=
|
||||
(LocalProject("std-benchmarks") / Benchmark / run / javaOptions).value
|
||||
)
|
||||
.dependsOn(runtime % "compile->compile;test->test;runtime->runtime")
|
||||
.dependsOn(`runtime-with-instruments`)
|
||||
.dependsOn(LocalProject("runtime"))
|
||||
|
||||
/* Note [Unmanaged Classpath]
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -1833,7 +1998,7 @@ lazy val `engine-runner` = project
|
||||
)
|
||||
.settings(
|
||||
assembly := assembly
|
||||
.dependsOn(`runtime-with-instruments` / assembly)
|
||||
.dependsOn(`runtime-fat-jar` / assembly)
|
||||
.value,
|
||||
rebuildNativeImage :=
|
||||
NativeImage
|
||||
@ -2016,7 +2181,7 @@ lazy val `bench-processor` = (project in file("lib/scala/bench-processor"))
|
||||
),
|
||||
// Append enso language on the class-path
|
||||
(Test / unmanagedClasspath) :=
|
||||
(LocalProject("runtime-with-instruments") / Compile / fullClasspath).value
|
||||
(LocalProject("runtime-fat-jar") / Compile / fullClasspath).value
|
||||
)
|
||||
.dependsOn(`polyglot-api`)
|
||||
.dependsOn(runtime)
|
||||
@ -2055,11 +2220,11 @@ lazy val `std-benchmarks` = (project in file("std-bits/benchmarks"))
|
||||
(Benchmark / parallelExecution) := false,
|
||||
(Benchmark / run / fork) := true,
|
||||
(Benchmark / run / connectInput) := true,
|
||||
// This ensures that the full class-path of runtime-with-instruments is put on
|
||||
// This ensures that the full class-path of runtime-fat-jar is put on
|
||||
// class-path of the Java compiler (and thus the benchmark annotation processor).
|
||||
(Benchmark / compile / unmanagedClasspath) ++=
|
||||
(LocalProject(
|
||||
"runtime-with-instruments"
|
||||
"runtime-fat-jar"
|
||||
) / Compile / fullClasspath).value,
|
||||
(Benchmark / compile / javacOptions) ++= Seq(
|
||||
"-s",
|
||||
@ -2083,13 +2248,13 @@ lazy val `std-benchmarks` = (project in file("std-bits/benchmarks"))
|
||||
.map(_.data.getAbsolutePath)
|
||||
val runtimeJar =
|
||||
(LocalProject(
|
||||
"runtime-with-instruments"
|
||||
"runtime-fat-jar"
|
||||
) / assembly / assemblyOutputPath).value.getAbsolutePath
|
||||
val allModulePaths = requiredModulesPaths ++ Seq(runtimeJar)
|
||||
val runtimeModuleName =
|
||||
(LocalProject(
|
||||
"runtime-with-instruments"
|
||||
) / moduleInfos).value.head.moduleName
|
||||
"runtime-fat-jar"
|
||||
) / javaModuleName).value
|
||||
Seq(
|
||||
// To enable logging in benchmarks, add ch.qos.logback module on the modulePath
|
||||
"-Dslf4j.provider=org.slf4j.nop.NOPServiceProvider",
|
||||
|
@ -0,0 +1,12 @@
|
||||
module org.enso.runtime.test {
|
||||
requires org.graalvm.truffle;
|
||||
requires org.openide.util.lookup.RELEASE180;
|
||||
|
||||
exports org.enso.interpreter.test.instruments;
|
||||
exports org.enso.interpreter.test.instruments.service;
|
||||
|
||||
provides com.oracle.truffle.api.instrumentation.provider.TruffleInstrumentProvider with
|
||||
org.enso.interpreter.test.instruments.CodeIdsTestInstrumentProvider,
|
||||
org.enso.interpreter.test.instruments.CodeLocationsTestInstrumentProvider,
|
||||
org.enso.interpreter.test.instruments.NodeCountingTestInstrumentProvider;
|
||||
}
|
@ -1,13 +1,14 @@
|
||||
package org.enso.interpreter.test;
|
||||
package org.enso.interpreter.test.instruments;
|
||||
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.instrumentation.*;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import java.util.UUID;
|
||||
import org.enso.interpreter.node.ExpressionNode;
|
||||
import org.enso.interpreter.runtime.control.TailCallException;
|
||||
import org.enso.interpreter.test.instruments.service.RuntimeTestService;
|
||||
import org.openide.util.Lookup;
|
||||
|
||||
/**
|
||||
* A debug instrument used to test code locations.
|
||||
@ -20,8 +21,13 @@ import org.enso.interpreter.runtime.control.TailCallException;
|
||||
services = CodeIdsTestInstrument.class)
|
||||
public class CodeIdsTestInstrument extends TruffleInstrument {
|
||||
public static final String INSTRUMENT_ID = "ids-test";
|
||||
private static final RuntimeTestService runtimeTestService;
|
||||
private Env env;
|
||||
|
||||
static {
|
||||
runtimeTestService = Lookup.getDefault().lookup(RuntimeTestService.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the instrument. Substitute for a constructor, called by the Truffle framework.
|
||||
*
|
||||
@ -94,7 +100,6 @@ public class CodeIdsTestInstrument extends TruffleInstrument {
|
||||
/**
|
||||
* Checks if the node to be executed is the node this listener was created to observe.
|
||||
*
|
||||
* @param context current execution context
|
||||
* @param frame current execution frame
|
||||
* @param result the result of executing the node
|
||||
*/
|
||||
@ -104,11 +109,11 @@ public class CodeIdsTestInstrument extends TruffleInstrument {
|
||||
return;
|
||||
}
|
||||
Node node = context.getInstrumentedNode();
|
||||
if (!(node instanceof ExpressionNode)) {
|
||||
if (!runtimeTestService.isExpressionNode(node)) {
|
||||
return;
|
||||
}
|
||||
nodes.put(this, result);
|
||||
UUID id = ((ExpressionNode) node).getId();
|
||||
UUID id = runtimeTestService.getNodeID(node);
|
||||
if (id == null || !id.equals(expectedId)) {
|
||||
return;
|
||||
}
|
||||
@ -120,19 +125,18 @@ public class CodeIdsTestInstrument extends TruffleInstrument {
|
||||
/**
|
||||
* Checks if the specified was called, if its execution triggered TCO.
|
||||
*
|
||||
* @param context current execution context.
|
||||
* @param frame current execution frame.
|
||||
* @param exception the exception thrown from this node's execution.
|
||||
*/
|
||||
@Override
|
||||
public void onReturnExceptional(VirtualFrame frame, Throwable exception) {
|
||||
if (!(exception instanceof TailCallException)) {
|
||||
if (!runtimeTestService.isTailCallException(exception)) {
|
||||
return;
|
||||
}
|
||||
if (!(context.getInstrumentedNode() instanceof ExpressionNode)) {
|
||||
if (!runtimeTestService.isExpressionNode(context.getInstrumentedNode())) {
|
||||
return;
|
||||
}
|
||||
UUID id = ((ExpressionNode) context.getInstrumentedNode()).getId();
|
||||
UUID id = runtimeTestService.getNodeID(context.getInstrumentedNode());
|
||||
if (expectedResult == null) {
|
||||
successful = true;
|
||||
}
|
||||
@ -142,8 +146,9 @@ public class CodeIdsTestInstrument extends TruffleInstrument {
|
||||
public String toString() {
|
||||
var sb = new StringBuilder();
|
||||
sb.append(context.getInstrumentedNode().getClass().getSimpleName());
|
||||
if (context.getInstrumentedNode() instanceof ExpressionNode expr) {
|
||||
sb.append("@").append(expr.getId());
|
||||
if (runtimeTestService.isExpressionNode(context.getInstrumentedNode())) {
|
||||
UUID id = runtimeTestService.getNodeID(context.getInstrumentedNode());
|
||||
sb.append("@").append(id);
|
||||
}
|
||||
sb.append(" ");
|
||||
sb.append(context.getInstrumentedSourceSection());
|
@ -1,4 +1,4 @@
|
||||
package org.enso.interpreter.test;
|
||||
package org.enso.interpreter.test.instruments;
|
||||
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.instrumentation.EventBinding;
|
@ -1,6 +1,4 @@
|
||||
package org.enso.interpreter.test;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
package org.enso.interpreter.test.instruments;
|
||||
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.instrumentation.EventContext;
|
||||
@ -9,18 +7,17 @@ import com.oracle.truffle.api.instrumentation.ExecutionEventNodeFactory;
|
||||
import com.oracle.truffle.api.instrumentation.SourceSectionFilter;
|
||||
import com.oracle.truffle.api.instrumentation.TruffleInstrument;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.nodes.RootNode;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.function.Function;
|
||||
import org.enso.interpreter.node.MethodRootNode;
|
||||
import org.enso.interpreter.node.callable.FunctionCallInstrumentationNode;
|
||||
import org.enso.pkg.QualifiedName;
|
||||
import org.enso.interpreter.test.instruments.service.FunctionCallInfo;
|
||||
import org.enso.interpreter.test.instruments.service.RuntimeTestService;
|
||||
import org.openide.util.Lookup;
|
||||
|
||||
/** Testing instrument to control newly created nodes. */
|
||||
@TruffleInstrument.Registration(
|
||||
@ -28,12 +25,17 @@ import org.enso.pkg.QualifiedName;
|
||||
services = NodeCountingTestInstrument.class)
|
||||
public class NodeCountingTestInstrument extends TruffleInstrument {
|
||||
public static final String INSTRUMENT_ID = "node-count-test";
|
||||
private static final RuntimeTestService runtimeTestService;
|
||||
private final Map<Node, Node> all = new ConcurrentHashMap<>();
|
||||
private Map<Class, List<Node>> counter = new ConcurrentHashMap<>();
|
||||
|
||||
private final Map<UUID, FunctionCallInfo> calls = new ConcurrentHashMap<>();
|
||||
private Env env;
|
||||
|
||||
static {
|
||||
runtimeTestService = Lookup.getDefault().lookup(RuntimeTestService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Env env) {
|
||||
env.registerService(this);
|
||||
@ -75,10 +77,10 @@ public class NodeCountingTestInstrument extends TruffleInstrument {
|
||||
};
|
||||
|
||||
if (value < min) {
|
||||
fail(dump.apply(msg + ". Minimal size should be " + min + ", but was: " + value + " in"));
|
||||
throw new AssertionError(dump.apply(msg + ". Minimal size should be " + min + ", but was: " + value + " in"));
|
||||
}
|
||||
if (value > max) {
|
||||
fail(dump.apply(msg + ". Maximal size should be " + max + ", but was: " + value + " in"));
|
||||
throw new AssertionError(dump.apply(msg + ". Maximal size should be " + max + ", but was: " + value + " in"));
|
||||
}
|
||||
counter = new ConcurrentHashMap<>();
|
||||
return prev;
|
||||
@ -120,73 +122,16 @@ public class NodeCountingTestInstrument extends TruffleInstrument {
|
||||
|
||||
public void onReturnValue(VirtualFrame frame, Object result) {
|
||||
Node node = context.getInstrumentedNode();
|
||||
if (node instanceof FunctionCallInstrumentationNode instrumentableNode
|
||||
&& result instanceof FunctionCallInstrumentationNode.FunctionCall functionCall) {
|
||||
onFunctionReturn(instrumentableNode, functionCall);
|
||||
|
||||
if (runtimeTestService.isFunctionCallInstrumentationNode(node)
|
||||
&& runtimeTestService.isFunctionCall(result)) {
|
||||
UUID nodeId = runtimeTestService.getNodeID(node);
|
||||
if (nodeId != null) {
|
||||
var funcCallInfo = runtimeTestService.extractFunctionCallInfo(result);
|
||||
calls.put(nodeId, funcCallInfo);
|
||||
}
|
||||
}
|
||||
|
||||
private void onFunctionReturn(
|
||||
FunctionCallInstrumentationNode node, FunctionCallInstrumentationNode.FunctionCall result) {
|
||||
if (node.getId() != null) {
|
||||
calls.put(node.getId(), new FunctionCallInfo(result));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class FunctionCallInfo {
|
||||
|
||||
private final QualifiedName moduleName;
|
||||
private final QualifiedName typeName;
|
||||
private final String functionName;
|
||||
|
||||
public FunctionCallInfo(FunctionCallInstrumentationNode.FunctionCall call) {
|
||||
RootNode rootNode = call.getFunction().getCallTarget().getRootNode();
|
||||
if (rootNode instanceof MethodRootNode methodNode) {
|
||||
moduleName = methodNode.getModuleScope().getModule().getName();
|
||||
typeName = methodNode.getType().getQualifiedName();
|
||||
functionName = methodNode.getMethodName();
|
||||
} else {
|
||||
moduleName = null;
|
||||
typeName = null;
|
||||
functionName = rootNode.getName();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
FunctionCallInfo that = (FunctionCallInfo) o;
|
||||
return Objects.equals(moduleName, that.moduleName)
|
||||
&& Objects.equals(typeName, that.typeName)
|
||||
&& Objects.equals(functionName, that.functionName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(moduleName, typeName, functionName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return moduleName + "::" + typeName + "::" + functionName;
|
||||
}
|
||||
|
||||
public QualifiedName getModuleName() {
|
||||
return moduleName;
|
||||
}
|
||||
|
||||
public QualifiedName getTypeName() {
|
||||
return typeName;
|
||||
}
|
||||
|
||||
public String getFunctionName() {
|
||||
return functionName;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package org.enso.interpreter.test.instruments.service;
|
||||
|
||||
public record FunctionCallInfo(
|
||||
String moduleName,
|
||||
String typeName,
|
||||
String functionName
|
||||
) {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return moduleName + "::" + typeName + "::" + functionName;
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package org.enso.interpreter.test.instruments.service;
|
||||
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.nodes.RootNode;
|
||||
import java.util.UUID;
|
||||
import org.enso.interpreter.test.instruments.service.FunctionCallInfo;
|
||||
|
||||
/**
|
||||
* A service that provides information from the `runtime` project to the instruments in this project
|
||||
* (`runtime-test-instruments`). Note that this project cannot have a compile time dependency on
|
||||
* `runtime`, thus, we need to use a service provider mechanism.
|
||||
*/
|
||||
public interface RuntimeTestService {
|
||||
UUID getNodeID(Node node);
|
||||
boolean isExpressionNode(Object node);
|
||||
boolean isTailCallException(Object obj);
|
||||
boolean isFunctionCallInstrumentationNode(Object node);
|
||||
boolean isFunctionCall(Object obj);
|
||||
FunctionCallInfo extractFunctionCallInfo(Object functionCall);
|
||||
}
|
@ -1,77 +0,0 @@
|
||||
package org.enso.interpreter.test.instrument;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import java.nio.file.Paths;
|
||||
import java.util.logging.Level;
|
||||
import org.enso.interpreter.test.MockLogHandler;
|
||||
import org.enso.polyglot.MethodNames;
|
||||
import org.enso.polyglot.RuntimeOptions;
|
||||
import org.graalvm.polyglot.Context;
|
||||
import org.graalvm.polyglot.Source;
|
||||
import org.graalvm.polyglot.io.IOAccess;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
public class VerifyLanguageAvailabilityTest {
|
||||
private static Context ctx;
|
||||
private static MockLogHandler handler;
|
||||
|
||||
@BeforeClass
|
||||
public static void initEnsoContext() {
|
||||
handler = new MockLogHandler();
|
||||
ctx =
|
||||
Context.newBuilder()
|
||||
.allowExperimentalOptions(true)
|
||||
.allowIO(IOAccess.ALL)
|
||||
.option(RuntimeOptions.PREINITIALIZE, "js")
|
||||
.option(
|
||||
RuntimeOptions.LANGUAGE_HOME_OVERRIDE,
|
||||
Paths.get("../../distribution/component").toFile().getAbsolutePath())
|
||||
.option(RuntimeOptions.LOG_LEVEL, Level.FINE.getName())
|
||||
.logHandler(handler)
|
||||
.allowAllAccess(true)
|
||||
.build();
|
||||
assertNotNull("Enso language is supported", ctx.getEngine().getLanguages().get("enso"));
|
||||
var fourtyTwo =
|
||||
ctx.eval("enso", "mul x y = x * y").invokeMember("eval_expression", "mul").execute(6, 7);
|
||||
assertEquals(42, fourtyTwo.asInt());
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void closeEnsoContext() throws Exception {
|
||||
ctx.close();
|
||||
|
||||
var args =
|
||||
handler.assertMessage("epb.org.enso.interpreter.epb.EpbContext", "Parsing foreign script");
|
||||
assertEquals("js", args[0]);
|
||||
assertEquals("mul.mul", args[1]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void javaScriptIsPresent() throws Exception {
|
||||
var js = ctx.getEngine().getLanguages().get("js");
|
||||
assertNotNull("JavaScript is available", js);
|
||||
var src =
|
||||
Source.newBuilder(
|
||||
"enso",
|
||||
"""
|
||||
foreign js mul a b = \"\"\"
|
||||
return a * b
|
||||
|
||||
run = mul 6 7
|
||||
""",
|
||||
"mul.enso")
|
||||
.build();
|
||||
var fourtyTwo = ctx.eval(src).invokeMember(MethodNames.Module.EVAL_EXPRESSION, "run");
|
||||
assertEquals(42, fourtyTwo.asInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ensoIsPresent() {
|
||||
var enso = ctx.getEngine().getLanguages().get("enso");
|
||||
assertNotNull("Enso is available", enso);
|
||||
}
|
||||
}
|
@ -85,36 +85,6 @@ public class BigNumberTest extends TestBase {
|
||||
return powers;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void averageOfMixedArrayOverDouble() throws Exception {
|
||||
boolean assertsOn = false;
|
||||
assert assertsOn = true;
|
||||
if (assertsOn) {
|
||||
// skip this test when asserts are on
|
||||
return;
|
||||
}
|
||||
var code =
|
||||
"""
|
||||
from Standard.Base.Data.Vector import Vector
|
||||
polyglot java import org.enso.example.TestClass
|
||||
|
||||
powers n =
|
||||
go x v b = if x > n then b.to_vector else
|
||||
b.append v
|
||||
@Tail_Call go x+1 v*2 b
|
||||
go 1 1 Vector.new_builder
|
||||
|
||||
avg n = TestClass.doubleArrayAverage (powers n)
|
||||
""";
|
||||
var fn = evalCode(code, "avg");
|
||||
var avg = fn.execute(200);
|
||||
|
||||
assertTrue("Got a number back " + avg, avg.isNumber());
|
||||
assertFalse("It's not a long", avg.fitsInLong());
|
||||
assertTrue("It's a double", avg.fitsInDouble());
|
||||
assertEquals("It is big enough", Math.pow(2, 200) / 200, avg.asDouble(), 300);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void averageOfMixedArrayOverNumber() throws Exception {
|
||||
var code =
|
||||
|
@ -42,7 +42,6 @@ import org.graalvm.polyglot.io.IOAccess;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
public class DebuggingEnsoTest {
|
||||
@ -58,7 +57,7 @@ public class DebuggingEnsoTest {
|
||||
.option(
|
||||
RuntimeOptions.LANGUAGE_HOME_OVERRIDE,
|
||||
Paths.get("../../distribution/component").toFile().getAbsolutePath())
|
||||
.option(RuntimeOptions.LOG_LEVEL, Level.FINEST.getName())
|
||||
.option(RuntimeOptions.LOG_LEVEL, Level.WARNING.getName())
|
||||
.logHandler(System.err)
|
||||
.build();
|
||||
|
||||
@ -504,9 +503,7 @@ public class DebuggingEnsoTest {
|
||||
bar 42 # 6
|
||||
end = 0 # 7
|
||||
""");
|
||||
// Steps into line 2 - declaration of the method, which is fine.
|
||||
// (5, 6, 7) would be better.
|
||||
List<Integer> expectedLineNumbers = List.of(5, 6, 2, 7);
|
||||
List<Integer> expectedLineNumbers = List.of(5, 6, 7);
|
||||
Queue<SuspendedCallback> steps = createStepOverEvents(expectedLineNumbers.size());
|
||||
testStepping(src, "foo", new Object[] {0}, steps, expectedLineNumbers);
|
||||
}
|
||||
@ -514,12 +511,7 @@ public class DebuggingEnsoTest {
|
||||
/**
|
||||
* Use some methods from Vector in stdlib. Stepping over methods from different modules might be
|
||||
* problematic.
|
||||
*
|
||||
* <p>TODO[pm] This test is ignored, because the current behavior of step over is that it first
|
||||
* steps into the declaration (name) of the method that is being stepped over and then steps back.
|
||||
* So there would be weird line numbers from std lib.
|
||||
*/
|
||||
@Ignore
|
||||
@Test
|
||||
public void testSteppingOverUseStdLib() {
|
||||
Source src =
|
||||
@ -562,7 +554,7 @@ public class DebuggingEnsoTest {
|
||||
bar 42 # 4
|
||||
end = 0 # 5
|
||||
""");
|
||||
List<Integer> expectedLineNumbers = List.of(3, 4, 2, 1, 5);
|
||||
List<Integer> expectedLineNumbers = List.of(3, 4, 2, 1, 2, 4, 5);
|
||||
Queue<SuspendedCallback> steps =
|
||||
new ArrayDeque<>(
|
||||
Collections.nCopies(expectedLineNumbers.size(), (event) -> event.prepareStepInto(1)));
|
||||
@ -581,7 +573,7 @@ public class DebuggingEnsoTest {
|
||||
bar (baz x) # 4
|
||||
end = 0 # 5
|
||||
""");
|
||||
List<Integer> expectedLineNumbers = List.of(3, 4, 1, 2, 5);
|
||||
List<Integer> expectedLineNumbers = List.of(3, 4, 1, 4, 2, 4, 5);
|
||||
Queue<SuspendedCallback> steps =
|
||||
new ArrayDeque<>(
|
||||
Collections.nCopies(expectedLineNumbers.size(), (event) -> event.prepareStepInto(1)));
|
||||
|
@ -47,6 +47,7 @@ public class MetaIsATest extends TestBase {
|
||||
public static void disposeCtx() {
|
||||
if (generator != null) {
|
||||
generator.dispose();
|
||||
generator = null;
|
||||
}
|
||||
ctx.close();
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ public class MetaObjectTest extends TestBase {
|
||||
public static void disposeCtx() {
|
||||
if (generator != null) {
|
||||
generator.dispose();
|
||||
generator = null;
|
||||
}
|
||||
ctx.close();
|
||||
}
|
||||
|
@ -0,0 +1,62 @@
|
||||
package org.enso.interpreter.test;
|
||||
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.nodes.RootNode;
|
||||
import java.util.UUID;
|
||||
import org.enso.interpreter.node.ExpressionNode;
|
||||
import org.enso.interpreter.node.MethodRootNode;
|
||||
import org.enso.interpreter.node.callable.FunctionCallInstrumentationNode;
|
||||
import org.enso.interpreter.node.callable.FunctionCallInstrumentationNode.FunctionCall;
|
||||
import org.enso.interpreter.runtime.control.TailCallException;
|
||||
import org.enso.interpreter.test.instruments.service.FunctionCallInfo;
|
||||
import org.enso.interpreter.test.instruments.service.RuntimeTestService;
|
||||
import org.openide.util.lookup.ServiceProvider;
|
||||
|
||||
@ServiceProvider(service = RuntimeTestService.class)
|
||||
public class RuntimeTestServiceImpl implements RuntimeTestService {
|
||||
@Override
|
||||
public UUID getNodeID(Node node) {
|
||||
if (node instanceof ExpressionNode exprNode) {
|
||||
return exprNode.getId();
|
||||
} else if (node instanceof FunctionCallInstrumentationNode funcNode) {
|
||||
return funcNode.getId();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExpressionNode(Object node) {
|
||||
return node instanceof ExpressionNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTailCallException(Object obj) {
|
||||
return obj instanceof TailCallException;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFunctionCallInstrumentationNode(Object node) {
|
||||
return node instanceof FunctionCallInstrumentationNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFunctionCall(Object obj) {
|
||||
return obj instanceof FunctionCall;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionCallInfo extractFunctionCallInfo(Object functionCallObj) {
|
||||
if (functionCallObj instanceof FunctionCall functionCall) {
|
||||
RootNode rootNode = functionCall.getFunction().getCallTarget().getRootNode();
|
||||
if (rootNode instanceof MethodRootNode methodNode) {
|
||||
String moduleName = methodNode.getModuleScope().getModule().getName().toString();
|
||||
String typeName = methodNode.getType().getQualifiedName().toString();
|
||||
String functionName = methodNode.getMethodName();
|
||||
return new FunctionCallInfo(moduleName, typeName, functionName);
|
||||
} else {
|
||||
return new FunctionCallInfo(null, null, rootNode.getName());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -16,7 +16,7 @@ import java.util.logging.Level;
|
||||
import org.enso.interpreter.node.ClosureRootNode;
|
||||
import org.enso.interpreter.runtime.tag.AvoidIdInstrumentationTag;
|
||||
import org.enso.interpreter.runtime.tag.IdentifiedTag;
|
||||
import org.enso.interpreter.test.NodeCountingTestInstrument;
|
||||
import org.enso.interpreter.test.instruments.NodeCountingTestInstrument;
|
||||
import org.enso.polyglot.RuntimeOptions;
|
||||
import org.graalvm.polyglot.Context;
|
||||
import org.graalvm.polyglot.Language;
|
@ -14,8 +14,8 @@ import org.enso.compiler.core.ir.Literal;
|
||||
import org.enso.interpreter.node.expression.literal.LiteralNode;
|
||||
import org.enso.interpreter.runtime.type.ConstantsGen;
|
||||
import org.enso.interpreter.test.Metadata;
|
||||
import org.enso.interpreter.test.NodeCountingTestInstrument;
|
||||
import org.enso.interpreter.test.instrument.RuntimeServerTest.TestContext;
|
||||
import org.enso.interpreter.test.instruments.NodeCountingTestInstrument;
|
||||
import org.enso.polyglot.runtime.Runtime$Api$CreateContextRequest;
|
||||
import org.enso.polyglot.runtime.Runtime$Api$CreateContextResponse;
|
||||
import org.enso.polyglot.runtime.Runtime$Api$EditFileNotification;
|
||||
@ -35,6 +35,7 @@ import org.enso.polyglot.runtime.Runtime$Api$StackItem$ExplicitCall;
|
||||
import org.enso.polyglot.runtime.Runtime$Api$StackItem$LocalCall;
|
||||
import org.enso.text.editing.model;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import scala.Option;
|
||||
@ -77,7 +78,7 @@ public class IncrementalUpdatesTest {
|
||||
var m = context.languageContext().findModule(MODULE_NAME).orElse(null);
|
||||
assertNotNull("Module found", m);
|
||||
var numbers = m.getIr().preorder().filter((v1) -> v1 instanceof Literal.Number);
|
||||
assertEquals("One number found: " + numbers, 1, numbers.size());
|
||||
Assert.assertEquals("One number found: " + numbers, 1, numbers.size());
|
||||
if (numbers.head() instanceof Literal.Number n) {
|
||||
assertEquals("updated to 5", "5", n.value());
|
||||
}
|
||||
@ -100,10 +101,10 @@ public class IncrementalUpdatesTest {
|
||||
sendUpdatesWhenFunctionBodyIsChangedBySettingValue(
|
||||
"4", ConstantsGen.INTEGER, "4", "1000", "1000", LiteralNode.class);
|
||||
sendExpressionValue("1000", "333");
|
||||
assertEquals(List.newBuilder().addOne("333"), context.consumeOut());
|
||||
Assert.assertEquals(List.newBuilder().addOne("333"), context.consumeOut());
|
||||
nodeCountingInstrument.assertNewNodes("No execution on 333, no nodes yet", 0, 0);
|
||||
sendExpressionValue("333", "22");
|
||||
assertEquals(List.newBuilder().addOne("22"), context.consumeOut());
|
||||
Assert.assertEquals(List.newBuilder().addOne("22"), context.consumeOut());
|
||||
nodeCountingInstrument.assertNewNodes("No execution on 22, no nodes yet", 0, 0);
|
||||
}
|
||||
|
||||
@ -127,7 +128,7 @@ public class IncrementalUpdatesTest {
|
||||
assertTrue(
|
||||
"Execution succeeds: " + result,
|
||||
result.head().payload() instanceof Runtime$Api$ExecutionComplete);
|
||||
assertEquals(
|
||||
Assert.assertEquals(
|
||||
"Error is printed as a result",
|
||||
List.newBuilder().addOne("(Error: Uninitialized value)"),
|
||||
context.consumeOut());
|
||||
@ -273,7 +274,7 @@ public class IncrementalUpdatesTest {
|
||||
Vector$.MODULE$.empty())),
|
||||
TestMessages.update(contextId, mainRes, ConstantsGen.NOTHING),
|
||||
context.executionComplete(contextId));
|
||||
assertEquals(List.newBuilder().addOne(originalOutput), context.consumeOut());
|
||||
Assert.assertEquals(List.newBuilder().addOne(originalOutput), context.consumeOut());
|
||||
|
||||
var allNodesAfterException =
|
||||
nodeCountingInstrument.assertNewNodes("Execution creates some nodes", 20, 35);
|
||||
@ -290,7 +291,7 @@ public class IncrementalUpdatesTest {
|
||||
TestMessages.update(contextId, fooX, exprType),
|
||||
TestMessages.update(contextId, fooRes, exprType),
|
||||
context.executionComplete(contextId));
|
||||
assertEquals(List.newBuilder().addOne(originalOutput), context.consumeOut());
|
||||
Assert.assertEquals(List.newBuilder().addOne(originalOutput), context.consumeOut());
|
||||
|
||||
nodeCountingInstrument.assertNewNodes("No new nodes created", 0, 0);
|
||||
var literalNode = findLiteralNode(truffleNodeType, allNodesAfterException);
|
||||
@ -302,7 +303,7 @@ public class IncrementalUpdatesTest {
|
||||
var executionCompleteEvents = sendEdit.apply(originalText, newText);
|
||||
if (executionOutput != null) {
|
||||
assertSameElements(executionCompleteEvents, context.executionComplete(contextId));
|
||||
assertEquals(List.newBuilder().addOne(executionOutput), context.consumeOut());
|
||||
Assert.assertEquals(List.newBuilder().addOne(executionOutput), context.consumeOut());
|
||||
nodeCountingInstrument.assertNewNodes("No new nodes created", 0, 0);
|
||||
|
||||
assertEquals(
|
@ -1,6 +1,7 @@
|
||||
package org.enso.interpreter.test.instrument;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import com.oracle.truffle.api.instrumentation.SourceSectionFilter;
|
||||
import com.oracle.truffle.api.instrumentation.StandardTags;
|
||||
@ -10,7 +11,7 @@ import java.util.logging.Level;
|
||||
import org.enso.interpreter.runtime.tag.AvoidIdInstrumentationTag;
|
||||
import org.enso.interpreter.runtime.tag.IdentifiedTag;
|
||||
import org.enso.interpreter.test.Metadata;
|
||||
import org.enso.interpreter.test.NodeCountingTestInstrument;
|
||||
import org.enso.interpreter.test.instruments.NodeCountingTestInstrument;
|
||||
import org.enso.polyglot.RuntimeOptions;
|
||||
import org.graalvm.polyglot.Context;
|
||||
import org.graalvm.polyglot.Language;
|
||||
@ -92,9 +93,9 @@ public class WarningInstrumentationTest {
|
||||
var calls = instrument.registeredCalls();
|
||||
|
||||
assertEquals(calls.keySet().size(), 3);
|
||||
assertEquals(calls.get(idOp1).getFunctionName(), "new");
|
||||
assertEquals(calls.get(idOp2).getFunctionName(), "attach");
|
||||
assertEquals(calls.get(idOp3).getTypeName().item(), "Table");
|
||||
assertEquals(calls.get(idOp3).getFunctionName(), "get");
|
||||
assertEquals(calls.get(idOp1).functionName(), "new");
|
||||
assertEquals(calls.get(idOp2).functionName(), "attach");
|
||||
assertTrue(calls.get(idOp3).typeName().contains("Table"));
|
||||
assertEquals(calls.get(idOp3).functionName(), "get");
|
||||
}
|
||||
}
|
@ -1,8 +1,12 @@
|
||||
package org.enso.interpreter.test
|
||||
|
||||
import com.oracle.truffle.api.instrumentation.EventBinding
|
||||
import org.enso.interpreter.test.CodeIdsTestInstrument.IdEventListener
|
||||
import org.enso.interpreter.test.CodeLocationsTestInstrument.LocationsEventListener
|
||||
import org.enso.interpreter.test.instruments.CodeIdsTestInstrument.IdEventListener
|
||||
import org.enso.interpreter.test.instruments.{
|
||||
CodeIdsTestInstrument,
|
||||
CodeLocationsTestInstrument
|
||||
}
|
||||
import org.enso.interpreter.test.instruments.CodeLocationsTestInstrument.LocationsEventListener
|
||||
import org.enso.polyglot.debugger.{
|
||||
DebugServerInfo,
|
||||
DebuggerSessionManagerEndpoint,
|
||||
|
@ -2,9 +2,9 @@ package org.enso.interpreter.test.instrument
|
||||
|
||||
import org.enso.polyglot.{LanguageInfo, RuntimeOptions}
|
||||
import org.graalvm.polyglot.{Context, PolyglotException}
|
||||
import org.scalatest.{BeforeAndAfterEach, Suite}
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import org.scalatest.wordspec.AnyWordSpec
|
||||
import org.scalatest.{BeforeAndAfterEach, Suite}
|
||||
|
||||
import java.nio.file.Paths
|
||||
import java.util.logging.Level
|
||||
@ -44,7 +44,6 @@ class RuntimeProjectContextTest
|
||||
.toFile
|
||||
.getAbsolutePath
|
||||
)
|
||||
.option("engine.WarnInterpreterOnly", "false")
|
||||
.option(RuntimeOptions.EDITION_OVERRIDE, "0.0.0-dev")
|
||||
.option(RuntimeOptions.LOG_LEVEL, Level.WARNING.getName)
|
||||
.logHandler(System.err)
|
@ -5,9 +5,9 @@ import org.enso.interpreter.runtime.`type`.ConstantsGen
|
||||
import org.enso.interpreter.test.Metadata
|
||||
import org.enso.polyglot._
|
||||
import org.enso.polyglot.runtime.Runtime.Api
|
||||
import org.enso.text.{ContentBasedVersioning, Sha3_224VersionCalculator}
|
||||
import org.enso.text.editing.model
|
||||
import org.enso.text.editing.model.TextEdit
|
||||
import org.enso.text.{ContentBasedVersioning, Sha3_224VersionCalculator}
|
||||
import org.graalvm.polyglot.Context
|
||||
import org.scalatest.BeforeAndAfterEach
|
||||
import org.scalatest.flatspec.AnyFlatSpec
|
@ -1,7 +1,8 @@
|
||||
package org.enso.interpreter.test.instrument
|
||||
|
||||
import org.enso.interpreter.runtime.`type`.{Constants, ConstantsGen, Types}
|
||||
import org.apache.commons.io.output.TeeOutputStream
|
||||
import org.enso.interpreter.runtime.EnsoContext
|
||||
import org.enso.interpreter.runtime.`type`.{Constants, ConstantsGen, Types}
|
||||
import org.enso.interpreter.test.Metadata
|
||||
import org.enso.polyglot._
|
||||
import org.enso.polyglot.data.TypeGraph
|
||||
@ -16,7 +17,6 @@ import org.scalatest.matchers.should.Matchers
|
||||
import java.io.{ByteArrayOutputStream, File}
|
||||
import java.nio.file.{Files, Paths}
|
||||
import java.util.UUID
|
||||
import org.apache.commons.io.output.TeeOutputStream
|
||||
|
||||
@scala.annotation.nowarn("msg=multiarg infix syntax")
|
||||
class RuntimeServerTest
|
@ -83,7 +83,11 @@ object GraalVM {
|
||||
"org.graalvm.tools" % "dap-tool" % version
|
||||
)
|
||||
|
||||
val toolsPkgs = chromeInspectorPkgs ++ debugAdapterProtocolPkgs
|
||||
val insightPkgs = Seq(
|
||||
"org.graalvm.tools" % "insight-tool" % version
|
||||
)
|
||||
|
||||
val toolsPkgs = chromeInspectorPkgs ++ debugAdapterProtocolPkgs ++ insightPkgs
|
||||
|
||||
val langsPkgs = jsPkgs ++ pythonPkgs
|
||||
|
||||
|
182
project/JPMSPlugin.scala
Normal file
182
project/JPMSPlugin.scala
Normal file
@ -0,0 +1,182 @@
|
||||
import sbt.*
|
||||
import sbt.Keys.*
|
||||
import sbt.internal.inc.{CompileOutput, PlainVirtualFile}
|
||||
import sbt.util.CacheStore
|
||||
import sbtassembly.Assembly.{Dependency, JarEntry, Project}
|
||||
import sbtassembly.{CustomMergeStrategy, MergeStrategy}
|
||||
import xsbti.compile.IncToolOptionsUtil
|
||||
|
||||
import java.io.File
|
||||
|
||||
/** An automatic plugin that handles everything related to JPMS modules. One needs to explicitly
|
||||
* enable this plugin in a project with `.enablePlugins(JPMSPlugin)`. The keys and tasks provided by this plugin
|
||||
* corresponds to the module-related options of `javac` and `java` commands.
|
||||
*
|
||||
* This plugin injects all the module-specific options to `javaOptions`, based on
|
||||
* the settings of this plugin.
|
||||
*
|
||||
* If this plugin is enabled, and no settings/tasks from this plugin are used, then the plugin will
|
||||
* not inject anything into `javaOptions` or `javacOptions`.
|
||||
*/
|
||||
object JPMSPlugin extends AutoPlugin {
|
||||
object autoImport {
|
||||
val javaModuleName =
|
||||
settingKey[String]("The name of the Java (JPMS) module")
|
||||
val addModules = settingKey[Seq[String]](
|
||||
"Module names that will be added to --add-modules option"
|
||||
)
|
||||
val modulePath = taskKey[Seq[File]](
|
||||
"Directories (Jar archives or expanded Jar archives) that will be put into " +
|
||||
"--module-path option"
|
||||
)
|
||||
val patchModules = taskKey[Map[String, Seq[File]]](
|
||||
"""
|
||||
|A map of module names to directories (Jar archives or expanded Jar archives) that will be
|
||||
|put into --patch-module option.
|
||||
|""".stripMargin
|
||||
)
|
||||
val addExports = taskKey[Map[String, Seq[String]]](
|
||||
"""
|
||||
|A map of module names to packages that will be put into --add-exports option.
|
||||
|The format of `--add-exports` option is `module/package=target-module(,target-module)*`
|
||||
|The key in the map is `module/package` and the value is a sequence of target modules
|
||||
|""".stripMargin
|
||||
)
|
||||
val addReads = taskKey[Map[String, Seq[String]]](
|
||||
"""
|
||||
|A map of module names to modules that will be put into --add-reads option.
|
||||
|When a module A reads a module B, it means that it "depends" on it - it has the same
|
||||
|effect as if module A would have `requires B` in its module-info.java file.
|
||||
|""".stripMargin
|
||||
)
|
||||
val compileModuleInfo = taskKey[Unit]("Compile module-info.java")
|
||||
val modulePathTestOptions_ = taskKey[Seq[String]](
|
||||
"Assembles options for the JVM for running tests with all the required modules. " +
|
||||
"Including truffle-compiler and org.enso.runtime modules and all their dependencies."
|
||||
)
|
||||
}
|
||||
|
||||
import autoImport.*
|
||||
|
||||
override lazy val projectSettings: Seq[Setting[_]] = Seq(
|
||||
addModules := Seq.empty,
|
||||
modulePath := Seq.empty,
|
||||
patchModules := Map.empty,
|
||||
addExports := Map.empty,
|
||||
addReads := Map.empty,
|
||||
compileModuleInfo := {},
|
||||
// javacOptions only inject --module-path and --add-modules, not the rest of the
|
||||
// options.
|
||||
Compile / javacOptions ++= {
|
||||
constructOptions(
|
||||
streams.value.log,
|
||||
modulePath = (Compile / modulePath).value,
|
||||
addModules = (Compile / addModules).value
|
||||
)
|
||||
},
|
||||
Compile / javaOptions ++= {
|
||||
constructOptions(
|
||||
streams.value.log,
|
||||
(Compile / modulePath).value,
|
||||
(Compile / addModules).value,
|
||||
(Compile / patchModules).value,
|
||||
(Compile / addExports).value,
|
||||
(Compile / addReads).value
|
||||
)
|
||||
},
|
||||
Test / javacOptions ++= {
|
||||
constructOptions(
|
||||
streams.value.log,
|
||||
modulePath = (Test / modulePath).value,
|
||||
addModules = (Test / addModules).value
|
||||
)
|
||||
},
|
||||
Test / javaOptions ++= {
|
||||
constructOptions(
|
||||
streams.value.log,
|
||||
(Test / modulePath).value,
|
||||
(Test / addModules).value,
|
||||
(Test / patchModules).value,
|
||||
(Test / addExports).value,
|
||||
(Test / addReads).value
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
def constructOptions(
|
||||
log: Logger,
|
||||
modulePath: Seq[File],
|
||||
addModules: Seq[String] = Seq.empty,
|
||||
patchModules: Map[String, Seq[File]] = Map.empty,
|
||||
addExports: Map[String, Seq[String]] = Map.empty,
|
||||
addReads: Map[String, Seq[String]] = Map.empty
|
||||
): Seq[String] = {
|
||||
val patchOpts: Seq[String] = patchModules.flatMap {
|
||||
case (moduleName, dirsToPatch) =>
|
||||
ensureDirectoriesExist(dirsToPatch, log)
|
||||
val patchStr = dirsToPatch
|
||||
.map(_.getAbsolutePath)
|
||||
.mkString(File.pathSeparator)
|
||||
Seq(
|
||||
"--patch-module",
|
||||
s"$moduleName=$patchStr"
|
||||
)
|
||||
}.toSeq
|
||||
|
||||
ensureDirectoriesExist(modulePath, log)
|
||||
|
||||
val addExportsOpts: Seq[String] = addExports.flatMap {
|
||||
case (modPkgName, targetModules) =>
|
||||
if (!modPkgName.contains("/")) {
|
||||
log.error(s"JPMSPlugin: Invalid module/package name: $modPkgName")
|
||||
}
|
||||
Seq(
|
||||
"--add-exports",
|
||||
modPkgName + "=" + targetModules.mkString(",")
|
||||
)
|
||||
}.toSeq
|
||||
|
||||
val modulePathOpts = if (modulePath.isEmpty) {
|
||||
Seq.empty
|
||||
} else {
|
||||
Seq(
|
||||
"--module-path",
|
||||
modulePath.map(_.getAbsolutePath).mkString(File.pathSeparator)
|
||||
)
|
||||
}
|
||||
|
||||
val addModsOpts = if (addModules.isEmpty) {
|
||||
Seq.empty
|
||||
} else {
|
||||
Seq(
|
||||
"--add-modules",
|
||||
addModules.mkString(",")
|
||||
)
|
||||
}
|
||||
|
||||
val addReadsOpts = addReads.flatMap { case (modName, targetModules) =>
|
||||
Seq(
|
||||
"--add-reads",
|
||||
modName + "=" + targetModules.mkString(",")
|
||||
)
|
||||
}.toSeq
|
||||
|
||||
modulePathOpts ++ addModsOpts ++ patchOpts ++ addExportsOpts ++ addReadsOpts
|
||||
}
|
||||
|
||||
/** Java does not mandate that the directories specified in the module path or
|
||||
* in --patch-module exist, but it is usefull to report at least warnings.
|
||||
* @param dirs
|
||||
* @param log
|
||||
*/
|
||||
private def ensureDirectoriesExist(
|
||||
dirs: Seq[File],
|
||||
log: Logger
|
||||
): Unit = {
|
||||
dirs.foreach { dir =>
|
||||
if (!dir.exists()) {
|
||||
log.warn(s"JPMSPlugin: Directory $dir does not exist.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,21 +1,22 @@
|
||||
import JPMSPlugin.autoImport.javaModuleName
|
||||
import sbt.*
|
||||
import sbt.Keys.*
|
||||
import sbtassembly.Assembly.{Dependency, JarEntry, Library, Project}
|
||||
import sbtassembly.MergeStrategy
|
||||
|
||||
import java.io.{File, FilenameFilter}
|
||||
import sbtassembly.CustomMergeStrategy
|
||||
import xsbti.compile.IncToolOptionsUtil
|
||||
import sbt.internal.inc.{CompileOutput, PlainVirtualFile}
|
||||
import sbt.util.CacheStore
|
||||
import sbtassembly.Assembly.{Dependency, JarEntry, Project}
|
||||
import sbtassembly.{CustomMergeStrategy, MergeStrategy}
|
||||
import xsbti.compile.IncToolOptionsUtil
|
||||
|
||||
import java.io.File
|
||||
import java.nio.file.attribute.BasicFileAttributes
|
||||
import java.nio.file.{
|
||||
FileVisitOption,
|
||||
FileVisitResult,
|
||||
FileVisitor,
|
||||
Files,
|
||||
Path,
|
||||
SimpleFileVisitor
|
||||
}
|
||||
import scala.collection.mutable
|
||||
|
||||
/** Collection of utility methods dealing with JPMS modules.
|
||||
* The motivation comes from the update of GraalVM to
|
||||
@ -40,6 +41,7 @@ object JPMSUtils {
|
||||
)
|
||||
|
||||
/** Filters modules by their IDs from the given classpath.
|
||||
*
|
||||
* @param cp The classpath to filter
|
||||
* @param modules These modules are looked for in the class path, can be duplicated.
|
||||
* @param shouldContainAll If true, the method will throw an exception if not all modules were found
|
||||
@ -76,6 +78,43 @@ object JPMSUtils {
|
||||
ret
|
||||
}
|
||||
|
||||
/** Filters all the requested modules from the given [[UpdateReport]].
|
||||
*
|
||||
* @param updateReport The update report to filter. This is the result of `update.value`.
|
||||
* @param modules The modules to filter from the update report. Can be duplicated.
|
||||
* @param log The logger to use for logging.
|
||||
* @param shouldContainAll If true, the method will log an error if not all modules were found.
|
||||
* @return The list of files (Jar archives, directories, etc.) that were found in the update report.
|
||||
*/
|
||||
def filterModulesFromUpdate(
|
||||
updateReport: UpdateReport,
|
||||
modules: Seq[ModuleID],
|
||||
log: sbt.util.Logger,
|
||||
shouldContainAll: Boolean = false
|
||||
): Seq[File] = {
|
||||
val distinctModules = modules.distinct
|
||||
|
||||
def shouldFilterModule(module: ModuleID): Boolean = {
|
||||
distinctModules.exists(m =>
|
||||
m.organization == module.organization &&
|
||||
m.name == module.name &&
|
||||
m.revision == module.revision
|
||||
)
|
||||
}
|
||||
|
||||
val foundFiles = updateReport.select(
|
||||
module = shouldFilterModule
|
||||
)
|
||||
if (shouldContainAll) {
|
||||
if (foundFiles.size < distinctModules.size) {
|
||||
log.error("Not all modules from update were found")
|
||||
log.error(s"Returned (${foundFiles.size}): $foundFiles")
|
||||
log.error(s"Expected: (${distinctModules.size}): $distinctModules")
|
||||
}
|
||||
}
|
||||
foundFiles
|
||||
}
|
||||
|
||||
def filterTruffleAndGraalArtifacts(
|
||||
classPath: Def.Classpath
|
||||
): Def.Classpath = {
|
||||
@ -130,17 +169,23 @@ object JPMSUtils {
|
||||
* Note that sbt is not able to correctly handle `module-info.java` files when
|
||||
* compilation order is defined to mixed order.
|
||||
*
|
||||
* Compilation of `module-info.java` is skipped iff none of all the classes from all the dependencies
|
||||
* changed and if the `module-info.java` itself have not changed.
|
||||
*
|
||||
* @param copyDepsFilter The filter of scopes of the projects from which the class files are first
|
||||
* copied into the `target` directory before `module-info.java` is compiled.
|
||||
* @param modulePath IDs of dependencies that should be put on the module path. The modules
|
||||
* put into `modulePath` are filtered away from class-path, so that module-path
|
||||
* and class-path passed to the `javac` are exclusive.
|
||||
* @param modulePathExtra More directories that should be added on `--module-path`. This parameter is of
|
||||
* type [[File]], because this is how inter project dependencies are gathered.
|
||||
*
|
||||
* @see https://users.scala-lang.org/t/scala-jdk-11-and-jpms/6102/19
|
||||
*/
|
||||
def compileModuleInfo(
|
||||
copyDepsFilter: ScopeFilter,
|
||||
modulePath: Seq[ModuleID] = Seq()
|
||||
modulePath: Seq[ModuleID] = Seq(),
|
||||
modulePathExtra: Seq[File] = Seq()
|
||||
): Def.Initialize[Task[Unit]] =
|
||||
Def
|
||||
.task {
|
||||
@ -153,47 +198,47 @@ object JPMSUtils {
|
||||
// copied to.
|
||||
val outputPath: Path = output.getSingleOutputAsPath
|
||||
.get()
|
||||
// Class directories of all the dependencies.
|
||||
val sourceProducts =
|
||||
productDirectories.all(copyDepsFilter).value.flatten
|
||||
|
||||
/** Copy classes into the target directory from all the dependencies */
|
||||
log.debug(s"Copying classes to $output")
|
||||
val sourceProducts = products.all(copyDepsFilter).value.flatten
|
||||
|
||||
if (!(outputPath.toFile.exists())) {
|
||||
Files.createDirectory(outputPath)
|
||||
}
|
||||
|
||||
val outputLangProvider =
|
||||
outputPath / "META-INF" / "services" / "com.oracle.truffle.api.provider.TruffleLanguageProvider"
|
||||
sourceProducts.foreach { sourceProduct =>
|
||||
log.debug(s"Copying ${sourceProduct} to ${output}")
|
||||
val sourceLangProvider =
|
||||
sourceProduct / "META-INF" / "services" / "com.oracle.truffle.api.provider.TruffleLanguageProvider"
|
||||
if (
|
||||
outputLangProvider.toFile.exists() && sourceLangProvider.exists()
|
||||
) {
|
||||
log.debug(
|
||||
s"Merging ${sourceLangProvider} into ${outputLangProvider}"
|
||||
val moduleName = javaModuleName.value
|
||||
val cacheStore = streams.value.cacheStoreFactory
|
||||
val repoRootDir = (LocalProject("enso") / baseDirectory).value
|
||||
var someDepChanged = false
|
||||
sourceProducts.foreach(sourceProduct => {
|
||||
if (!sourceProduct.exists()) {
|
||||
log.error(s"Source product ${sourceProduct} does not exist")
|
||||
log.error(
|
||||
"This means that the Compile/compile task was probably not run in " +
|
||||
"the corresponding project"
|
||||
)
|
||||
val sourceLines = IO.readLines(sourceLangProvider)
|
||||
val destLines = IO.readLines(outputLangProvider.toFile)
|
||||
val outLines = (sourceLines ++ destLines).distinct
|
||||
IO.writeLines(outputLangProvider.toFile, outLines)
|
||||
log.error("Run Compile/compile before this task")
|
||||
}
|
||||
// Copy the rest of the directory - don't override META-INF.
|
||||
IO.copyDirectory(
|
||||
sourceProduct,
|
||||
outputPath.toFile,
|
||||
CopyOptions(
|
||||
overwrite = false,
|
||||
preserveLastModified = true,
|
||||
preserveExecutable = true
|
||||
)
|
||||
)
|
||||
val relPath = repoRootDir.toPath.relativize(sourceProduct.toPath)
|
||||
val cache = cacheStore.make("cache-" + relPath.toString)
|
||||
val depChanged = copyClasses(sourceProduct, output, cache, log)
|
||||
if (depChanged) {
|
||||
someDepChanged = true
|
||||
}
|
||||
})
|
||||
|
||||
log.info("Compiling module-info.java with javac")
|
||||
val baseJavacOpts = (Compile / javacOptions).value
|
||||
val fullCp = (Compile / fullClasspath).value
|
||||
val javaCompiler =
|
||||
(Compile / compile / compilers).value.javaTools.javac()
|
||||
|
||||
// Skip module-info.java compilation if the source have not changed
|
||||
// Force the compilation if some class file from one of the dependencies changed,
|
||||
// just to be sure that we don't cause any weird compilation errors.
|
||||
val moduleInfoCache = cacheStore.make("cache-module-info-" + moduleName)
|
||||
Tracked.diffInputs(moduleInfoCache, FileInfo.lastModified)(
|
||||
Set(moduleInfo)
|
||||
) { changeReport =>
|
||||
if (
|
||||
someDepChanged || changeReport.modified.nonEmpty || changeReport.added.nonEmpty
|
||||
) {
|
||||
log.info(s"Compiling $moduleInfo with javac")
|
||||
val (mp, cp) = fullCp.partition(file => {
|
||||
val moduleID =
|
||||
file.metadata.get(AttributeKey[ModuleID]("moduleID")).get
|
||||
@ -203,17 +248,18 @@ object JPMSUtils {
|
||||
mod.revision == moduleID.revision
|
||||
})
|
||||
})
|
||||
val allDirsOnMp = mp.map(_.data) ++ modulePathExtra
|
||||
|
||||
val allOpts = baseJavacOpts ++ Seq(
|
||||
"--class-path",
|
||||
cp.map(_.data.getAbsolutePath).mkString(File.pathSeparator),
|
||||
"--module-path",
|
||||
mp.map(_.data.getAbsolutePath).mkString(File.pathSeparator),
|
||||
allDirsOnMp.map(_.getAbsolutePath).mkString(File.pathSeparator),
|
||||
"-d",
|
||||
outputPath.toAbsolutePath().toString()
|
||||
outputPath.toAbsolutePath.toString
|
||||
)
|
||||
val javaCompiler =
|
||||
(Compile / compile / compilers).value.javaTools.javac()
|
||||
log.debug(s"javac options: $allOpts")
|
||||
|
||||
val succ = javaCompiler.run(
|
||||
Array(PlainVirtualFile(moduleInfo.toPath)),
|
||||
allOpts.toArray,
|
||||
@ -223,9 +269,82 @@ object JPMSUtils {
|
||||
log
|
||||
)
|
||||
if (!succ) {
|
||||
sys.error(s"Compilation of ${moduleInfo} failed")
|
||||
log.error(s"Compilation of ${moduleInfo} failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.dependsOn(Compile / compile)
|
||||
|
||||
/** Copies all classes from all the dependencies `classes` directories into the target directory.
|
||||
* @param sourceClassesDir Directory from where the classes will be copied.
|
||||
* @param output Target directory where all the classes from all the dependencies
|
||||
* will be copied to.
|
||||
* @param log
|
||||
* @return True iff some of the dependencies changed, i.e., if there is a modified class file, or
|
||||
* some class file was added, or removed
|
||||
*/
|
||||
private def copyClasses(
|
||||
sourceClassesDir: File,
|
||||
output: xsbti.compile.Output,
|
||||
cache: CacheStore,
|
||||
log: Logger
|
||||
): Boolean = {
|
||||
require(sourceClassesDir.isDirectory)
|
||||
val outputPath: Path = output.getSingleOutputAsPath.get()
|
||||
val outputDir = outputPath.toFile
|
||||
val filesToCopy = mutable.HashSet.empty[File]
|
||||
val fileVisitor = new SimpleFileVisitor[Path] {
|
||||
override def visitFile(
|
||||
path: Path,
|
||||
attrs: BasicFileAttributes
|
||||
): FileVisitResult = {
|
||||
if (!path.toFile.isDirectory) {
|
||||
filesToCopy.add(path.toFile)
|
||||
}
|
||||
FileVisitResult.CONTINUE
|
||||
}
|
||||
|
||||
override def preVisitDirectory(
|
||||
dir: Path,
|
||||
attrs: BasicFileAttributes
|
||||
): FileVisitResult = {
|
||||
// We do not care about files in META-INF directory. Everything should be described
|
||||
// in the `module-info.java`.
|
||||
if (dir.getFileName.toString == "META-INF") {
|
||||
FileVisitResult.SKIP_SUBTREE
|
||||
} else {
|
||||
FileVisitResult.CONTINUE
|
||||
}
|
||||
}
|
||||
}
|
||||
Files.walkFileTree(sourceClassesDir.toPath, fileVisitor)
|
||||
if (!outputDir.exists()) {
|
||||
IO.createDirectory(outputDir)
|
||||
}
|
||||
var someDependencyChanged = false
|
||||
Tracked.diffInputs(cache, FileInfo.lastModified)(filesToCopy.toSet) {
|
||||
changeReport =>
|
||||
for (f <- changeReport.removed) {
|
||||
val relPath = sourceClassesDir.toPath.relativize(f.toPath)
|
||||
val dest = outputDir.toPath.resolve(relPath)
|
||||
IO.delete(dest.toFile)
|
||||
someDependencyChanged = true
|
||||
}
|
||||
for (f <- changeReport.modified -- changeReport.removed) {
|
||||
val relPath = sourceClassesDir.toPath.relativize(f.toPath)
|
||||
val dest = outputDir.toPath.resolve(relPath)
|
||||
IO.copyFile(f, dest.toFile)
|
||||
someDependencyChanged = true
|
||||
}
|
||||
for (f <- changeReport.unmodified) {
|
||||
val relPath = sourceClassesDir.toPath.relativize(f.toPath)
|
||||
val dest = outputDir.toPath.resolve(relPath)
|
||||
if (!dest.toFile.exists()) {
|
||||
IO.copyFile(f, dest.toFile)
|
||||
someDependencyChanged = true
|
||||
}
|
||||
}
|
||||
}
|
||||
someDependencyChanged
|
||||
}
|
||||
}
|
||||
|
@ -61,11 +61,13 @@ object WithDebugCommand {
|
||||
val (debugFlags, prefixedRunArgs) = args.span(_ != argSeparator)
|
||||
val runArgs = " " + prefixedRunArgs.drop(1).mkString(" ")
|
||||
|
||||
val taskKey =
|
||||
if (debugFlags.contains(benchOnlyCommandName)) BenchTasks.benchOnly
|
||||
else if (debugFlags.contains(runCommandName)) Compile / Keys.run
|
||||
else if (debugFlags.contains(testOnlyCommandName)) Test / Keys.testOnly
|
||||
else throw new IllegalArgumentException("Invalid command name.")
|
||||
val taskKeyOpt =
|
||||
if (debugFlags.contains(benchOnlyCommandName))
|
||||
Some(BenchTasks.benchOnly)
|
||||
else if (debugFlags.contains(runCommandName)) Some(Compile / Keys.run)
|
||||
else if (debugFlags.contains(testOnlyCommandName))
|
||||
Some(Test / Keys.testOnly)
|
||||
else None
|
||||
|
||||
val dumpGraphsOpts =
|
||||
if (debugFlags.contains(dumpGraphsOption)) truffleDumpGraphsOptions
|
||||
@ -90,6 +92,13 @@ object WithDebugCommand {
|
||||
debuggerOpts
|
||||
).flatten
|
||||
|
||||
taskKeyOpt match {
|
||||
case None =>
|
||||
state.log.error(
|
||||
s"Invalid command name. Expected one of $benchOnlyCommandName, $runCommandName, or $testOnlyCommandName"
|
||||
)
|
||||
state.fail
|
||||
case Some(taskKey) =>
|
||||
val extracted = Project.extract(state)
|
||||
val withJavaOpts = extracted.appendWithoutSession(
|
||||
Seq(Compile / Keys.javaOptions ++= javaOpts),
|
||||
@ -100,4 +109,5 @@ object WithDebugCommand {
|
||||
.runInputTask(taskKey, runArgs, withJavaOpts)
|
||||
state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
addSbtPlugin("com.sandinh" % "sbt-java-module-info" % "0.4.0")
|
||||
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.1.3")
|
||||
addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.5.6")
|
||||
addSbtPlugin("com.github.sbt" % "sbt-license-report" % "1.5.0")
|
||||
|
Loading…
Reference in New Issue
Block a user