Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve performance of multimodule build by reusing compiler objects #720

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 7 additions & 12 deletions src/main/java/sbt_inc/ForkedSbtIncrementalCompilerMain.java
Expand Up @@ -8,7 +8,6 @@
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import sbt.internal.inc.ScalaInstance;
import sbt.util.Level;
import sbt.util.Logger;
import scala.Enumeration;
Expand Down Expand Up @@ -165,26 +164,22 @@ public void success(Function0<String> message) {
public void trace(Function0<Throwable> t) {}
};

ScalaInstance scalaInstance =
ScalaInstances.makeScalaInstance(
parsedArgs.scalaVersion,
parsedArgs.compilerAndDependencies,
parsedArgs.libraryAndDependencies);

SbtIncrementalCompiler incrementalCompiler =
SbtIncrementalCompilers.makeInProcess(
parsedArgs.javaHome,
parsedArgs.cacheFile,
parsedArgs.compileOrder,
scalaInstance,
parsedArgs.compilerBridgeJar,
sbtLogger);
sbtLogger,
parsedArgs.scalaVersion,
parsedArgs.compilerAndDependencies,
parsedArgs.libraryAndDependencies);

incrementalCompiler.compile(
parsedArgs.classpathElements,
parsedArgs.sources,
parsedArgs.classesDirectory,
parsedArgs.scalacOptions,
parsedArgs.javacOptions);
parsedArgs.javacOptions,
parsedArgs.compileOrder,
parsedArgs.cacheFile);
}
}
112 changes: 94 additions & 18 deletions src/main/java/sbt_inc/InProcessSbtIncrementalCompiler.java
Expand Up @@ -5,34 +5,56 @@
package sbt_inc;

import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import sbt.internal.inc.*;
import sbt.internal.inc.FileAnalysisStore;
import sbt.internal.inc.ScalaInstance;
import scala.Option;
import scala.jdk.FunctionWrappers;
import xsbti.Logger;
import xsbti.PathBasedFile;
import xsbti.T2;
import xsbti.VirtualFile;
import xsbti.compile.*;

public final class InProcessSbtIncrementalCompiler implements SbtIncrementalCompiler {
private final Compilers compilers;
private final AnalysisStore analysisStore;
private final Setup setup;

private final IncrementalCompiler compiler;
private final CompileOrder compileOrder;
private final Logger sbtLogger;

public InProcessSbtIncrementalCompiler(
Compilers compilers,
AnalysisStore analysisStore,
Setup setup,
IncrementalCompiler compiler,
CompileOrder compileOrder,
Logger sbtLogger) {
this.compilers = compilers;
this.analysisStore = analysisStore;
this.setup = setup;
this.compiler = compiler;
this.compileOrder = compileOrder;
File javaHome,
File compilerBridgeJar,
Logger sbtLogger,
String scalaVersion,
Collection<File> compilerAndDependencies,
Collection<File> libraryAndDependencies) {

this.sbtLogger = sbtLogger;

ScalaInstance scalaInstance =
ScalaInstances.makeScalaInstance(
scalaVersion, compilerAndDependencies, libraryAndDependencies);

compilers = makeCompilers(scalaInstance, javaHome, compilerBridgeJar);
compiler = ZincUtil.defaultIncrementalCompiler();
}

private static Compilers makeCompilers(
ScalaInstance scalaInstance, File javaHome, File compilerBridgeJar) {
ScalaCompiler scalaCompiler =
new AnalyzingCompiler(
scalaInstance, // scalaInstance
ZincCompilerUtil.constantBridgeProvider(scalaInstance, compilerBridgeJar), // provider
ClasspathOptionsUtil.auto(), // classpathOptions
new FunctionWrappers.FromJavaConsumer<>(noop -> {}), // onArgsHandler
Option.apply(null) // classLoaderCache
);

return ZincUtil.compilers(
scalaInstance, ClasspathOptionsUtil.boot(), Option.apply(javaHome.toPath()), scalaCompiler);
}

@Override
Expand All @@ -41,7 +63,9 @@ public void compile(
Collection<File> sources,
File classesDirectory,
Collection<String> scalacOptions,
Collection<String> javacOptions) {
Collection<String> javacOptions,
CompileOrder compileOrder,
File cacheFile) {

// incremental compiler needs to add the output dir in the classpath for Java + Scala
Collection<File> fullClasspathElements = new ArrayList<>(classpathElements);
Expand All @@ -67,13 +91,65 @@ public void compile(
Optional.empty() // _earlyOutput
);

Inputs inputs = Inputs.of(compilers, options, setup, previousResult());
AnalysisStore analysisStore = AnalysisStore.getCachedStore(FileAnalysisStore.binary(cacheFile));
Inputs inputs =
Inputs.of(
compilers, options, makeSetup(cacheFile, sbtLogger), previousResult(analysisStore));

CompileResult newResult = compiler.compile(inputs, sbtLogger);

analysisStore.set(AnalysisContents.create(newResult.analysis(), newResult.setup()));
}

private PreviousResult previousResult() {
private static Setup makeSetup(File cacheFile, xsbti.Logger sbtLogger) {
PerClasspathEntryLookup lookup =
new PerClasspathEntryLookup() {
@Override
public Optional<CompileAnalysis> analysis(VirtualFile classpathEntry) {
Path path = ((PathBasedFile) classpathEntry).toPath();

String analysisStoreFileName = null;
if (Files.isDirectory(path)) {
if (path.getFileName().toString().equals("classes")) {
analysisStoreFileName = "compile";

} else if (path.getFileName().toString().equals("test-classes")) {
analysisStoreFileName = "test-compile";
}
}

if (analysisStoreFileName != null) {
File analysisStoreFile =
path.getParent().resolve("analysis").resolve(analysisStoreFileName).toFile();
if (analysisStoreFile.exists()) {
return AnalysisStore.getCachedStore(FileAnalysisStore.binary(analysisStoreFile))
.get()
.map(AnalysisContents::getAnalysis);
}
}
return Optional.empty();
}

@Override
public DefinesClass definesClass(VirtualFile classpathEntry) {
return classpathEntry.name().equals("rt.jar")
? className -> false
: Locate.definesClass(classpathEntry);
}
};

return Setup.of(
lookup, // lookup
false, // skip
cacheFile, // cacheFile
CompilerCache.fresh(), // cache
IncOptions.of(), // incOptions
new LoggedReporter(100, sbtLogger, pos -> pos), // reporter
Optional.empty(), // optionProgress
new T2[] {});
}

private PreviousResult previousResult(AnalysisStore analysisStore) {
Optional<AnalysisContents> analysisContents = analysisStore.get();
if (analysisContents.isPresent()) {
AnalysisContents analysisContents0 = analysisContents.get();
Expand Down
5 changes: 4 additions & 1 deletion src/main/java/sbt_inc/SbtIncrementalCompiler.java
Expand Up @@ -6,6 +6,7 @@

import java.io.File;
import java.util.Collection;
import xsbti.compile.CompileOrder;

public interface SbtIncrementalCompiler {

Expand All @@ -14,5 +15,7 @@ void compile(
Collection<File> sources,
File classesDirectory,
Collection<String> scalacOptions,
Collection<String> javacOptions);
Collection<String> javacOptions,
CompileOrder compileOrder,
File cacheFile);
}