diff --git a/.github/workflows/ts-tests.yml b/.github/workflows/ts-tests.yml index 825818dbd2..f0bea1dffb 100644 --- a/.github/workflows/ts-tests.yml +++ b/.github/workflows/ts-tests.yml @@ -14,7 +14,7 @@ jobs: run: strategy: matrix: - platform: [ubuntu-latest, macos-latest] + platform: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.platform }} steps: - name: Setup Java JDK @@ -23,13 +23,8 @@ jobs: java-version: 11 - name: Setup Node.js environment uses: actions/setup-node@v2.1.2 - - name: Cache .pnpm-store - uses: actions/cache@v1 - with: - path: ~/.pnpm-store - key: ${{ runner.os }}-node${{ matrix.node-version }}-${{ hashFiles('**/pnpm-lock.yaml') }} - name: Install pnpm - run: sudo npm i -g pnpm + run: npm i -g pnpm - name: Install Dependencies Ubuntu run: sudo apt-get install libprotobuf-dev protobuf-compiler if: ${{ runner.os == 'Linux' }} diff --git a/org.lflang.tests/src/org/lflang/tests/runtime/TypeScriptTest.java b/org.lflang.tests/src/org/lflang/tests/runtime/TypeScriptTest.java index f626306504..108bf220e8 100644 --- a/org.lflang.tests/src/org/lflang/tests/runtime/TypeScriptTest.java +++ b/org.lflang.tests/src/org/lflang/tests/runtime/TypeScriptTest.java @@ -1,5 +1,6 @@ package org.lflang.tests.runtime; +import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Test; import org.lflang.Target; import org.lflang.tests.AbstractTest; @@ -46,6 +47,7 @@ public void runMultiportTests() { @Test @Override public void runSerializationTests() { + Assumptions.assumeFalse(isWindows(), Message.NO_WINDOWS_SUPPORT); super.runSerializationTests(); } diff --git a/org.lflang/src/lib/ts/package.json b/org.lflang/src/lib/ts/package.json index 5c14cd6b77..367a812ab7 100644 --- a/org.lflang/src/lib/ts/package.json +++ b/org.lflang/src/lib/ts/package.json @@ -26,11 +26,12 @@ "@babel/preset-typescript": "^7.8.3", "@types/google-protobuf": "^3.7.4", "@types/node": "^13.9.2", + "rimraf": "^3.0.2", "typescript": "^3.8.3", "ts-protoc-gen": "^0.12.0" }, "scripts": { "check-types": "tsc", - "build": "rm -rf dist && babel src --out-dir dist --extensions '.ts,.js'" + "build": "rimraf dist && babel src --out-dir dist --extensions .ts,.js" } } diff --git a/org.lflang/src/org/lflang/util/LFCommand.java b/org.lflang/src/org/lflang/util/LFCommand.java index bfc1189d8c..d0a6bfb634 100644 --- a/org.lflang/src/org/lflang/util/LFCommand.java +++ b/org.lflang/src/org/lflang/util/LFCommand.java @@ -33,12 +33,15 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY import java.io.PrintStream; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; import org.eclipse.xtext.util.CancelIndicator; @@ -174,13 +177,8 @@ public int run(CancelIndicator cancelIndicator) { System.out.println("--- Current working directory: " + processBuilder.directory().toString()); System.out.println("--- Executing command: " + String.join(" ", processBuilder.command())); - final Process process; - try { - process = processBuilder.start(); - } catch (IOException e) { - e.printStackTrace(); - return -1; - } + final Process process = startProcess(); + if (process == null) return -1; ScheduledExecutorService poller = Executors.newSingleThreadScheduledExecutor(); poller.scheduleAtFixedRate( @@ -303,7 +301,7 @@ public static LFCommand get(final String cmd, final List args, Path dir) final File cmdFile = dir.resolve(cmd).toFile(); if (cmdFile.exists() && cmdFile.canExecute()) { builder = new ProcessBuilder(cmdList); - } else if (checkIfCommandIsOnPath(cmd, dir)) { + } else if (findCommand(cmd) != null) { builder = new ProcessBuilder(cmdList); } else if (checkIfCommandIsExecutableWithBash(cmd, dir)) { builder = new ProcessBuilder("bash", "--login", "-c", String.join(" ", cmdList)); @@ -318,23 +316,58 @@ public static LFCommand get(final String cmd, final List args, Path dir) } - private static boolean checkIfCommandIsOnPath(final String command, final Path dir) { + /** + * Search for matches to the given command by following the PATH environment variable. + * @param command A command for which to search. + * @return The file locations of matches to the given command. + */ + private static List findCommand(final String command) { final String whichCmd = System.getProperty("os.name").startsWith("Windows") ? "where" : "which"; final ProcessBuilder whichBuilder = new ProcessBuilder(List.of(whichCmd, command)); - whichBuilder.directory(dir.toFile()); try { - int whichReturn = whichBuilder.start().waitFor(); - return whichReturn == 0; + Process p = whichBuilder.start(); + if (p.waitFor() != 0) return null; + return Arrays.stream(new String(p.getInputStream().readAllBytes()).split("\n")) + .map(String::strip).map(File::new).filter(File::canExecute).collect(Collectors.toList()); } catch (InterruptedException | IOException e) { e.printStackTrace(); - return false; + return null; + } + } + + /** + * Attempt to start the execution of this command. + * + * First collect a list of paths where the executable might be found, + * then select an executable that successfully executes from the + * list of paths. Return the {@code Process} instance that is the + * result of a successful execution, or {@code null} if no successful + * execution happened. + * @return The {@code Process} that is started by this command, or {@code null} in case of failure. + */ + private Process startProcess() { + ArrayDeque commands = new ArrayDeque<>(); + List matchesOnPath = findCommand(processBuilder.command().get(0)); + if (matchesOnPath != null) { + matchesOnPath.stream().map(File::toString).forEach(commands::addLast); + } + while (true) { + try { + return processBuilder.start(); + } catch (IOException e) { + if (commands.isEmpty()) { + e.printStackTrace(); + return null; + } + } + processBuilder.command().set(0, commands.removeFirst()); } } private static boolean checkIfCommandIsExecutableWithBash(final String command, final Path dir) { // check first if bash is installed - if (!checkIfCommandIsOnPath("bash", dir)) { + if (findCommand("bash") == null) { return false; } diff --git a/test/TypeScript/src/Person.proto b/test/TypeScript/src/serialization/Person.proto similarity index 100% rename from test/TypeScript/src/Person.proto rename to test/TypeScript/src/serialization/Person.proto diff --git a/test/TypeScript/src/ProtoHelloWorld.proto b/test/TypeScript/src/serialization/ProtoHelloWorld.proto similarity index 100% rename from test/TypeScript/src/ProtoHelloWorld.proto rename to test/TypeScript/src/serialization/ProtoHelloWorld.proto diff --git a/test/TypeScript/src/ProtoNoPacking.lf b/test/TypeScript/src/serialization/ProtoNoPacking.lf similarity index 100% rename from test/TypeScript/src/ProtoNoPacking.lf rename to test/TypeScript/src/serialization/ProtoNoPacking.lf