Skip to content

Commit

Permalink
Use -Ypickle-java to generate pickles from Java
Browse files Browse the repository at this point in the history
  • Loading branch information
eed3si9n authored and dwijnand committed Jul 6, 2020
1 parent 8c74981 commit 0504f70
Show file tree
Hide file tree
Showing 14 changed files with 93 additions and 25 deletions.
15 changes: 14 additions & 1 deletion internal/compiler-bridge/src/main/scala/xsbt/API.scala
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,21 @@ final class API(val global: CallbackGlobal) extends Compat with GlobalHelpers wi
debuglog("API phase took : " + ((stop - start) / 1000.0) + " s")
}

// TODO In 2.13, shouldSkipThisPhaseForJava should be overridden instead of cancelled
// override def shouldSkipThisPhaseForJava = !global.callback.isPickleJava
override def cancelled(unit: CompilationUnit) = {
if (Thread.interrupted()) reporter.cancelled = true
reporter.cancelled || unit.isJava && !global.callback.isPickleJava
}

def apply(unit: global.CompilationUnit): Unit = processUnit(unit)
private def processUnit(unit: CompilationUnit) = if (!unit.isJava) processScalaUnit(unit)

private def processUnit(unit: CompilationUnit): Unit = {
if (!unit.isJava || global.callback.isPickleJava) {
processScalaUnit(unit)
}
}

private def processScalaUnit(unit: CompilationUnit): Unit = {
val sourceFile: VirtualFile = unit.source.file match {
case v: VirtualFileWrap => v.underlying
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,15 @@ final class Dependency(val global: CallbackGlobal) extends LocateClassFile with
debuglog("Dependency phase took : " + ((stop - start) / 1000.0) + " s")
}

// TODO In 2.13, shouldSkipThisPhaseForJava should be overridden instead of cancelled
// override def shouldSkipThisPhaseForJava = !global.callback.isPickleJava
override def cancelled(unit: CompilationUnit) = {
if (Thread.interrupted()) reporter.cancelled = true
reporter.cancelled || unit.isJava && !global.callback.isPickleJava
}

def apply(unit: CompilationUnit): Unit = {
if (!unit.isJava) {
if (!unit.isJava || global.callback.isPickleJava) {
// Process dependencies if name hashing is enabled, fail otherwise
val dependencyProcessor = new DependencyProcessor(unit)
val dependencyTraverser = new DependencyTraverser(dependencyProcessor)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,4 +209,9 @@ void problem(String what,
* Pass new pickle data as of this point.
*/
void pickleData(PickleData[] data);

/**
* Returns true if -Ypickle-java is on.
*/
boolean isPickleJava();
}
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ object Incremental {
),
incremental,
options,
currentSetup,
output,
outputJarContent,
log
Expand Down Expand Up @@ -229,6 +230,7 @@ object Incremental {
callbackBuilder: AnalysisCallback.Builder,
incremental: IncrementalCommon,
options: IncOptions,
currentSetup: MiniSetup,
output: Output,
outputJarContent: JarUtils.OutputJarContent,
log: sbt.util.Logger
Expand All @@ -243,16 +245,30 @@ object Incremental {
val modifiedClasses = initialChanges.external.allModified.toArray
def isEmpty = modifiedLibraries.isEmpty && modifiedClasses.isEmpty
}
val (initialInvClasses, initialInvSources) =
val (initialInvClasses, initialInvSources0) =
incremental.invalidateInitial(previous.relations, initialChanges)
if (initialInvClasses.nonEmpty || initialInvSources.nonEmpty)

// If there's any compilation at all, invalidate all java sources too, so we have access to their type information.
val javaSources: Set[VirtualFileRef] = sources
.filter(_.name.endsWith(".java"))
.map(_.asInstanceOf[VirtualFileRef])
val isPickleJava = currentSetup.options.scalacOptions.contains("-Ypickle-java")
assert(
javaSources.isEmpty || !options.pipelining || isPickleJava,
s"-Ypickle-java must be included into scalacOptions if pipelining is enabled with Java sources"
)
val initialInvSources =
if (initialInvSources0.nonEmpty) initialInvSources0 ++ javaSources
else Set.empty[VirtualFileRef]
if (initialInvClasses.nonEmpty || initialInvSources.nonEmpty) {
if (initialInvSources == sources)
incremental.log.debug(s"all ${initialInvSources.size} sources are invalidated")
else
incremental.log.debug(
"All initially invalidated classes: " + initialInvClasses + "\n" +
"All initially invalidated sources:" + initialInvSources + "\n"
)
}
val analysis = manageClassfiles(options, converter, output, outputJarContent) {
classfileManager =>
incremental.cycle(
Expand Down Expand Up @@ -295,7 +311,7 @@ object Incremental {
// to report its own analysis individually.
val callback = callbackBuilder.build(incHandler)
compile(srcs, changes, callback, classFileManager)
callback.getOnce
callback.getCycleResultOnce
}
}

Expand Down Expand Up @@ -466,6 +482,10 @@ private final class AnalysisCallback(
()
}

override def isPickleJava: Boolean = {
currentSetup.options.scalacOptions.contains("-Ypickle-java")
}

def startSource(source: VirtualFile): Unit = {
if (options.strictMode()) {
assert(
Expand Down Expand Up @@ -628,8 +648,8 @@ private final class AnalysisCallback(
override def enabled(): Boolean = options.enabled

private[this] var gotten: Boolean = false
def getOnce: CompileCycleResult = {
assert(!gotten, "can't call AnalysisCallback#getOnce more than once")
def getCycleResultOnce: CompileCycleResult = {
assert(!gotten, "can't call AnalysisCallback#getCycleResultOnce more than once")
gotten = true
// notify that early artifact writing is not going to happen because of macros
def notifyEarlyArifactFailure(): Unit =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ private[inc] abstract class IncrementalCommon(
output: Output,
cycleNum: Int
) {
lazy val javaSources: Set[VirtualFileRef] = allSources
.filter(_.name.endsWith(".java"))
.map(_.asInstanceOf[VirtualFileRef])

def hasNext: Boolean = invalidatedClasses.nonEmpty || initialChangedSources.nonEmpty
def next: CycleState = {
// Compute all the invalidated classes by aggregating invalidated package objects
Expand Down Expand Up @@ -128,6 +132,10 @@ private[inc] abstract class IncrementalCommon(
val nextInvalidations = result.nextInvalidations
val current = result.analysis

val nextChangedSources: Set[VirtualFileRef] =
if (continue) javaSources
else Set.empty

// Return immediate analysis as all sources have been recompiled
if (invalidatedSources == allSources)
CycleState(
Expand All @@ -146,7 +154,7 @@ private[inc] abstract class IncrementalCommon(
else {
CycleState(
if (continue) nextInvalidations else Set.empty,
Set.empty,
nextChangedSources,
allSources,
converter,
IncrementalCommon.emptyChanges,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ case class ProjectStructure(
// We specify the class file manager explicitly even though it's noew possible
// to specify it in the incremental option property file (this is the default for sbt)
val (incOptions, scalacOptions) = {
val properties = loadIncProperties(baseDirectory / "incOptions.properties")
val properties = loadIncProperties(baseDirectory)
val (incOptions0, sco) = loadIncOptions(properties)
val storeApis = Option(properties.getProperty("incOptions.storeApis"))
.map(_.toBoolean)
Expand Down Expand Up @@ -695,10 +695,12 @@ case class ProjectStructure(
()
}

def loadIncProperties(src: Path): Properties = {
def loadIncProperties(base: Path): Properties = {
val prop0 = base / "incoptions.properties"
val prop1 = if (Files.exists(prop0)) prop0 else base / "incOptions.properties"
val properties = new Properties()
if (Files.exists(src)) {
val stream = Files.newInputStream(src)
if (Files.exists(prop1)) {
val stream = Files.newInputStream(prop1)
try properties.load(stream)
finally stream.close()
}
Expand All @@ -716,8 +718,11 @@ case class ProjectStructure(
else opts.withRecompileAllFraction(1.0)
}
val scalacOptions: List[String] =
Option(map.get("scalac.options")).toList
.flatMap(_.toString.split(" +").toList)
(Option(map.get("scalac.options")) match {
case Some(x) => List(x)
case _ if incOptions.pipelining => List("-Ypickle-java")
case _ => Nil
}).flatMap(_.toString.split(" +").toList)
(incOptions, scalacOptions.toArray)
}

Expand Down
2 changes: 2 additions & 0 deletions internal/zinc-testing/src/main/scala/xsbti/TestCallback.scala
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ class TestCallback extends AnalysisCallback {

override def classesInOutputJar(): util.Set[String] = java.util.Collections.emptySet()

override def isPickleJava: Boolean = false

override def pickleData(data: Array[PickleData]): Unit = ()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,11 @@ final class MixedAnalyzingCompiler(
val incSrc = config.sources.filter(include)
val (javaSrcs, scalaSrcs) = incSrc.partition(javaOnly(_))
logInputs(log, javaSrcs.size, scalaSrcs.size, outputDirs)
val isPickleJava = config.incOptions.pipelining && javaSrcs.nonEmpty

// Compile Scala sources.
def compileScala(): Unit =
if (scalaSrcs.nonEmpty) {
if (scalaSrcs.nonEmpty || isPickleJava) {
JarUtils.withPreviousJar(output) { extraClasspath: Seq[Path] =>
val sources =
if (config.currentSetup.order == Mixed) incSrc
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,9 @@ final class AnalyzingJavaCompiler private[sbt] (
val loader = ClasspathUtil.toLoader(searchClasspath.map(converter.toPath))

timed(javaAnalysisPhase, log) {
for ((classesFinder, oldClasses, srcs) <- memo) {
for {
(classesFinder, oldClasses, srcs) <- memo
} {
val newClasses = Set(classesFinder.get: _*) -- oldClasses
JavaAnalyze(newClasses.toSeq.map(_.toPath), srcs, log, output, finalJarOutput)(
callback,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@

$ copy-file changes/A2.java A.java
> compile

# D should be compiled only at the beginning; it depends by inheritance only
# on C and C's public interface is not affected by changes to A
> checkRecompilations 0 D

# A is explicitly changed
> checkRecompilations 1 A
> checkRecompilations 1

# B is recompiled because it depends by inheritance on A
# C is recompiled because it depends by local inheritance on B but its
# dependencies (D) are not recompiled
> checkRecompilations 2 B C
> checkRecompilations 2 A B C
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ $ copy-file changes/A2.java A.java
# 1 iteration to recompile all descendents and direct dependencies
# no further iteration, because APIs of directs don't change
> run
> checkIterations 3
> checkIterations 2
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ $ copy-file changes/A2.java A.java
# on C and C's public interface is not affected by changes to A
> checkRecompilations 0 D
# A is explicitly changed
> checkRecompilations 1 A
> checkRecompilations 1
# B is recompiled because it depends by inheritance on A
# C is recompiled because it depends by local inheritance on B but its
# dependencies (D) are not recompiled
> checkRecompilations 2 B C
> checkRecompilations 2 A B C
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
relationsDebug = true
scalac.options = -deprecation

8 changes: 4 additions & 4 deletions zinc/src/test/scala/sbt/inc/TestProjectSetup.scala
Original file line number Diff line number Diff line change
Expand Up @@ -233,15 +233,15 @@ object TestProjectSetup {
sources.toArray,
output,
Some(earlyOutput),
scalacOptions.toArray,
Array(),
scalacOptions = (Vector("-Ypickle-java") ++ scalacOptions).toArray,
javacOptions = Array(),
maxErrors,
Array(),
sourcePositionMappers = Array(),
CompileOrder.Mixed,
cs,
setup,
prev,
Optional.empty(),
temporaryClassesDirectory = Optional.empty(),
converter,
stamper
)
Expand Down

0 comments on commit 0504f70

Please sign in to comment.