diff --git a/tools/idea/jps-common/src/main/java/org/teavm/idea/jps/model/TeaVMBuildResult.java b/tools/idea/jps-common/src/main/java/org/teavm/idea/jps/model/TeaVMBuildResult.java new file mode 100644 index 000000000..ea89cdecb --- /dev/null +++ b/tools/idea/jps-common/src/main/java/org/teavm/idea/jps/model/TeaVMBuildResult.java @@ -0,0 +1,32 @@ +/* + * Copyright 2017 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.idea.jps.model; + +import java.util.Collection; +import org.teavm.callgraph.CallGraph; +import org.teavm.diagnostics.ProblemProvider; + +public interface TeaVMBuildResult { + CallGraph getCallGraph(); + + boolean isErrorOccurred(); + + ProblemProvider getProblems(); + + Collection getUsedResources(); + + Collection getClasses(); +} diff --git a/tools/idea/jps-common/src/main/java/org/teavm/idea/jps/model/TeaVMBuildStrategy.java b/tools/idea/jps-common/src/main/java/org/teavm/idea/jps/model/TeaVMBuildStrategy.java new file mode 100644 index 000000000..282ac3395 --- /dev/null +++ b/tools/idea/jps-common/src/main/java/org/teavm/idea/jps/model/TeaVMBuildStrategy.java @@ -0,0 +1,43 @@ +/* + * Copyright 2017 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.idea.jps.model; + +import java.util.List; +import org.teavm.tooling.TeaVMTargetType; + +public interface TeaVMBuildStrategy { + void init(); + + void addSourcesDirectory(String directory); + + void addSourcesJar(String jarFile); + + void setClassPathEntries(List entries); + + void setTargetType(TeaVMTargetType targetType); + + void setMainClass(String mainClass); + + void setTargetDirectory(String targetDirectory); + + void setSourceMapsFileGenerated(boolean sourceMapsFileGenerated); + + void setDebugInformationGenerated(boolean debugInformationGenerated); + + void setSourceFilesCopied(boolean sourceFilesCopied); + + TeaVMBuildResult build(); +} diff --git a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/InProcessBuildStrategy.java b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/InProcessBuildStrategy.java new file mode 100644 index 000000000..d57da2e2e --- /dev/null +++ b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/InProcessBuildStrategy.java @@ -0,0 +1,234 @@ +/* + * Copyright 2017 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.idea.jps; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import org.jetbrains.jps.incremental.CompileContext; +import org.jetbrains.jps.incremental.messages.CompilerMessage; +import org.jetbrains.jps.incremental.messages.ProgressMessage; +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.tooling.EmptyTeaVMToolLog; +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.JarSourceFileProvider; +import org.teavm.tooling.sources.SourceFileProvider; +import org.teavm.vm.TeaVMPhase; +import org.teavm.vm.TeaVMProgressFeedback; +import org.teavm.vm.TeaVMProgressListener; + +public class InProcessBuildStrategy implements TeaVMBuildStrategy { + private final CompileContext context; + private List classPathEntries = new ArrayList<>(); + private TeaVMTargetType targetType; + private String mainClass; + private String targetDirectory; + private boolean sourceMapsFileGenerated; + private boolean debugInformationGenerated; + private boolean sourceFilesCopied; + private final List sourceFileProviders = new ArrayList<>(); + + public InProcessBuildStrategy(CompileContext context) { + this.context = context; + } + + @Override + public void init() { + sourceFileProviders.clear(); + } + + @Override + public void addSourcesDirectory(String directory) { + sourceFileProviders.add(new DirectorySourceFileProvider(new File(directory))); + } + + @Override + public void addSourcesJar(String jarFile) { + sourceFileProviders.add(new JarSourceFileProvider(new File(jarFile))); + } + + @Override + public void setClassPathEntries(List entries) { + classPathEntries.clear(); + classPathEntries.addAll(entries); + } + + @Override + public void setTargetType(TeaVMTargetType targetType) { + this.targetType = targetType; + } + + @Override + public void setMainClass(String mainClass) { + this.mainClass = mainClass; + } + + @Override + public void setTargetDirectory(String targetDirectory) { + this.targetDirectory = targetDirectory; + } + + @Override + public void setSourceMapsFileGenerated(boolean sourceMapsFileGenerated) { + this.sourceMapsFileGenerated = sourceMapsFileGenerated; + } + + @Override + public void setDebugInformationGenerated(boolean debugInformationGenerated) { + this.debugInformationGenerated = debugInformationGenerated; + } + + @Override + public void setSourceFilesCopied(boolean sourceFilesCopied) { + this.sourceFilesCopied = sourceFilesCopied; + } + + @Override + public TeaVMBuildResult build() { + TeaVMTool tool = new TeaVMTool(); + tool.setProgressListener(createProgressListener(context)); + tool.setLog(new EmptyTeaVMToolLog()); + tool.setTargetType(targetType); + tool.setMainClass(mainClass); + tool.setTargetDirectory(new File(targetDirectory)); + tool.setClassLoader(buildClassLoader()); + + tool.setSourceMapsFileGenerated(sourceMapsFileGenerated); + tool.setDebugInformationGenerated(debugInformationGenerated); + tool.setSourceFilesCopied(sourceFilesCopied); + + for (SourceFileProvider fileProvider : sourceFileProviders) { + tool.addSourceFileProvider(fileProvider); + } + + boolean errorOccurred = false; + try { + tool.generate(); + } catch (TeaVMToolException | RuntimeException | Error e) { + e.printStackTrace(System.err); + context.processMessage(new CompilerMessage("TeaVM", e)); + errorOccurred = true; + } + + return new InProcessBuildResult(tool.getDependencyInfo().getCallGraph(), errorOccurred, + tool.getProblemProvider(), tool.getClasses(), tool.getUsedResources()); + } + + private ClassLoader buildClassLoader() { + URL[] urls = classPathEntries.stream().map(entry -> { + try { + return new File(entry).toURI().toURL(); + } catch (MalformedURLException e) { + throw new RuntimeException(entry); + } + }).toArray(URL[]::new); + + RenamingClassLoader classLoader = new RenamingClassLoader(urls, TeaVMBuilder.class.getClassLoader()); + classLoader.rename("org/objectweb/asm/", "org/teavm/asm/"); + return classLoader; + } + + private TeaVMProgressListener createProgressListener(CompileContext context) { + return new TeaVMProgressListener() { + private TeaVMPhase currentPhase; + int expectedCount; + + @Override + public TeaVMProgressFeedback phaseStarted(TeaVMPhase phase, int count) { + expectedCount = count; + context.processMessage(new ProgressMessage(phaseName(phase), 0)); + currentPhase = phase; + return context.getCancelStatus().isCanceled() ? TeaVMProgressFeedback.CANCEL + : TeaVMProgressFeedback.CONTINUE; + } + + @Override + public TeaVMProgressFeedback progressReached(int progress) { + context.processMessage(new ProgressMessage(phaseName(currentPhase), (float) progress / expectedCount)); + return context.getCancelStatus().isCanceled() ? TeaVMProgressFeedback.CANCEL + : TeaVMProgressFeedback.CONTINUE; + } + }; + } + + private static String phaseName(TeaVMPhase phase) { + switch (phase) { + case DEPENDENCY_CHECKING: + return "Discovering classes to compile"; + case LINKING: + return "Resolving method invocations"; + case DECOMPILATION: + return "Compiling classes"; + case OPTIMIZATION: + return "Optimizing code"; + case RENDERING: + return "Building JS file"; + default: + throw new AssertionError(); + } + } + + static class InProcessBuildResult implements TeaVMBuildResult { + private CallGraph callGraph; + private boolean errorOccurred; + private ProblemProvider problemProvider; + private Collection classes; + private Collection usedResources; + + InProcessBuildResult(CallGraph callGraph, boolean errorOccurred, ProblemProvider problemProvider, + Collection classes, Collection usedResources) { + this.callGraph = callGraph; + this.errorOccurred = errorOccurred; + this.problemProvider = problemProvider; + this.classes = classes; + this.usedResources = usedResources; + } + + @Override + public CallGraph getCallGraph() { + return callGraph; + } + + @Override + public boolean isErrorOccurred() { + return errorOccurred; + } + + @Override + public ProblemProvider getProblems() { + return problemProvider; + } + + @Override + public Collection getClasses() { + return classes; + } + + @Override + public Collection getUsedResources() { + return usedResources; + } + } +} 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 0b74e3803..ef7804275 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,8 +22,6 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; -import java.net.MalformedURLException; -import java.net.URL; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; @@ -35,13 +33,11 @@ import java.util.Map; import java.util.Objects; import java.util.Queue; import java.util.Set; -import java.util.stream.Collectors; import java.util.stream.Stream; import org.jetbrains.jps.incremental.CompileContext; import org.jetbrains.jps.incremental.ModuleBuildTarget; import org.jetbrains.jps.incremental.messages.BuildMessage; import org.jetbrains.jps.incremental.messages.CompilerMessage; -import org.jetbrains.jps.incremental.messages.ProgressMessage; import org.jetbrains.jps.model.JpsProject; import org.jetbrains.jps.model.java.JpsJavaExtensionService; import org.jetbrains.jps.model.library.JpsLibrary; @@ -59,6 +55,8 @@ 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.remote.TeaVMBuilderAssistant; import org.teavm.idea.jps.remote.TeaVMElementLocation; @@ -66,16 +64,7 @@ import org.teavm.model.CallLocation; import org.teavm.model.MethodReference; import org.teavm.model.TextLocation; import org.teavm.model.ValueType; -import org.teavm.tooling.EmptyTeaVMToolLog; 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.JarSourceFileProvider; -import org.teavm.tooling.sources.SourceFileProvider; -import org.teavm.vm.TeaVMPhase; -import org.teavm.vm.TeaVMProgressFeedback; -import org.teavm.vm.TeaVMProgressListener; class TeaVMBuild { private final CompileContext context; @@ -87,11 +76,12 @@ class TeaVMBuild { private final TeaVMBuilderAssistant assistant; private final Map sourceFileCache = new HashMap<>(); private final Map fileLineCache = new HashMap<>(); - private final List sourceFileProviders = new ArrayList<>(); + private TeaVMBuildStrategy buildStrategy; - TeaVMBuild(CompileContext context, TeaVMBuilderAssistant assistant) { + TeaVMBuild(CompileContext context, TeaVMBuilderAssistant assistant, TeaVMBuildStrategy buildStrategy) { this.context = context; this.assistant = assistant; + this.buildStrategy = buildStrategy; } boolean perform(JpsModule module, ModuleBuildTarget target) throws IOException { @@ -117,6 +107,7 @@ class TeaVMBuild { } classPathEntries.clear(); + buildStrategy.init(); buildClassPath(module, new HashSet<>()); directoryClassPathEntries = classPathEntries.stream().filter(name -> new File(name).isDirectory()) .collect(toList()); @@ -125,37 +116,20 @@ class TeaVMBuild { return false; } - TeaVMTool tool = new TeaVMTool(); - tool.setProgressListener(createProgressListener(context)); - tool.setLog(new EmptyTeaVMToolLog()); - tool.setTargetType(config.getTargetType()); - tool.setMainClass(config.getMainClass()); - tool.setTargetDirectory(new File(config.getTargetDirectory())); - tool.setClassLoader(buildClassLoader()); + buildStrategy.setClassPathEntries(classPathEntries); + buildStrategy.setDebugInformationGenerated(config.isSourceMapsFileGenerated()); + buildStrategy.setSourceMapsFileGenerated(config.isSourceMapsFileGenerated()); + buildStrategy.setSourceFilesCopied(config.isSourceFilesCopied()); + buildStrategy.setMainClass(config.getMainClass()); + buildStrategy.setTargetType(config.getTargetType()); + buildStrategy.setTargetDirectory(config.getTargetDirectory()); + TeaVMBuildResult buildResult = buildStrategy.build(); - tool.setSourceMapsFileGenerated(config.isSourceMapsFileGenerated()); - tool.setDebugInformationGenerated(config.isSourceMapsFileGenerated()); - tool.setSourceFilesCopied(config.isSourceFilesCopied()); - - for (SourceFileProvider fileProvider : sourceFileProviders) { - tool.addSourceFileProvider(fileProvider); + if (!buildResult.isErrorOccurred() && buildResult.getProblems().getSevereProblems().isEmpty()) { + updateStorage(buildResult, config.getTargetType()); } - boolean errorOccurred = false; - try { - tool.generate(); - } catch (TeaVMToolException | RuntimeException | Error e) { - e.printStackTrace(System.err); - context.processMessage(new CompilerMessage("TeaVM", e)); - errorOccurred = true; - } - - if (!errorOccurred && tool.getProblemProvider().getSevereProblems().isEmpty()) { - updateStorage(tool, config.getTargetType()); - } - - CallGraph callGraph = tool.getDependencyInfo().getCallGraph(); - reportProblems(tool.getProblemProvider(), callGraph); + reportProblems(buildResult.getProblems(), buildResult.getCallGraph()); return true; } @@ -411,9 +385,9 @@ class TeaVMBuild { return false; } - private void updateStorage(TeaVMTool tool, TeaVMTargetType targetType) { - Set resources = Stream.concat(tool.getClasses().stream().map(cls -> cls.replace('.', '/') + ".class"), - tool.getUsedResources().stream()) + private void updateStorage(TeaVMBuildResult buildResult, TeaVMTargetType targetType) { + Set resources = Stream.concat(buildResult.getClasses().stream() + .map(cls -> cls.replace('.', '/') + ".class"), buildResult.getUsedResources().stream()) .sorted() .collect(toSet()); List participatingFiles = resources.stream() @@ -437,60 +411,6 @@ class TeaVMBuild { return null; } - private TeaVMProgressListener createProgressListener(CompileContext context) { - return new TeaVMProgressListener() { - private TeaVMPhase currentPhase; - int expectedCount; - - @Override - public TeaVMProgressFeedback phaseStarted(TeaVMPhase phase, int count) { - expectedCount = count; - context.processMessage(new ProgressMessage(phaseName(phase), 0)); - currentPhase = phase; - return context.getCancelStatus().isCanceled() ? TeaVMProgressFeedback.CANCEL - : TeaVMProgressFeedback.CONTINUE; - } - - @Override - public TeaVMProgressFeedback progressReached(int progress) { - context.processMessage(new ProgressMessage(phaseName(currentPhase), (float) progress / expectedCount)); - return context.getCancelStatus().isCanceled() ? TeaVMProgressFeedback.CANCEL - : TeaVMProgressFeedback.CONTINUE; - } - }; - } - - private static String phaseName(TeaVMPhase phase) { - switch (phase) { - case DEPENDENCY_CHECKING: - return "Discovering classes to compile"; - case LINKING: - return "Resolving method invocations"; - case DECOMPILATION: - return "Compiling classes"; - case OPTIMIZATION: - return "Optimizing code"; - case RENDERING: - return "Building JS file"; - default: - throw new AssertionError(); - } - } - - private ClassLoader buildClassLoader() { - URL[] urls = classPathEntries.stream().map(entry -> { - try { - return new File(entry).toURI().toURL(); - } catch (MalformedURLException e) { - throw new RuntimeException(entry); - } - }).toArray(URL[]::new); - - RenamingClassLoader classLoader = new RenamingClassLoader(urls, TeaVMBuilder.class.getClassLoader()); - classLoader.rename("org/objectweb/asm/", "org/teavm/asm/"); - return classLoader; - } - private void buildClassPath(JpsModule module, Set visited) { if (!visited.add(module)) { return; @@ -500,9 +420,9 @@ class TeaVMBuild { classPathEntries.add(output.getPath()); } - sourceFileProviders.addAll(module.getSourceRoots().stream() - .map(sourceRoot -> new DirectorySourceFileProvider(sourceRoot.getFile())) - .collect(Collectors.toList())); + for (JpsModuleSourceRoot sourceRoot : module.getSourceRoots()) { + buildStrategy.addSourcesDirectory(sourceRoot.getFile().getAbsolutePath()); + } for (JpsDependencyElement dependency : module.getDependenciesList().getDependencies()) { if (dependency instanceof JpsModuleDependency) { @@ -520,9 +440,9 @@ class TeaVMBuild { File file = getFileFromUrl(libraryRoot.getUrl()); if (file != null) { if (file.isDirectory()) { - sourceFileProviders.add(new DirectorySourceFileProvider(file)); + buildStrategy.addSourcesDirectory(file.getAbsolutePath()); } else { - sourceFileProviders.add(new JarSourceFileProvider(file)); + buildStrategy.addSourcesJar(file.getAbsolutePath()); } } } 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 5fcc254d1..ffead272e 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 @@ -63,7 +63,7 @@ public class TeaVMBuilder extends ModuleLevelBuilder { boolean doneSomething = false; - TeaVMBuild build = new TeaVMBuild(context, assistant); + TeaVMBuild build = new TeaVMBuild(context, assistant, new InProcessBuildStrategy(context)); for (JpsModule module : chunk.getModules()) { doneSomething |= build.perform(module, chunk.representativeTarget()); if (context.getCancelStatus().isCanceled()) {