Skip to content

Commit

Permalink
Add profiler to ExternalHooks, as a Java interface
Browse files Browse the repository at this point in the history
The entry point API of Zinc, xsbti.compile.IncrementalCompiler, is
defined in compiler-interface and is, therefore, entirely in Java, only.

Therefore, in order to allow a user (e.g. an external build tool) to
define their own profiler using this interface I extracted a Java-only
interface for InvalidationProfiler and its components.

Having done that I could add it to compiler-interface's
xsbti.compile.ExternalHooks.

I've kept the original, Scala variant, interface methods (e.g. using
Scala's Iterable instead of Array or java.util.Set) for backwards
compatibility and I've created an AdaptedRunProfiler that knows how to
adapt the calls to the underlying XRunProfiler it contains.
  • Loading branch information
dwijnand committed Jul 6, 2020
1 parent 6923660 commit fb8e03e
Show file tree
Hide file tree
Showing 11 changed files with 304 additions and 80 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Zinc - The incremental compiler for Scala.
* Copyright Lightbend, Inc. and Mark Harrah
*
* Licensed under Apache License 2.0
* (http://www.apache.org/licenses/LICENSE-2.0).
*
* See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*/

package xsbti.compile;

import java.util.Set;

public interface APIChange {
String getModifiedClass();
Set<UsedName> getModifiedNames();
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,8 @@ enum NoProvenance implements GetProvenance {
ExternalHooks withExternalLookup(Lookup externalLookup);

default ExternalHooks withGetProvenance(GetProvenance getProvenance) { return this; }

default InvalidationProfiler getInvalidationProfiler() { return InvalidationProfiler.EMPTY.INSTANCE; }

default ExternalHooks withInvalidationProfiler(InvalidationProfiler profiler) { return this; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Zinc - The incremental compiler for Scala.
* Copyright Lightbend, Inc. and Mark Harrah
*
* Licensed under Apache License 2.0
* (http://www.apache.org/licenses/LICENSE-2.0).
*
* See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*/

package xsbti.compile;

import xsbti.VirtualFileRef;

import java.util.Set;

public interface InitialChanges {
Changes<VirtualFileRef> getInternalSrc();
Set<VirtualFileRef> getRemovedProducts();
Set<VirtualFileRef> getLibraryDeps();
APIChange[] getExternal();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Zinc - The incremental compiler for Scala.
* Copyright Lightbend, Inc. and Mark Harrah
*
* Licensed under Apache License 2.0
* (http://www.apache.org/licenses/LICENSE-2.0).
*
* See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*/

package xsbti.compile;

public interface InvalidationProfiler {
RunProfiler profileRun();

enum EMPTY implements InvalidationProfiler {
INSTANCE;

@Override public RunProfiler profileRun() { return RunProfiler.EMPTY.INSTANCE; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* Zinc - The incremental compiler for Scala.
* Copyright Lightbend, Inc. and Mark Harrah
*
* Licensed under Apache License 2.0
* (http://www.apache.org/licenses/LICENSE-2.0).
*
* See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*/

package xsbti.compile;

import xsbti.VirtualFileRef;

public interface RunProfiler {
void timeCompilation(long startNanos, long durationNanos);
void registerInitial(InitialChanges changes);
void registerEvent(String kind, String[] inputs, String[] outputs, String reason);
void registerCycle(
String[] invalidatedClasses,
String[] invalidatedPackageObjects,
VirtualFileRef[] initialSources,
VirtualFileRef[] invalidatedSources,
String[] recompiledClasses,
APIChange[] changesAfterRecompilation,
String[] nextInvalidations,
boolean shouldCompileIncrementally
);
void registerRun();

enum EMPTY implements RunProfiler {
INSTANCE;

@Override public void timeCompilation(long startNanos, long durationNanos) {}
@Override public void registerInitial(InitialChanges changes) {}
@Override public void registerEvent(String kind, String[] inputs, String[] outputs, String reason) {}
@Override public void registerCycle(
String[] invalidatedClasses,
String[] invalidatedPackageObjects,
VirtualFileRef[] initialSources,
VirtualFileRef[] invalidatedSources,
String[] recompiledClasses,
APIChange[] changesAfterRecompilation,
String[] nextInvalidations,
boolean shouldCompileIncrementally
) {}
@Override public void registerRun() {}
}

interface DelegatingRunProfiler extends RunProfiler {
RunProfiler profiler();

default void timeCompilation(long startNanos, long durationNanos) {
profiler().timeCompilation(startNanos, durationNanos);
}

default void registerInitial(InitialChanges changes) {
profiler().registerInitial(changes);
}

default void registerEvent(String kind, String[] inputs, String[] outputs, String reason) {
profiler().registerEvent(kind, inputs, outputs, reason);
}

default public void registerCycle(
String[] invalidatedClasses,
String[] invalidatedPackageObjects,
VirtualFileRef[] initialSources,
VirtualFileRef[] invalidatedSources,
String[] recompiledClasses,
APIChange[] changesAfterRecompilation,
String[] nextInvalidations,
boolean shouldCompileIncrementally
) {
profiler().registerCycle(
invalidatedClasses,
invalidatedPackageObjects,
initialSources,
invalidatedSources,
recompiledClasses,
changesAfterRecompilation,
nextInvalidations,
shouldCompileIncrementally
);
}

default void registerRun() {
profiler().registerRun();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Zinc - The incremental compiler for Scala.
* Copyright Lightbend, Inc. and Mark Harrah
*
* Licensed under Apache License 2.0
* (http://www.apache.org/licenses/LICENSE-2.0).
*
* See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*/

package xsbti.compile;

import xsbti.UseScope;

public interface UsedName {
String getName();
java.util.EnumSet<UseScope> getScopes();
}

21 changes: 19 additions & 2 deletions internal/zinc-core/src/main/scala/sbt/internal/inc/Changes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,31 +15,48 @@ package inc

import java.io.File

import scala.collection.JavaConverters._

import xsbti.{ UseScope, VirtualFileRef }
import xsbti.api.NameHash
import xsbti.compile.Changes
import xsbti.compile.{ APIChange => XAPIChange }
import xsbti.compile.{ InitialChanges => XInitialChanges }
import xsbti.compile.{ UsedName => XUsedName }
import xsbti.compile.analysis.{ Stamp => XStamp }

final case class InitialChanges(
internalSrc: Changes[VirtualFileRef],
removedProducts: Set[VirtualFileRef],
libraryDeps: Set[VirtualFileRef],
external: APIChanges
) {
) extends XInitialChanges {

def isEmpty: Boolean =
internalSrc.isEmpty &&
removedProducts.isEmpty &&
libraryDeps.isEmpty &&
external.apiChanges.isEmpty

def getInternalSrc: Changes[VirtualFileRef] = internalSrc
def getRemovedProducts: java.util.Set[VirtualFileRef] = removedProducts.asJava
def getLibraryDeps: java.util.Set[VirtualFileRef] = libraryDeps.asJava
def getExternal: Array[XAPIChange] = external.apiChanges.toArray
}

final class APIChanges(val apiChanges: Iterable[APIChange]) {
override def toString = "API Changes: " + apiChanges
def allModified: Iterable[String] = apiChanges.map(_.modifiedClass)
}

sealed abstract class APIChange(val modifiedClass: String)
sealed abstract class APIChange(val modifiedClass: String) extends XAPIChange {
override def getModifiedClass: String = modifiedClass
override def getModifiedNames: java.util.Set[XUsedName] = this match {
case _: APIChangeDueToMacroDefinition => java.util.Collections.emptySet[XUsedName]
case _: TraitPrivateMembersModified => java.util.Collections.emptySet[XUsedName]
case NamesChange(_, modifiedNames) => modifiedNames.names.map(x => x: XUsedName).asJava
}
}

/**
* If we recompile a source file that contains a macro definition then we always assume that it's
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,17 +149,21 @@ object Incremental {
val internalSourceToClassNamesMap: VirtualFile => Set[String] = (f: VirtualFile) =>
previous.relations.classNames(f)
val externalAPI = getExternalAPI(lookup)
val profiler = InvalidationProfiler.empty
val runProfiler = profiler.profileRun
val profiler = options.externalHooks.getInvalidationProfiler
val runProfiler = new AdaptedRunProfiler(profiler.profileRun)
val incremental: IncrementalCommon = new IncrementalNameHashing(log, options, runProfiler)
try {
val result = try {
incrementalCompile(
sources,
converter,
lookup,
previous,
currentStamper,
compile,
(vs, depCh, cb, cfm) => {
val startTime = System.nanoTime()
compile(vs, depCh, cb, cfm)
runProfiler.timeCompilation(startTime, System.nanoTime() - startTime)
},
new AnalysisCallback.Builder(
internalBinaryToSourceClassName,
internalSourceToClassNamesMap,
Expand Down Expand Up @@ -190,6 +194,8 @@ object Incremental {
// and we can report back as there was no change (false) and return a previous Analysis which is still up-to-date
(false, previous)
}
runProfiler.registerRun()
result
}

def getExternalAPI(lookup: Lookup): (VirtualFileRef, String) => Option[AnalyzedClass] =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,8 @@ import sbt.util.Logger
import xsbti.{ FileConverter, VirtualFile, VirtualFileRef }
import xsbt.api.APIUtil
import xsbti.api.AnalyzedClass
import xsbti.compile.{
Changes,
DependencyChanges,
IncOptions,
Output,
ClassFileManager => XClassFileManager
}
import xsbti.compile.{ Changes, DependencyChanges, IncOptions, Output }
import xsbti.compile.{ ClassFileManager => XClassFileManager }
import xsbti.compile.analysis.{ ReadStamps, Stamp => XStamp }
import scala.collection.Iterator
import Incremental.{ CompileCycle, CompileCycleResult, IncrementalCallback, PrefixingLogger }
Expand Down Expand Up @@ -105,23 +100,18 @@ private[inc] abstract class IncrementalCommon(
invalidatedSources,
classfileManager,
pruned,
classesToRecompile, {
(
recompiledClasses: Set[String],
newApiChanges: APIChanges,
nextInvalidations: Set[String],
continuePerLookup: Boolean
) =>
profiler.registerCycle(
invalidatedClasses,
invalidatedByPackageObjects,
initialChangedSources,
invalidatedSources,
recompiledClasses,
newApiChanges,
nextInvalidations,
continuePerLookup
)
classesToRecompile,
(recompiledClasses, newApiChanges, nextInvalidations, continuePerLookup) => {
profiler.registerCycle(
invalidatedClasses,
invalidatedByPackageObjects,
initialChangedSources,
invalidatedSources,
recompiledClasses,
newApiChanges,
nextInvalidations,
continuePerLookup,
)
}
)

Expand Down
Loading

0 comments on commit fb8e03e

Please sign in to comment.