diff --git a/tools/idea/idea-artifacts/dep-pom.xml b/tools/idea/idea-artifacts/dep-pom.xml
index 87840e85f..d52e6c7e6 100644
--- a/tools/idea/idea-artifacts/dep-pom.xml
+++ b/tools/idea/idea-artifacts/dep-pom.xml
@@ -251,6 +251,17 @@
jps-builders
+
+ log4j
+
+ install-file
+
+ prepare-package
+
+ dependencies/idea/lib/commons-logging-1.2.jar
+ commons-logging
+
+
dependencies/maven
diff --git a/tools/idea/jps-common/src/main/java/org/teavm/idea/jps/model/TeaVMBuildStrategy.java b/tools/idea/jps-common/src/main/java/org/teavm/idea/jps/model/TeaVMBuildStrategy.java
index 91efb2ce0..c9cc9ca9e 100644
--- a/tools/idea/jps-common/src/main/java/org/teavm/idea/jps/model/TeaVMBuildStrategy.java
+++ b/tools/idea/jps-common/src/main/java/org/teavm/idea/jps/model/TeaVMBuildStrategy.java
@@ -42,5 +42,7 @@ public interface TeaVMBuildStrategy {
void setProgressListener(TeaVMProgressListener progressListener);
+ void setIncremental(boolean incremental);
+
TeaVMBuildResult build();
}
diff --git a/tools/idea/jps-common/src/main/java/org/teavm/idea/jps/model/TeaVMJpsWorkspaceConfiguration.java b/tools/idea/jps-common/src/main/java/org/teavm/idea/jps/model/TeaVMJpsWorkspaceConfiguration.java
index d6e57cc1f..da8ceb373 100644
--- a/tools/idea/jps-common/src/main/java/org/teavm/idea/jps/model/TeaVMJpsWorkspaceConfiguration.java
+++ b/tools/idea/jps-common/src/main/java/org/teavm/idea/jps/model/TeaVMJpsWorkspaceConfiguration.java
@@ -20,6 +20,7 @@ import org.jetbrains.jps.model.ex.JpsElementBase;
public class TeaVMJpsWorkspaceConfiguration extends JpsElementBase {
private boolean daemonEnabled;
+ private boolean incremental;
public boolean isDaemonEnabled() {
return daemonEnabled;
@@ -29,6 +30,14 @@ public class TeaVMJpsWorkspaceConfiguration extends JpsElementBase${idea.version}
provided
+
+ org.teavm.idea
+ commons-logging
+ ${idea.version}
+ provided
+
org.teavm.idea
teavm
diff --git a/tools/idea/plugin/src/main/java/org/teavm/idea/TeaVMDaemonComponent.java b/tools/idea/plugin/src/main/java/org/teavm/idea/TeaVMDaemonComponent.java
index 51a61e0d9..27f22fb80 100644
--- a/tools/idea/plugin/src/main/java/org/teavm/idea/TeaVMDaemonComponent.java
+++ b/tools/idea/plugin/src/main/java/org/teavm/idea/TeaVMDaemonComponent.java
@@ -25,6 +25,7 @@ import org.teavm.idea.jps.model.TeaVMJpsWorkspaceConfiguration;
public class TeaVMDaemonComponent implements ApplicationComponent {
private TeaVMDaemonInfo daemonInfo;
+ private boolean incremental;
@Override
public void initComponent() {
@@ -32,6 +33,7 @@ public class TeaVMDaemonComponent implements ApplicationComponent {
TeaVMWorkspaceConfigurationStorage.class);
if (configurationStorage != null) {
TeaVMJpsWorkspaceConfiguration configuration = configurationStorage.getState();
+ incremental = configuration.isIncremental();
if (configuration.isDaemonEnabled()) {
startDaemon();
}
@@ -60,7 +62,7 @@ public class TeaVMDaemonComponent implements ApplicationComponent {
public void startDaemon() {
if (daemonInfo == null) {
try {
- daemonInfo = TeaVMBuildDaemon.start();
+ daemonInfo = TeaVMBuildDaemon.start(incremental);
} catch (IOException e) {
throw new RuntimeException(e);
}
@@ -76,6 +78,21 @@ public class TeaVMDaemonComponent implements ApplicationComponent {
}
}
+ public boolean isIncremental() {
+ return incremental;
+ }
+
+ public void setIncremental(boolean incremental) {
+ this.incremental = incremental;
+ }
+
+ public void applyChanges() {
+ TeaVMWorkspaceConfigurationStorage configurationStorage = getConfigurationStorage();
+ TeaVMJpsWorkspaceConfiguration configuration = configurationStorage.getState();
+ configuration.setIncremental(incremental);
+ configurationStorage.loadState(configuration);
+ }
+
private void updateConfiguration(boolean daemonEnabled) {
TeaVMWorkspaceConfigurationStorage configurationStorage = getConfigurationStorage();
TeaVMJpsWorkspaceConfiguration configuration = configurationStorage.getState();
diff --git a/tools/idea/plugin/src/main/java/org/teavm/idea/daemon/TeaVMBuildDaemon.java b/tools/idea/plugin/src/main/java/org/teavm/idea/daemon/TeaVMBuildDaemon.java
index 361d19205..d650be9b8 100644
--- a/tools/idea/plugin/src/main/java/org/teavm/idea/daemon/TeaVMBuildDaemon.java
+++ b/tools/idea/plugin/src/main/java/org/teavm/idea/daemon/TeaVMBuildDaemon.java
@@ -25,6 +25,7 @@ import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
+import java.nio.file.Files;
import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
@@ -37,6 +38,9 @@ import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.stream.Collectors;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.teavm.idea.jps.remote.TeaVMRemoteBuildCallback;
import org.teavm.idea.jps.remote.TeaVMRemoteBuildRequest;
import org.teavm.idea.jps.remote.TeaVMRemoteBuildResponse;
@@ -58,8 +62,11 @@ public class TeaVMBuildDaemon extends UnicastRemoteObject implements TeaVMRemote
private static final String DAEMON_CLASS = TeaVMBuildDaemon.class.getName().replace('.', '/') + ".class";
private static final int DAEMON_CLASS_DEPTH;
private static final String DAEMON_MESSAGE_PREFIX = "TeaVM daemon port: ";
+ private static final String INCREMENTAL_PROPERTY = "teavm.daemon.incremental";
+ private boolean incremental;
private int port;
private Registry registry;
+ private File incrementalCache;
static {
int depth = 0;
@@ -71,8 +78,9 @@ public class TeaVMBuildDaemon extends UnicastRemoteObject implements TeaVMRemote
DAEMON_CLASS_DEPTH = depth;
}
- TeaVMBuildDaemon() throws RemoteException {
+ TeaVMBuildDaemon(boolean incremental) throws RemoteException {
super();
+ this.incremental = incremental;
Random random = new Random();
for (int i = 0; i < 20; ++i) {
port = random.nextInt(MAX_PORT - MIN_PORT) + MIN_PORT;
@@ -86,14 +94,36 @@ public class TeaVMBuildDaemon extends UnicastRemoteObject implements TeaVMRemote
} catch (RemoteException | AlreadyBoundException e) {
throw new IllegalStateException("Could not bind remote build assistant service", e);
}
+
+ setupIncrementalCache();
+
return;
}
throw new IllegalStateException("Could not create RMI registry");
}
+ private void setupIncrementalCache() {
+ if (!incremental) {
+ return;
+ }
+
+ try {
+ incrementalCache = Files.createTempDirectory("teavm-cache").toFile();
+ incrementalCache.deleteOnExit();
+ } catch (IOException e) {
+ System.err.println("Could not setup incremental cache");
+ e.printStackTrace(System.err);
+ incremental = false;
+ }
+ }
+
public static void main(String[] args) throws RemoteException {
- TeaVMBuildDaemon daemon = new TeaVMBuildDaemon();
+ boolean incremental = Boolean.parseBoolean(System.getProperty(INCREMENTAL_PROPERTY, "false"));
+ TeaVMBuildDaemon daemon = new TeaVMBuildDaemon(incremental);
System.out.println(DAEMON_MESSAGE_PREFIX + daemon.port);
+ if (daemon.incrementalCache != null) {
+ System.out.println("Incremental cache set up in " + daemon.incrementalCache);
+ }
while (true) {
try {
Thread.sleep(1000);
@@ -106,7 +136,22 @@ public class TeaVMBuildDaemon extends UnicastRemoteObject implements TeaVMRemote
@Override
public TeaVMRemoteBuildResponse build(TeaVMRemoteBuildRequest request, TeaVMRemoteBuildCallback callback)
throws RemoteException {
+ System.out.println("Build started");
+
+ if (!request.incremental && incremental) {
+ try {
+ System.out.println("Dropping incremental cache");
+ FileUtils.cleanDirectory(incrementalCache);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
TeaVMTool tool = new TeaVMTool();
+ tool.setIncremental(incremental);
+ if (tool.isIncremental()) {
+ tool.setCacheDirectory(incrementalCache);
+ }
tool.setProgressListener(createProgressListener(callback));
tool.setLog(new EmptyTeaVMToolLog());
tool.setTargetType(request.targetType);
@@ -130,6 +175,7 @@ public class TeaVMBuildDaemon extends UnicastRemoteObject implements TeaVMRemote
boolean errorOccurred = false;
try {
tool.generate();
+ System.out.println("Build complete");
} catch (TeaVMToolException | RuntimeException | Error e) {
e.printStackTrace(System.err);
errorOccurred = true;
@@ -190,19 +236,24 @@ public class TeaVMBuildDaemon extends UnicastRemoteObject implements TeaVMRemote
};
}
- public static TeaVMDaemonInfo start() throws IOException {
+ public static TeaVMDaemonInfo start(boolean incremental) throws IOException {
String javaHome = System.getProperty("java.home");
String javaCommand = javaHome + "/bin/java";
String classPath = detectClassPath().stream().collect(Collectors.joining(File.pathSeparator));
- ProcessBuilder builder = new ProcessBuilder(javaCommand, "-cp", classPath, TeaVMBuildDaemon.class.getName());
+ ProcessBuilder builder = new ProcessBuilder(javaCommand, "-cp", classPath,
+ "-D" + INCREMENTAL_PROPERTY + "=" + incremental,
+ TeaVMBuildDaemon.class.getName());
Process process = builder.start();
- BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8"));
- String line = reader.readLine();
+
+ Log log = LogFactory.getLog(TeaVMBuildDaemon.class);
+
+ BufferedReader stdoutReader = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8"));
+ BufferedReader stderrReader = new BufferedReader(new InputStreamReader(process.getErrorStream(), "UTF-8"));
+ String line = stdoutReader.readLine();
if (line == null || !line.startsWith(DAEMON_MESSAGE_PREFIX)) {
StringBuilder sb = new StringBuilder();
- reader = new BufferedReader(new InputStreamReader(process.getErrorStream(), "UTF-8"));
while (true) {
- line = reader.readLine();
+ line = stderrReader.readLine();
if (line == null) {
break;
}
@@ -211,9 +262,41 @@ public class TeaVMBuildDaemon extends UnicastRemoteObject implements TeaVMRemote
throw new IllegalStateException("Could not start daemon. Stderr: " + sb);
}
int port = Integer.parseInt(line.substring(DAEMON_MESSAGE_PREFIX.length()));
+
+ new Thread(new DaemonProcessOutputWatcher(log, stdoutReader, "stdout", false)).start();
+ new Thread(new DaemonProcessOutputWatcher(log, stderrReader, "stderr", true)).start();
+
return new TeaVMDaemonInfo(port, process);
}
+ static class DaemonProcessOutputWatcher implements Runnable {
+ private Log log;
+ private BufferedReader reader;
+ private String name;
+ private boolean isError;
+
+ public DaemonProcessOutputWatcher(Log log, BufferedReader reader, String name, boolean isError) {
+ this.log = log;
+ this.reader = reader;
+ this.name = name;
+ this.isError = isError;
+ }
+
+ @Override
+ public void run() {
+ try {
+ String line = reader.readLine();
+ if (isError) {
+ log.error("Build daemon [" + name + "]: " + line);
+ } else {
+ log.info("Build daemon [" + name + "]: " + line);
+ }
+ } catch (IOException e) {
+ log.error("Error reading build daemon output", e);
+ }
+ }
+ }
+
private static List detectClassPath() {
IdeaPluginDescriptor plugin = PluginManager.getPlugin(PluginId.getId("org.teavm.idea"));
Set visited = new HashSet<>();
diff --git a/tools/idea/plugin/src/main/java/org/teavm/idea/ui/TeaVMSettingsEditorTab.java b/tools/idea/plugin/src/main/java/org/teavm/idea/ui/TeaVMSettingsEditorTab.java
index 3433aea0a..9dce07478 100644
--- a/tools/idea/plugin/src/main/java/org/teavm/idea/ui/TeaVMSettingsEditorTab.java
+++ b/tools/idea/plugin/src/main/java/org/teavm/idea/ui/TeaVMSettingsEditorTab.java
@@ -30,6 +30,7 @@ import org.teavm.idea.TeaVMDaemonComponent;
public class TeaVMSettingsEditorTab implements SearchableConfigurable {
private JPanel contentPane;
private JCheckBox daemonCheckBox;
+ private JCheckBox incrementalCheckBox;
private TeaVMDaemonComponent daemonComponent;
public TeaVMSettingsEditorTab(TeaVMDaemonComponent daemonComponent) {
@@ -37,8 +38,11 @@ public class TeaVMSettingsEditorTab implements SearchableConfigurable {
contentPane = new JPanel();
daemonCheckBox = new JCheckBox("use build daemon (can increase performance in most cases)");
+ incrementalCheckBox = new JCheckBox("incremental build (only available with daemon)");
contentPane.setLayout(new GridBagLayout());
+ daemonCheckBox.addActionListener(e -> incrementalCheckBox.setEnabled(daemonCheckBox.isSelected()));
+
GridBagConstraints labelConstraints = new GridBagConstraints();
labelConstraints.gridwidth = GridBagConstraints.REMAINDER;
labelConstraints.anchor = GridBagConstraints.BASELINE_LEADING;
@@ -48,6 +52,13 @@ public class TeaVMSettingsEditorTab implements SearchableConfigurable {
labelConstraints.insets.right = 5;
contentPane.add(daemonCheckBox, labelConstraints);
+ contentPane.add(incrementalCheckBox, labelConstraints);
+
+ GridBagConstraints constraints = new GridBagConstraints();
+ constraints.fill = GridBagConstraints.BOTH;
+ constraints.weighty = 100;
+ constraints.weightx = 1;
+ contentPane.add(new JPanel(), constraints);
}
@NotNull
@@ -76,20 +87,40 @@ public class TeaVMSettingsEditorTab implements SearchableConfigurable {
@Override
public boolean isModified() {
- return daemonCheckBox.isSelected() != daemonComponent.isDaemonRunning();
+ return daemonCheckBox.isSelected() != daemonComponent.isDaemonRunning()
+ || incrementalCheckBox.isSelected() != daemonComponent.isIncremental();
}
@Override
public void apply() throws ConfigurationException {
+ boolean shouldRestartDaemon = true;
+
+ if (incrementalCheckBox.isSelected() && !daemonComponent.isIncremental()) {
+ shouldRestartDaemon = true;
+ daemonComponent.setIncremental(incrementalCheckBox.isSelected());
+ }
+
if (daemonCheckBox.isSelected()) {
- daemonComponent.startDaemon();
+ if (!daemonComponent.isDaemonRunning()) {
+ daemonComponent.startDaemon();
+ shouldRestartDaemon = false;
+ }
} else {
daemonComponent.stopDaemon();
+ shouldRestartDaemon = false;
}
+
+ if (shouldRestartDaemon) {
+ daemonComponent.stopDaemon();
+ daemonComponent.startDaemon();
+ }
+
+ daemonComponent.applyChanges();
}
@Override
public void reset() {
daemonCheckBox.setSelected(daemonComponent.isDaemonRunning());
+ incrementalCheckBox.setSelected(daemonComponent.isIncremental());
}
}