C: allow to run external tool after successful incremental build

This commit is contained in:
Alexey Andreev 2019-05-15 16:37:51 +03:00
parent 054db3e8d1
commit 7a99258cab
2 changed files with 114 additions and 0 deletions

View File

@ -15,11 +15,14 @@
*/
package org.teavm.tooling.c.incremental;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@ -27,6 +30,7 @@ import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import org.teavm.backend.c.CTarget;
import org.teavm.backend.c.generate.SimpleStringPool;
@ -61,6 +65,8 @@ public class IncrementalCBuilder {
private String[] classPath;
private int minHeapSize = 32;
private String targetPath;
private String externalTool;
private String externalToolWorkingDir;
private IncrementalDirectoryBuildTarget buildTarget;
private FileSystemWatcher watcher;
@ -87,6 +93,8 @@ public class IncrementalCBuilder {
private boolean waiting;
private Thread buildThread;
private boolean needsExternalTool;
public void setMainClass(String mainClass) {
this.mainClass = mainClass;
}
@ -107,6 +115,14 @@ public class IncrementalCBuilder {
this.log = log;
}
public void setExternalTool(String externalTool) {
this.externalTool = externalTool;
}
public void setExternalToolWorkingDir(String externalToolWorkingDir) {
this.externalToolWorkingDir = externalToolWorkingDir;
}
public void addProgressHandler(ProgressHandler handler) {
synchronized (progressHandlers) {
progressHandlers.add(handler);
@ -316,9 +332,12 @@ public class IncrementalCBuilder {
vm.build(buildTarget, "");
postBuild(vm, startTime);
runExternalTool();
}
private void postBuild(TeaVM vm, long startTime) {
needsExternalTool = false;
if (!vm.wasCancelled()) {
log.info("Recompiled stale methods: " + programCache.getPendingItemsCount());
fireBuildComplete(vm);
@ -329,6 +348,7 @@ public class IncrementalCBuilder {
programCache.commit();
astCache.commit();
reportCompilationComplete(true);
needsExternalTool = true;
} else {
log.info("Build complete with errors");
reportCompilationComplete(false);
@ -363,6 +383,46 @@ public class IncrementalCBuilder {
log.info("Compilation took " + (System.currentTimeMillis() - startTime) + " ms");
}
private void runExternalTool() {
if (externalTool == null || !needsExternalTool) {
return;
}
try {
log.info("Running external tool");
ProcessBuilder pb = new ProcessBuilder(externalTool);
if (externalToolWorkingDir != null) {
pb.directory(new File(externalToolWorkingDir));
}
Process process = pb.start();
BufferedReader stdoutReader = new BufferedReader(new InputStreamReader(process.getInputStream(),
StandardCharsets.UTF_8));
BufferedReader stderrReader = new BufferedReader(new InputStreamReader(process.getErrorStream(),
StandardCharsets.UTF_8));
daemon("external tool stdout watcher", new ExternalOutputWatcher(stdoutReader,
s -> log.info("[external tool] " + s)));
daemon("external tool stderr watcher", new ExternalOutputWatcher(stderrReader,
s -> log.error("[external tool] " + s)));
int code = process.waitFor();
if (code != 0) {
log.error("External tool returned non-zero code: " + code);
}
} catch (IOException e) {
log.error("Could not start external tool", e);
} catch (InterruptedException e) {
log.info("Interrupted while running external tool");
}
}
private static Thread daemon(String name, Runnable runnable) {
Thread thread = new Thread(runnable);
thread.setDaemon(true);
thread.setName(name);
thread.start();
return thread;
}
private ClassReaderSource packClasses(ClassReaderSource source, Collection<? extends String> classNames) {
MemoryCachedClassReaderSource packedSource = createCachedSource();
packedSource.setProvider(source::get);
@ -507,4 +567,29 @@ public class IncrementalCBuilder {
return TeaVMProgressFeedback.CONTINUE;
}
}
class ExternalOutputWatcher implements Runnable {
private BufferedReader reader;
private Consumer<String> consumer;
ExternalOutputWatcher(BufferedReader reader, Consumer<String> consumer) {
this.reader = reader;
this.consumer = consumer;
}
@Override
public void run() {
try {
while (true) {
String line = reader.readLine();
if (line == null) {
break;
}
consumer.accept(line);
}
} catch (IOException e) {
log.error("Error reading build daemon output", e);
}
}
}
}

View File

@ -58,6 +58,18 @@ public class TeaVMCBuilderRunner {
.hasArg()
.withDescription("Minimum heap size in bytes")
.create());
options.addOption(OptionBuilder
.withLongOpt("external-tool")
.withArgName("path")
.hasArg()
.withDescription("Process to run after successful build")
.create());
options.addOption(OptionBuilder
.withLongOpt("external-tool-workdir")
.withArgName("path")
.hasArg()
.withDescription("Working directory of process")
.create());
}
private TeaVMCBuilderRunner(CommandLine commandLine) {
@ -88,6 +100,7 @@ public class TeaVMCBuilderRunner {
parseClassPathOptions();
parseOutputOptions();
parseHeap();
parseExternalTool();
builder.setLog(new ConsoleTeaVMToolLog(commandLine.hasOption('v')));
@ -100,6 +113,22 @@ public class TeaVMCBuilderRunner {
}
}
private void parseExternalTool() {
boolean hasExternalTool = false;
if (commandLine.hasOption("external-tool")) {
builder.setExternalTool(commandLine.getOptionValue("external-tool"));
hasExternalTool = true;
}
if (commandLine.hasOption("external-tool-workdir")) {
if (!hasExternalTool) {
System.err.println("Redundant 'external-tool-workdir' option: no external tool specified");
} else {
builder.setExternalToolWorkingDir(commandLine.getOptionValue("external-tool-workdir"));
}
}
}
private void parseOutputOptions() {
if (commandLine.hasOption("d")) {
builder.setTargetPath(commandLine.getOptionValue("d"));