Add TeaVM build daemon to IDEA

This commit is contained in:
Alexey Andreev 2017-03-15 22:42:35 +03:00
parent 56398f58f9
commit 1ae4a95128
15 changed files with 865 additions and 18 deletions

View File

@ -0,0 +1,44 @@
/*
* Copyright 2017 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.idea.jps.model;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.model.ex.JpsElementBase;
public class TeaVMJpsWorkspaceConfiguration extends JpsElementBase<TeaVMJpsWorkspaceConfiguration> {
private boolean daemonEnabled;
public boolean isDaemonEnabled() {
return daemonEnabled;
}
public void setDaemonEnabled(boolean daemonEnabled) {
this.daemonEnabled = daemonEnabled;
}
@NotNull
@Override
public TeaVMJpsWorkspaceConfiguration createCopy() {
TeaVMJpsWorkspaceConfiguration copy = new TeaVMJpsWorkspaceConfiguration();
copy.applyChanges(this);
return copy;
}
@Override
public void applyChanges(@NotNull TeaVMJpsWorkspaceConfiguration configuration) {
daemonEnabled = configuration.daemonEnabled;
}
}

View File

@ -0,0 +1,27 @@
/*
* Copyright 2017 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.idea.jps.remote;
import java.rmi.Remote;
import java.rmi.RemoteException;
import org.teavm.vm.TeaVMPhase;
import org.teavm.vm.TeaVMProgressFeedback;
public interface TeaVMRemoteBuildCallback extends Remote {
TeaVMProgressFeedback phaseStarted(TeaVMPhase phase, int count) throws RemoteException;
TeaVMProgressFeedback progressReached(int progress) throws RemoteException;
}

View File

@ -0,0 +1,33 @@
/*
* Copyright 2017 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.idea.jps.remote;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import org.teavm.tooling.TeaVMTargetType;
public class TeaVMRemoteBuildRequest implements Serializable {
public final List<String> sourceDirectories = new ArrayList<>();
public final List<String> sourceJarFiles = new ArrayList<>();
public final List<String> classPath = new ArrayList<>();
public TeaVMTargetType targetType;
public String mainClass;
public String targetDirectory;
public boolean sourceMapsFileGenerated;
public boolean debugInformationGenerated;
public boolean sourceFilesCopied;
}

View File

@ -0,0 +1,33 @@
/*
* Copyright 2017 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.idea.jps.remote;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.teavm.callgraph.CallGraph;
import org.teavm.diagnostics.Problem;
public class TeaVMRemoteBuildResponse implements Serializable {
public CallGraph callGraph;
public boolean errorOccurred;
public final List<Problem> problems = new ArrayList<>();
public final List<Problem> severeProblems = new ArrayList<>();
public final Set<String> usedResources = new HashSet<>();
public final Set<String> classes = new HashSet<>();
}

View File

@ -0,0 +1,27 @@
/*
* Copyright 2017 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.idea.jps.remote;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface TeaVMRemoteBuildService extends Remote {
String REMOTE_PORT = "teavm.daemon.remote-port";
String ID = "TeaVM-Daemon";
TeaVMRemoteBuildResponse build(TeaVMRemoteBuildRequest request, TeaVMRemoteBuildCallback callback)
throws RemoteException;
}

View File

@ -0,0 +1,188 @@
/*
* Copyright 2017 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.idea.jps;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.Collection;
import java.util.List;
import org.jetbrains.jps.incremental.CompileContext;
import org.jetbrains.jps.incremental.messages.ProgressMessage;
import org.teavm.callgraph.CallGraph;
import org.teavm.diagnostics.Problem;
import org.teavm.diagnostics.ProblemProvider;
import org.teavm.idea.jps.model.TeaVMBuildResult;
import org.teavm.idea.jps.model.TeaVMBuildStrategy;
import org.teavm.idea.jps.remote.TeaVMRemoteBuildCallback;
import org.teavm.idea.jps.remote.TeaVMRemoteBuildRequest;
import org.teavm.idea.jps.remote.TeaVMRemoteBuildResponse;
import org.teavm.idea.jps.remote.TeaVMRemoteBuildService;
import org.teavm.tooling.TeaVMTargetType;
import org.teavm.vm.TeaVMPhase;
import org.teavm.vm.TeaVMProgressFeedback;
public class RemoteBuildStrategy implements TeaVMBuildStrategy {
private final CompileContext context;
private TeaVMRemoteBuildRequest request;
private TeaVMRemoteBuildService buildService;
public RemoteBuildStrategy(CompileContext context, TeaVMRemoteBuildService buildService) {
this.context = context;
this.buildService = buildService;
}
@Override
public void init() {
request = new TeaVMRemoteBuildRequest();
}
@Override
public void addSourcesDirectory(String directory) {
request.sourceDirectories.add(directory);
}
@Override
public void addSourcesJar(String jarFile) {
request.sourceJarFiles.add(jarFile);
}
@Override
public void setClassPathEntries(List<String> entries) {
request.classPath.addAll(entries);
}
@Override
public void setTargetType(TeaVMTargetType targetType) {
request.targetType = targetType;
}
@Override
public void setMainClass(String mainClass) {
request.mainClass = mainClass;
}
@Override
public void setTargetDirectory(String targetDirectory) {
request.targetDirectory = targetDirectory;
}
@Override
public void setSourceMapsFileGenerated(boolean sourceMapsFileGenerated) {
request.sourceMapsFileGenerated = sourceMapsFileGenerated;
}
@Override
public void setDebugInformationGenerated(boolean debugInformationGenerated) {
request.debugInformationGenerated = debugInformationGenerated;
}
@Override
public void setSourceFilesCopied(boolean sourceFilesCopied) {
request.sourceFilesCopied = sourceFilesCopied;
}
@Override
public TeaVMBuildResult build() {
TeaVMRemoteBuildResponse response;
try {
response = buildService.build(request, new CallbackImpl(context));
} catch (Throwable e) {
throw new RuntimeException(e);
}
return new TeaVMBuildResult() {
private ProblemProvider problems = new ProblemProvider() {
@Override
public List<Problem> getProblems() {
return response.problems;
}
@Override
public List<Problem> getSevereProblems() {
return response.severeProblems;
}
};
@Override
public CallGraph getCallGraph() {
return response.callGraph;
}
@Override
public boolean isErrorOccurred() {
return response.errorOccurred;
}
@Override
public ProblemProvider getProblems() {
return problems;
}
@Override
public Collection<String> getUsedResources() {
return response.usedResources;
}
@Override
public Collection<String> getClasses() {
return response.classes;
}
};
}
static class CallbackImpl extends UnicastRemoteObject implements TeaVMRemoteBuildCallback {
private CompileContext context;
int expectedCount;
TeaVMPhase currentPhase;
public CallbackImpl(CompileContext context) throws RemoteException {
super();
this.context = context;
}
@Override
public TeaVMProgressFeedback phaseStarted(TeaVMPhase phase, int count) throws RemoteException {
expectedCount = count;
context.processMessage(new ProgressMessage(phaseName(phase), 0));
currentPhase = phase;
return context.getCancelStatus().isCanceled() ? TeaVMProgressFeedback.CANCEL
: TeaVMProgressFeedback.CONTINUE;
}
@Override
public TeaVMProgressFeedback progressReached(int progress) throws RemoteException {
context.processMessage(new ProgressMessage(phaseName(currentPhase), (float) progress / expectedCount));
return context.getCancelStatus().isCanceled() ? TeaVMProgressFeedback.CANCEL
: TeaVMProgressFeedback.CONTINUE;
}
}
private static String phaseName(TeaVMPhase phase) {
switch (phase) {
case DEPENDENCY_CHECKING:
return "Discovering classes to compile";
case LINKING:
return "Resolving method invocations";
case DECOMPILATION:
return "Compiling classes";
case OPTIMIZATION:
return "Optimizing code";
case RENDERING:
return "Building JS file";
default:
throw new AssertionError();
}
}
}

View File

@ -33,10 +33,13 @@ import org.jetbrains.jps.incremental.ProjectBuildException;
import org.jetbrains.jps.incremental.messages.BuildMessage;
import org.jetbrains.jps.incremental.messages.CompilerMessage;
import org.jetbrains.jps.model.module.JpsModule;
import org.teavm.idea.jps.model.TeaVMBuildStrategy;
import org.teavm.idea.jps.remote.TeaVMBuilderAssistant;
import org.teavm.idea.jps.remote.TeaVMRemoteBuildService;
public class TeaVMBuilder extends ModuleLevelBuilder {
private static TeaVMBuilderAssistant assistant;
private TeaVMBuilderAssistant assistant;
private TeaVMRemoteBuildService buildService;
public TeaVMBuilder() {
super(BuilderCategory.CLASS_POST_PROCESSOR);
@ -50,6 +53,16 @@ public class TeaVMBuilder extends ModuleLevelBuilder {
e.printStackTrace();
}
}
String daemonPortString = System.getProperty(TeaVMRemoteBuildService.REMOTE_PORT);
if (daemonPortString != null) {
try {
Registry registry = LocateRegistry.getRegistry(Integer.parseInt(daemonPortString));
buildService = (TeaVMRemoteBuildService) registry.lookup(TeaVMRemoteBuildService.ID);
} catch (NumberFormatException | RemoteException | NotBoundException e) {
e.printStackTrace();
}
}
}
@Override
@ -63,7 +76,10 @@ public class TeaVMBuilder extends ModuleLevelBuilder {
boolean doneSomething = false;
TeaVMBuild build = new TeaVMBuild(context, assistant, new InProcessBuildStrategy(context));
TeaVMBuildStrategy buildStrategy = buildService != null
? new RemoteBuildStrategy(context, buildService)
: new InProcessBuildStrategy(context);
TeaVMBuild build = new TeaVMBuild(context, assistant, buildStrategy);
for (JpsModule module : chunk.getModules()) {
doneSomething |= build.perform(module, chunk.representativeTarget());
if (context.getCancelStatus().isCanceled()) {

View File

@ -0,0 +1,91 @@
/*
* Copyright 2017 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.idea;
import com.intellij.openapi.components.ApplicationComponent;
import com.intellij.openapi.components.ServiceManager;
import java.io.IOException;
import org.jetbrains.annotations.NotNull;
import org.teavm.idea.daemon.TeaVMBuildDaemon;
import org.teavm.idea.daemon.TeaVMDaemonInfo;
import org.teavm.idea.jps.model.TeaVMJpsWorkspaceConfiguration;
public class TeaVMDaemonComponent implements ApplicationComponent {
private TeaVMDaemonInfo daemonInfo;
@Override
public void initComponent() {
TeaVMWorkspaceConfigurationStorage configurationStorage = ServiceManager.getService(
TeaVMWorkspaceConfigurationStorage.class);
if (configurationStorage != null) {
TeaVMJpsWorkspaceConfiguration configuration = configurationStorage.getState();
if (configuration.isDaemonEnabled()) {
startDaemon();
}
}
}
private TeaVMWorkspaceConfigurationStorage getConfigurationStorage() {
return ServiceManager.getService(TeaVMWorkspaceConfigurationStorage.class);
}
@Override
public void disposeComponent() {
if (daemonInfo != null) {
daemonInfo.getProcess().destroy();
}
}
public boolean isDaemonRunning() {
return daemonInfo != null;
}
public int getDaemonPort() {
return daemonInfo.getPort();
}
public void startDaemon() {
if (daemonInfo == null) {
try {
daemonInfo = TeaVMBuildDaemon.start();
} catch (IOException e) {
throw new RuntimeException(e);
}
updateConfiguration(true);
}
}
public void stopDaemon() {
if (daemonInfo != null) {
daemonInfo.getProcess().destroy();
daemonInfo = null;
updateConfiguration(false);
}
}
private void updateConfiguration(boolean daemonEnabled) {
TeaVMWorkspaceConfigurationStorage configurationStorage = getConfigurationStorage();
TeaVMJpsWorkspaceConfiguration configuration = configurationStorage.getState();
configuration.setDaemonEnabled(daemonEnabled);
configurationStorage.loadState(configuration);
}
@NotNull
@Override
public String getComponentName() {
return "TeaVM daemon";
}
}

View File

@ -17,20 +17,28 @@ package org.teavm.idea;
import static org.teavm.idea.jps.remote.TeaVMBuilderAssistant.REMOTE_PORT;
import com.intellij.compiler.server.BuildProcessParametersProvider;
import java.util.Collections;
import java.util.ArrayList;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.teavm.idea.jps.remote.TeaVMRemoteBuildService;
public class TeaVMJPSConfigurator extends BuildProcessParametersProvider {
private TeaVMJPSRemoteService remoteService;
private TeaVMDaemonComponent daemonComponent;
public TeaVMJPSConfigurator(TeaVMJPSRemoteService remoteService) {
public TeaVMJPSConfigurator(TeaVMJPSRemoteService remoteService, TeaVMDaemonComponent daemonComponent) {
this.remoteService = remoteService;
this.daemonComponent = daemonComponent;
}
@NotNull
@Override
public List<String> getVMArguments() {
return Collections.singletonList("-D" + REMOTE_PORT + "=" + remoteService.getPort());
List<String> result = new ArrayList<>();
result.add("-D" + REMOTE_PORT + "=" + remoteService.getPort());
if (daemonComponent.isDaemonRunning()) {
result.add("-D" + TeaVMRemoteBuildService.REMOTE_PORT + "=" + daemonComponent.getDaemonPort());
}
return result;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016 Alexey Andreev.
* Copyright 2017 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,20 +19,20 @@ import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import org.jetbrains.annotations.Nullable;
import org.teavm.idea.jps.model.TeaVMJpsConfiguration;
import org.teavm.idea.jps.model.TeaVMJpsWorkspaceConfiguration;
@State(name = "teavm", storages = @Storage(value = "$MODULE_FILE$"))
public class TeaVMConfigurationStorage implements PersistentStateComponent<TeaVMJpsConfiguration> {
private TeaVMJpsConfiguration state = new TeaVMJpsConfiguration();
@State(name = "teavm", storages = @Storage(value = "teavm.xml"))
public class TeaVMWorkspaceConfigurationStorage implements PersistentStateComponent<TeaVMJpsWorkspaceConfiguration> {
private TeaVMJpsWorkspaceConfiguration state = new TeaVMJpsWorkspaceConfiguration();
@Nullable
@Override
public TeaVMJpsConfiguration getState() {
public TeaVMJpsWorkspaceConfiguration getState() {
return state.createCopy();
}
@Override
public void loadState(TeaVMJpsConfiguration state) {
this.state.applyChanges(state);
public void loadState(TeaVMJpsWorkspaceConfiguration configuration) {
state.applyChanges(configuration);
}
}

View File

@ -0,0 +1,242 @@
/*
* Copyright 2017 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.idea.daemon;
import com.intellij.ide.plugins.IdeaPluginDescriptor;
import com.intellij.ide.plugins.PluginManager;
import com.intellij.openapi.extensions.PluginId;
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.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.stream.Collectors;
import org.teavm.idea.jps.remote.TeaVMRemoteBuildCallback;
import org.teavm.idea.jps.remote.TeaVMRemoteBuildRequest;
import org.teavm.idea.jps.remote.TeaVMRemoteBuildResponse;
import org.teavm.idea.jps.remote.TeaVMRemoteBuildService;
import org.teavm.tooling.EmptyTeaVMToolLog;
import org.teavm.tooling.TeaVMTool;
import org.teavm.tooling.TeaVMToolException;
import org.teavm.tooling.sources.DirectorySourceFileProvider;
import org.teavm.tooling.sources.JarSourceFileProvider;
import org.teavm.vm.TeaVMPhase;
import org.teavm.vm.TeaVMProgressFeedback;
import org.teavm.vm.TeaVMProgressListener;
public class TeaVMBuildDaemon extends UnicastRemoteObject implements TeaVMRemoteBuildService {
private static final int MIN_PORT = 10000;
private static final int MAX_PORT = 1 << 16;
private static final Set<String> KOTLIN_FILES = new HashSet<>(Arrays.asList("teavm-jps-common.jar",
"teavm-plugin.jar", "teavm.jar"));
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 int port;
private Registry registry;
static {
int depth = 0;
for (int i = 0; i < DAEMON_CLASS.length(); ++i) {
if (DAEMON_CLASS.charAt(i) == '/') {
depth++;
}
}
DAEMON_CLASS_DEPTH = depth;
}
TeaVMBuildDaemon() throws RemoteException {
super();
Random random = new Random();
for (int i = 0; i < 20; ++i) {
port = random.nextInt(MAX_PORT - MIN_PORT) + MIN_PORT;
try {
registry = LocateRegistry.createRegistry(port);
} catch (RemoteException e) {
continue;
}
try {
registry.bind(TeaVMRemoteBuildService.ID, this);
} catch (RemoteException | AlreadyBoundException e) {
throw new IllegalStateException("Could not bind remote build assistant service", e);
}
return;
}
throw new IllegalStateException("Could not create RMI registry");
}
public static void main(String[] args) throws RemoteException {
TeaVMBuildDaemon daemon = new TeaVMBuildDaemon();
System.out.println(DAEMON_MESSAGE_PREFIX + daemon.port);
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
break;
}
}
}
@Override
public TeaVMRemoteBuildResponse build(TeaVMRemoteBuildRequest request, TeaVMRemoteBuildCallback callback)
throws RemoteException {
TeaVMTool tool = new TeaVMTool();
tool.setProgressListener(createProgressListener(callback));
tool.setLog(new EmptyTeaVMToolLog());
tool.setTargetType(request.targetType);
tool.setMainClass(request.mainClass);
tool.setTargetDirectory(new File(request.targetDirectory));
tool.setClassLoader(buildClassLoader(request.classPath));
tool.setSourceMapsFileGenerated(request.sourceMapsFileGenerated);
tool.setDebugInformationGenerated(request.debugInformationGenerated);
tool.setSourceFilesCopied(request.sourceFilesCopied);
for (String sourceDirectory : request.sourceDirectories) {
tool.addSourceFileProvider(new DirectorySourceFileProvider(new File(sourceDirectory)));
}
for (String sourceJar : request.sourceJarFiles) {
tool.addSourceFileProvider(new JarSourceFileProvider(new File(sourceJar)));
}
boolean errorOccurred = false;
try {
tool.generate();
} catch (TeaVMToolException | RuntimeException | Error e) {
e.printStackTrace(System.err);
errorOccurred = true;
}
TeaVMRemoteBuildResponse response = new TeaVMRemoteBuildResponse();
response.errorOccurred = errorOccurred;
response.callGraph = tool.getDependencyInfo().getCallGraph();
response.problems.addAll(tool.getProblemProvider().getProblems());
response.severeProblems.addAll(tool.getProblemProvider().getSevereProblems());
response.classes.addAll(tool.getClasses());
response.usedResources.addAll(tool.getUsedResources());
return response;
}
private ClassLoader buildClassLoader(List<String> classPathEntries) {
URL[] urls = classPathEntries.stream().map(entry -> {
try {
return new File(entry).toURI().toURL();
} catch (MalformedURLException e) {
throw new RuntimeException(entry);
}
}).toArray(URL[]::new);
return new URLClassLoader(urls);
}
private TeaVMProgressListener createProgressListener(TeaVMRemoteBuildCallback callback) {
return new TeaVMProgressListener() {
private long lastReportedTime;
@Override
public TeaVMProgressFeedback phaseStarted(TeaVMPhase phase, int count) {
if ((System.currentTimeMillis() - lastReportedTime) > 100) {
lastReportedTime = System.currentTimeMillis();
try {
return callback.phaseStarted(phase, count);
} catch (RemoteException e) {
return TeaVMProgressFeedback.CANCEL;
}
} else {
return TeaVMProgressFeedback.CONTINUE;
}
}
@Override
public TeaVMProgressFeedback progressReached(int progress) {
if ((System.currentTimeMillis() - lastReportedTime) > 100) {
lastReportedTime = System.currentTimeMillis();
try {
return callback.progressReached(progress);
} catch (RemoteException e) {
return TeaVMProgressFeedback.CANCEL;
}
} else {
return TeaVMProgressFeedback.CONTINUE;
}
}
};
}
public static TeaVMDaemonInfo start() 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());
Process process = builder.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8"));
String line = reader.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();
if (line == null) {
break;
}
sb.append(line).append('\n');
}
throw new IllegalStateException("Could not start daemon. Stderr: " + sb);
}
int port = Integer.parseInt(line.substring(DAEMON_MESSAGE_PREFIX.length()));
return new TeaVMDaemonInfo(port, process);
}
private static List<String> detectClassPath() {
IdeaPluginDescriptor plugin = PluginManager.getPlugin(PluginId.getId("org.teavm.idea"));
Set<File> visited = new HashSet<>();
List<String> classPath = new ArrayList<>();
findInHierarchy(plugin.getPath(), classPath, visited);
return classPath;
}
private static void findInHierarchy(File file, List<String> targetFiles, Set<File> visited) {
if (!visited.add(file)) {
return;
}
if (file.isFile() && KOTLIN_FILES.contains(file.getName())) {
targetFiles.add(file.getAbsolutePath());
} else if (file.getPath().endsWith(DAEMON_CLASS)) {
for (int i = 0; i <= DAEMON_CLASS_DEPTH; ++i) {
file = file.getParentFile();
}
targetFiles.add(file.getAbsolutePath());
} else if (file.isDirectory()) {
for (File childFile : file.listFiles()) {
findInHierarchy(childFile, targetFiles, visited);
}
}
}
}

View File

@ -0,0 +1,34 @@
/*
* Copyright 2017 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.idea.daemon;
public class TeaVMDaemonInfo {
private int port;
private Process process;
TeaVMDaemonInfo(int port, Process process) {
this.port = port;
this.process = process;
}
public int getPort() {
return port;
}
public Process getProcess() {
return process;
}
}

View File

@ -0,0 +1,95 @@
/*
* Copyright 2017 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.idea.ui;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.options.SearchableConfigurable;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JPanel;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.teavm.idea.TeaVMDaemonComponent;
public class TeaVMSettingsEditorTab implements SearchableConfigurable {
private JPanel contentPane;
private JCheckBox daemonCheckBox;
private TeaVMDaemonComponent daemonComponent;
public TeaVMSettingsEditorTab(TeaVMDaemonComponent daemonComponent) {
this.daemonComponent = daemonComponent;
contentPane = new JPanel();
daemonCheckBox = new JCheckBox("use build daemon (can increase performance in most cases)");
contentPane.setLayout(new GridBagLayout());
GridBagConstraints labelConstraints = new GridBagConstraints();
labelConstraints.gridwidth = GridBagConstraints.REMAINDER;
labelConstraints.anchor = GridBagConstraints.BASELINE_LEADING;
labelConstraints.weightx = 1;
labelConstraints.weighty = 1;
labelConstraints.insets.left = 5;
labelConstraints.insets.right = 5;
contentPane.add(daemonCheckBox, labelConstraints);
}
@NotNull
@Override
public String getId() {
return "project.teavm.settings";
}
@Nls
@Override
public String getDisplayName() {
return "TeaVM";
}
@Nullable
@Override
public String getHelpTopic() {
return null;
}
@Nullable
@Override
public JComponent createComponent() {
return contentPane;
}
@Override
public boolean isModified() {
return daemonCheckBox.isSelected() != daemonComponent.isDaemonRunning();
}
@Override
public void apply() throws ConfigurationException {
if (daemonCheckBox.isSelected()) {
daemonComponent.startDaemon();
} else {
daemonComponent.stopDaemon();
}
}
@Override
public void reset() {
daemonCheckBox.setSelected(daemonComponent.isDaemonRunning());
}
}

View File

@ -7,13 +7,10 @@
<depends>org.jetbrains.idea.maven</depends>
<description><![CDATA[
Enter short description for your plugin here.<br>
<em>most HTML tags may be used</em>
Plugin that allows to run TeaVM compiler and debug applications generated by TeaVM.
]]></description>
<change-notes><![CDATA[
Add change notes here.<br>
<em>most HTML tags may be used</em>
]]>
</change-notes>
@ -23,14 +20,24 @@
<component>
<implementation-class>org.teavm.idea.TeaVMJPSRemoteService</implementation-class>
</component>
<component>
<implementation-class>org.teavm.idea.TeaVMDaemonComponent</implementation-class>
</component>
</application-components>
<extensions defaultExtensionNs="com.intellij">
<applicationService serviceImplementation="org.teavm.idea.TeaVMWorkspaceConfigurationStorage"/>
<compileServer.plugin classpath="jps/teavm-jps-plugin.jar;teavm.jar;teavm-jps-common.jar"/>
<buildProcess.parametersProvider implementation="org.teavm.idea.TeaVMJPSConfigurator"/>
<configurationType implementation="org.teavm.idea.debug.TeaVMDebugConfigurationType"/>
<programRunner implementation="org.teavm.idea.debug.TeaVMDebugRunner"/>
<applicationConfigurable instance="org.teavm.idea.ui.TeaVMSettingsEditorTab"
id="project.teavm.settings"
displayName="TeaVM compiler"
parentId="project.propCompiler"/>
<facetType implementation="org.teavm.idea.TeaVMFacetType"/>
<facetType implementation="org.teavm.idea.TeaVMWebAssemblyFacetType"/>
</extensions>

View File

@ -19,7 +19,9 @@
<root url="jar://$MODULE_DIR$/../idea-artifacts/dependencies/teavm.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
<SOURCES>
<root url="file://$MODULE_DIR$/../../../core/src/main/java" />
</SOURCES>
</library>
</orderEntry>
</component>