diff --git a/.github/scripts/package_lfc.sh b/.github/scripts/package_lfc.sh index ece4438eeb..f72f2c299f 100755 --- a/.github/scripts/package_lfc.sh +++ b/.github/scripts/package_lfc.sh @@ -6,8 +6,8 @@ set -euo pipefail ./gradlew clean buildLfc # find the version number -jar_path="org.lflang.lfc/build/libs/org.lflang.lfc-*-all.jar" -version="$(ls ${jar_path} | xargs -n 1 basename | sed 's/^org.lflang.lfc-\(.*\)-all.jar$/\1/')" +jar_path="org.lflang.cli/build/libs/org.lflang.cli-*-lfc.jar" +version="$(ls ${jar_path} | xargs -n 1 basename | sed 's/^org.lflang.cli-\(.*\)-lfc.jar$/\1/')" # use a different naming convention for nightly build artifacts if [[ "$#" > 0 && "$1" = "nightly" ]]; then @@ -24,7 +24,7 @@ mkdir -p "${outname}/lib/scripts" mkdir -p "${outname}/lib/jars" # move the jar -mv org.lflang.lfc/build/libs/org.lflang.lfc-*-all.jar "${outname}/lib/jars" +mv org.lflang.cli/build/libs/org.lflang.cli-*-lfc.jar "${outname}/lib/jars" # copy the Bash scripts cp -a lib/scripts "${outname}/lib/" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2d9ae662d4..9bc1c1298c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,8 +31,9 @@ jobs: needs: cancel # Run tests for the standalone compiler. + # TODO: Change this back to master branch once merged! cli-tests: - uses: lf-lang/lingua-franca/.github/workflows/cli-tests.yml@master + uses: lf-lang/lingua-franca/.github/workflows/cli-tests.yml@pretty-printer needs: cancel # Run the C benchmark tests. diff --git a/.github/workflows/cli-tests.yml b/.github/workflows/cli-tests.yml index 9a16d36aa2..c6342ed464 100644 --- a/.github/workflows/cli-tests.yml +++ b/.github/workflows/cli-tests.yml @@ -15,9 +15,9 @@ jobs: submodules: true - name: Prepare build environment uses: ./.github/actions/prepare-build-env - - name: Run standalone lfc tests + - name: Run standalone cli tests run: | - ./gradlew :org.lflang.lfc:test --stacktrace + ./gradlew :org.lflang.cli:test --stacktrace - name: Test Bash scripts (Linux or macOS only) run: | .github/scripts/test-lfc.sh diff --git a/bin/lfc.ps1 b/bin/lfc.ps1 index 808bb622b3..f4e5187152 100644 --- a/bin/lfc.ps1 +++ b/bin/lfc.ps1 @@ -7,11 +7,11 @@ $base="$PSScriptRoot\.." $java_home = "$Env:JAVA_HOME" $java_cmd = "$java_home\bin\java.exe" -$jarpath_dev="$base\org.lflang.lfc\build\libs\org.lflang.lfc-*-all.jar" -$jarpath_release="$base\lib\jars\org.lflang.lfc-*-all.jar" +$jarpath_dev="$base\org.lflang.cli\build\libs\org.lflang.cli-*-lfc.jar" +$jarpath_release="$base\lib\jars\org.lflang.cli-*-lfc.jar" function Test-Dev { - Test-Path "$base\org.lflang.lfc" -PathType container + Test-Path "$base\org.lflang.cli" -PathType container } function Get-JarPath { diff --git a/lib/scripts/include.sh b/lib/scripts/include.sh index 6e58fab0fd..3163d029c3 100644 --- a/lib/scripts/include.sh +++ b/lib/scripts/include.sh @@ -21,10 +21,10 @@ set -euo pipefail # Paths (relative to ${base}), which is assumed to have been set. src_pkg_name="org.lflang" src_pkg_path="${base}/${src_pkg_name}" -lfc_src_pkg_name="${src_pkg_name}.lfc" +lfc_src_pkg_name="${src_pkg_name}.cli" lfc_src_pkg_path="${base}/${lfc_src_pkg_name}" -lfc_jar_build_path_pattern="${lfc_src_pkg_name}/build/libs/${lfc_src_pkg_name}-*-all.jar" -lfc_jar_release_path_pattern="lib/jars/${lfc_src_pkg_name}-*-all.jar" +lfc_jar_build_path_pattern="${lfc_src_pkg_name}/build/libs/${lfc_src_pkg_name}-*-lfc.jar" +lfc_jar_release_path_pattern="lib/jars/${lfc_src_pkg_name}-*-lfc.jar" # Enter directory silently (without printing). pushd() { diff --git a/org.lflang.cli/build.gradle b/org.lflang.cli/build.gradle new file mode 100644 index 0000000000..84913c02c5 --- /dev/null +++ b/org.lflang.cli/build.gradle @@ -0,0 +1,116 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar + +sourceSets { + test { + java.srcDirs = [] + kotlin.srcDirs = ['test/kotlin'] + resources.srcDir('test/resources') + resources.include '**/*' + } +} + +configurations { + cli_impl { + extendsFrom implementation + } +} + +compileTestKotlin { + destinationDir = compileTestJava.destinationDir + kotlinOptions { + jvmTarget = kotlinJvmTarget + } +} + + + +dependencies { + implementation project(':org.lflang') + implementation "org.eclipse.xtext:org.eclipse.xtext.ide:${xtextVersion}" + implementation "org.eclipse.xtext:org.eclipse.xtext.xbase.ide:${xtextVersion}" + + testImplementation "junit:junit:${jUnitVersion}" +} + +apply plugin: 'application' +apply plugin: 'com.github.johnrengelman.shadow' + +task buildLfc() { + apply plugin: 'application' + apply plugin: 'com.github.johnrengelman.shadow' + mainClassName = 'org.lflang.cli.Lfc' +} + +task jarLfc(type: ShadowJar) { + manifest { + attributes('Main-Class': 'org.lflang.cli.Lfc') + } + configurations = [project.configurations.cli_impl] + archiveClassifier.set('lfc') + exclude 'test/*' + exclude 'META-INF/*.RSA', 'META-INF/*.SF', 'META-INF/*.DSA' + // We should use minimize() here to reduce the size of the JAR, but it causes problems + // with regard to our Kotlin classes. Since we don't use imports to load them but load + // the classes manually, minimize does not see the dependency. While we can add an exclude + // rule, this does not seem to work very well and causes problems when compiling for a + // second time. Also see https://github.com/lf-lang/lingua-franca/pull/1285 + transform(com.github.jengelman.gradle.plugins.shadow.transformers.AppendingTransformer){ + resource = 'plugin.properties' + } + from sourceSets.main.output +} + +buildLfc.finalizedBy jarLfc + +task runLfc(type: JavaExec) { + // builds and runs lfc + // The working directory will be the root directory of the lingua franca project + // CLI arguments can be passed to lfc by using --args. Note that you need + // to escape cli flags which start with --.For instance --args ' --help'. + // Otherwise they're parsed as arguments to the Gradle CLI, not lfc. + description = "Build and run lfc, use --args to pass arguments" + group = "application" + classpath = sourceSets.main.runtimeClasspath + mainClass = 'org.lflang.cli.Lfc' + workingDir = '..' +} + +task buildLff() { + apply plugin: 'application' + apply plugin: 'com.github.johnrengelman.shadow' + mainClassName = 'org.lflang.cli.Lff' +} + +task jarLff(type: ShadowJar) { + manifest { + attributes('Main-Class': 'org.lflang.cli.Lff') + } + configurations = [project.configurations.cli_impl] + archiveClassifier.set('lff') + exclude 'test/*' + exclude 'META-INF/*.RSA', 'META-INF/*.SF', 'META-INF/*.DSA' + // We should use minimize() here to reduce the size of the JAR, but it causes problems + // with regard to our Kotlin classes. Since we don't use imports to load them but load + // the classes manually, minimize does not see the dependency. While we can add an exclude + // rule, this does not seem to work very well and causes problems when compiling for a + // second time. Also see https://github.com/lf-lang/lingua-franca/pull/1285 + transform(com.github.jengelman.gradle.plugins.shadow.transformers.AppendingTransformer){ + resource = 'plugin.properties' + } + from sourceSets.main.output +} + +buildLff.finalizedBy jarLff + +task runLff(type: JavaExec) { + // builds and runs lff + // The working directory will be the root directory of the lingua franca project + // CLI arguments can be passed to lff by using --args. Note that you need + // to escape cli flags which start with --.For instance --args ' --help'. + // Otherwise they're parsed as arguments to the Gradle CLI, not lfc. + description = "Build and run lff, use --args to pass arguments" + group = "application" + classpath = sourceSets.main.runtimeClasspath + mainClass = 'org.lflang.cli.Lff' + workingDir = '..' +} diff --git a/org.lflang.cli/src/org/lflang/cli/CliBase.java b/org.lflang.cli/src/org/lflang/cli/CliBase.java new file mode 100644 index 0000000000..d67edde7d8 --- /dev/null +++ b/org.lflang.cli/src/org/lflang/cli/CliBase.java @@ -0,0 +1,135 @@ +package org.lflang.cli; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.List; +import java.util.Properties; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.Option; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.xtext.util.CancelIndicator; +import org.eclipse.xtext.validation.CheckMode; +import org.eclipse.xtext.validation.IResourceValidator; +import org.eclipse.xtext.validation.Issue; + +import org.lflang.util.FileUtil; + +import com.google.inject.Inject; +import com.google.inject.Provider; + +/** + * Base class for standalone CLI applications. + * + * @author {Marten Lohstroh } + * @author {Christian Menard } + * @author {Billy Bao } + */ +public class CliBase { + /** + * Object for interpreting command line arguments. + */ + protected CommandLine cmd; + /** + * Used to collect all errors that happen during validation/generation. + */ + @Inject + protected IssueCollector issueCollector; + /** + * Used to report error messages at the end. + */ + @Inject + protected ReportingBackend reporter; + /** + * Injected resource provider. + */ + @Inject + private Provider resourceSetProvider; + /** + * Injected resource validator. + */ + @Inject + private IResourceValidator validator; + + /** + * Store command-line arguments as properties, to be passed on to the runtime. + * @param passOptions Which options should be passed to the runtime. + * @return Provided arguments in cmd as properties, which should be passed to the runtime. + */ + protected Properties filterProps(List