From fc799afcda3f73b3523c9b3b4ab21a15da9f3c23 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Tue, 20 Nov 2018 14:22:35 +0300 Subject: [PATCH] Add Maven option to run TeaVM in a separate process --- .idea/runConfigurations/IDEA_JPS_Debug.xml | 7 +- classlib/pom.xml | 2 + jso/impl/pom.xml | 1 + metaprogramming/impl/pom.xml | 1 + platform/pom.xml | 1 + .../java/org/teavm/tooling/BaseTeaVMTool.java | 46 --- .../teavm/tooling/TeaVMProblemRenderer.java | 8 +- .../java/org/teavm/tooling/TeaVMTool.java | 60 ++-- .../teavm/tooling/builder/BuildException.java | 22 ++ .../teavm/tooling/builder/BuildResult.java} | 10 +- .../teavm/tooling/builder/BuildStrategy.java} | 27 +- .../tooling/builder/ClassLoaderFactory.java | 22 ++ .../builder}/InProcessBuildStrategy.java | 118 ++++--- .../tooling/builder}/RemoteBuildStrategy.java | 138 ++++++--- .../teavm/tooling/daemon/BuildDaemon.java} | 150 ++++----- .../org/teavm/tooling/daemon/DaemonInfo.java} | 8 +- .../org/teavm/tooling/daemon/DaemonLog.java | 24 ++ .../tooling/daemon/RemoteBuildCallback.java} | 18 +- .../teavm/tooling/daemon/RemoteBuildLog.java | 90 ++++++ .../tooling/daemon/RemoteBuildRequest.java} | 15 +- .../tooling/daemon/RemoteBuildResponse.java} | 9 +- .../tooling/daemon/RemoteBuildService.java} | 9 +- .../teavm/eclipse/TeaVMProjectBuilder.java | 40 +-- .../org/teavm/idea/jps/util/DaemonUtil.java | 73 +++++ .../teavm/idea/jps/RenamingClassLoader.java | 2 +- .../org/teavm/idea/jps/RenamingVisitor.java | 2 +- .../java/org/teavm/idea/jps/TeaVMBuild.java | 33 +- .../java/org/teavm/idea/jps/TeaVMBuilder.java | 42 ++- .../java/org/teavm/idea/jps/TeaVMStorage.java | 7 +- .../org/teavm/idea/TeaVMDaemonComponent.java | 37 ++- .../org/teavm/idea/TeaVMJPSConfigurator.java | 4 +- .../org/teavm/idea/TeaVMJPSRemoteService.java | 3 +- .../org/teavm/maven/AbstractTeaVMMojo.java | 217 ------------- .../maven/MavenSourceFileProviderLookup.java | 14 +- .../org/teavm/maven/TeaVMCompileMojo.java | 287 +++++++++++++++--- 35 files changed, 930 insertions(+), 617 deletions(-) delete mode 100644 tools/core/src/main/java/org/teavm/tooling/BaseTeaVMTool.java create mode 100644 tools/core/src/main/java/org/teavm/tooling/builder/BuildException.java rename tools/{idea/jps-common/src/main/java/org/teavm/idea/jps/model/TeaVMBuildResult.java => core/src/main/java/org/teavm/tooling/builder/BuildResult.java} (84%) rename tools/{idea/jps-common/src/main/java/org/teavm/idea/jps/model/TeaVMBuildStrategy.java => core/src/main/java/org/teavm/tooling/builder/BuildStrategy.java} (66%) create mode 100644 tools/core/src/main/java/org/teavm/tooling/builder/ClassLoaderFactory.java rename tools/{idea/jps-plugin/src/main/java/org/teavm/idea/jps => core/src/main/java/org/teavm/tooling/builder}/InProcessBuildStrategy.java (67%) rename tools/{idea/jps-plugin/src/main/java/org/teavm/idea/jps => core/src/main/java/org/teavm/tooling/builder}/RemoteBuildStrategy.java (56%) rename tools/{idea/plugin/src/main/java/org/teavm/idea/daemon/TeaVMBuildDaemon.java => core/src/main/java/org/teavm/tooling/daemon/BuildDaemon.java} (71%) rename tools/{idea/plugin/src/main/java/org/teavm/idea/daemon/TeaVMDaemonInfo.java => core/src/main/java/org/teavm/tooling/daemon/DaemonInfo.java} (84%) create mode 100644 tools/core/src/main/java/org/teavm/tooling/daemon/DaemonLog.java rename tools/{idea/jps-common/src/main/java/org/teavm/idea/jps/remote/TeaVMRemoteBuildCallback.java => core/src/main/java/org/teavm/tooling/daemon/RemoteBuildCallback.java} (61%) create mode 100644 tools/core/src/main/java/org/teavm/tooling/daemon/RemoteBuildLog.java rename tools/{idea/jps-common/src/main/java/org/teavm/idea/jps/remote/TeaVMRemoteBuildRequest.java => core/src/main/java/org/teavm/tooling/daemon/RemoteBuildRequest.java} (71%) rename tools/{idea/jps-common/src/main/java/org/teavm/idea/jps/remote/TeaVMRemoteBuildResponse.java => core/src/main/java/org/teavm/tooling/daemon/RemoteBuildResponse.java} (85%) rename tools/{idea/jps-common/src/main/java/org/teavm/idea/jps/remote/TeaVMRemoteBuildService.java => core/src/main/java/org/teavm/tooling/daemon/RemoteBuildService.java} (73%) create mode 100644 tools/idea/jps-common/src/main/java/org/teavm/idea/jps/util/DaemonUtil.java delete mode 100644 tools/maven/plugin/src/main/java/org/teavm/maven/AbstractTeaVMMojo.java diff --git a/.idea/runConfigurations/IDEA_JPS_Debug.xml b/.idea/runConfigurations/IDEA_JPS_Debug.xml index ed9acd170..6f4356779 100644 --- a/.idea/runConfigurations/IDEA_JPS_Debug.xml +++ b/.idea/runConfigurations/IDEA_JPS_Debug.xml @@ -1,14 +1,15 @@ - + \ No newline at end of file diff --git a/classlib/pom.xml b/classlib/pom.xml index feb133eec..53898bc71 100644 --- a/classlib/pom.xml +++ b/classlib/pom.xml @@ -48,6 +48,7 @@ org.teavm teavm-core ${project.version} + provided org.teavm @@ -77,6 +78,7 @@ org.ow2.asm asm-util + provided com.google.code.gson diff --git a/jso/impl/pom.xml b/jso/impl/pom.xml index 7d076f6b5..0d58066fc 100644 --- a/jso/impl/pom.xml +++ b/jso/impl/pom.xml @@ -33,6 +33,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xs org.teavm teavm-core ${project.version} + provided org.teavm diff --git a/metaprogramming/impl/pom.xml b/metaprogramming/impl/pom.xml index ada51eb94..009228526 100644 --- a/metaprogramming/impl/pom.xml +++ b/metaprogramming/impl/pom.xml @@ -38,6 +38,7 @@ org.teavm teavm-core ${project.version} + provided org.teavm diff --git a/platform/pom.xml b/platform/pom.xml index 5693a3652..f253a7922 100644 --- a/platform/pom.xml +++ b/platform/pom.xml @@ -34,6 +34,7 @@ org.teavm teavm-core ${project.version} + provided org.teavm diff --git a/tools/core/src/main/java/org/teavm/tooling/BaseTeaVMTool.java b/tools/core/src/main/java/org/teavm/tooling/BaseTeaVMTool.java deleted file mode 100644 index cf1e0ca28..000000000 --- a/tools/core/src/main/java/org/teavm/tooling/BaseTeaVMTool.java +++ /dev/null @@ -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 getTransformers(); - - void setLog(TeaVMToolLog log); - - void setClassLoader(ClassLoader classLoader); - - void addSourceFileProvider(SourceFileProvider sourceFileProvider); -} diff --git a/tools/core/src/main/java/org/teavm/tooling/TeaVMProblemRenderer.java b/tools/core/src/main/java/org/teavm/tooling/TeaVMProblemRenderer.java index 8288e45b7..e3285d7a9 100644 --- a/tools/core/src/main/java/org/teavm/tooling/TeaVMProblemRenderer.java +++ b/tools/core/src/main/java/org/teavm/tooling/TeaVMProblemRenderer.java @@ -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(); diff --git a/tools/core/src/main/java/org/teavm/tooling/TeaVMTool.java b/tools/core/src/main/java/org/teavm/tooling/TeaVMTool.java index c717a67d6..2b27e2f9d 100644 --- a/tools/core/src/main/java/org/teavm/tooling/TeaVMTool.java +++ b/tools/core/src/main/java/org/teavm/tooling/TeaVMTool.java @@ -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 transformers = new ArrayList<>(); + private List transformers = new ArrayList<>(); private List 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 getTransformers() { + public List 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 resolveTransformers(ClassLoader classLoader) { + List 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 transformerType = transformerRawType.asSubclass( + ClassHolderTransformer.class); + Constructor 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; + } } diff --git a/tools/core/src/main/java/org/teavm/tooling/builder/BuildException.java b/tools/core/src/main/java/org/teavm/tooling/builder/BuildException.java new file mode 100644 index 000000000..388ad7b6e --- /dev/null +++ b/tools/core/src/main/java/org/teavm/tooling/builder/BuildException.java @@ -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); + } +} diff --git a/tools/idea/jps-common/src/main/java/org/teavm/idea/jps/model/TeaVMBuildResult.java b/tools/core/src/main/java/org/teavm/tooling/builder/BuildResult.java similarity index 84% rename from tools/idea/jps-common/src/main/java/org/teavm/idea/jps/model/TeaVMBuildResult.java rename to tools/core/src/main/java/org/teavm/tooling/builder/BuildResult.java index ffca62f2b..f068d66ac 100644 --- a/tools/idea/jps-common/src/main/java/org/teavm/idea/jps/model/TeaVMBuildResult.java +++ b/tools/core/src/main/java/org/teavm/tooling/builder/BuildResult.java @@ -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 getUsedResources(); diff --git a/tools/idea/jps-common/src/main/java/org/teavm/idea/jps/model/TeaVMBuildStrategy.java b/tools/core/src/main/java/org/teavm/tooling/builder/BuildStrategy.java similarity index 66% rename from tools/idea/jps-common/src/main/java/org/teavm/idea/jps/model/TeaVMBuildStrategy.java rename to tools/core/src/main/java/org/teavm/tooling/builder/BuildStrategy.java index 168a666cb..6bfcc1b3d 100644 --- a/tools/idea/jps-common/src/main/java/org/teavm/idea/jps/model/TeaVMBuildStrategy.java +++ b/tools/core/src/main/java/org/teavm/tooling/builder/BuildStrategy.java @@ -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; } diff --git a/tools/core/src/main/java/org/teavm/tooling/builder/ClassLoaderFactory.java b/tools/core/src/main/java/org/teavm/tooling/builder/ClassLoaderFactory.java new file mode 100644 index 000000000..ac62b7d10 --- /dev/null +++ b/tools/core/src/main/java/org/teavm/tooling/builder/ClassLoaderFactory.java @@ -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); +} diff --git a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/InProcessBuildStrategy.java b/tools/core/src/main/java/org/teavm/tooling/builder/InProcessBuildStrategy.java similarity index 67% rename from tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/InProcessBuildStrategy.java rename to tools/core/src/main/java/org/teavm/tooling/builder/InProcessBuildStrategy.java index 77f4f8a16..63c957a40 100644 --- a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/InProcessBuildStrategy.java +++ b/tools/core/src/main/java/org/teavm/tooling/builder/InProcessBuildStrategy.java @@ -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 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 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 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 classes; private Collection usedResources; private Collection generatedFiles; - InProcessBuildResult(CallGraph callGraph, boolean errorOccurred, String stackTrace, - ProblemProvider problemProvider, Collection classes, Collection usedResources, - Collection generatedFiles) { + InProcessBuildResult(CallGraph callGraph, ProblemProvider problemProvider, + Collection classes, Collection usedResources, Collection 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; diff --git a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/RemoteBuildStrategy.java b/tools/core/src/main/java/org/teavm/tooling/builder/RemoteBuildStrategy.java similarity index 56% rename from tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/RemoteBuildStrategy.java rename to tools/core/src/main/java/org/teavm/tooling/builder/RemoteBuildStrategy.java index 165211183..207c7ea96 100644 --- a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/RemoteBuildStrategy.java +++ b/tools/core/src/main/java/org/teavm/tooling/builder/RemoteBuildStrategy.java @@ -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; + public void setLog(TeaVMToolLog log) { + this.log = log; + } + + @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)); + response = buildService.build(request, new CallbackImpl(progressListener, log)); } catch (Throwable e) { - throw new RuntimeException(e); + throw new BuildException(e); } - return new TeaVMBuildResult() { + if (response.exception != null) { + throw new BuildException(response.exception); + } + return new BuildResult() { private ProblemProvider problems = new ProblemProvider() { @Override public List 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); } } } diff --git a/tools/idea/plugin/src/main/java/org/teavm/idea/daemon/TeaVMBuildDaemon.java b/tools/core/src/main/java/org/teavm/tooling/daemon/BuildDaemon.java similarity index 71% rename from tools/idea/plugin/src/main/java/org/teavm/idea/daemon/TeaVMBuildDaemon.java rename to tools/core/src/main/java/org/teavm/tooling/daemon/BuildDaemon.java index 1feae14e1..223f4eb8a 100644 --- a/tools/idea/plugin/src/main/java/org/teavm/idea/daemon/TeaVMBuildDaemon.java +++ b/tools/core/src/main/java/org/teavm/tooling/daemon/BuildDaemon.java @@ -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 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 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); - tool.getProperties().putAll(request.properties); + 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,27 +172,24 @@ 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; - 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()); - response.generatedFiles.addAll(tool.getGeneratedFiles().stream() - .map(File::getAbsolutePath) - .collect(Collectors.toSet())); + if (response.exception == null) { + 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()); + 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 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 detectClassPath() { - IdeaPluginDescriptor plugin = PluginManager.getPlugin(PluginId.getId("org.teavm.idea")); - Set visited = new HashSet<>(); - List classPath = new ArrayList<>(); - findInHierarchy(plugin.getPath(), classPath, visited); - return classPath; - } - - private static void findInHierarchy(File file, List targetFiles, Set 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); - } - } - } } diff --git a/tools/idea/plugin/src/main/java/org/teavm/idea/daemon/TeaVMDaemonInfo.java b/tools/core/src/main/java/org/teavm/tooling/daemon/DaemonInfo.java similarity index 84% rename from tools/idea/plugin/src/main/java/org/teavm/idea/daemon/TeaVMDaemonInfo.java rename to tools/core/src/main/java/org/teavm/tooling/daemon/DaemonInfo.java index 2354f6733..075f39ab9 100644 --- a/tools/idea/plugin/src/main/java/org/teavm/idea/daemon/TeaVMDaemonInfo.java +++ b/tools/core/src/main/java/org/teavm/tooling/daemon/DaemonInfo.java @@ -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; } diff --git a/tools/core/src/main/java/org/teavm/tooling/daemon/DaemonLog.java b/tools/core/src/main/java/org/teavm/tooling/daemon/DaemonLog.java new file mode 100644 index 000000000..827153fce --- /dev/null +++ b/tools/core/src/main/java/org/teavm/tooling/daemon/DaemonLog.java @@ -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); +} diff --git a/tools/idea/jps-common/src/main/java/org/teavm/idea/jps/remote/TeaVMRemoteBuildCallback.java b/tools/core/src/main/java/org/teavm/tooling/daemon/RemoteBuildCallback.java similarity index 61% rename from tools/idea/jps-common/src/main/java/org/teavm/idea/jps/remote/TeaVMRemoteBuildCallback.java rename to tools/core/src/main/java/org/teavm/tooling/daemon/RemoteBuildCallback.java index 9a19b224c..8a1ce0733 100644 --- a/tools/idea/jps-common/src/main/java/org/teavm/idea/jps/remote/TeaVMRemoteBuildCallback.java +++ b/tools/core/src/main/java/org/teavm/tooling/daemon/RemoteBuildCallback.java @@ -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; } diff --git a/tools/core/src/main/java/org/teavm/tooling/daemon/RemoteBuildLog.java b/tools/core/src/main/java/org/teavm/tooling/daemon/RemoteBuildLog.java new file mode 100644 index 000000000..de09afc30 --- /dev/null +++ b/tools/core/src/main/java/org/teavm/tooling/daemon/RemoteBuildLog.java @@ -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(); + } + } +} diff --git a/tools/idea/jps-common/src/main/java/org/teavm/idea/jps/remote/TeaVMRemoteBuildRequest.java b/tools/core/src/main/java/org/teavm/tooling/daemon/RemoteBuildRequest.java similarity index 71% rename from tools/idea/jps-common/src/main/java/org/teavm/idea/jps/remote/TeaVMRemoteBuildRequest.java rename to tools/core/src/main/java/org/teavm/tooling/daemon/RemoteBuildRequest.java index 67f7e86dc..78e4cf865 100644 --- a/tools/idea/jps-common/src/main/java/org/teavm/idea/jps/remote/TeaVMRemoteBuildRequest.java +++ b/tools/core/src/main/java/org/teavm/tooling/daemon/RemoteBuildRequest.java @@ -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 sourceDirectories = new ArrayList<>(); public final List sourceJarFiles = new ArrayList<>(); public final List 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; } diff --git a/tools/idea/jps-common/src/main/java/org/teavm/idea/jps/remote/TeaVMRemoteBuildResponse.java b/tools/core/src/main/java/org/teavm/tooling/daemon/RemoteBuildResponse.java similarity index 85% rename from tools/idea/jps-common/src/main/java/org/teavm/idea/jps/remote/TeaVMRemoteBuildResponse.java rename to tools/core/src/main/java/org/teavm/tooling/daemon/RemoteBuildResponse.java index 6364a5e4b..dc68e2683 100644 --- a/tools/idea/jps-common/src/main/java/org/teavm/idea/jps/remote/TeaVMRemoteBuildResponse.java +++ b/tools/core/src/main/java/org/teavm/tooling/daemon/RemoteBuildResponse.java @@ -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 problems = new ArrayList<>(); public final List severeProblems = new ArrayList<>(); public final Set usedResources = new HashSet<>(); public final Set classes = new HashSet<>(); public final Set generatedFiles = new HashSet<>(); - public String stackTrace; + public Throwable exception; } diff --git a/tools/idea/jps-common/src/main/java/org/teavm/idea/jps/remote/TeaVMRemoteBuildService.java b/tools/core/src/main/java/org/teavm/tooling/daemon/RemoteBuildService.java similarity index 73% rename from tools/idea/jps-common/src/main/java/org/teavm/idea/jps/remote/TeaVMRemoteBuildService.java rename to tools/core/src/main/java/org/teavm/tooling/daemon/RemoteBuildService.java index 951991ca3..bb04a3392 100644 --- a/tools/idea/jps-common/src/main/java/org/teavm/idea/jps/remote/TeaVMRemoteBuildService.java +++ b/tools/core/src/main/java/org/teavm/tooling/daemon/RemoteBuildService.java @@ -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; } diff --git a/tools/eclipse/plugin/src/main/java/org/teavm/eclipse/TeaVMProjectBuilder.java b/tools/eclipse/plugin/src/main/java/org/teavm/eclipse/TeaVMProjectBuilder.java index 0e87ee95f..ac7aed340 100644 --- a/tools/eclipse/plugin/src/main/java/org/teavm/eclipse/TeaVMProjectBuilder.java +++ b/tools/eclipse/plugin/src/main/java/org/teavm/eclipse/TeaVMProjectBuilder.java @@ -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 instantiateTransformers(TeaVMProfile profile, ClassLoader classLoader) - throws CoreException{ - List 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 transformerType = transformerRawType.asSubclass( - ClassHolderTransformer.class); - Constructor 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); diff --git a/tools/idea/jps-common/src/main/java/org/teavm/idea/jps/util/DaemonUtil.java b/tools/idea/jps-common/src/main/java/org/teavm/idea/jps/util/DaemonUtil.java new file mode 100644 index 000000000..0b4b41da8 --- /dev/null +++ b/tools/idea/jps-common/src/main/java/org/teavm/idea/jps/util/DaemonUtil.java @@ -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 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 detectClassPath() { + IdeaPluginDescriptor plugin = PluginManager.getPlugin(PluginId.getId("org.teavm.idea")); + Set visited = new HashSet<>(); + List classPath = new ArrayList<>(); + findInHierarchy(plugin.getPath(), classPath, visited); + return classPath; + } + + private static void findInHierarchy(File file, List targetFiles, Set 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); + } + } + } +} diff --git a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/RenamingClassLoader.java b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/RenamingClassLoader.java index 484229829..c64162c53 100644 --- a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/RenamingClassLoader.java +++ b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/RenamingClassLoader.java @@ -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. diff --git a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/RenamingVisitor.java b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/RenamingVisitor.java index b05f11627..5b279f8c3 100644 --- a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/RenamingVisitor.java +++ b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/RenamingVisitor.java @@ -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. diff --git a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuild.java b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuild.java index d4f76300c..4f6341298 100644 --- a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuild.java +++ b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuild.java @@ -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 sourceFileCache = new HashMap<>(); private final Map 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,24 +130,16 @@ 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)); + for (String fileName : buildResult.getGeneratedFiles()) { + outputConsumer.registerOutputFile(new File(fileName), Collections.emptyList()); } 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 resources = Stream.concat(buildResult.getClasses().stream() .map(cls -> cls.replace('.', '/') + ".class"), buildResult.getUsedResources().stream()) - .sorted() .collect(toSet()); List participatingFiles = resources.stream() .map(path -> { diff --git a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuilder.java b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuilder.java index 934255721..93cb72158 100644 --- a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuilder.java +++ b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuilder.java @@ -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 { 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 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 - ? new RemoteBuildStrategy(buildService) - : new InProcessBuildStrategy(context); - TeaVMBuild build = new TeaVMBuild(context, assistant, buildStrategy, outputConsumer); + try { + BuildStrategy buildStrategy = buildService != null + ? new RemoteBuildStrategy(buildService) + : createInProcessBuilder(); + TeaVMBuild build = new TeaVMBuild(context, assistant, buildStrategy, outputConsumer); - build.perform(target.getModule(), target); + 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 diff --git a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMStorage.java b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMStorage.java index f64712a75..8bfa4d536 100644 --- a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMStorage.java +++ b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMStorage.java @@ -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); diff --git a/tools/idea/plugin/src/main/java/org/teavm/idea/TeaVMDaemonComponent.java b/tools/idea/plugin/src/main/java/org/teavm/idea/TeaVMDaemonComponent.java index 2e1def345..99373dfbb 100644 --- a/tools/idea/plugin/src/main/java/org/teavm/idea/TeaVMDaemonComponent.java +++ b/tools/idea/plugin/src/main/java/org/teavm/idea/TeaVMDaemonComponent.java @@ -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); + } + } } diff --git a/tools/idea/plugin/src/main/java/org/teavm/idea/TeaVMJPSConfigurator.java b/tools/idea/plugin/src/main/java/org/teavm/idea/TeaVMJPSConfigurator.java index 507964634..4ced5634d 100644 --- a/tools/idea/plugin/src/main/java/org/teavm/idea/TeaVMJPSConfigurator.java +++ b/tools/idea/plugin/src/main/java/org/teavm/idea/TeaVMJPSConfigurator.java @@ -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 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; } diff --git a/tools/idea/plugin/src/main/java/org/teavm/idea/TeaVMJPSRemoteService.java b/tools/idea/plugin/src/main/java/org/teavm/idea/TeaVMJPSRemoteService.java index 75da5ec14..05a77af02 100644 --- a/tools/idea/plugin/src/main/java/org/teavm/idea/TeaVMJPSRemoteService.java +++ b/tools/idea/plugin/src/main/java/org/teavm/idea/TeaVMJPSRemoteService.java @@ -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(() -> { diff --git a/tools/maven/plugin/src/main/java/org/teavm/maven/AbstractTeaVMMojo.java b/tools/maven/plugin/src/main/java/org/teavm/maven/AbstractTeaVMMojo.java deleted file mode 100644 index 3209d1ab1..000000000 --- a/tools/maven/plugin/src/main/java/org/teavm/maven/AbstractTeaVMMojo.java +++ /dev/null @@ -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 remoteRepositories; - - @Parameter(readonly = true, defaultValue = "${plugin.artifacts}") - protected List pluginArtifacts; - - @Parameter(defaultValue = "${project.build.outputDirectory}") - protected File classFiles; - - @Parameter - protected List 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 instantiateTransformers(ClassLoader classLoader) - throws MojoExecutionException { - List 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 transformerType = transformerRawType.asSubclass( - ClassHolderTransformer.class); - Constructor 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 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 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 getSourceFileProviders() { - MavenSourceFileProviderLookup lookup = new MavenSourceFileProviderLookup(); - lookup.setMavenProject(project); - lookup.setRepositorySystem(repositorySystem); - lookup.setLocalRepository(localRepository); - lookup.setRemoteRepositories(remoteRepositories); - lookup.setPluginDependencies(pluginArtifacts); - List providers = lookup.resolve(); - addSourceProviders(providers); - return providers; - } - - protected void addSourceProviders(@SuppressWarnings("unused") List providers) { - } -} diff --git a/tools/maven/plugin/src/main/java/org/teavm/maven/MavenSourceFileProviderLookup.java b/tools/maven/plugin/src/main/java/org/teavm/maven/MavenSourceFileProviderLookup.java index a9b049a65..8408acf0f 100644 --- a/tools/maven/plugin/src/main/java/org/teavm/maven/MavenSourceFileProviderLookup.java +++ b/tools/maven/plugin/src/main/java/org/teavm/maven/MavenSourceFileProviderLookup.java @@ -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 resolve() { + public void resolve(BuildStrategy builder) { List initialArtifacts = new ArrayList<>(); initialArtifacts.addAll(mavenProject.getArtifacts()); if (pluginDependencies != null) { @@ -74,7 +72,6 @@ public class MavenSourceFileProviderLookup { } artifacts.addAll(initialArtifacts); - List 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; } } diff --git a/tools/maven/plugin/src/main/java/org/teavm/maven/TeaVMCompileMojo.java b/tools/maven/plugin/src/main/java/org/teavm/maven/TeaVMCompileMojo.java index ddbbadee2..9fde07aba 100644 --- a/tools/maven/plugin/src/main/java/org/teavm/maven/TeaVMCompileMojo.java +++ b/tools/maven/plugin/src/main/java/org/teavm/maven/TeaVMCompileMojo.java @@ -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 remoteRepositories; + + @Parameter(readonly = true, defaultValue = "${plugin.artifacts}") + private List pluginArtifacts; + + @Parameter(defaultValue = "${project.build.outputDirectory}") + private File classFiles; + + @Parameter + private List 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 prepareClassPath() { + Log log = getLog(); + log.info("Preparing classpath for JavaScript generation"); + List 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)); } } - @Override - protected void addSourceProviders(List providers) { - providers.add(new DirectorySourceFileProvider(sourceDirectory)); + 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 + 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); + } } }