Improve incremental compilation in IDEA.

Fix bug in source copier.
This commit is contained in:
Alexey Andreev 2017-06-01 19:15:24 +03:00
parent 89eb87e8ca
commit d8913f85d1
3 changed files with 85 additions and 22 deletions

View File

@ -64,19 +64,20 @@ public class SourceFilesCopier {
for (String fileName : sourceFiles) { for (String fileName : sourceFiles) {
try { try {
SourceFileInfo sourceFile = findSourceFile(fileName); SourceFileInfo sourceFile = findSourceFile(fileName);
if (sourceFile == null) {
log.info("Missing source file: " + fileName);
continue;
}
File outputFile = new File(targetDirectory, fileName); File outputFile = new File(targetDirectory, fileName);
if (outputFile.exists() && outputFile.lastModified() > sourceFile.lastModified() if (outputFile.exists() && outputFile.lastModified() > sourceFile.lastModified()
&& sourceFile.lastModified() > 0) { && sourceFile.lastModified() > 0) {
continue; continue;
} }
try (InputStream input = sourceFile.open()) { try (InputStream input = sourceFile.open()) {
if (input != null) { outputFile.getParentFile().mkdirs();
outputFile.getParentFile().mkdirs(); try (OutputStream output = new FileOutputStream(outputFile)) {
try (OutputStream output = new FileOutputStream(outputFile)) { IOUtils.copy(input, output);
IOUtils.copy(input, output);
}
} else {
log.info("Missing source file: " + fileName);
} }
} }
} catch (IOException e) { } catch (IOException e) {

View File

@ -37,8 +37,10 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
import java.util.Set; import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.teavm.idea.jps.remote.TeaVMRemoteBuildCallback; import org.teavm.idea.jps.remote.TeaVMRemoteBuildCallback;
@ -67,6 +69,8 @@ public class TeaVMBuildDaemon extends UnicastRemoteObject implements TeaVMRemote
private int port; private int port;
private Registry registry; private Registry registry;
private File incrementalCache; private File incrementalCache;
private ClassLoader lastJarClassLoader;
private List<String> lastJarClassPath;
static { static {
int depth = 0; int depth = 0;
@ -107,9 +111,23 @@ public class TeaVMBuildDaemon extends UnicastRemoteObject implements TeaVMRemote
return; return;
} }
Thread mainThread = Thread.currentThread();
try { try {
incrementalCache = Files.createTempDirectory("teavm-cache").toFile(); incrementalCache = Files.createTempDirectory("teavm-cache").toFile();
incrementalCache.deleteOnExit(); Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try {
if (incrementalCache != null) {
FileUtils.deleteDirectory(incrementalCache);
}
} catch (IOException e) {
e.printStackTrace();
}
try {
mainThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}));
} catch (IOException e) { } catch (IOException e) {
System.err.println("Could not setup incremental cache"); System.err.println("Could not setup incremental cache");
e.printStackTrace(System.err); e.printStackTrace(System.err);
@ -148,7 +166,7 @@ public class TeaVMBuildDaemon extends UnicastRemoteObject implements TeaVMRemote
} }
TeaVMTool tool = new TeaVMTool(); TeaVMTool tool = new TeaVMTool();
tool.setIncremental(incremental); tool.setIncremental(incremental && request.incremental);
if (tool.isIncremental()) { if (tool.isIncremental()) {
tool.setCacheDirectory(incrementalCache); tool.setCacheDirectory(incrementalCache);
} }
@ -157,7 +175,7 @@ public class TeaVMBuildDaemon extends UnicastRemoteObject implements TeaVMRemote
tool.setTargetType(request.targetType); tool.setTargetType(request.targetType);
tool.setMainClass(request.mainClass); tool.setMainClass(request.mainClass);
tool.setTargetDirectory(new File(request.targetDirectory)); tool.setTargetDirectory(new File(request.targetDirectory));
tool.setClassLoader(buildClassLoader(request.classPath)); tool.setClassLoader(buildClassLoader(request.classPath, incremental && request.incremental));
tool.setSourceMapsFileGenerated(request.sourceMapsFileGenerated); tool.setSourceMapsFileGenerated(request.sourceMapsFileGenerated);
tool.setDebugInformationGenerated(request.debugInformationGenerated); tool.setDebugInformationGenerated(request.debugInformationGenerated);
@ -195,16 +213,47 @@ public class TeaVMBuildDaemon extends UnicastRemoteObject implements TeaVMRemote
return response; return response;
} }
private ClassLoader buildClassLoader(List<String> classPathEntries) { private ClassLoader buildClassLoader(List<String> classPathEntries, boolean incremental) {
URL[] urls = classPathEntries.stream().map(entry -> { System.out.println("Classpath: " + classPathEntries);
Function<String, URL> mapper = entry -> {
try { try {
return new File(entry).toURI().toURL(); return new File(entry).toURI().toURL();
} catch (MalformedURLException e) { } catch (MalformedURLException e) {
throw new RuntimeException(entry); throw new RuntimeException(entry);
} }
}).toArray(URL[]::new); };
List<String> jarEntries = classPathEntries.stream()
.filter(entry -> entry.endsWith(".jar"))
.collect(Collectors.toList());
return new URLClassLoader(urls); ClassLoader jarClassLoader = null;
if (incremental) {
if (jarEntries.equals(lastJarClassPath) && lastJarClassLoader != null) {
jarClassLoader = lastJarClassLoader;
System.out.println("Reusing previous class path");
}
} else {
lastJarClassLoader = null;
lastJarClassPath = null;
}
if (jarClassLoader == null) {
URL[] jarUrls = jarEntries.stream()
.map(mapper)
.toArray(URL[]::new);
jarClassLoader = new URLClassLoader(jarUrls);
}
if (incremental) {
lastJarClassPath = jarEntries;
lastJarClassLoader = jarClassLoader;
}
URL[] urls = classPathEntries.stream()
.filter(entry -> !entry.endsWith(".jar"))
.map(mapper)
.toArray(URL[]::new);
return new URLClassLoader(urls, jarClassLoader);
} }
private TeaVMProgressListener createProgressListener(TeaVMRemoteBuildCallback callback) { private TeaVMProgressListener createProgressListener(TeaVMRemoteBuildCallback callback) {
@ -259,16 +308,24 @@ public class TeaVMBuildDaemon extends UnicastRemoteObject implements TeaVMRemote
} }
sb.append(line).append('\n'); sb.append(line).append('\n');
} }
IOUtils.closeQuietly(stderrReader);
IOUtils.closeQuietly(stdoutReader);
throw new IllegalStateException("Could not start daemon. Stderr: " + sb); throw new IllegalStateException("Could not start daemon. Stderr: " + sb);
} }
int port = Integer.parseInt(line.substring(DAEMON_MESSAGE_PREFIX.length())); int port = Integer.parseInt(line.substring(DAEMON_MESSAGE_PREFIX.length()));
new Thread(new DaemonProcessOutputWatcher(log, stdoutReader, "stdout", false)).start(); daemonThread(new DaemonProcessOutputWatcher(log, stdoutReader, "stdout", false)).start();
new Thread(new DaemonProcessOutputWatcher(log, stderrReader, "stderr", true)).start(); daemonThread(new DaemonProcessOutputWatcher(log, stderrReader, "stderr", true)).start();
return new TeaVMDaemonInfo(port, process); return new TeaVMDaemonInfo(port, process);
} }
private static Thread daemonThread(Runnable runnable) {
Thread thread = new Thread(runnable);
thread.setDaemon(true);
return thread;
}
static class DaemonProcessOutputWatcher implements Runnable { static class DaemonProcessOutputWatcher implements Runnable {
private Log log; private Log log;
private BufferedReader reader; private BufferedReader reader;
@ -285,11 +342,16 @@ public class TeaVMBuildDaemon extends UnicastRemoteObject implements TeaVMRemote
@Override @Override
public void run() { public void run() {
try { try {
String line = reader.readLine(); while (true) {
if (isError) { String line = reader.readLine();
log.error("Build daemon [" + name + "]: " + line); if (line == null) {
} else { break;
log.info("Build daemon [" + name + "]: " + line); }
if (isError) {
log.error("Build daemon [" + name + "]: " + line);
} else {
log.info("Build daemon [" + name + "]: " + line);
}
} }
} catch (IOException e) { } catch (IOException e) {
log.error("Error reading build daemon output", e); log.error("Error reading build daemon output", e);

View File

@ -97,8 +97,8 @@ public class TeaVMSettingsEditorTab implements SearchableConfigurable {
if (incrementalCheckBox.isSelected() && !daemonComponent.isIncremental()) { if (incrementalCheckBox.isSelected() && !daemonComponent.isIncremental()) {
shouldRestartDaemon = true; shouldRestartDaemon = true;
daemonComponent.setIncremental(incrementalCheckBox.isSelected());
} }
daemonComponent.setIncremental(incrementalCheckBox.isSelected());
if (daemonCheckBox.isSelected()) { if (daemonCheckBox.isSelected()) {
if (!daemonComponent.isDaemonRunning()) { if (!daemonComponent.isDaemonRunning()) {