diff --git a/.github/scripts/test-lfd.sh b/.github/scripts/test-lfd.sh new file mode 100755 index 0000000000..041daf7029 --- /dev/null +++ b/.github/scripts/test-lfd.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +# Exit 1 if any command returns with a non-zero exit code. +set -euo pipefail + +cd $GITHUB_WORKSPACE + +function test_with_links() { + rm -rf foo + mkdir -p foo/bar/baz + ln -s ../bin/${1} foo/link-foo + ln -s ../link-foo foo/bar/link-bar + ln -s ../link-bar foo/bar/baz/link-${1} + foo/bar/baz/link-${1} --help +} + +bin/lfd test/C/src/Minimal.lf + +# Ensure that lfd is robust to symbolic links. +test_with_links "lfd" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5095a8bef9..7b0dd9ff6e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -31,7 +31,13 @@ jobs: - name: Prepare build environment uses: ./.github/actions/prepare-build-env - name: Build and package lf cli tools (nightly build) - run: ./gradlew build -Pnightly + # We assume, that the nightly build only runs once on Ubuntu + run: | + ./gradlew build -Pnightly -PtargetOS=Linux -PtargetPlatform=x86_64 + ./gradlew assemble -Pnightly -PtargetOS=Linux -PtargetPlatform=aarch64 + ./gradlew assemble -Pnightly -PtargetOS=MacOS -PtargetPlatform=x86_64 + ./gradlew assemble -Pnightly -PtargetOS=MacOS -PtargetPlatform=aarch64 + ./gradlew assemble -Pnightly -PtargetOS=Windows -PtargetPlatform=x86_64 shell: bash if: ${{ inputs.nightly == true }} - name: Build and package lf cli tools (regular build) diff --git a/.github/workflows/cli-tests.yml b/.github/workflows/cli-tests.yml index 9e56c40d28..2b718a774d 100644 --- a/.github/workflows/cli-tests.yml +++ b/.github/workflows/cli-tests.yml @@ -24,16 +24,45 @@ jobs: - name: Test lfc bash scripts (Linux or macOS only) run: | .github/scripts/test-lfc.sh + ./gradlew assemble + ./build/install/lf-cli/bin/lfc --version + ./build/install/lf-cli/bin/lfc test/C/src/Minimal.lf if: ${{ runner.os == 'macOS' || runner.os == 'Linux' }} - name: Test lff bash scripts (Linux or macOS only) run: | .github/scripts/test-lff.sh + ./gradlew assemble + ./build/install/lf-cli/bin/lff --version + ./build/install/lf-cli/bin/lff test/C/src/Minimal.lf + if: ${{ runner.os == 'macOS' || runner.os == 'Linux' }} + - name: Test lfd bash scripts (Linux or macOS only) + run: | + .github/scripts/test-lfd.sh + ./gradlew assemble + ./build/install/lf-cli/bin/lfd --version + ./build/install/lf-cli/bin/lfd test/C/src/Minimal.lf if: ${{ runner.os == 'macOS' || runner.os == 'Linux' }} - name: Test lfc PowerShell script (Windows only) run: | - bin/lfc.ps1 --help + bin/lfc.ps1 --version + bin/lfc.ps1 test/C/src/Minimal.lf + ./gradlew assemble + ./build/install/lf-cli/bin/lfc.bat --version + ./build/install/lf-cli/bin/lfc.bat test/C/src/Minimal.lf if: ${{ runner.os == 'Windows' }} - name: Test lff PowerShell script (Windows only) run: | - bin/lff.ps1 --help + bin/lff.ps1 --version + bin/lff.ps1 test/C/src/Minimal.lf + ./gradlew assemble + ./build/install/lf-cli/bin/lff.bat --version + ./build/install/lf-cli/bin/lff.bat test/C/src/Minimal.lf + if: ${{ runner.os == 'Windows' }} + - name: Test lfd PowerShell script (Windows only) + run: | + bin/lfd.ps1 --version + bin/lfd.ps1 test/C/src/Minimal.lf + ./gradlew assemble + ./build/install/lf-cli/bin/lfd.bat --version + ./build/install/lf-cli/bin/lfd.bat test/C/src/Minimal.lf if: ${{ runner.os == 'Windows' }} diff --git a/bin/lfd b/bin/lfd new file mode 120000 index 0000000000..1a3eb8bc0d --- /dev/null +++ b/bin/lfd @@ -0,0 +1 @@ +../util/scripts/launch.sh \ No newline at end of file diff --git a/bin/lfd.ps1 b/bin/lfd.ps1 new file mode 100644 index 0000000000..54ce9397f4 --- /dev/null +++ b/bin/lfd.ps1 @@ -0,0 +1,9 @@ +#========================================================== +# Description: Run the lff compiler. +# Authors: Ruomu Xu +# Usage: Usage: lff [options] files... +#========================================================== + +$launchScript="$PSScriptRoot\..\util\scripts\launch.ps1" +# PS requires spattling: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_Splatting?view=powershell-7.2 +. $launchScript @args diff --git a/build.gradle b/build.gradle index beb7cad6a5..81b5ae81aa 100644 --- a/build.gradle +++ b/build.gradle @@ -38,11 +38,14 @@ distributions { if (project.hasProperty('nightly')) { def date = new Date() def formattedDate = date.format('yyyyMMddHHmmss') - distributionClassifier = 'nightly-' + formattedDate + distributionClassifier = 'nightly-' + formattedDate + '-' + platform.os + '-' + platform.arch + } else if (!platform.isNative) { + distributionClassifier = platform.os + '-' + platform.arch } contents { from tasks.getByPath('cli:lfc:installDist').outputs from tasks.getByPath('cli:lff:installDist').outputs + from tasks.getByPath('cli:lfd:installDist').outputs duplicatesStrategy = DuplicatesStrategy.EXCLUDE } } diff --git a/buildSrc/src/main/groovy/org.lflang.distribution-conventions.gradle b/buildSrc/src/main/groovy/org.lflang.distribution-conventions.gradle index 4d25bbb461..58e1b07f37 100644 --- a/buildSrc/src/main/groovy/org.lflang.distribution-conventions.gradle +++ b/buildSrc/src/main/groovy/org.lflang.distribution-conventions.gradle @@ -1,8 +1,31 @@ plugins { id 'distribution' + id 'org.lflang.platform' } tasks.withType(Tar) { compression = Compression.GZIP archiveExtension = 'tar.gz' + enabled = !platform.isWindows +} + +tasks.withType(Zip) { + enabled = platform.isWindows +} + +tasks.withType(Jar) { + enabled = true +} + +tasks.withType(CreateStartScripts) { + doLast { + if (platform.isWindows) { + delete unixScript + // Fix long path issue on Windows + // See https://github.com/gradle/gradle/issues/1989 + windowsScript.text = windowsScript.text.replaceAll('set CLASSPATH=.*', 'set CLASSPATH=.;%APP_HOME%/lib/*') + } else { + delete windowsScript + } + } } diff --git a/buildSrc/src/main/groovy/org.lflang.java-application-conventions.gradle b/buildSrc/src/main/groovy/org.lflang.java-application-conventions.gradle index 432ebacf1b..d49afafac0 100644 --- a/buildSrc/src/main/groovy/org.lflang.java-application-conventions.gradle +++ b/buildSrc/src/main/groovy/org.lflang.java-application-conventions.gradle @@ -4,16 +4,3 @@ plugins { id 'org.lflang.test-conventions' id "org.lflang.distribution-conventions" } - -tasks.withType(Tar) { - compression = Compression.GZIP - archiveExtension = 'tar.gz' -} - -// Fix long path issue on Windows -// See https://github.com/gradle/gradle/issues/1989 -startScripts { - doLast { - windowsScript.text = windowsScript.text.replaceAll('set CLASSPATH=.*', 'set CLASSPATH=.;%APP_HOME%/lib/*') - } -} diff --git a/buildSrc/src/main/groovy/org.lflang.java-conventions.gradle b/buildSrc/src/main/groovy/org.lflang.java-conventions.gradle index 4cbea8bf20..5165d03fa6 100644 --- a/buildSrc/src/main/groovy/org.lflang.java-conventions.gradle +++ b/buildSrc/src/main/groovy/org.lflang.java-conventions.gradle @@ -1,6 +1,7 @@ plugins { id 'java' id 'com.diffplug.spotless' + id 'org.lflang.platform' } repositories { @@ -17,3 +18,28 @@ spotless { formatAnnotations() } } + +configurations.all { + resolutionStrategy { + dependencySubstitution { + // The maven property ${osgi.platform} is not handled by Gradle + // so we replace the dependency, using the osgi platform from the project settings + def arch = platform.arch + if (arch != 'x86_64' && arch != 'aarch64') { + throw new GradleException("Your system architecture $arch is not supported") + } + + if (platform.isWindows) { + substitute module('org.eclipse.platform:org.eclipse.swt.${osgi.platform}') using module("org.eclipse.platform:org.eclipse.swt.win32.win32.$arch:$swtVersion") + } + else if (platform.isLinux) { + substitute module('org.eclipse.platform:org.eclipse.swt.${osgi.platform}') using module("org.eclipse.platform:org.eclipse.swt.gtk.linux.$arch:$swtVersion") + } + else if (platform.isMacos) { + substitute module('org.eclipse.platform:org.eclipse.swt.${osgi.platform}') using module("org.eclipse.platform:org.eclipse.swt.cocoa.macosx.$arch:$swtVersion") + } else { + throw new GradleException("Your operating system ${platform.os} is not supported") + } + } + } +} diff --git a/buildSrc/src/main/groovy/org.lflang.platform.gradle b/buildSrc/src/main/groovy/org.lflang.platform.gradle new file mode 100644 index 0000000000..dfe2a0ccf3 --- /dev/null +++ b/buildSrc/src/main/groovy/org.lflang.platform.gradle @@ -0,0 +1,16 @@ +tasks.register('platform') { + def osVar = project.hasProperty('targetOS') ? project.getProperty('targetOS') : System.getProperty('os.name') + def archVar = project.hasProperty('targetArch') ? project.getProperty('targetArch') : System.getProperty('os.arch') + if (archVar == 'amd64') { + archVar = 'x86_64' + } + + ext { + os = osVar + arch = archVar + isWindows = osVar.toLowerCase().contains('windows') + isLinux = osVar.toLowerCase().contains('linux') + isMacos = osVar.toLowerCase().contains('mac') + isNative = !project.hasProperty('targetOS') && !project.hasProperty('targetArch') + } +} diff --git a/cli/lfd/build.gradle b/cli/lfd/build.gradle new file mode 100644 index 0000000000..9a8c52c46c --- /dev/null +++ b/cli/lfd/build.gradle @@ -0,0 +1,25 @@ +plugins { + id 'org.lflang.java-application-conventions' +} + +dependencies { + implementation project(':cli:base') + implementation("de.cau.cs.kieler.klighd:de.cau.cs.kieler.klighd.standalone:$klighdVersion") { + exclude group: 'de.cau.cs.kieler.swt.mock' + } + implementation("de.cau.cs.kieler.klighd:de.cau.cs.kieler.klighd.piccolo:${klighdVersion}") { + exclude group: 'de.cau.cs.kieler.swt.mock' + } + implementation("de.cau.cs.kieler.klighd:de.cau.cs.kieler.klighd.piccolo.freehep:${klighdVersion}") { + exclude group: 'de.cau.cs.kieler.swt.mock' + } + implementation("org.freehep:freehep-graphicsio-svg:${freehepVersion}") + + testImplementation(testFixtures(project(':core'))) + testImplementation(testFixtures(project(':cli:base'))) +} + +application { + mainClass = 'org.lflang.cli.Lfd' + tasks.run.workingDir = System.getProperty("user.dir") +} diff --git a/cli/lfd/src/main/java/org/lflang/cli/Lfd.java b/cli/lfd/src/main/java/org/lflang/cli/Lfd.java new file mode 100644 index 0000000000..fa19751eb1 --- /dev/null +++ b/cli/lfd/src/main/java/org/lflang/cli/Lfd.java @@ -0,0 +1,78 @@ +package org.lflang.cli; + +import de.cau.cs.kieler.klighd.Klighd; +import de.cau.cs.kieler.klighd.LightDiagramServices; +import de.cau.cs.kieler.klighd.standalone.KlighdStandaloneSetup; +import java.nio.file.Path; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.emf.ecore.resource.Resource; +import org.lflang.lf.Model; +import org.lflang.util.FileUtil; +import picocli.CommandLine.Command; + +/** + * Command lin tool for generating diagrams from Lingua Franca programs. + * + * @author Christian Menard + */ +@Command( + name = "lfd", + // Enable usageHelp (--help) and versionHelp (--version) options. + mixinStandardHelpOptions = true, + versionProvider = VersionProvider.class) +public class Lfd extends CliBase { + + @Override + public void doRun() { + KlighdStandaloneSetup.initialize(); + Klighd.setStatusManager( + (status, style) -> { + switch (status.getSeverity()) { + case IStatus.ERROR -> { + reporter.printError(status.getMessage()); + if (status.getException() != null) { + status.getException().printStackTrace(); + } + } + case IStatus.WARNING -> reporter.printWarning(status.getMessage()); + default -> reporter.printInfo(status.getMessage()); + } + }); + + for (Path relativePath : getInputPaths()) { + Path path = toAbsolutePath(relativePath); + final Resource resource = getResource(path); + if (resource == null) { + reporter.printFatalErrorAndExit(path.toString() + " is not an LF program."); + } + final Model model = (Model) resource.getContents().get(0); + String baseName = FileUtil.nameWithoutExtension(relativePath); + Path outFile = io.getWd().resolve(baseName + ".svg").toAbsolutePath(); + IStatus status = LightDiagramServices.renderOffScreen(model, "svg", outFile.toString()); + if (!status.isOK()) { + reporter.printFatalErrorAndExit(status.getMessage()); + } + } + + reporter.exit(); + } + + /** + * Main entry point of the diagram tool. + * + * @param args CLI arguments + */ + public static void main(String[] args) { + main(Io.SYSTEM, args); + } + + /** + * Programmatic entry point, with a custom IO. + * + * @param io IO streams. + * @param args Command-line arguments. + */ + public static void main(Io io, final String... args) { + cliMain("lfd", Lfd.class, io, args); + } +} diff --git a/cli/lfd/src/test/java/org/lflang/cli/DiagramGenerationTest.java b/cli/lfd/src/test/java/org/lflang/cli/DiagramGenerationTest.java new file mode 100644 index 0000000000..2fcb332fe6 --- /dev/null +++ b/cli/lfd/src/test/java/org/lflang/cli/DiagramGenerationTest.java @@ -0,0 +1,89 @@ +package org.lflang.cli; + +import com.google.inject.Inject; +import com.google.inject.Provider; +import de.cau.cs.kieler.klighd.Klighd; +import de.cau.cs.kieler.klighd.LightDiagramServices; +import de.cau.cs.kieler.klighd.standalone.KlighdStandaloneSetup; +import java.net.URI; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.xtext.testing.InjectWith; +import org.eclipse.xtext.testing.extensions.InjectionExtension; +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.TestFactory; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; +import org.lflang.Target; +import org.lflang.lf.Model; +import org.lflang.tests.LFInjectorProvider; +import org.lflang.tests.LFTest; +import org.lflang.tests.TestRegistry; + +@ExtendWith(InjectionExtension.class) +@InjectWith(LFInjectorProvider.class) +@Execution(ExecutionMode.SAME_THREAD) +public class DiagramGenerationTest { + @Inject private TestRegistry testRegistry; + + @Inject private Provider resourceSetProvider; + + @TestFactory + public Collection diagramGenerationTestFactory(@TempDir Path tempDir) { + KlighdStandaloneSetup.initialize(); + List result = new ArrayList<>(); + Path cwd = Paths.get(".").toAbsolutePath(); + int id = 0; + for (Target target : Target.values()) { + for (TestRegistry.TestCategory category : TestRegistry.TestCategory.values()) { + for (LFTest test : testRegistry.getRegisteredTests(target, category, false)) { + URI testSourceUri = test.getSrcPath().toUri(); + final ResourceSet set = this.resourceSetProvider.get(); + Resource resource = + set.getResource( + org.eclipse.emf.common.util.URI.createFileURI(test.getSrcPath().toString()), + true); + Path outPath = tempDir.resolve("diagram_" + id + ".svg"); + id++; + result.add( + DynamicTest.dynamicTest( + cwd.relativize(test.getSrcPath()).toString(), + testSourceUri, + () -> run(resource, outPath.toString()))); + } + } + } + return result; + } + + private static void run(Resource resource, String outPath) { + AtomicBoolean errorOccurred = new AtomicBoolean(false); + Klighd.setStatusManager( + (status, style) -> { + if (status.getSeverity() == IStatus.ERROR) { + System.out.println(status.getMessage()); + if (status.getException() != null) { + status.getException().printStackTrace(); + } + } + errorOccurred.set(true); + }); + + final Model model = (Model) resource.getContents().get(0); + IStatus status = LightDiagramServices.renderOffScreen(model, "svg", outPath); + if (!status.isOK()) { + System.out.println(status.getMessage()); + } + assert status.isOK(); + assert !errorOccurred.get(); + } +} diff --git a/cli/lfd/src/test/java/org/lflang/cli/LfdCliTest.java b/cli/lfd/src/test/java/org/lflang/cli/LfdCliTest.java new file mode 100644 index 0000000000..4ab37245e6 --- /dev/null +++ b/cli/lfd/src/test/java/org/lflang/cli/LfdCliTest.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2022, TU Dresden. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.lflang.cli; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.lflang.cli.TestUtils.TempDirBuilder.dirBuilder; + +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.lflang.LocalStrings; +import org.lflang.cli.CliToolTestFixture.ExecutionResult; + +/** + * @author Clément Fournier + */ +public class LfdCliTest { + + private static final String VALID_FILE = + """ + target Python + main reactor { + reaction(startup) {==} + } + """; + LfdTestFixture lfdTester = new LfdTestFixture(); + + @Test + public void testHelpArg() { + ExecutionResult result = lfdTester.run("--help", "--version"); + result.checkOk(); + result.checkNoErrorOutput(); + result.checkStdOut(containsString("Usage:")); + result.checkStdOut(containsString("lfd")); + } + + @Test + public void testVersion() { + ExecutionResult result = lfdTester.run("--version"); + result.checkOk(); + result.checkNoErrorOutput(); + result.checkStdOut(equalTo("lfd " + LocalStrings.VERSION + System.lineSeparator())); + } + + @Test + public void testWrongCliArg() { + ExecutionResult result = lfdTester.run("--notanargument", "File.lf"); + result.checkStdErr(containsString("Unknown option: '--notanargument'")); + result.checkFailed(); + } + + @Test + public void testValidFile(@TempDir Path tempDir) throws IOException { + dirBuilder(tempDir).file("src/File.lf", VALID_FILE); + ExecutionResult result = lfdTester.run(tempDir, "src/File.lf"); + result.checkOk(); + result.checkNoErrorOutput(); + Files.walkFileTree( + tempDir, + new SimpleFileVisitor<>() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { + System.out.println(file); + return FileVisitResult.CONTINUE; + } + }); + assert tempDir.resolve("File.svg").toFile().exists(); + } + + @Test + public void testInValidFile(@TempDir Path tempDir) throws IOException { + ExecutionResult result = lfdTester.run(tempDir, "."); + result.checkFailed(); + } + + static class LfdTestFixture extends CliToolTestFixture { + @Override + protected void runCliProgram(Io io, String[] args) { + Lfd.main(io, args); + } + } +} diff --git a/cli/lff/build.gradle b/cli/lff/build.gradle index 9f2269120c..cbccc2f87f 100644 --- a/cli/lff/build.gradle +++ b/cli/lff/build.gradle @@ -6,24 +6,9 @@ dependencies { implementation project(':cli:base') testImplementation(testFixtures(project(':cli:base'))) - testImplementation "org.junit.jupiter:junit-jupiter-api:$jupiterVersion" - testImplementation "org.junit.jupiter:junit-jupiter-engine:$jupiterVersion" - testImplementation "org.junit.platform:junit-platform-commons:$jUnitPlatformVersion" - testImplementation "org.junit.platform:junit-platform-engine:$jUnitPlatformVersion" - testImplementation "org.opentest4j:opentest4j:$openTest4jVersion" - testImplementation "org.eclipse.xtext:org.eclipse.xtext.testing:$xtextVersion" - testImplementation "org.eclipse.xtext:org.eclipse.xtext.xbase.testing:$xtextVersion" } application { mainClass = 'org.lflang.cli.Lff' tasks.run.workingDir = System.getProperty("user.dir") } - -test { - useJUnitPlatform() - testLogging { - events "passed", "skipped", "failed" - showStandardStreams = true - } -} diff --git a/core/build.gradle b/core/build.gradle index 234fc07aff..0d87a3e729 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -26,10 +26,12 @@ dependencies { implementation ("de.cau.cs.kieler.klighd:de.cau.cs.kieler.klighd.lsp:$klighdVersion") { exclude group: 'org.eclipse.platform', module: 'org.eclipse.swt.*' exclude group: 'org.eclipse.platform', module: 'org.eclipse.swt' + exclude group: 'de.cau.cs.kieler.swt.mock' } implementation ("de.cau.cs.kieler.klighd:de.cau.cs.kieler.klighd.standalone:$klighdVersion") { exclude group: 'org.eclipse.platform', module: 'org.eclipse.swt.*' exclude group: 'org.eclipse.platform', module: 'org.eclipse.swt' + exclude group: 'de.cau.cs.kieler.swt.mock' } testImplementation "org.junit.jupiter:junit-jupiter-api:$jupiterVersion" diff --git a/core/src/main/java/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java b/core/src/main/java/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java index b26fea12a2..b82b49461b 100644 --- a/core/src/main/java/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java +++ b/core/src/main/java/org/lflang/diagram/synthesis/LinguaFrancaSynthesis.java @@ -30,6 +30,7 @@ import com.google.common.collect.Multimap; import com.google.common.collect.Table; import de.cau.cs.kieler.klighd.DisplayedActionData; +import de.cau.cs.kieler.klighd.Klighd; import de.cau.cs.kieler.klighd.SynthesisOption; import de.cau.cs.kieler.klighd.kgraph.KEdge; import de.cau.cs.kieler.klighd.kgraph.KLabel; @@ -68,6 +69,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import javax.inject.Inject; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; import org.eclipse.elk.alg.layered.options.LayerConstraint; import org.eclipse.elk.alg.layered.options.LayeredOptions; import org.eclipse.elk.alg.layered.options.NodePlacementStrategy; @@ -366,7 +369,12 @@ public KNode transform(final Model model) { } } } catch (Exception e) { - e.printStackTrace(); + Klighd.log( + new Status( + IStatus.ERROR, + LinguaFrancaSynthesis.class, + "An exception occurred during diagram synthesis", + e)); KNode messageNode = _kNodeExtensions.createNode(); _linguaFrancaShapeExtensions.addErrorMessage( diff --git a/core/src/main/java/org/lflang/diagram/synthesis/util/SynthesisErrorReporter.java b/core/src/main/java/org/lflang/diagram/synthesis/util/SynthesisErrorReporter.java index a249c009fd..5d229e015b 100644 --- a/core/src/main/java/org/lflang/diagram/synthesis/util/SynthesisErrorReporter.java +++ b/core/src/main/java/org/lflang/diagram/synthesis/util/SynthesisErrorReporter.java @@ -24,7 +24,10 @@ ***************/ package org.lflang.diagram.synthesis.util; +import de.cau.cs.kieler.klighd.Klighd; import java.nio.file.Path; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; import org.eclipse.emf.ecore.EObject; import org.lflang.ErrorReporter; @@ -32,53 +35,60 @@ * @author Alexander Schulz-Rosengarten */ public class SynthesisErrorReporter implements ErrorReporter { + + private boolean errorsOccurred = false; + @Override public String reportError(String message) { - return null; + errorsOccurred = true; + Klighd.log(new Status(IStatus.ERROR, SynthesisErrorReporter.class, message)); + return message; } @Override public String reportError(EObject object, String message) { - return null; + return reportError(message); } @Override public String reportError(Path file, Integer line, String message) { - return null; + return reportError(message); } @Override public String reportWarning(String message) { - return null; + Klighd.log(new Status(IStatus.WARNING, SynthesisErrorReporter.class, message)); + return message; } @Override public String reportWarning(EObject object, String message) { - return null; + return reportWarning(message); } @Override public String reportWarning(Path file, Integer line, String message) { - return null; + return reportWarning(message); } @Override public String reportInfo(String message) { - return null; + Klighd.log(new Status(IStatus.INFO, SynthesisErrorReporter.class, message)); + return message; } @Override public String reportInfo(EObject object, String message) { - return null; + return reportInfo(message); } @Override public String reportInfo(Path file, Integer line, String message) { - return null; + return reportInfo(message); } @Override public boolean getErrorsOccurred() { - return false; + return errorsOccurred; } } diff --git a/gradle.properties b/gradle.properties index e294dedf05..2164bf48ef 100644 --- a/gradle.properties +++ b/gradle.properties @@ -16,6 +16,8 @@ picocliVersion=4.7.0 resourcesVersion=3.16.0 xtextVersion=2.28.0 klighdVersion=2.3.0.v20230606 +freehepVersion=2.4 +swtVersion=3.124.0 [manifestPropertyNames] org.eclipse.xtext=xtextVersion diff --git a/settings.gradle b/settings.gradle index b7b8588ae0..27a7c330bd 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,2 +1,2 @@ rootProject.name = 'org.lflang' -include('core', 'lsp', 'cli:base', 'cli:lfc', 'cli:lff') +include('core', 'lsp', 'cli:base', 'cli:lfc', 'cli:lff', 'cli:lfd') diff --git a/util/scripts/launch.ps1 b/util/scripts/launch.ps1 index be874fd26a..dda258cc44 100644 --- a/util/scripts/launch.ps1 +++ b/util/scripts/launch.ps1 @@ -10,7 +10,7 @@ $invokerPath = $MyInvocation.PSCommandPath $invokerName = [System.IO.Path]::GetFileNameWithoutExtension("$(Split-Path -Path $invokerPath -Leaf -Resolve)") -$mainClassTable = @{"lfc" = "org.lflang.cli.Lfc"; "lff" = "org.lflang.cli.Lff"} +$mainClassTable = @{"lfc" = "org.lflang.cli.Lfc"; "lff" = "org.lflang.cli.Lff"; "lfd" = "org.lflang.cli.Lfd"} $tool = $null foreach ($k in $mainClassTable.Keys) { if ($invokerName.EndsWith($k)) { diff --git a/util/scripts/launch.sh b/util/scripts/launch.sh index f1e685f288..36a342bd25 100755 --- a/util/scripts/launch.sh +++ b/util/scripts/launch.sh @@ -57,8 +57,10 @@ if [[ "$0" == *lfc ]]; then tool="lfc" elif [[ "$0" == *lff ]]; then tool="lff" +elif [[ "$0" == *lfd ]]; then + tool="lfd" else - known_commands="[lfc, lff]" + known_commands="[lfc, lff, lfd]" echo \ "ERROR: $0 is not a known lf command! Known commands are ${known_commands}. In case you use a symbolic or hard link to one of the Lingua Franca