Add Maven option to run TeaVM in a separate process

This commit is contained in:
Alexey Andreev 2018-11-20 14:22:35 +03:00
parent 0f951f8c86
commit fc799afcda
35 changed files with 930 additions and 617 deletions

View File

@ -1,14 +1,15 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="IDEA JPS Debug" type="Remote" factoryName="Remote">
<configuration default="false" name="IDEA JPS Debug" type="Remote">
<option name="USE_SOCKET_TRANSPORT" value="true" />
<option name="SERVER_MODE" value="false" />
<option name="SHMEM_ADDRESS" value="javadebug" />
<option name="SHMEM_ADDRESS" />
<option name="HOST" value="localhost" />
<option name="PORT" value="5005" />
<option name="AUTO_RESTART" value="false" />
<RunnerSettings RunnerId="Debug">
<option name="DEBUG_PORT" value="5005" />
<option name="LOCAL" value="false" />
</RunnerSettings>
<method />
<method v="2" />
</configuration>
</component>

View File

@ -48,6 +48,7 @@
<groupId>org.teavm</groupId>
<artifactId>teavm-core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.teavm</groupId>
@ -77,6 +78,7 @@
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-util</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>

View File

@ -33,6 +33,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xs
<groupId>org.teavm</groupId>
<artifactId>teavm-core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.teavm</groupId>

View File

@ -38,6 +38,7 @@
<groupId>org.teavm</groupId>
<artifactId>teavm-core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.teavm</groupId>

View File

@ -34,6 +34,7 @@
<groupId>org.teavm</groupId>
<artifactId>teavm-core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.teavm</groupId>

View File

@ -1,46 +0,0 @@
/*
* Copyright 2015 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.tooling;
import java.io.File;
import java.util.List;
import java.util.Properties;
import org.teavm.model.ClassHolderTransformer;
import org.teavm.tooling.sources.SourceFileProvider;
public interface BaseTeaVMTool {
void setTargetDirectory(File targetDirectory);
void setMinifying(boolean minifying);
void setIncremental(boolean incremental);
void setDebugInformationGenerated(boolean debugInformationGenerated);
void setSourceMapsFileGenerated(boolean sourceMapsFileGenerated);
void setSourceFilesCopied(boolean sourceFilesCopied);
Properties getProperties();
List<ClassHolderTransformer> getTransformers();
void setLog(TeaVMToolLog log);
void setClassLoader(ClassLoader classLoader);
void addSourceFileProvider(SourceFileProvider sourceFileProvider);
}

View File

@ -21,6 +21,7 @@ import org.teavm.callgraph.CallGraphNode;
import org.teavm.callgraph.CallSite;
import org.teavm.diagnostics.DefaultProblemTextConsumer;
import org.teavm.diagnostics.Problem;
import org.teavm.diagnostics.ProblemProvider;
import org.teavm.model.CallLocation;
import org.teavm.model.MethodReference;
import org.teavm.model.TextLocation;
@ -31,9 +32,12 @@ public final class TeaVMProblemRenderer {
}
public static void describeProblems(TeaVM vm, TeaVMToolLog log) {
CallGraph cg = vm.getDependencyInfo().getCallGraph();
describeProblems(vm.getDependencyInfo().getCallGraph(), vm.getProblemProvider(), log);
}
public static void describeProblems(CallGraph cg, ProblemProvider problems, TeaVMToolLog log) {
DefaultProblemTextConsumer consumer = new DefaultProblemTextConsumer();
for (Problem problem : vm.getProblemProvider().getProblems()) {
for (Problem problem : problems.getProblems()) {
consumer.clear();
problem.render(consumer);
StringBuilder sb = new StringBuilder();

View File

@ -22,6 +22,8 @@ import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
@ -61,7 +63,7 @@ import org.teavm.vm.TeaVMOptimizationLevel;
import org.teavm.vm.TeaVMProgressListener;
import org.teavm.vm.TeaVMTarget;
public class TeaVMTool implements BaseTeaVMTool {
public class TeaVMTool {
private File targetDirectory = new File(".");
private TeaVMTargetType targetType = TeaVMTargetType.JAVASCRIPT;
private String targetFileName = "";
@ -73,7 +75,7 @@ public class TeaVMTool implements BaseTeaVMTool {
private boolean sourceFilesCopied;
private boolean incremental;
private File cacheDirectory = new File("./teavm-cache");
private List<ClassHolderTransformer> transformers = new ArrayList<>();
private List<String> transformers = new ArrayList<>();
private List<String> classesToPreserve = new ArrayList<>();
private TeaVMToolLog log = new EmptyTeaVMToolLog();
private ClassLoader classLoader = TeaVMTool.class.getClassLoader();
@ -99,7 +101,6 @@ public class TeaVMTool implements BaseTeaVMTool {
return targetDirectory;
}
@Override
public void setTargetDirectory(File targetDirectory) {
this.targetDirectory = targetDirectory;
}
@ -116,7 +117,6 @@ public class TeaVMTool implements BaseTeaVMTool {
return minifying;
}
@Override
public void setMinifying(boolean minifying) {
this.minifying = minifying;
}
@ -125,7 +125,6 @@ public class TeaVMTool implements BaseTeaVMTool {
return incremental;
}
@Override
public void setIncremental(boolean incremental) {
this.incremental = incremental;
}
@ -142,7 +141,6 @@ public class TeaVMTool implements BaseTeaVMTool {
return debugInformationGenerated;
}
@Override
public void setDebugInformationGenerated(boolean debugInformationGenerated) {
this.debugInformationGenerated = debugInformationGenerated;
}
@ -159,7 +157,6 @@ public class TeaVMTool implements BaseTeaVMTool {
return sourceMapsFileGenerated;
}
@Override
public void setSourceMapsFileGenerated(boolean sourceMapsFileGenerated) {
this.sourceMapsFileGenerated = sourceMapsFileGenerated;
}
@ -168,18 +165,15 @@ public class TeaVMTool implements BaseTeaVMTool {
return sourceFilesCopied;
}
@Override
public void setSourceFilesCopied(boolean sourceFilesCopied) {
this.sourceFilesCopied = sourceFilesCopied;
}
@Override
public Properties getProperties() {
return properties;
}
@Override
public List<ClassHolderTransformer> getTransformers() {
public List<String> getTransformers() {
return transformers;
}
@ -191,7 +185,6 @@ public class TeaVMTool implements BaseTeaVMTool {
return log;
}
@Override
public void setLog(TeaVMToolLog log) {
this.log = log;
}
@ -220,7 +213,6 @@ public class TeaVMTool implements BaseTeaVMTool {
return classLoader;
}
@Override
public void setClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
@ -289,7 +281,6 @@ public class TeaVMTool implements BaseTeaVMTool {
return resources;
}
@Override
public void addSourceFileProvider(SourceFileProvider sourceFileProvider) {
sourceFileProviders.add(sourceFileProvider);
}
@ -377,7 +368,7 @@ public class TeaVMTool implements BaseTeaVMTool {
vm.setOptimizationLevel(optimizationLevel);
vm.installPlugins();
for (ClassHolderTransformer transformer : transformers) {
for (ClassHolderTransformer transformer : resolveTransformers(classLoader)) {
vm.add(transformer);
}
if (mainClass != null) {
@ -402,10 +393,8 @@ public class TeaVMTool implements BaseTeaVMTool {
log.info("Output file successfully built");
} else if (problemProvider.getSevereProblems().isEmpty()) {
log.info("Output file built with warnings");
TeaVMProblemRenderer.describeProblems(vm, log);
} else {
log.info("Output file built with errors");
TeaVMProblemRenderer.describeProblems(vm, log);
}
File outputFile = new File(targetDirectory, outputName);
@ -507,4 +496,41 @@ public class TeaVMTool implements BaseTeaVMTool {
copier.setLog(log);
copier.copy(new File(targetDirectory, "src"));
}
private List<ClassHolderTransformer> resolveTransformers(ClassLoader classLoader) {
List<ClassHolderTransformer> transformerInstances = new ArrayList<>();
if (transformers == null) {
return transformerInstances;
}
for (String transformerName : transformers) {
Class<?> transformerRawType;
try {
transformerRawType = Class.forName(transformerName, true, classLoader);
} catch (ClassNotFoundException e) {
log.error("Transformer not found: " + transformerName, e);
continue;
}
if (!ClassHolderTransformer.class.isAssignableFrom(transformerRawType)) {
log.error("Transformer " + transformerName + " is not subtype of "
+ ClassHolderTransformer.class.getName());
continue;
}
Class<? extends ClassHolderTransformer> transformerType = transformerRawType.asSubclass(
ClassHolderTransformer.class);
Constructor<? extends ClassHolderTransformer> ctor;
try {
ctor = transformerType.getConstructor();
} catch (NoSuchMethodException e) {
log.error("Transformer " + transformerName + " has no default constructor");
continue;
}
try {
ClassHolderTransformer transformer = ctor.newInstance();
transformerInstances.add(transformer);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
log.error("Error instantiating transformer " + transformerName, e);
}
}
return transformerInstances;
}
}

View File

@ -0,0 +1,22 @@
/*
* Copyright 2018 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.tooling.builder;
public class BuildException extends Exception {
public BuildException(Throwable cause) {
super(cause);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017 Alexey Andreev.
* Copyright 2018 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -13,19 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.idea.jps.model;
package org.teavm.tooling.builder;
import java.util.Collection;
import org.teavm.callgraph.CallGraph;
import org.teavm.diagnostics.ProblemProvider;
public interface TeaVMBuildResult {
public interface BuildResult {
CallGraph getCallGraph();
boolean isErrorOccurred();
String getStackTrace();
ProblemProvider getProblems();
Collection<String> getUsedResources();

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017 Alexey Andreev.
* Copyright 2018 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -13,16 +13,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.idea.jps.model;
package org.teavm.tooling.builder;
import java.util.List;
import java.util.Properties;
import org.teavm.backend.wasm.render.WasmBinaryVersion;
import org.teavm.tooling.TeaVMTargetType;
import org.teavm.tooling.TeaVMToolLog;
import org.teavm.vm.TeaVMOptimizationLevel;
import org.teavm.vm.TeaVMProgressListener;
public interface TeaVMBuildStrategy {
public interface BuildStrategy {
void init();
void setLog(TeaVMToolLog log);
void addSourcesDirectory(String directory);
void addSourcesJar(String jarFile);
@ -45,7 +50,21 @@ public interface TeaVMBuildStrategy {
void setIncremental(boolean incremental);
void setMinifying(boolean minifying);
void setProperties(Properties properties);
TeaVMBuildResult build();
void setTransformers(String[] transformers);
void setOptimizationLevel(TeaVMOptimizationLevel level);
void setTargetFileName(String targetFileName);
void setClassesToPreserve(String[] classesToPreserve);
void setCacheDirectory(String cacheDirectory);
void setWasmVersion(WasmBinaryVersion wasmVersion);
BuildResult build() throws BuildException;
}

View File

@ -0,0 +1,22 @@
/*
* Copyright 2018 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.tooling.builder;
import java.net.URL;
public interface ClassLoaderFactory {
ClassLoader create(URL[] urls, ClassLoader innerClassLoader);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017 Alexey Andreev.
* Copyright 2018 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -13,48 +13,56 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.idea.jps;
package org.teavm.tooling.builder;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;
import org.jetbrains.jps.incremental.CompileContext;
import org.jetbrains.jps.incremental.messages.CompilerMessage;
import org.teavm.backend.wasm.render.WasmBinaryVersion;
import org.teavm.callgraph.CallGraph;
import org.teavm.diagnostics.ProblemProvider;
import org.teavm.idea.jps.model.TeaVMBuildResult;
import org.teavm.idea.jps.model.TeaVMBuildStrategy;
import org.teavm.idea.jps.util.ExceptionUtil;
import org.teavm.tooling.EmptyTeaVMToolLog;
import org.teavm.tooling.TeaVMTargetType;
import org.teavm.tooling.TeaVMTool;
import org.teavm.tooling.TeaVMToolException;
import org.teavm.tooling.TeaVMToolLog;
import org.teavm.tooling.sources.DirectorySourceFileProvider;
import org.teavm.tooling.sources.JarSourceFileProvider;
import org.teavm.tooling.sources.SourceFileProvider;
import org.teavm.vm.TeaVMOptimizationLevel;
import org.teavm.vm.TeaVMProgressListener;
public class InProcessBuildStrategy implements TeaVMBuildStrategy {
private final CompileContext context;
public class InProcessBuildStrategy implements BuildStrategy {
private final ClassLoaderFactory classLoaderFactory;
private List<String> classPathEntries = new ArrayList<>();
private TeaVMTargetType targetType;
private String mainClass;
private String targetDirectory;
private String targetFileName = "";
private boolean incremental;
private String cacheDirectory;
private TeaVMOptimizationLevel optimizationLevel = TeaVMOptimizationLevel.ADVANCED;
private boolean minifying;
private boolean sourceMapsFileGenerated;
private boolean debugInformationGenerated;
private boolean sourceFilesCopied;
private String[] transformers = new String[0];
private String[] classesToPreserve = new String[0];
private WasmBinaryVersion wasmVersion = WasmBinaryVersion.V_0x1;
private final List<SourceFileProvider> sourceFileProviders = new ArrayList<>();
private TeaVMProgressListener progressListener;
private Properties properties = new Properties();
private TeaVMToolLog log = new EmptyTeaVMToolLog();
public InProcessBuildStrategy(CompileContext context) {
this.context = context;
public InProcessBuildStrategy(ClassLoaderFactory classLoaderFactory) {
this.classLoaderFactory = classLoaderFactory;
}
@Override
@ -115,6 +123,7 @@ public class InProcessBuildStrategy implements TeaVMBuildStrategy {
@Override
public void setIncremental(boolean incremental) {
this.incremental = incremental;
}
@Override
@ -124,20 +133,67 @@ public class InProcessBuildStrategy implements TeaVMBuildStrategy {
}
@Override
public TeaVMBuildResult build() {
public void setLog(TeaVMToolLog log) {
this.log = log;
}
@Override
public void setMinifying(boolean minifying) {
this.minifying = minifying;
}
@Override
public void setTransformers(String[] transformers) {
this.transformers = transformers.clone();
}
@Override
public void setOptimizationLevel(TeaVMOptimizationLevel level) {
this.optimizationLevel = level;
}
@Override
public void setTargetFileName(String targetFileName) {
this.targetFileName = targetFileName;
}
@Override
public void setClassesToPreserve(String[] classesToPreserve) {
this.classesToPreserve = classesToPreserve.clone();
}
@Override
public void setCacheDirectory(String cacheDirectory) {
this.cacheDirectory = cacheDirectory;
}
@Override
public void setWasmVersion(WasmBinaryVersion wasmVersion) {
this.wasmVersion = wasmVersion;
}
@Override
public BuildResult build() throws BuildException {
TeaVMTool tool = new TeaVMTool();
tool.setProgressListener(progressListener);
tool.setLog(new EmptyTeaVMToolLog());
tool.setLog(log);
tool.setTargetType(targetType);
tool.setMainClass(mainClass);
tool.setTargetDirectory(new File(targetDirectory));
tool.setTargetFileName(targetFileName);
tool.setClassLoader(buildClassLoader());
tool.setOptimizationLevel(optimizationLevel);
tool.setSourceMapsFileGenerated(sourceMapsFileGenerated);
tool.setDebugInformationGenerated(debugInformationGenerated);
tool.setSourceFilesCopied(sourceFilesCopied);
tool.setMinifying(false);
tool.setMinifying(minifying);
tool.setIncremental(incremental);
tool.getTransformers().addAll(Arrays.asList(transformers));
tool.getClassesToPreserve().addAll(Arrays.asList(classesToPreserve));
tool.setCacheDirectory(cacheDirectory != null ? new File(cacheDirectory) : null);
tool.setWasmVersion(wasmVersion);
tool.getProperties().putAll(properties);
@ -145,22 +201,17 @@ public class InProcessBuildStrategy implements TeaVMBuildStrategy {
tool.addSourceFileProvider(fileProvider);
}
boolean errorOccurred = false;
String stackTrace = null;
try {
tool.generate();
} catch (TeaVMToolException | RuntimeException | Error e) {
e.printStackTrace(System.err);
stackTrace = ExceptionUtil.exceptionToString(e);
context.processMessage(new CompilerMessage("TeaVM", e));
errorOccurred = true;
throw new BuildException(e);
}
Set<String> generatedFiles = tool.getGeneratedFiles().stream()
.map(File::getAbsolutePath)
.collect(Collectors.toSet());
return new InProcessBuildResult(tool.getDependencyInfo().getCallGraph(), errorOccurred, stackTrace,
return new InProcessBuildResult(tool.getDependencyInfo().getCallGraph(),
tool.getProblemProvider(), tool.getClasses(), tool.getUsedResources(), generatedFiles);
}
@ -173,26 +224,19 @@ public class InProcessBuildStrategy implements TeaVMBuildStrategy {
}
}).toArray(URL[]::new);
RenamingClassLoader classLoader = new RenamingClassLoader(urls, TeaVMBuilder.class.getClassLoader());
classLoader.rename("org/objectweb/asm/", "org/teavm/asm/");
return classLoader;
return classLoaderFactory.create(urls, InProcessBuildStrategy.class.getClassLoader());
}
static class InProcessBuildResult implements TeaVMBuildResult {
static class InProcessBuildResult implements BuildResult {
private CallGraph callGraph;
private boolean errorOccurred;
private String stackTrace;
private ProblemProvider problemProvider;
private Collection<String> classes;
private Collection<String> usedResources;
private Collection<String> generatedFiles;
InProcessBuildResult(CallGraph callGraph, boolean errorOccurred, String stackTrace,
ProblemProvider problemProvider, Collection<String> classes, Collection<String> usedResources,
Collection<String> generatedFiles) {
InProcessBuildResult(CallGraph callGraph, ProblemProvider problemProvider,
Collection<String> classes, Collection<String> usedResources, Collection<String> generatedFiles) {
this.callGraph = callGraph;
this.errorOccurred = errorOccurred;
this.stackTrace = stackTrace;
this.problemProvider = problemProvider;
this.classes = classes;
this.usedResources = usedResources;
@ -204,16 +248,6 @@ public class InProcessBuildStrategy implements TeaVMBuildStrategy {
return callGraph;
}
@Override
public boolean isErrorOccurred() {
return errorOccurred;
}
@Override
public String getStackTrace() {
return stackTrace;
}
@Override
public ProblemProvider getProblems() {
return problemProvider;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017 Alexey Andreev.
* Copyright 2018 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -13,39 +13,44 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.idea.jps;
package org.teavm.tooling.builder;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.Collection;
import java.util.List;
import java.util.Properties;
import org.teavm.backend.wasm.render.WasmBinaryVersion;
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.EmptyTeaVMToolLog;
import org.teavm.tooling.TeaVMTargetType;
import org.teavm.tooling.TeaVMToolLog;
import org.teavm.tooling.daemon.RemoteBuildCallback;
import org.teavm.tooling.daemon.RemoteBuildRequest;
import org.teavm.tooling.daemon.RemoteBuildResponse;
import org.teavm.tooling.daemon.RemoteBuildService;
import org.teavm.vm.TeaVMOptimizationLevel;
import org.teavm.vm.TeaVMPhase;
import org.teavm.vm.TeaVMProgressFeedback;
import org.teavm.vm.TeaVMProgressListener;
public class RemoteBuildStrategy implements TeaVMBuildStrategy {
private TeaVMRemoteBuildRequest request;
private TeaVMRemoteBuildService buildService;
public class RemoteBuildStrategy implements BuildStrategy {
private RemoteBuildRequest request;
private RemoteBuildService buildService;
private TeaVMProgressListener progressListener;
private TeaVMToolLog log = new EmptyTeaVMToolLog();
public RemoteBuildStrategy(TeaVMRemoteBuildService buildService) {
public RemoteBuildStrategy(RemoteBuildService buildService) {
this.buildService = buildService;
}
@Override
public void init() {
request = new TeaVMRemoteBuildRequest();
request = new RemoteBuildRequest();
request.optimizationLevel = TeaVMOptimizationLevel.ADVANCED;
request.wasmVersion = WasmBinaryVersion.V_0x1;
}
@Override
@ -110,14 +115,57 @@ public class RemoteBuildStrategy implements TeaVMBuildStrategy {
}
@Override
public TeaVMBuildResult build() {
TeaVMRemoteBuildResponse response;
try {
response = buildService.build(request, new CallbackImpl(progressListener));
} catch (Throwable e) {
throw new RuntimeException(e);
public void setLog(TeaVMToolLog log) {
this.log = log;
}
return new TeaVMBuildResult() {
@Override
public void setMinifying(boolean minifying) {
request.minifying = minifying;
}
@Override
public void setTransformers(String[] transformers) {
request.transformers = transformers.clone();
}
@Override
public void setOptimizationLevel(TeaVMOptimizationLevel level) {
request.optimizationLevel = level;
}
@Override
public void setTargetFileName(String targetFileName) {
request.tagetFileName = targetFileName;
}
@Override
public void setClassesToPreserve(String[] classesToPreserve) {
request.classesToPreserve = classesToPreserve.clone();
}
@Override
public void setCacheDirectory(String cacheDirectory) {
request.cacheDirectory = cacheDirectory;
}
@Override
public void setWasmVersion(WasmBinaryVersion wasmVersion) {
request.wasmVersion = wasmVersion;
}
@Override
public BuildResult build() throws BuildException {
RemoteBuildResponse response;
try {
response = buildService.build(request, new CallbackImpl(progressListener, log));
} catch (Throwable e) {
throw new BuildException(e);
}
if (response.exception != null) {
throw new BuildException(response.exception);
}
return new BuildResult() {
private ProblemProvider problems = new ProblemProvider() {
@Override
public List<Problem> getProblems() {
@ -135,16 +183,6 @@ public class RemoteBuildStrategy implements TeaVMBuildStrategy {
return response.callGraph;
}
@Override
public boolean isErrorOccurred() {
return response.errorOccurred;
}
@Override
public String getStackTrace() {
return response.stackTrace;
}
@Override
public ProblemProvider getProblems() {
return problems;
@ -167,22 +205,54 @@ public class RemoteBuildStrategy implements TeaVMBuildStrategy {
};
}
static class CallbackImpl extends UnicastRemoteObject implements TeaVMRemoteBuildCallback {
static class CallbackImpl extends UnicastRemoteObject implements RemoteBuildCallback {
private TeaVMProgressListener listener;
private TeaVMToolLog log;
public CallbackImpl(TeaVMProgressListener listener) throws RemoteException {
public CallbackImpl(TeaVMProgressListener listener, TeaVMToolLog log) throws RemoteException {
super();
this.listener = listener;
this.log = log;
}
@Override
public TeaVMProgressFeedback phaseStarted(TeaVMPhase phase, int count) throws RemoteException {
return listener.phaseStarted(phase, count);
public TeaVMProgressFeedback phaseStarted(TeaVMPhase phase, int count) {
return listener != null ? listener.phaseStarted(phase, count) : TeaVMProgressFeedback.CONTINUE;
}
@Override
public TeaVMProgressFeedback progressReached(int progress) throws RemoteException {
return listener.progressReached(progress);
public TeaVMProgressFeedback progressReached(int progress) {
return listener != null ? listener.progressReached(progress) : TeaVMProgressFeedback.CONTINUE;
}
@Override
public void errorReported(String message, Throwable e) {
log.error(message, e);
}
@Override
public void errorReported(String message) {
log.error(message);
}
@Override
public void warningReported(String message, Throwable e) {
log.warning(message, e);
}
@Override
public void warningReported(String message) {
log.warning(message);
}
@Override
public void infoReported(String message, Throwable e) {
log.info(message, e);
}
@Override
public void infoReported(String message) {
log.info(message);
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017 Alexey Andreev.
* Copyright 2018 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -13,11 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.idea.daemon;
package org.teavm.tooling.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;
@ -34,22 +31,12 @@ 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.function.Function;
import java.util.stream.Collectors;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
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;
import org.teavm.idea.jps.remote.TeaVMRemoteBuildService;
import org.teavm.idea.jps.util.ExceptionUtil;
import org.teavm.tooling.EmptyTeaVMToolLog;
import org.teavm.tooling.TeaVMTool;
import org.teavm.tooling.TeaVMToolException;
import org.teavm.tooling.sources.DirectorySourceFileProvider;
@ -58,15 +45,12 @@ import org.teavm.vm.TeaVMPhase;
import org.teavm.vm.TeaVMProgressFeedback;
import org.teavm.vm.TeaVMProgressListener;
public class TeaVMBuildDaemon extends UnicastRemoteObject implements TeaVMRemoteBuildService {
public class BuildDaemon extends UnicastRemoteObject implements RemoteBuildService {
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 static final String INCREMENTAL_PROPERTY = "teavm.daemon.incremental";
private static final String DEBUG_PORT_PROPERTY = "teavm.daemon.debug.port";
private boolean incremental;
private int port;
private Registry registry;
@ -74,17 +58,7 @@ public class TeaVMBuildDaemon extends UnicastRemoteObject implements TeaVMRemote
private ClassLoader lastJarClassLoader;
private List<String> lastJarClassPath;
static {
int depth = 0;
for (int i = 0; i < DAEMON_CLASS.length(); ++i) {
if (DAEMON_CLASS.charAt(i) == '/') {
depth++;
}
}
DAEMON_CLASS_DEPTH = depth;
}
TeaVMBuildDaemon(boolean incremental) throws RemoteException {
BuildDaemon(boolean incremental) throws RemoteException {
super();
this.incremental = incremental;
Random random = new Random();
@ -96,7 +70,7 @@ public class TeaVMBuildDaemon extends UnicastRemoteObject implements TeaVMRemote
continue;
}
try {
registry.bind(TeaVMRemoteBuildService.ID, this);
registry.bind(RemoteBuildService.ID, this);
} catch (RemoteException | AlreadyBoundException e) {
throw new IllegalStateException("Could not bind remote build assistant service", e);
}
@ -139,7 +113,7 @@ public class TeaVMBuildDaemon extends UnicastRemoteObject implements TeaVMRemote
public static void main(String[] args) throws RemoteException {
boolean incremental = Boolean.parseBoolean(System.getProperty(INCREMENTAL_PROPERTY, "false"));
TeaVMBuildDaemon daemon = new TeaVMBuildDaemon(incremental);
BuildDaemon daemon = new BuildDaemon(incremental);
System.out.println(DAEMON_MESSAGE_PREFIX + daemon.port);
if (daemon.incrementalCache != null) {
System.out.println("Incremental cache set up in " + daemon.incrementalCache);
@ -147,8 +121,7 @@ public class TeaVMBuildDaemon extends UnicastRemoteObject implements TeaVMRemote
}
@Override
public TeaVMRemoteBuildResponse build(TeaVMRemoteBuildRequest request, TeaVMRemoteBuildCallback callback)
throws RemoteException {
public RemoteBuildResponse build(RemoteBuildRequest request, RemoteBuildCallback callback) {
System.out.println("Build started");
if (!request.incremental && incremental) {
@ -163,21 +136,34 @@ public class TeaVMBuildDaemon extends UnicastRemoteObject implements TeaVMRemote
TeaVMTool tool = new TeaVMTool();
tool.setIncremental(incremental && request.incremental);
if (tool.isIncremental()) {
tool.setCacheDirectory(incrementalCache);
tool.setCacheDirectory(request.cacheDirectory != null
? new File(request.cacheDirectory)
: incrementalCache);
}
tool.setProgressListener(createProgressListener(callback));
tool.setLog(new EmptyTeaVMToolLog());
tool.setLog(new RemoteBuildLog(callback));
if (request.transformers != null) {
tool.getTransformers().addAll(Arrays.asList(request.transformers));
}
if (request.classesToPreserve != null) {
tool.getClassesToPreserve().addAll(Arrays.asList(request.classesToPreserve));
}
tool.setTargetType(request.targetType);
tool.setMainClass(request.mainClass);
tool.setTargetDirectory(new File(request.targetDirectory));
tool.setTargetFileName(request.tagetFileName);
tool.setClassLoader(buildClassLoader(request.classPath, incremental && request.incremental));
tool.setSourceMapsFileGenerated(request.sourceMapsFileGenerated);
tool.setDebugInformationGenerated(request.debugInformationGenerated);
tool.setSourceFilesCopied(request.sourceFilesCopied);
if (request.properties != null) {
tool.getProperties().putAll(request.properties);
}
tool.setMinifying(false);
tool.setOptimizationLevel(request.optimizationLevel);
tool.setMinifying(request.minifying);
tool.setWasmVersion(request.wasmVersion);
for (String sourceDirectory : request.sourceDirectories) {
tool.addSourceFileProvider(new DirectorySourceFileProvider(new File(sourceDirectory)));
@ -186,19 +172,15 @@ public class TeaVMBuildDaemon extends UnicastRemoteObject implements TeaVMRemote
tool.addSourceFileProvider(new JarSourceFileProvider(new File(sourceJar)));
}
boolean errorOccurred = false;
String stackTrace = null;
RemoteBuildResponse response = new RemoteBuildResponse();
try {
tool.generate();
System.out.println("Build complete");
} catch (TeaVMToolException | RuntimeException | Error e) {
stackTrace = ExceptionUtil.exceptionToString(e);
errorOccurred = true;
response.exception = e;
}
TeaVMRemoteBuildResponse response = new TeaVMRemoteBuildResponse();
response.errorOccurred = errorOccurred;
response.stackTrace = stackTrace;
if (response.exception == null) {
response.callGraph = tool.getDependencyInfo().getCallGraph();
response.problems.addAll(tool.getProblemProvider().getProblems());
response.severeProblems.addAll(tool.getProblemProvider().getSevereProblems());
@ -207,6 +189,7 @@ public class TeaVMBuildDaemon extends UnicastRemoteObject implements TeaVMRemote
response.generatedFiles.addAll(tool.getGeneratedFiles().stream()
.map(File::getAbsolutePath)
.collect(Collectors.toSet()));
}
return response;
}
@ -254,7 +237,7 @@ public class TeaVMBuildDaemon extends UnicastRemoteObject implements TeaVMRemote
return new URLClassLoader(urls, jarClassLoader);
}
private TeaVMProgressListener createProgressListener(TeaVMRemoteBuildCallback callback) {
private TeaVMProgressListener createProgressListener(RemoteBuildCallback callback) {
return new TeaVMProgressListener() {
private long lastReportedTime;
@ -283,18 +266,26 @@ public class TeaVMBuildDaemon extends UnicastRemoteObject implements TeaVMRemote
};
}
public static TeaVMDaemonInfo start(boolean incremental, int daemonMemory) throws IOException {
public static DaemonInfo start(boolean incremental, int daemonMemory, DaemonLog log,
String... classPathEntries) throws IOException {
String javaHome = System.getProperty("java.home");
String javaCommand = javaHome + "/bin/java";
String classPath = String.join(File.pathSeparator, detectClassPath());
ProcessBuilder builder = new ProcessBuilder(javaCommand, "-cp", classPath,
String classPath = String.join(File.pathSeparator, classPathEntries);
List<String> arguments = new ArrayList<>();
arguments.addAll(Arrays.asList(javaCommand, "-cp", classPath,
"-D" + INCREMENTAL_PROPERTY + "=" + incremental,
"-Xmx" + daemonMemory + "m",
TeaVMBuildDaemon.class.getName());
"-Xmx" + daemonMemory + "m"));
String debugPort = System.getProperty(DEBUG_PORT_PROPERTY);
if (debugPort != null) {
arguments.add("-agentlib:jdwp=transport=dt_socket,quiet=y,server=y,address=" + debugPort + ",suspend=y");
}
arguments.add(BuildDaemon.class.getName());
ProcessBuilder builder = new ProcessBuilder(arguments.toArray(new String[0]));
Process process = builder.start();
Log log = LogFactory.getLog(TeaVMBuildDaemon.class);
BufferedReader stdoutReader = new BufferedReader(new InputStreamReader(process.getInputStream(),
StandardCharsets.UTF_8));
BufferedReader stderrReader = new BufferedReader(new InputStreamReader(process.getErrorStream(),
@ -311,6 +302,7 @@ public class TeaVMBuildDaemon extends UnicastRemoteObject implements TeaVMRemote
}
IOUtils.closeQuietly(stderrReader);
IOUtils.closeQuietly(stdoutReader);
process.destroy();
throw new IllegalStateException("Could not start daemon. Stderr: " + sb);
}
int port = Integer.parseInt(line.substring(DAEMON_MESSAGE_PREFIX.length()));
@ -318,7 +310,7 @@ public class TeaVMBuildDaemon extends UnicastRemoteObject implements TeaVMRemote
daemonThread(new DaemonProcessOutputWatcher(log, stdoutReader, "stdout", false)).start();
daemonThread(new DaemonProcessOutputWatcher(log, stderrReader, "stderr", true)).start();
return new TeaVMDaemonInfo(port, process);
return new DaemonInfo(port, process);
}
private static Thread daemonThread(Runnable runnable) {
@ -328,12 +320,12 @@ public class TeaVMBuildDaemon extends UnicastRemoteObject implements TeaVMRemote
}
static class DaemonProcessOutputWatcher implements Runnable {
private Log log;
private DaemonLog log;
private BufferedReader reader;
private String name;
private boolean isError;
public DaemonProcessOutputWatcher(Log log, BufferedReader reader, String name, boolean isError) {
DaemonProcessOutputWatcher(DaemonLog log, BufferedReader reader, String name, boolean isError) {
this.log = log;
this.reader = reader;
this.name = name;
@ -359,30 +351,4 @@ public class TeaVMBuildDaemon extends UnicastRemoteObject implements TeaVMRemote
}
}
}
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

@ -1,5 +1,5 @@
/*
* Copyright 2017 Alexey Andreev.
* Copyright 2018 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -13,13 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.idea.daemon;
package org.teavm.tooling.daemon;
public class TeaVMDaemonInfo {
public class DaemonInfo {
private int port;
private Process process;
TeaVMDaemonInfo(int port, Process process) {
DaemonInfo(int port, Process process) {
this.port = port;
this.process = process;
}

View File

@ -0,0 +1,24 @@
/*
* Copyright 2018 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.tooling.daemon;
public interface DaemonLog {
void error(String message);
void error(String message, Throwable e);
void info(String message);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017 Alexey Andreev.
* Copyright 2018 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -13,15 +13,27 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.idea.jps.remote;
package org.teavm.tooling.daemon;
import java.rmi.Remote;
import java.rmi.RemoteException;
import org.teavm.vm.TeaVMPhase;
import org.teavm.vm.TeaVMProgressFeedback;
public interface TeaVMRemoteBuildCallback extends Remote {
public interface RemoteBuildCallback extends Remote {
TeaVMProgressFeedback phaseStarted(TeaVMPhase phase, int count) throws RemoteException;
TeaVMProgressFeedback progressReached(int progress) throws RemoteException;
void errorReported(String message, Throwable e) throws RemoteException;
void errorReported(String message) throws RemoteException;
void warningReported(String message, Throwable e) throws RemoteException;
void warningReported(String message) throws RemoteException;
void infoReported(String message, Throwable e) throws RemoteException;
void infoReported(String message) throws RemoteException;
}

View File

@ -0,0 +1,90 @@
/*
* Copyright 2018 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.tooling.daemon;
import java.rmi.RemoteException;
import org.teavm.tooling.TeaVMToolLog;
class RemoteBuildLog implements TeaVMToolLog {
private RemoteBuildCallback callback;
RemoteBuildLog(RemoteBuildCallback callback) {
this.callback = callback;
}
@Override
public void info(String text) {
try {
callback.infoReported(text);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void debug(String text) {
}
@Override
public void warning(String text) {
try {
callback.warningReported(text);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void error(String text) {
try {
callback.errorReported(text);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void info(String text, Throwable e) {
try {
callback.infoReported(text, e);
} catch (RemoteException e2) {
e.printStackTrace();
}
}
@Override
public void debug(String text, Throwable e) {
}
@Override
public void warning(String text, Throwable e) {
try {
callback.warningReported(text, e);
} catch (RemoteException e2) {
e.printStackTrace();
}
}
@Override
public void error(String text, Throwable e) {
try {
callback.errorReported(text, e);
} catch (RemoteException e2) {
e.printStackTrace();
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017 Alexey Andreev.
* Copyright 2018 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -13,24 +13,33 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.idea.jps.remote;
package org.teavm.tooling.daemon;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.teavm.backend.wasm.render.WasmBinaryVersion;
import org.teavm.tooling.TeaVMTargetType;
import org.teavm.vm.TeaVMOptimizationLevel;
public class TeaVMRemoteBuildRequest implements Serializable {
public class RemoteBuildRequest implements Serializable {
public final List<String> sourceDirectories = new ArrayList<>();
public final List<String> sourceJarFiles = new ArrayList<>();
public final List<String> classPath = new ArrayList<>();
public String[] transformers;
public String[] classesToPreserve;
public TeaVMTargetType targetType;
public String mainClass;
public String targetDirectory;
public String tagetFileName = "";
public boolean sourceMapsFileGenerated;
public boolean debugInformationGenerated;
public boolean sourceFilesCopied;
public boolean incremental;
public String cacheDirectory;
public boolean minifying;
public Properties properties;
public TeaVMOptimizationLevel optimizationLevel;
public WasmBinaryVersion wasmVersion;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017 Alexey Andreev.
* Copyright 2018 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.idea.jps.remote;
package org.teavm.tooling.daemon;
import java.io.Serializable;
import java.util.ArrayList;
@ -23,13 +23,12 @@ import java.util.Set;
import org.teavm.callgraph.CallGraph;
import org.teavm.diagnostics.Problem;
public class TeaVMRemoteBuildResponse implements Serializable {
public class RemoteBuildResponse 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<>();
public final Set<String> generatedFiles = new HashSet<>();
public String stackTrace;
public Throwable exception;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017 Alexey Andreev.
* Copyright 2018 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -13,15 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.idea.jps.remote;
package org.teavm.tooling.daemon;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface TeaVMRemoteBuildService extends Remote {
public interface RemoteBuildService extends Remote {
String REMOTE_PORT = "teavm.daemon.remote-port";
String ID = "TeaVM-Daemon";
TeaVMRemoteBuildResponse build(TeaVMRemoteBuildRequest request, TeaVMRemoteBuildCallback callback)
throws RemoteException;
RemoteBuildResponse build(RemoteBuildRequest request, RemoteBuildCallback callback) throws RemoteException;
}

View File

@ -157,9 +157,7 @@ public class TeaVMProjectBuilder extends IncrementalProjectBuilder {
String cacheDir = profile.getCacheDirectory();
tool.setCacheDirectory(!cacheDir.isEmpty() ?
new File(varManager.performStringSubstitution(cacheDir, false)) : null);
for (ClassHolderTransformer transformer : instantiateTransformers(profile, classLoader)) {
tool.getTransformers().add(transformer);
}
tool.getTransformers().addAll(Arrays.asList(profile.getTransformers()));
tool.getClassesToPreserve().addAll(profile.getClassesToPreserve());
for (SourceFileProvider provider : sourceProviders) {
tool.addSourceFileProvider(provider);
@ -540,42 +538,6 @@ public class TeaVMProjectBuilder extends IncrementalProjectBuilder {
return projects;
}
private List<ClassHolderTransformer> instantiateTransformers(TeaVMProfile profile, ClassLoader classLoader)
throws CoreException{
List<ClassHolderTransformer> transformerInstances = new ArrayList<>();
for (String transformerName : profile.getTransformers()) {
Class<?> transformerRawType;
try {
transformerRawType = Class.forName(transformerName, true, classLoader);
} catch (ClassNotFoundException e) {
putConfigMarker("Transformer not found: " + transformerName);
continue;
}
if (!ClassHolderTransformer.class.isAssignableFrom(transformerRawType)) {
putConfigMarker("Transformer " + transformerName + " is not a subtype of " +
ClassHolderTransformer.class.getName());
continue;
}
Class<? extends ClassHolderTransformer> transformerType = transformerRawType.asSubclass(
ClassHolderTransformer.class);
Constructor<? extends ClassHolderTransformer> ctor;
try {
ctor = transformerType.getConstructor();
} catch (NoSuchMethodException e) {
putConfigMarker("Transformer " + transformerName + " has no default constructor");
continue;
}
try {
ClassHolderTransformer transformer = ctor.newInstance();
transformerInstances.add(transformer);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
putConfigMarker("Error instantiating transformer " + transformerName);
continue;
}
}
return transformerInstances;
}
private void putConfigMarker(String message) throws CoreException {
IMarker marker = getProject().createMarker(TeaVMEclipsePlugin.CONFIG_MARKER_ID);
marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);

View File

@ -0,0 +1,73 @@
/*
* Copyright 2018 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.util;
import com.intellij.ide.plugins.IdeaPluginDescriptor;
import com.intellij.ide.plugins.PluginManager;
import com.intellij.openapi.extensions.PluginId;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.teavm.tooling.daemon.BuildDaemon;
public final class DaemonUtil {
private static final Set<String> PLUGIN_FILES = new HashSet<>(Arrays.asList("teavm-jps-common.jar",
"teavm-plugin.jar", "teavm.jar"));
private static final String DAEMON_CLASS = BuildDaemon.class.getName().replace('.', '/') + ".class";
private static final int DAEMON_CLASS_DEPTH;
static {
int depth = 0;
for (int i = 0; i < DAEMON_CLASS.length(); ++i) {
if (DAEMON_CLASS.charAt(i) == '/') {
depth++;
}
}
DAEMON_CLASS_DEPTH = depth;
}
private DaemonUtil() {
}
public 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() && PLUGIN_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

@ -1,5 +1,5 @@
/*
* Copyright 2017 Alexey Andreev.
* Copyright 2018 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2017 Alexey Andreev.
* Copyright 2018 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@ -22,6 +22,7 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
@ -57,8 +58,6 @@ import org.teavm.common.IntegerArray;
import org.teavm.diagnostics.DefaultProblemTextConsumer;
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.model.TeaVMJpsConfiguration;
import org.teavm.idea.jps.model.TeaVMProperty;
import org.teavm.idea.jps.remote.TeaVMBuilderAssistant;
@ -67,6 +66,9 @@ import org.teavm.model.CallLocation;
import org.teavm.model.MethodReference;
import org.teavm.model.TextLocation;
import org.teavm.model.ValueType;
import org.teavm.tooling.builder.BuildException;
import org.teavm.tooling.builder.BuildResult;
import org.teavm.tooling.builder.BuildStrategy;
import org.teavm.vm.TeaVMPhase;
import org.teavm.vm.TeaVMProgressFeedback;
import org.teavm.vm.TeaVMProgressListener;
@ -79,10 +81,10 @@ class TeaVMBuild {
private final TeaVMBuilderAssistant assistant;
private final Map<String, File> sourceFileCache = new HashMap<>();
private final Map<File, int[]> fileLineCache = new HashMap<>();
private TeaVMBuildStrategy buildStrategy;
private BuildStrategy buildStrategy;
private BuildOutputConsumer outputConsumer;
TeaVMBuild(CompileContext context, TeaVMBuilderAssistant assistant, TeaVMBuildStrategy buildStrategy,
TeaVMBuild(CompileContext context, TeaVMBuilderAssistant assistant, BuildStrategy buildStrategy,
BuildOutputConsumer outputConsumer) {
this.context = context;
this.assistant = assistant;
@ -90,7 +92,7 @@ class TeaVMBuild {
this.outputConsumer = outputConsumer;
}
boolean perform(JpsModule module, TeaVMBuildTarget target) throws IOException {
boolean perform(JpsModule module, TeaVMBuildTarget target) throws IOException, BuildException {
TeaVMStorageProvider storageProvider = new TeaVMStorageProvider(
target.getConfiguration().getTargetType().name());
storage = context.getProjectDescriptor().dataManager.getStorage(target, storageProvider);
@ -128,25 +130,17 @@ class TeaVMBuild {
}
buildStrategy.setProperties(properties);
TeaVMBuildResult buildResult = buildStrategy.build();
BuildResult buildResult = buildStrategy.build();
if (!buildResult.isErrorOccurred() && buildResult.getProblems().getSevereProblems().isEmpty()) {
if (!buildResult.getProblems().getSevereProblems().isEmpty()) {
updateStorage(buildResult);
}
reportProblems(buildResult.getProblems(), buildResult.getCallGraph());
if (!buildResult.isErrorOccurred()) {
for (String fileName : buildResult.getGeneratedFiles()) {
outputConsumer.registerOutputFile(new File(fileName), Collections.emptyList());
}
}
if (buildResult.getStackTrace() != null) {
context.processMessage(new CompilerMessage("TeaVM", BuildMessage.Kind.ERROR,
"Compiler crashed:\n" + buildResult.getStackTrace(), "",
-1, -1, -1, -1, -1));
}
return true;
}
@ -390,7 +384,7 @@ class TeaVMBuild {
private int[] getLineOffsetsCacheMiss(File file) {
IntegerArray lines = new IntegerArray(50);
try (Reader reader = new InputStreamReader(new FileInputStream(file), "UTF-8")) {
try (Reader reader = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8)) {
int offset = 0;
lines.add(0);
@ -450,10 +444,9 @@ class TeaVMBuild {
return false;
}
private void updateStorage(TeaVMBuildResult buildResult) {
private void updateStorage(BuildResult buildResult) {
Set<String> resources = Stream.concat(buildResult.getClasses().stream()
.map(cls -> cls.replace('.', '/') + ".class"), buildResult.getUsedResources().stream())
.sorted()
.collect(toSet());
List<TeaVMStorage.Entry> participatingFiles = resources.stream()
.map(path -> {

View File

@ -16,7 +16,6 @@
package org.teavm.idea.jps;
import static org.teavm.idea.jps.remote.TeaVMBuilderAssistant.REMOTE_PORT;
import java.io.IOException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
@ -27,17 +26,19 @@ import org.jetbrains.jps.builders.BuildOutputConsumer;
import org.jetbrains.jps.builders.BuildRootDescriptor;
import org.jetbrains.jps.builders.DirtyFilesHolder;
import org.jetbrains.jps.incremental.CompileContext;
import org.jetbrains.jps.incremental.ProjectBuildException;
import org.jetbrains.jps.incremental.TargetBuilder;
import org.jetbrains.jps.incremental.messages.BuildMessage;
import org.jetbrains.jps.incremental.messages.CompilerMessage;
import org.teavm.idea.jps.model.TeaVMBuildStrategy;
import org.teavm.idea.jps.remote.TeaVMBuilderAssistant;
import org.teavm.idea.jps.remote.TeaVMRemoteBuildService;
import org.teavm.tooling.builder.BuildException;
import org.teavm.tooling.builder.BuildStrategy;
import org.teavm.tooling.builder.InProcessBuildStrategy;
import org.teavm.tooling.builder.RemoteBuildStrategy;
import org.teavm.tooling.daemon.RemoteBuildService;
public class TeaVMBuilder extends TargetBuilder<BuildRootDescriptor, TeaVMBuildTarget> {
private TeaVMBuilderAssistant assistant;
private TeaVMRemoteBuildService buildService;
private RemoteBuildService buildService;
public TeaVMBuilder() {
super(Collections.singletonList(TeaVMBuildTargetType.INSTANCE));
@ -52,11 +53,11 @@ public class TeaVMBuilder extends TargetBuilder<BuildRootDescriptor, TeaVMBuildT
}
}
String daemonPortString = System.getProperty(TeaVMRemoteBuildService.REMOTE_PORT);
String daemonPortString = System.getProperty(RemoteBuildService.REMOTE_PORT);
if (daemonPortString != null) {
try {
Registry registry = LocateRegistry.getRegistry(Integer.parseInt(daemonPortString));
buildService = (TeaVMRemoteBuildService) registry.lookup(TeaVMRemoteBuildService.ID);
buildService = (RemoteBuildService) registry.lookup(RemoteBuildService.ID);
} catch (NumberFormatException | RemoteException | NotBoundException e) {
e.printStackTrace();
}
@ -66,19 +67,32 @@ public class TeaVMBuilder extends TargetBuilder<BuildRootDescriptor, TeaVMBuildT
@Override
public void build(@NotNull TeaVMBuildTarget target,
@NotNull DirtyFilesHolder<BuildRootDescriptor, TeaVMBuildTarget> holder,
@NotNull BuildOutputConsumer outputConsumer, @NotNull CompileContext context) throws ProjectBuildException,
IOException {
@NotNull BuildOutputConsumer outputConsumer, @NotNull CompileContext context) {
if (assistant == null) {
context.processMessage(new CompilerMessage("TeaVM", BuildMessage.Kind.WARNING,
"No TeaVM builder assistant available. Diagnostic messages will be less informative"));
}
TeaVMBuildStrategy buildStrategy = buildService != null
try {
BuildStrategy buildStrategy = buildService != null
? new RemoteBuildStrategy(buildService)
: new InProcessBuildStrategy(context);
: createInProcessBuilder();
TeaVMBuild build = new TeaVMBuild(context, assistant, buildStrategy, outputConsumer);
build.perform(target.getModule(), target);
} catch (BuildException e) {
context.processMessage(CompilerMessage.createInternalBuilderError("TeaVM", e.getCause()));
} catch (Exception e) {
context.processMessage(CompilerMessage.createInternalBuilderError("TeaVM", e));
}
}
private BuildStrategy createInProcessBuilder() {
return new InProcessBuildStrategy((urls, innerClassLoader) -> {
RenamingClassLoader loader = new RenamingClassLoader(urls, innerClassLoader);
loader.rename("org/objectweb/asm/", "org/teavm/asm/");
return loader;
});
}
@NotNull

View File

@ -25,6 +25,7 @@ import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import org.jetbrains.jps.incremental.storage.StorageOwner;
@ -39,7 +40,7 @@ public class TeaVMStorage implements StorageOwner {
this.file = file;
if (file.exists()) {
participatingFiles = new ArrayList<>();
try (Reader innerReader = new InputStreamReader(new FileInputStream(file), "UTF-8");
try (Reader innerReader = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8);
BufferedReader reader = new BufferedReader(innerReader)) {
while (true) {
String line = reader.readLine();
@ -82,7 +83,7 @@ public class TeaVMStorage implements StorageOwner {
}
@Override
public void clean() throws IOException {
public void clean() {
file.delete();
participatingFiles = null;
}
@ -95,7 +96,7 @@ public class TeaVMStorage implements StorageOwner {
file.delete();
}
} else {
try (Writer innerWriter = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");
try (Writer innerWriter = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8);
BufferedWriter writer = new BufferedWriter(innerWriter)) {
for (Entry participatingFile : participatingFiles) {
writer.append(participatingFile.path + ":" + participatingFile.timestamp);

View File

@ -18,13 +18,17 @@ package org.teavm.idea;
import com.intellij.openapi.components.ApplicationComponent;
import com.intellij.openapi.components.ServiceManager;
import java.io.IOException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jetbrains.annotations.NotNull;
import org.teavm.idea.daemon.TeaVMBuildDaemon;
import org.teavm.idea.daemon.TeaVMDaemonInfo;
import org.teavm.idea.jps.model.TeaVMJpsWorkspaceConfiguration;
import org.teavm.idea.jps.util.DaemonUtil;
import org.teavm.tooling.daemon.BuildDaemon;
import org.teavm.tooling.daemon.DaemonInfo;
import org.teavm.tooling.daemon.DaemonLog;
public class TeaVMDaemonComponent implements ApplicationComponent {
private TeaVMDaemonInfo daemonInfo;
private DaemonInfo daemonInfo;
private boolean incremental;
private int daemonMemory;
@ -64,7 +68,9 @@ public class TeaVMDaemonComponent implements ApplicationComponent {
public void startDaemon() {
if (daemonInfo == null) {
try {
daemonInfo = TeaVMBuildDaemon.start(incremental, daemonMemory);
Log log = LogFactory.getLog(TeaVMDaemonComponent.class);
daemonInfo = BuildDaemon.start(incremental, daemonMemory, new LogWrapper(log),
DaemonUtil.detectClassPath().toArray(new String[0]));
} catch (IOException e) {
throw new RuntimeException(e);
}
@ -116,4 +122,27 @@ public class TeaVMDaemonComponent implements ApplicationComponent {
public String getComponentName() {
return "TeaVM daemon";
}
static class LogWrapper implements DaemonLog {
private Log log;
public LogWrapper(Log log) {
this.log = log;
}
@Override
public void error(String s) {
log.error(s);
}
@Override
public void error(String s, Throwable throwable) {
log.error(s, throwable);
}
@Override
public void info(String s) {
log.info(s);
}
}
}

View File

@ -20,7 +20,7 @@ import com.intellij.compiler.server.BuildProcessParametersProvider;
import java.util.ArrayList;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.teavm.idea.jps.remote.TeaVMRemoteBuildService;
import org.teavm.tooling.daemon.RemoteBuildService;
public class TeaVMJPSConfigurator extends BuildProcessParametersProvider {
private TeaVMJPSRemoteService remoteService;
@ -37,7 +37,7 @@ public class TeaVMJPSConfigurator extends BuildProcessParametersProvider {
List<String> result = new ArrayList<>();
result.add("-D" + REMOTE_PORT + "=" + remoteService.getPort());
if (daemonComponent.isDaemonRunning()) {
result.add("-D" + TeaVMRemoteBuildService.REMOTE_PORT + "=" + daemonComponent.getDaemonPort());
result.add("-D" + RemoteBuildService.REMOTE_PORT + "=" + daemonComponent.getDaemonPort());
}
return result;
}

View File

@ -99,8 +99,7 @@ public class TeaVMJPSRemoteService extends UnicastRemoteObject implements Applic
}
@Override
public TeaVMElementLocation getMethodLocation(String className, String methodName, String methodDesc)
throws RemoteException {
public TeaVMElementLocation getMethodLocation(String className, String methodName, String methodDesc) {
TeaVMElementLocation[] resultHolder = new TeaVMElementLocation[1];
ApplicationManager.getApplication().runReadAction(() -> {

View File

@ -1,217 +0,0 @@
/*
* Copyright 2015 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.maven;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.repository.MavenArtifactRepository;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.apache.maven.repository.RepositorySystem;
import org.teavm.model.ClassHolderTransformer;
import org.teavm.tooling.BaseTeaVMTool;
import org.teavm.tooling.sources.SourceFileProvider;
public abstract class AbstractTeaVMMojo extends AbstractMojo {
@Component
protected MavenProject project;
@Component
protected RepositorySystem repositorySystem;
@Parameter(required = true, readonly = true, defaultValue = "${localRepository}")
protected MavenArtifactRepository localRepository;
@Parameter(required = true, readonly = true, defaultValue = "${project.remoteArtifactRepositories}")
protected List<MavenArtifactRepository> remoteRepositories;
@Parameter(readonly = true, defaultValue = "${plugin.artifacts}")
protected List<Artifact> pluginArtifacts;
@Parameter(defaultValue = "${project.build.outputDirectory}")
protected File classFiles;
@Parameter
protected List<String> compileScopes;
@Parameter
protected boolean minifying = true;
@Parameter
protected Properties properties;
@Parameter
protected boolean debugInformationGenerated;
@Parameter
protected boolean sourceMapsGenerated;
@Parameter
protected boolean sourceFilesCopied;
@Parameter
protected boolean incremental;
@Parameter
protected String[] transformers;
protected ClassLoader classLoader;
protected abstract File getTargetDirectory();
protected final List<ClassHolderTransformer> instantiateTransformers(ClassLoader classLoader)
throws MojoExecutionException {
List<ClassHolderTransformer> transformerInstances = new ArrayList<>();
if (transformers == null) {
return transformerInstances;
}
for (String transformerName : transformers) {
Class<?> transformerRawType;
try {
transformerRawType = Class.forName(transformerName, true, classLoader);
} catch (ClassNotFoundException e) {
throw new MojoExecutionException("Transformer not found: " + transformerName, e);
}
if (!ClassHolderTransformer.class.isAssignableFrom(transformerRawType)) {
throw new MojoExecutionException("Transformer " + transformerName + " is not subtype of "
+ ClassHolderTransformer.class.getName());
}
Class<? extends ClassHolderTransformer> transformerType = transformerRawType.asSubclass(
ClassHolderTransformer.class);
Constructor<? extends ClassHolderTransformer> ctor;
try {
ctor = transformerType.getConstructor();
} catch (NoSuchMethodException e) {
throw new MojoExecutionException("Transformer " + transformerName + " has no default constructor");
}
try {
ClassHolderTransformer transformer = ctor.newInstance();
transformerInstances.add(transformer);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new MojoExecutionException("Error instantiating transformer " + transformerName, e);
}
}
return transformerInstances;
}
protected void setupTool(BaseTeaVMTool tool) throws MojoExecutionException {
tool.setLog(new MavenTeaVMToolLog(getLog()));
try {
ClassLoader classLoader = prepareClassLoader();
tool.setClassLoader(classLoader);
tool.setMinifying(minifying);
tool.setTargetDirectory(getTargetDirectory());
tool.getTransformers().addAll(instantiateTransformers(classLoader));
if (sourceFilesCopied) {
for (SourceFileProvider provider : getSourceFileProviders()) {
tool.addSourceFileProvider(provider);
}
}
if (properties != null) {
tool.getProperties().putAll(properties);
}
tool.setIncremental(incremental);
tool.setDebugInformationGenerated(debugInformationGenerated);
tool.setSourceMapsFileGenerated(sourceMapsGenerated);
tool.setSourceFilesCopied(sourceFilesCopied);
} catch (RuntimeException e) {
throw new MojoExecutionException("Unexpected error occured", e);
}
}
protected final ClassLoader prepareClassLoader() throws MojoExecutionException {
try {
Log log = getLog();
log.info("Preparing classpath for JavaScript generation");
List<URL> urls = new ArrayList<>();
StringBuilder classpath = new StringBuilder();
for (Artifact artifact : project.getArtifacts()) {
if (!filterByScope(artifact)) {
continue;
}
File file = artifact.getFile();
if (classpath.length() > 0) {
classpath.append(':');
}
classpath.append(file.getPath());
urls.add(file.toURI().toURL());
}
if (classpath.length() > 0) {
classpath.append(':');
}
classpath.append(classFiles.getPath());
urls.add(classFiles.toURI().toURL());
for (File additionalEntry : getAdditionalClassPath()) {
classpath.append(':').append(additionalEntry.getPath());
urls.add(additionalEntry.toURI().toURL());
}
log.info("Using the following classpath for JavaScript generation: " + classpath);
classLoader = new URLClassLoader(urls.toArray(new URL[urls.size()]),
AbstractTeaVMMojo.class.getClassLoader());
return classLoader;
} catch (MalformedURLException e) {
throw new MojoExecutionException("Error gathering classpath information", e);
}
}
protected List<File> getAdditionalClassPath() {
return Collections.emptyList();
}
protected boolean filterByScope(Artifact artifact) {
return compileScopes == null ? isSupportedScope(artifact.getScope())
: compileScopes.contains(artifact.getScope());
}
protected boolean isSupportedScope(String scope) {
switch (scope) {
case Artifact.SCOPE_COMPILE:
case Artifact.SCOPE_PROVIDED:
case Artifact.SCOPE_SYSTEM:
return true;
default:
return false;
}
}
protected final List<SourceFileProvider> getSourceFileProviders() {
MavenSourceFileProviderLookup lookup = new MavenSourceFileProviderLookup();
lookup.setMavenProject(project);
lookup.setRepositorySystem(repositorySystem);
lookup.setLocalRepository(localRepository);
lookup.setRemoteRepositories(remoteRepositories);
lookup.setPluginDependencies(pluginArtifacts);
List<SourceFileProvider> providers = lookup.resolve();
addSourceProviders(providers);
return providers;
}
protected void addSourceProviders(@SuppressWarnings("unused") List<SourceFileProvider> providers) {
}
}

View File

@ -26,9 +26,7 @@ import org.apache.maven.artifact.resolver.ArtifactResolutionRequest;
import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
import org.apache.maven.project.MavenProject;
import org.apache.maven.repository.RepositorySystem;
import org.teavm.tooling.sources.DirectorySourceFileProvider;
import org.teavm.tooling.sources.JarSourceFileProvider;
import org.teavm.tooling.sources.SourceFileProvider;
import org.teavm.tooling.builder.BuildStrategy;
public class MavenSourceFileProviderLookup {
private MavenProject mavenProject;
@ -57,7 +55,7 @@ public class MavenSourceFileProviderLookup {
this.pluginDependencies = pluginDependencies;
}
public List<SourceFileProvider> resolve() {
public void resolve(BuildStrategy builder) {
List<Artifact> initialArtifacts = new ArrayList<>();
initialArtifacts.addAll(mavenProject.getArtifacts());
if (pluginDependencies != null) {
@ -74,7 +72,6 @@ public class MavenSourceFileProviderLookup {
}
artifacts.addAll(initialArtifacts);
List<SourceFileProvider> providers = new ArrayList<>();
for (Artifact artifact : artifacts) {
ArtifactResolutionRequest request = new ArtifactResolutionRequest()
.setLocalRepository(localRepository)
@ -85,16 +82,15 @@ public class MavenSourceFileProviderLookup {
if (resolvedArtifact.getFile() != null) {
File file = resolvedArtifact.getFile();
if (!file.isDirectory()) {
providers.add(new JarSourceFileProvider(file));
builder.addSourcesJar(file.getAbsolutePath());
} else {
providers.add(new DirectorySourceFileProvider(file));
builder.addSourcesDirectory(file.getAbsolutePath());
}
}
}
}
for (String sourceRoot : mavenProject.getCompileSourceRoots()) {
providers.add(new DirectorySourceFileProvider(new File(sourceRoot)));
builder.addSourcesDirectory(new File(sourceRoot).getAbsolutePath());
}
return providers;
}
}

View File

@ -16,26 +16,88 @@
package org.teavm.maven;
import java.io.File;
import java.util.Arrays;
import java.net.URLClassLoader;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.repository.MavenArtifactRepository;
import org.apache.maven.artifact.resolver.ArtifactResolutionRequest;
import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
import org.apache.maven.repository.RepositorySystem;
import org.teavm.backend.wasm.render.WasmBinaryVersion;
import org.teavm.tooling.TeaVMProblemRenderer;
import org.teavm.tooling.TeaVMTargetType;
import org.teavm.tooling.TeaVMTool;
import org.teavm.tooling.TeaVMToolException;
import org.teavm.tooling.sources.DirectorySourceFileProvider;
import org.teavm.tooling.sources.SourceFileProvider;
import org.teavm.tooling.builder.BuildException;
import org.teavm.tooling.builder.BuildResult;
import org.teavm.tooling.builder.BuildStrategy;
import org.teavm.tooling.builder.InProcessBuildStrategy;
import org.teavm.tooling.builder.RemoteBuildStrategy;
import org.teavm.tooling.daemon.BuildDaemon;
import org.teavm.tooling.daemon.DaemonInfo;
import org.teavm.tooling.daemon.DaemonLog;
import org.teavm.tooling.daemon.RemoteBuildService;
import org.teavm.vm.TeaVMOptimizationLevel;
@Mojo(name = "compile", requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME,
requiresDependencyCollection = ResolutionScope.COMPILE_PLUS_RUNTIME,
defaultPhase = LifecyclePhase.PROCESS_CLASSES)
public class TeaVMCompileMojo extends AbstractTeaVMMojo {
public class TeaVMCompileMojo extends AbstractMojo {
@Component
private MavenProject project;
@Component
private RepositorySystem repositorySystem;
@Parameter(required = true, readonly = true, defaultValue = "${localRepository}")
private MavenArtifactRepository localRepository;
@Parameter(required = true, readonly = true, defaultValue = "${project.remoteArtifactRepositories}")
private List<MavenArtifactRepository> remoteRepositories;
@Parameter(readonly = true, defaultValue = "${plugin.artifacts}")
private List<Artifact> pluginArtifacts;
@Parameter(defaultValue = "${project.build.outputDirectory}")
private File classFiles;
@Parameter
private List<String> compileScopes;
@Parameter
private boolean minifying = true;
@Parameter
private Properties properties;
@Parameter
private boolean debugInformationGenerated;
@Parameter
private boolean sourceMapsGenerated;
@Parameter
private boolean sourceFilesCopied;
@Parameter
private boolean incremental;
@Parameter
private String[] transformers;
@Parameter(defaultValue = "${project.build.directory}/javascript")
private File targetDirectory;
@ -61,48 +123,197 @@ public class TeaVMCompileMojo extends AbstractTeaVMMojo {
private TeaVMTargetType targetType = TeaVMTargetType.JAVASCRIPT;
@Parameter(defaultValue = "${project.build.directory}/teavm-cache")
protected File cacheDirectory;
private TeaVMTool tool = new TeaVMTool();
private File cacheDirectory;
@Parameter
private WasmBinaryVersion wasmVersion = WasmBinaryVersion.V_0x1;
@Override
protected File getTargetDirectory() {
return targetDirectory;
@Parameter(property = "teavm.outOfProcess", defaultValue = "false")
private boolean outOfProcess;
@Parameter(property = "teavm.processMemory", defaultValue = "512")
private int processMemory;
private void setupBuilder(BuildStrategy builder) throws MojoExecutionException {
builder.setLog(new MavenTeaVMToolLog(getLog()));
try {
builder.setClassPathEntries(prepareClassPath());
builder.setMinifying(minifying);
builder.setTargetDirectory(targetDirectory.getAbsolutePath());
if (transformers != null) {
builder.setTransformers(transformers);
}
if (sourceFilesCopied) {
getSourceFileProviders(builder);
builder.addSourcesDirectory(sourceDirectory.getAbsolutePath());
}
if (properties != null) {
builder.setProperties(properties);
}
builder.setIncremental(incremental);
builder.setDebugInformationGenerated(debugInformationGenerated);
builder.setSourceMapsFileGenerated(sourceMapsGenerated);
builder.setSourceFilesCopied(sourceFilesCopied);
} catch (RuntimeException e) {
throw new MojoExecutionException("Unexpected error occurred", e);
}
}
private List<String> prepareClassPath() {
Log log = getLog();
log.info("Preparing classpath for JavaScript generation");
List<String> paths = new ArrayList<>();
StringBuilder classpath = new StringBuilder();
for (Artifact artifact : project.getArtifacts()) {
if (!filterByScope(artifact)) {
continue;
}
File file = artifact.getFile();
if (classpath.length() > 0) {
classpath.append(':');
}
classpath.append(file.getPath());
paths.add(file.getAbsolutePath());
}
if (classpath.length() > 0) {
classpath.append(':');
}
classpath.append(classFiles.getPath());
paths.add(classFiles.getAbsolutePath());
log.info("Using the following classpath for JavaScript generation: " + classpath);
return paths;
}
private boolean filterByScope(Artifact artifact) {
return compileScopes == null ? isSupportedScope(artifact.getScope())
: compileScopes.contains(artifact.getScope());
}
protected boolean isSupportedScope(String scope) {
switch (scope) {
case Artifact.SCOPE_COMPILE:
case Artifact.SCOPE_PROVIDED:
case Artifact.SCOPE_SYSTEM:
return true;
default:
return false;
}
}
private void getSourceFileProviders(BuildStrategy builder) {
MavenSourceFileProviderLookup lookup = new MavenSourceFileProviderLookup();
lookup.setMavenProject(project);
lookup.setRepositorySystem(repositorySystem);
lookup.setLocalRepository(localRepository);
lookup.setRemoteRepositories(remoteRepositories);
lookup.setPluginDependencies(pluginArtifacts);
lookup.resolve(builder);
}
@Override
public void execute() throws MojoExecutionException {
Log log = getLog();
setupTool(tool);
tool.setLog(new MavenTeaVMToolLog(log));
try {
tool.setMainClass(mainClass);
if (!targetFileName.isEmpty()) {
tool.setTargetFileName(targetFileName);
}
tool.setOptimizationLevel(optimizationLevel);
if (classesToPreserve != null) {
tool.getClassesToPreserve().addAll(Arrays.asList(classesToPreserve));
}
tool.setCacheDirectory(cacheDirectory);
tool.setTargetType(targetType);
tool.setWasmVersion(wasmVersion);
tool.generate();
if (stopOnErrors && !tool.getProblemProvider().getSevereProblems().isEmpty()) {
throw new MojoExecutionException("Build error");
}
} catch (RuntimeException e) {
throw new MojoExecutionException("Unexpected error occured", e);
} catch (TeaVMToolException e) {
throw new MojoExecutionException("IO error occured", e);
if (outOfProcess) {
executeInSeparateProcess();
} else {
executeWithBuilder(new InProcessBuildStrategy(URLClassLoader::new));
}
}
private void executeInSeparateProcess() throws MojoExecutionException {
DaemonInfo daemon;
try {
daemon = BuildDaemon.start(false, processMemory, new DaemonLogImpl(), createDaemonClassPath());
} catch (Throwable e) {
throw new MojoExecutionException("Error starting TeaVM process", e);
}
try {
RemoteBuildService buildService;
try {
Registry registry = LocateRegistry.getRegistry(daemon.getPort());
buildService = (RemoteBuildService) registry.lookup(RemoteBuildService.ID);
} catch (RemoteException | NotBoundException e) {
throw new MojoExecutionException("Error connecting TeaVM process", e);
}
RemoteBuildStrategy builder = new RemoteBuildStrategy(buildService);
executeWithBuilder(builder);
} finally {
daemon.getProcess().destroy();
}
}
private void executeWithBuilder(BuildStrategy builder) throws MojoExecutionException {
builder.init();
Log log = getLog();
setupBuilder(builder);
MavenTeaVMToolLog toolLog = new MavenTeaVMToolLog(log);
builder.setLog(toolLog);
try {
builder.setMainClass(mainClass);
if (!targetFileName.isEmpty()) {
builder.setTargetFileName(targetFileName);
}
builder.setOptimizationLevel(optimizationLevel);
if (classesToPreserve != null) {
builder.setClassesToPreserve(classesToPreserve);
}
builder.setCacheDirectory(cacheDirectory.getAbsolutePath());
builder.setTargetType(targetType);
builder.setWasmVersion(wasmVersion);
BuildResult result;
result = builder.build();
TeaVMProblemRenderer.describeProblems(result.getCallGraph(), result.getProblems(), toolLog);
if (stopOnErrors && !result.getProblems().getSevereProblems().isEmpty()) {
throw new MojoExecutionException("Build error");
}
} catch (BuildException e) {
throw new MojoExecutionException("Unexpected error occurred", e.getCause());
} catch (Exception e) {
throw new MojoExecutionException("Unexpected error occurred", e);
}
}
private String[] createDaemonClassPath() {
Artifact toolArtifact = pluginArtifacts.stream()
.filter(artifact -> artifact.getGroupId().equals("org.teavm")
&& artifact.getArtifactId().equals("teavm-tooling"))
.findFirst()
.orElse(null);
if (toolArtifact == null) {
return new String[0];
}
ArtifactResolutionResult resolutionResult = repositorySystem.resolve(new ArtifactResolutionRequest()
.setLocalRepository(localRepository)
.setRemoteRepositories(new ArrayList<>(remoteRepositories))
.setResolveTransitively(true)
.setResolveRoot(true)
.setArtifact(toolArtifact));
if (!resolutionResult.isSuccess()) {
return new String[0];
}
return resolutionResult.getArtifacts().stream()
.map(artifact -> artifact.getFile().getAbsolutePath())
.toArray(String[]::new);
}
class DaemonLogImpl implements DaemonLog {
@Override
protected void addSourceProviders(List<SourceFileProvider> providers) {
providers.add(new DirectorySourceFileProvider(sourceDirectory));
public void error(String message) {
getLog().error(message);
}
@Override
public void error(String message, Throwable e) {
getLog().error(message, e);
}
@Override
public void info(String message) {
getLog().info(message);
}
}
}