Add abstraction of build strategy. Implement in-process build strategy. Further step is to implement build strategy that communicates with build daemon

This commit is contained in:
Alexey Andreev 2017-03-13 23:04:30 +03:00
parent d654896833
commit 6b31e13cbb
5 changed files with 335 additions and 106 deletions

View File

@ -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<String> getUsedResources();
Collection<String> getClasses();
}

View File

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

View File

@ -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<String> classPathEntries = new ArrayList<>();
private TeaVMTargetType targetType;
private String mainClass;
private String targetDirectory;
private boolean sourceMapsFileGenerated;
private boolean debugInformationGenerated;
private boolean sourceFilesCopied;
private final List<SourceFileProvider> 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<String> 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<String> classes;
private Collection<String> usedResources;
InProcessBuildResult(CallGraph callGraph, boolean errorOccurred, ProblemProvider problemProvider,
Collection<String> classes, Collection<String> 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<String> getClasses() {
return classes;
}
@Override
public Collection<String> getUsedResources() {
return usedResources;
}
}
}

View File

@ -22,8 +22,6 @@ import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.Reader; import java.io.Reader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -35,13 +33,11 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Queue; import java.util.Queue;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.jetbrains.jps.incremental.CompileContext; import org.jetbrains.jps.incremental.CompileContext;
import org.jetbrains.jps.incremental.ModuleBuildTarget; import org.jetbrains.jps.incremental.ModuleBuildTarget;
import org.jetbrains.jps.incremental.messages.BuildMessage; import org.jetbrains.jps.incremental.messages.BuildMessage;
import org.jetbrains.jps.incremental.messages.CompilerMessage; 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.JpsProject;
import org.jetbrains.jps.model.java.JpsJavaExtensionService; import org.jetbrains.jps.model.java.JpsJavaExtensionService;
import org.jetbrains.jps.model.library.JpsLibrary; 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.DefaultProblemTextConsumer;
import org.teavm.diagnostics.Problem; import org.teavm.diagnostics.Problem;
import org.teavm.diagnostics.ProblemProvider; 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.TeaVMJpsConfiguration;
import org.teavm.idea.jps.remote.TeaVMBuilderAssistant; import org.teavm.idea.jps.remote.TeaVMBuilderAssistant;
import org.teavm.idea.jps.remote.TeaVMElementLocation; 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.MethodReference;
import org.teavm.model.TextLocation; import org.teavm.model.TextLocation;
import org.teavm.model.ValueType; import org.teavm.model.ValueType;
import org.teavm.tooling.EmptyTeaVMToolLog;
import org.teavm.tooling.TeaVMTargetType; 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 { class TeaVMBuild {
private final CompileContext context; private final CompileContext context;
@ -87,11 +76,12 @@ class TeaVMBuild {
private final TeaVMBuilderAssistant assistant; private final TeaVMBuilderAssistant assistant;
private final Map<String, File> sourceFileCache = new HashMap<>(); private final Map<String, File> sourceFileCache = new HashMap<>();
private final Map<File, int[]> fileLineCache = new HashMap<>(); private final Map<File, int[]> fileLineCache = new HashMap<>();
private final List<SourceFileProvider> sourceFileProviders = new ArrayList<>(); private TeaVMBuildStrategy buildStrategy;
TeaVMBuild(CompileContext context, TeaVMBuilderAssistant assistant) { TeaVMBuild(CompileContext context, TeaVMBuilderAssistant assistant, TeaVMBuildStrategy buildStrategy) {
this.context = context; this.context = context;
this.assistant = assistant; this.assistant = assistant;
this.buildStrategy = buildStrategy;
} }
boolean perform(JpsModule module, ModuleBuildTarget target) throws IOException { boolean perform(JpsModule module, ModuleBuildTarget target) throws IOException {
@ -117,6 +107,7 @@ class TeaVMBuild {
} }
classPathEntries.clear(); classPathEntries.clear();
buildStrategy.init();
buildClassPath(module, new HashSet<>()); buildClassPath(module, new HashSet<>());
directoryClassPathEntries = classPathEntries.stream().filter(name -> new File(name).isDirectory()) directoryClassPathEntries = classPathEntries.stream().filter(name -> new File(name).isDirectory())
.collect(toList()); .collect(toList());
@ -125,37 +116,20 @@ class TeaVMBuild {
return false; return false;
} }
TeaVMTool tool = new TeaVMTool(); buildStrategy.setClassPathEntries(classPathEntries);
tool.setProgressListener(createProgressListener(context)); buildStrategy.setDebugInformationGenerated(config.isSourceMapsFileGenerated());
tool.setLog(new EmptyTeaVMToolLog()); buildStrategy.setSourceMapsFileGenerated(config.isSourceMapsFileGenerated());
tool.setTargetType(config.getTargetType()); buildStrategy.setSourceFilesCopied(config.isSourceFilesCopied());
tool.setMainClass(config.getMainClass()); buildStrategy.setMainClass(config.getMainClass());
tool.setTargetDirectory(new File(config.getTargetDirectory())); buildStrategy.setTargetType(config.getTargetType());
tool.setClassLoader(buildClassLoader()); buildStrategy.setTargetDirectory(config.getTargetDirectory());
TeaVMBuildResult buildResult = buildStrategy.build();
tool.setSourceMapsFileGenerated(config.isSourceMapsFileGenerated()); if (!buildResult.isErrorOccurred() && buildResult.getProblems().getSevereProblems().isEmpty()) {
tool.setDebugInformationGenerated(config.isSourceMapsFileGenerated()); updateStorage(buildResult, config.getTargetType());
tool.setSourceFilesCopied(config.isSourceFilesCopied());
for (SourceFileProvider fileProvider : sourceFileProviders) {
tool.addSourceFileProvider(fileProvider);
} }
boolean errorOccurred = false; reportProblems(buildResult.getProblems(), buildResult.getCallGraph());
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);
return true; return true;
} }
@ -411,9 +385,9 @@ class TeaVMBuild {
return false; return false;
} }
private void updateStorage(TeaVMTool tool, TeaVMTargetType targetType) { private void updateStorage(TeaVMBuildResult buildResult, TeaVMTargetType targetType) {
Set<String> resources = Stream.concat(tool.getClasses().stream().map(cls -> cls.replace('.', '/') + ".class"), Set<String> resources = Stream.concat(buildResult.getClasses().stream()
tool.getUsedResources().stream()) .map(cls -> cls.replace('.', '/') + ".class"), buildResult.getUsedResources().stream())
.sorted() .sorted()
.collect(toSet()); .collect(toSet());
List<TeaVMStorage.Entry> participatingFiles = resources.stream() List<TeaVMStorage.Entry> participatingFiles = resources.stream()
@ -437,60 +411,6 @@ class TeaVMBuild {
return null; 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<JpsModule> visited) { private void buildClassPath(JpsModule module, Set<JpsModule> visited) {
if (!visited.add(module)) { if (!visited.add(module)) {
return; return;
@ -500,9 +420,9 @@ class TeaVMBuild {
classPathEntries.add(output.getPath()); classPathEntries.add(output.getPath());
} }
sourceFileProviders.addAll(module.getSourceRoots().stream() for (JpsModuleSourceRoot sourceRoot : module.getSourceRoots()) {
.map(sourceRoot -> new DirectorySourceFileProvider(sourceRoot.getFile())) buildStrategy.addSourcesDirectory(sourceRoot.getFile().getAbsolutePath());
.collect(Collectors.toList())); }
for (JpsDependencyElement dependency : module.getDependenciesList().getDependencies()) { for (JpsDependencyElement dependency : module.getDependenciesList().getDependencies()) {
if (dependency instanceof JpsModuleDependency) { if (dependency instanceof JpsModuleDependency) {
@ -520,9 +440,9 @@ class TeaVMBuild {
File file = getFileFromUrl(libraryRoot.getUrl()); File file = getFileFromUrl(libraryRoot.getUrl());
if (file != null) { if (file != null) {
if (file.isDirectory()) { if (file.isDirectory()) {
sourceFileProviders.add(new DirectorySourceFileProvider(file)); buildStrategy.addSourcesDirectory(file.getAbsolutePath());
} else { } else {
sourceFileProviders.add(new JarSourceFileProvider(file)); buildStrategy.addSourcesJar(file.getAbsolutePath());
} }
} }
} }

View File

@ -63,7 +63,7 @@ public class TeaVMBuilder extends ModuleLevelBuilder {
boolean doneSomething = false; boolean doneSomething = false;
TeaVMBuild build = new TeaVMBuild(context, assistant); TeaVMBuild build = new TeaVMBuild(context, assistant, new InProcessBuildStrategy(context));
for (JpsModule module : chunk.getModules()) { for (JpsModule module : chunk.getModules()) {
doneSomething |= build.perform(module, chunk.representativeTarget()); doneSomething |= build.perform(module, chunk.representativeTarget());
if (context.getCancelStatus().isCanceled()) { if (context.getCancelStatus().isCanceled()) {