Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lfd binary for generating diagrams from the command line #1713

Merged
merged 21 commits into from
Jun 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .github/scripts/test-lfd.sh
Original file line number Diff line number Diff line change
@@ -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"
8 changes: 7 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
33 changes: 31 additions & 2 deletions .github/workflows/cli-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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' }}
1 change: 1 addition & 0 deletions bin/lfd
9 changes: 9 additions & 0 deletions bin/lfd.ps1
Original file line number Diff line number Diff line change
@@ -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
5 changes: 4 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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/*')
}
}
26 changes: 26 additions & 0 deletions buildSrc/src/main/groovy/org.lflang.java-conventions.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
plugins {
id 'java'
id 'com.diffplug.spotless'
id 'org.lflang.platform'
}

repositories {
Expand All @@ -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")
}
}
}
}
16 changes: 16 additions & 0 deletions buildSrc/src/main/groovy/org.lflang.platform.gradle
Original file line number Diff line number Diff line change
@@ -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')
}
}
25 changes: 25 additions & 0 deletions cli/lfd/build.gradle
Original file line number Diff line number Diff line change
@@ -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")
}
78 changes: 78 additions & 0 deletions cli/lfd/src/main/java/org/lflang/cli/Lfd.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
Loading