Add incremental build to JPS

This commit is contained in:
Alexey Andreev 2017-06-01 15:14:30 +03:00
parent 87f7ee4b72
commit 36f2932caa
11 changed files with 188 additions and 13 deletions

View File

@ -251,6 +251,17 @@
<artifactId>jps-builders</artifactId>
</configuration>
</execution>
<execution>
<id>log4j</id>
<goals>
<goal>install-file</goal>
</goals>
<phase>prepare-package</phase>
<configuration>
<file>dependencies/idea/lib/commons-logging-1.2.jar</file>
<artifactId>commons-logging</artifactId>
</configuration>
</execution>
</executions>
<configuration>
<localRepositoryPath>dependencies/maven</localRepositoryPath>

View File

@ -42,5 +42,7 @@ public interface TeaVMBuildStrategy {
void setProgressListener(TeaVMProgressListener progressListener);
void setIncremental(boolean incremental);
TeaVMBuildResult build();
}

View File

@ -20,6 +20,7 @@ import org.jetbrains.jps.model.ex.JpsElementBase;
public class TeaVMJpsWorkspaceConfiguration extends JpsElementBase<TeaVMJpsWorkspaceConfiguration> {
private boolean daemonEnabled;
private boolean incremental;
public boolean isDaemonEnabled() {
return daemonEnabled;
@ -29,6 +30,14 @@ public class TeaVMJpsWorkspaceConfiguration extends JpsElementBase<TeaVMJpsWorks
this.daemonEnabled = daemonEnabled;
}
public boolean isIncremental() {
return incremental;
}
public void setIncremental(boolean incremental) {
this.incremental = incremental;
}
@NotNull
@Override
public TeaVMJpsWorkspaceConfiguration createCopy() {
@ -40,5 +49,6 @@ public class TeaVMJpsWorkspaceConfiguration extends JpsElementBase<TeaVMJpsWorks
@Override
public void applyChanges(@NotNull TeaVMJpsWorkspaceConfiguration configuration) {
daemonEnabled = configuration.daemonEnabled;
incremental = configuration.incremental;
}
}

View File

@ -30,4 +30,5 @@ public class TeaVMRemoteBuildRequest implements Serializable {
public boolean sourceMapsFileGenerated;
public boolean debugInformationGenerated;
public boolean sourceFilesCopied;
public boolean incremental;
}

View File

@ -110,6 +110,10 @@ public class InProcessBuildStrategy implements TeaVMBuildStrategy {
this.progressListener = progressListener;
}
@Override
public void setIncremental(boolean incremental) {
}
@Override
public TeaVMBuildResult build() {
TeaVMTool tool = new TeaVMTool();

View File

@ -97,6 +97,11 @@ public class RemoteBuildStrategy implements TeaVMBuildStrategy {
this.progressListener = progressListener;
}
@Override
public void setIncremental(boolean incremental) {
request.incremental = incremental;
}
@Override
public TeaVMBuildResult build() {
TeaVMRemoteBuildResponse response;

View File

@ -113,6 +113,7 @@ class TeaVMBuild {
buildStrategy.setTargetType(config.getTargetType());
buildStrategy.setTargetDirectory(config.getTargetDirectory());
buildStrategy.setProgressListener(createProgressListener(context));
buildStrategy.setIncremental(!isRebuild(target));
TeaVMBuildResult buildResult = buildStrategy.build();
if (!buildResult.isErrorOccurred() && buildResult.getProblems().getSevereProblems().isEmpty()) {
@ -399,9 +400,13 @@ class TeaVMBuild {
return lines.getAll();
}
private boolean isRebuild(TeaVMBuildTarget target) {
return !context.getScope().isBuildIncrementally(target.getTargetType())
|| context.getScope().isBuildForced(target);
}
private boolean hasChanges(TeaVMBuildTarget target) {
if (!context.getScope().isBuildIncrementally(target.getTargetType())
|| context.getScope().isBuildForced(target)) {
if (isRebuild(target)) {
return true;
}

View File

@ -82,6 +82,12 @@
<version>${idea.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.teavm.idea</groupId>
<artifactId>commons-logging</artifactId>
<version>${idea.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.teavm.idea</groupId>
<artifactId>teavm</artifactId>

View File

@ -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();

View File

@ -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<String> detectClassPath() {
IdeaPluginDescriptor plugin = PluginManager.getPlugin(PluginId.getId("org.teavm.idea"));
Set<File> visited = new HashSet<>();

View File

@ -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());
}
}