diff --git a/.gitignore b/.gitignore index 6f6542d25cb..2533b869dc2 100644 --- a/.gitignore +++ b/.gitignore @@ -97,6 +97,7 @@ bench-report*.xml /built-distribution/ /enso +/ensoup /project-manager /enso.exp /enso.lib diff --git a/engine/common/src/test/java/org/enso/common/EnsoSourcesTest.java b/engine/common/src/test/java/org/enso/common/EnsoSourcesTest.java new file mode 100644 index 00000000000..9daf9d8fdac --- /dev/null +++ b/engine/common/src/test/java/org/enso/common/EnsoSourcesTest.java @@ -0,0 +1,99 @@ +package org.enso.common; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.Properties; +import org.junit.Test; + +public class EnsoSourcesTest { + @Test + public void verifyEngineCommonSourcesFile() throws Exception { + var url = EnsoSourcesTest.class.getProtectionDomain().getCodeSource().getLocation(); + var where = new File(url.toURI()); + var engineCommonDir = findParentDir(where, ".enso-sources"); + var mainSrc = loadProps(file(engineCommonDir, ".enso-sources-classes")); + var mainInput = mainSrc.get("input"); + assertEquals( + "Points to src/main/java", + file(engineCommonDir, "src", "main", "java").toString(), + mainInput); + var testSrc = loadProps(file(engineCommonDir, ".enso-sources-test-classes")); + var testInput = testSrc.get("input"); + assertEquals( + "Points to src/test/java", + file(engineCommonDir, "src", "test", "java").toString(), + testInput); + } + + @Test + public void verifyScalaOnlyModularProject() throws Exception { + var url = EnsoSourcesTest.class.getProtectionDomain().getCodeSource().getLocation(); + var where = new File(url.toURI()); + var rootDir = findParentDir(where, "lib", "scala", "edition-updater"); + var updaterDir = file(rootDir, "lib", "scala", "edition-updater"); + var mainEnsoSources = file(updaterDir, ".enso-sources-classes"); + assertTrue( + "This file should exists: " + + mainEnsoSources + + " - have you compiled edition-updater project?", + mainEnsoSources.exists()); + var mainSrc = loadProps(mainEnsoSources); + var mainInput = mainSrc.get("input"); + assertEquals( + "Points to src/main/java", file(updaterDir, "src", "main", "java").toString(), mainInput); + var testEnsoSources = file(updaterDir, ".enso-sources-test-classes"); + assertFalse( + "No Java tests in this project. Checking " + testEnsoSources, testEnsoSources.exists()); + } + + @Test + public void verifyWrappersProject() throws Exception { + var url = EnsoSourcesTest.class.getProtectionDomain().getCodeSource().getLocation(); + var where = new File(url.toURI()); + var rootDir = findParentDir(where, "lib", "java", "fansi-wrapper"); + var updaterDir = file(rootDir, "lib", "java", "fansi-wrapper"); + var mainEnsoSources = file(updaterDir, ".enso-sources-classes"); + assertTrue( + "This file should exists: " + + mainEnsoSources + + " - have you compiled fansi-wrapper project?", + mainEnsoSources.exists()); + var mainSrc = loadProps(mainEnsoSources); + var mainInput = mainSrc.get("input"); + assertEquals( + "Points to src/main/java", file(updaterDir, "src", "main", "java").toString(), mainInput); + var testEnsoSources = file(updaterDir, ".enso-sources-test-classes"); + assertFalse( + "No Java tests in this project. Checking " + testEnsoSources, testEnsoSources.exists()); + } + + private Properties loadProps(File src) throws IOException { + var props = new Properties(); + props.load(new FileInputStream(src)); + return props; + } + + private static File findParentDir(File root, String... names) { + for (var d = root; d != null; d = d.getParentFile()) { + var f = file(d, names); + if (f.exists()) { + return d; + } + } + throw new AssertionError( + "Cannot find " + Arrays.toString(names) + " in parent directories of " + root); + } + + private static File file(File root, String... children) { + for (var ch : children) { + root = new File(root, ch); + } + return root; + } +} diff --git a/project/FrgaalJavaCompiler.scala b/project/FrgaalJavaCompiler.scala index e0bbc181d8a..0710b03e9fd 100644 --- a/project/FrgaalJavaCompiler.scala +++ b/project/FrgaalJavaCompiler.scala @@ -137,9 +137,21 @@ object FrgaalJavaCompiler { } ap } + val moduleInfoAndSources = + sources0.partition(_.toString().endsWith("module-info.java")) + val sourcesNoModuleInfo = moduleInfoAndSources._2 + val out = output.getSingleOutputAsPath().get() + val shared = sources0.fold(out)(asCommon).asInstanceOf[Path] - val out = output.getSingleOutputAsPath().get() - val shared = sources0.fold(out)(asCommon).asInstanceOf[Path] + log.debug( + s"[FrgaalJavaCompiler] sources0: ${sources0}" + ) + log.debug( + s"[FrgaalJavaCompiler] sourcesNoModuleInfo: ${sourcesNoModuleInfo}" + ) + log.debug( + s"[FrgaalJavaCompiler] shared: ${shared}" + ) val allSources = if (shouldCompileModuleInfo) { val moduleInfo = javaSourceDir.toPath.resolve("module-info.java").toFile @@ -197,17 +209,28 @@ object FrgaalJavaCompiler { inATargetDir } - val (withTarget, noTarget) = sources0.partition(checkTarget) + val (withTarget, noTarget) = sourcesNoModuleInfo + .partition(checkTarget) + log.debug( + s"[FrgaalJavaCompiler] withTarget: ${withTarget}" + ) + log.debug( + s"[FrgaalJavaCompiler] noTarget: ${noTarget}" + ) val in = if (noTarget.isEmpty) { - None + log.debug(s"No target. Using location of ${moduleInfoAndSources._1}") + moduleInfoAndSources._1.map(asPath(_).getParent()).headOption } else { Some( findUnder( - 1, + 3, noTarget.tail.fold(asPath(noTarget.head))(asCommon).asInstanceOf[Path] ) ) } + log.debug( + s"[FrgaalJavaCompiler] input sources are at: ${in}" + ) val generated = if (withTarget.isEmpty) { None } else { @@ -220,6 +243,9 @@ object FrgaalJavaCompiler { ) ) } + log.debug( + s"[FrgaalJavaCompiler] generated code is at ${generated}" + ) if (shared.toFile().exists()) { val ensoMarker = new File(shared.toFile(), ENSO_SOURCES) @@ -227,6 +253,9 @@ object FrgaalJavaCompiler { shared.toFile(), ENSO_SOURCES + "-" + out.getFileName().toString() ) + log.debug( + s"[FrgaalJavaCompiler] writing compiler configuration into ${ensoConfig}" + ) val ensoProperties = new java.util.Properties() def storeArray(name: String, values: Seq[String]) = { @@ -302,7 +331,9 @@ object FrgaalJavaCompiler { " You should attach the debugger now." ) } - log.debug("[frgaal] Running " + (exe +: forkArgs).mkString(" ")) + log.debug( + "[FrgaalJavaCompiler] Running " + (exe +: forkArgs).mkString(" ") + ) try { exitCode = Process(exe +: forkArgs, cwd) ! javacLogger } finally { diff --git a/tools/enso4igv/IGV.md b/tools/enso4igv/IGV.md index 72833b4e90d..0488cff6c63 100644 --- a/tools/enso4igv/IGV.md +++ b/tools/enso4igv/IGV.md @@ -17,9 +17,9 @@ major operating systems. ## Installation -Visit [GraalVM's IGV page](https://www.graalvm.org/jdk22/tools/igv/) to read and +Visit [GraalVM's IGV page](https://www.graalvm.org/22.1/tools/igv/) to read and download _IGV_. Or follow -[this link](https://lafo.ssw.uni-linz.ac.at/pub/idealgraphvisualizer/idealgraphvisualizer-0.31-cb98bbf5fef-all.zip) +[this link](https://lafo.ssw.uni-linz.ac.at/pub/idealgraphvisualizer/idealgraphvisualizer-1.20-ea624c6066a.zip) to get ZIP with the most up to date version of _Ideal Graph Visualizer_ (as of June 2024). Then: diff --git a/tools/enso4igv/src/main/java/org/enso/tools/enso4igv/EnsoActionProvider.java b/tools/enso4igv/src/main/java/org/enso/tools/enso4igv/EnsoActionProvider.java index 4ba695c5197..a4af1df4018 100644 --- a/tools/enso4igv/src/main/java/org/enso/tools/enso4igv/EnsoActionProvider.java +++ b/tools/enso4igv/src/main/java/org/enso/tools/enso4igv/EnsoActionProvider.java @@ -1,6 +1,7 @@ package org.enso.tools.enso4igv; import com.sun.jdi.connect.Connector; +import java.awt.GraphicsEnvironment; import java.io.File; import java.util.ArrayList; import java.util.HashMap; @@ -10,6 +11,7 @@ import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.Future; +import java.util.prefs.Preferences; import org.netbeans.api.debugger.jpda.JPDADebugger; import org.netbeans.api.debugger.jpda.ListeningDICookie; import org.netbeans.api.extexecution.ExecutionDescriptor; @@ -26,8 +28,11 @@ import org.netbeans.api.extexecution.base.ProcessBuilder; import org.netbeans.api.java.classpath.ClassPath; import org.netbeans.api.java.platform.JavaPlatform; import org.netbeans.spi.project.ActionProgress; +import org.openide.awt.Notification; +import org.openide.awt.NotificationDisplayer; import org.openide.filesystems.FileUtil; import org.openide.modules.Modules; +import org.openide.util.ImageUtilities; import org.openide.util.NbBundle; import org.openide.util.NbPreferences; import org.openide.util.RequestProcessor; @@ -40,7 +45,10 @@ import org.openide.windows.IOProvider; "MSG_CannotExecute=Cannot execute {0}", "# {0} - executable file", "# {1} - exit code", - "MSG_ExecutionError=Process {0} finished with exit code {1}" + "MSG_ExecutionError=Process {0} finished with exit code {1}.", + "MSG_IgvMode=Enso/IGV Integration", + "MSG_EnableIgvMode=Send compiler graphs to IGV next time?", + "MSG_EnableIgvModeDone=Run Enso file again to get compiler graphs." }) @ServiceProvider(service = ActionProvider.class) public final class EnsoActionProvider implements ActionProvider { @@ -86,8 +94,19 @@ public final class EnsoActionProvider implements ActionProvider { b.setRedirectErrorStream(true); var env = b.getEnvironment(); - if (isGraalVM && isIGVConnected()) { - env.setVariable("JAVA_OPTS", "-Dgraal.Dump=Truffle:2"); + var info = IgvInfo.find(); + if (isGraalVM && info.igvMode()) { + if (info.networkOn()) { + env.setVariable("JAVA_OPTS", info.toJavaOptions()); + } else { + var icon = ImageUtilities.loadImageIcon("org/enso/tools/enso4igv/enso.svg", false); + var note = new Notification[1]; + note[0] = NotificationDisplayer.getDefault().notify(Bundle.MSG_IgvMode(), icon, Bundle.MSG_EnableIgvMode(), (e) -> { + note[0].clear(); + info.enableNetworkMode(); + NotificationDisplayer.getDefault().notify(Bundle.MSG_IgvMode(), icon, Bundle.MSG_EnableIgvModeDone(), null); + }); + } } var path = env.getVariable("PATH"); if (path != null && java != null) { @@ -107,11 +126,15 @@ public final class EnsoActionProvider implements ActionProvider { var waitForProcessFuture = builderFuture.thenCompose((builder) -> { var cf = new CompletableFuture(); var descriptor = new ExecutionDescriptor() - .frontWindow(true).controllable(true) - .inputOutput(io) + .frontWindow(true) .postExecution((exitCode) -> { cf.complete(exitCode); }); + if (GraphicsEnvironment.isHeadless()) { + descriptor = descriptor.inputOutput(io); + } else { + descriptor = descriptor.controllable(true); + } var launch = COMMAND_DEBUG_SINGLE.equals(action) || COMMAND_DEBUG.equals(action) ? new DebugAndLaunch(fo, builder, params) : builder; var service = ExecutionService.newService(launch, descriptor, script.getName()); @@ -120,12 +143,20 @@ public final class EnsoActionProvider implements ActionProvider { }); waitForProcessFuture.thenAcceptBoth(builderFuture, (exitCode, builder) -> { - if (exitCode != 0) { - var msg = Bundle.MSG_ExecutionError(builder.getDescription(), exitCode); - var md = new NotifyDescriptor.Message(msg, NotifyDescriptor.ERROR_MESSAGE); - dd.notifyLater(md); + boolean success; + if (exitCode != null) { + if (exitCode != 0) { + var msg = Bundle.MSG_ExecutionError(builder.getDescription(), exitCode); + var md = new NotifyDescriptor.Message(msg, NotifyDescriptor.ERROR_MESSAGE); + dd.notifyLater(md); + success = false; + } else { + success = true; + } + } else { + success = false; } - process.finished(exitCode == 0); + process.finished(success); }).exceptionally((ex) -> { process.finished(false); if (ex instanceof CompletionException && ex.getCause() instanceof CancellationException) { @@ -136,10 +167,38 @@ public final class EnsoActionProvider implements ActionProvider { }); } - private static boolean isIGVConnected() { - return Modules.getDefault().findCodeNameBase("org.graalvm.visualizer.connection") != null; + record IgvInfo(boolean igvMode, boolean networkOn, int networkPort) { + static IgvInfo find() { + if (Modules.getDefault().findCodeNameBase("org.graalvm.visualizer.settings") != null) { + var settings = settingsNode(); + var port = settings.getInt("portBinary", -1); + var network = settings.getBoolean("acceptNetwork", false); + return new IgvInfo(true, network, port); + } else { + return new IgvInfo(false, false, -1); + } + } + + private static Preferences settingsNode() { + return Preferences.userRoot().node("org/graalvm/visualizer/settings/graal"); + } + + private String toJavaOptions() { + return + "-Dgraal.Dump=Truffle:2 " + + "-Djdk.graal.Dump=Truffle:2 " + + "-Dgraal.PrintGraph=Network " + + "-Djdk.graal.PrintGraph=Network " + + "-Dgraal.PrintGraphPort=" + networkPort() + " " + + "-Djdk.graal.PrintGraphPort=" + networkPort(); + } + + private void enableNetworkMode() { + settingsNode().putBoolean("acceptNetwork", true); + } } + private static List prepareArguments(File script) { var list = new ArrayList(); list.add("--run");