Merge pull request #2540 from eed3si9n/wip/synthetic

Adds withIncludeSynthToNameHashing.
This commit is contained in:
eugene yokota 2016-04-06 13:48:57 -04:00
commit ba72faab35
14 changed files with 85 additions and 36 deletions

View File

@ -232,23 +232,23 @@ object FileFunction {
type UpdateFunction = (ChangeReport[File], ChangeReport[File]) => Set[File]
/**
Generic change-detection helper used to help build / artifact generation /
etc. steps detect whether or not they need to run. Returns a function whose
input is a Set of input files, and subsequently executes the action function
(which does the actual work: compiles, generates resources, etc.), returning
a Set of output files that it generated.
The input file and resulting output file state is cached in
cacheBaseDirectory. On each invocation, the state of the input and output
files from the previous run is compared against the cache, as is the set of
input files. If a change in file state / input files set is detected, the
action function is re-executed.
@param cacheBaseDirectory The folder in which to store
@param inStyle The strategy by which to detect state change in the input files from the previous run
@param outStyle The strategy by which to detect state change in the output files from the previous run
@param action The work function, which receives a list of input files and returns a list of output files
*/
* Generic change-detection helper used to help build / artifact generation /
* etc. steps detect whether or not they need to run. Returns a function whose
* input is a Set of input files, and subsequently executes the action function
* (which does the actual work: compiles, generates resources, etc.), returning
* a Set of output files that it generated.
*
* The input file and resulting output file state is cached in
* cacheBaseDirectory. On each invocation, the state of the input and output
* files from the previous run is compared against the cache, as is the set of
* input files. If a change in file state / input files set is detected, the
* action function is re-executed.
*
* @param cacheBaseDirectory The folder in which to store
* @param inStyle The strategy by which to detect state change in the input files from the previous run
* @param outStyle The strategy by which to detect state change in the output files from the previous run
* @param action The work function, which receives a list of input files and returns a list of output files
*/
def cached(cacheBaseDirectory: File, inStyle: FilesInfo.Style = FilesInfo.lastModified, outStyle: FilesInfo.Style = FilesInfo.exists)(action: Set[File] => Set[File]): Set[File] => Set[File] =
cached(cacheBaseDirectory)(inStyle, outStyle)((in, out) => action(in.checked))

View File

@ -206,6 +206,7 @@ private final class AnalysisCallback(internalMap: File => Option[File], external
def usedName(sourceFile: File, name: String) = add(usedNames, sourceFile, name)
def nameHashing: Boolean = options.nameHashing
def includeSynthToNameHashing: Boolean = options.includeSynthToNameHashing
def get: Analysis = addUsedNames(addCompilation(addProductsAndDeps(Analysis.empty(nameHashing = nameHashing))))

View File

@ -80,7 +80,11 @@ final class IncOptions(
* Once Scala compiler sources are refactored to work well with name hashing algorithm this option will be
* deleted immediately.
*/
val antStyle: Boolean) extends Product with Serializable {
val antStyle: Boolean,
/**
* Include synthetic methods into the dependency tracking by name hashing.
*/
val includeSynthToNameHashing: Boolean) extends Product with Serializable {
/**
* Secondary constructor introduced to make IncOptions to be binary compatible with version that didn't have
@ -90,59 +94,72 @@ final class IncOptions(
apiDiffContextSize: Int, apiDumpDirectory: Option[java.io.File], newClassfileManager: () => ClassfileManager) = {
this(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize,
apiDumpDirectory, newClassfileManager, IncOptions.recompileOnMacroDefDefault, IncOptions.nameHashingDefault,
IncOptions.antStyleDefault)
IncOptions.antStyleDefault, IncOptions.includeSynthToNameHashingDefault)
}
def this(transitiveStep: Int, recompileAllFraction: Double, relationsDebug: Boolean, apiDebug: Boolean,
apiDiffContextSize: Int, apiDumpDirectory: Option[java.io.File], newClassfileManager: () => ClassfileManager,
recompileOnMacroDef: Boolean, nameHashing: Boolean, antStyle: Boolean) = {
this(transitiveStep, recompileAllFraction, relationsDebug, apiDebug,
apiDiffContextSize, apiDumpDirectory, newClassfileManager, recompileOnMacroDef, nameHashing,
antStyle, IncOptions.includeSynthToNameHashingDefault)
}
assert(!(antStyle && nameHashing), "Name hashing and Ant-style cannot be enabled at the same time.")
def withTransitiveStep(transitiveStep: Int): IncOptions = {
new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize,
apiDumpDirectory, newClassfileManager, recompileOnMacroDef, nameHashing, antStyle)
apiDumpDirectory, newClassfileManager, recompileOnMacroDef, nameHashing, antStyle, includeSynthToNameHashing)
}
def withRecompileAllFraction(recompileAllFraction: Double): IncOptions = {
new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize,
apiDumpDirectory, newClassfileManager, recompileOnMacroDef, nameHashing, antStyle)
apiDumpDirectory, newClassfileManager, recompileOnMacroDef, nameHashing, antStyle, includeSynthToNameHashing)
}
def withRelationsDebug(relationsDebug: Boolean): IncOptions = {
new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize,
apiDumpDirectory, newClassfileManager, recompileOnMacroDef, nameHashing, antStyle)
apiDumpDirectory, newClassfileManager, recompileOnMacroDef, nameHashing, antStyle, includeSynthToNameHashing)
}
def withApiDebug(apiDebug: Boolean): IncOptions = {
new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize,
apiDumpDirectory, newClassfileManager, recompileOnMacroDef, nameHashing, antStyle)
apiDumpDirectory, newClassfileManager, recompileOnMacroDef, nameHashing, antStyle, includeSynthToNameHashing)
}
def withApiDiffContextSize(apiDiffContextSize: Int): IncOptions = {
new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize,
apiDumpDirectory, newClassfileManager, recompileOnMacroDef, nameHashing, antStyle)
apiDumpDirectory, newClassfileManager, recompileOnMacroDef, nameHashing, antStyle, includeSynthToNameHashing)
}
def withApiDumpDirectory(apiDumpDirectory: Option[File]): IncOptions = {
new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize,
apiDumpDirectory, newClassfileManager, recompileOnMacroDef, nameHashing, antStyle)
apiDumpDirectory, newClassfileManager, recompileOnMacroDef, nameHashing, antStyle, includeSynthToNameHashing)
}
def withNewClassfileManager(newClassfileManager: () => ClassfileManager): IncOptions = {
new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize,
apiDumpDirectory, newClassfileManager, recompileOnMacroDef, nameHashing, antStyle)
apiDumpDirectory, newClassfileManager, recompileOnMacroDef, nameHashing, antStyle, includeSynthToNameHashing)
}
def withRecompileOnMacroDef(recompileOnMacroDef: Boolean): IncOptions = {
new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize,
apiDumpDirectory, newClassfileManager, recompileOnMacroDef, nameHashing, antStyle)
apiDumpDirectory, newClassfileManager, recompileOnMacroDef, nameHashing, antStyle, includeSynthToNameHashing)
}
def withNameHashing(nameHashing: Boolean): IncOptions = {
new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize,
apiDumpDirectory, newClassfileManager, recompileOnMacroDef, nameHashing, antStyle)
apiDumpDirectory, newClassfileManager, recompileOnMacroDef, nameHashing, antStyle, includeSynthToNameHashing)
}
def withIncludeSynthToNameHashing(includeSynthToNameHashing: Boolean): IncOptions = {
new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize,
apiDumpDirectory, newClassfileManager, recompileOnMacroDef, nameHashing, antStyle, includeSynthToNameHashing)
}
def withAntStyle(antStyle: Boolean): IncOptions = {
new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize,
apiDumpDirectory, newClassfileManager, recompileOnMacroDef, nameHashing, antStyle)
apiDumpDirectory, newClassfileManager, recompileOnMacroDef, nameHashing, antStyle, includeSynthToNameHashing)
}
//- EXPANDED CASE CLASS METHOD BEGIN -//
@ -219,6 +236,8 @@ object IncOptions extends Serializable {
private val recompileOnMacroDefDefault: Boolean = true
private[sbt] val nameHashingDefault: Boolean = true
private val antStyleDefault: Boolean = false
// This should default to false
private[sbt] val includeSynthToNameHashingDefault = java.lang.Boolean.getBoolean("sbt.inc.include_synth")
val Default = IncOptions(
// 1. recompile changed sources
// 2(3). recompile direct dependencies and transitive public inheritance dependencies of sources with API changes in 1(2).

View File

@ -41,6 +41,8 @@ import scala.tools.nsc._
class ExtractUsedNames[GlobalType <: CallbackGlobal](val global: GlobalType) extends Compat {
import global._
@inline def debug(msg: => String) = if (settings.verbose.value) inform(msg)
def extract(unit: CompilationUnit): Set[String] = {
val tree = unit.body
val extractedByTreeWalk = extractByTreeWalk(tree)
@ -122,7 +124,7 @@ class ExtractUsedNames[GlobalType <: CallbackGlobal](val global: GlobalType) ext
}
(symbol != NoSymbol) &&
!symbol.isSynthetic &&
(callback.includeSynthToNameHashing || !symbol.isSynthetic) &&
!emptyName(symbol.name)
}
}

View File

@ -15,7 +15,7 @@ class ExtractAPISpecification extends Specification {
def stableExistentialNames: Boolean = {
def compileAndGetFooMethodApi(src: String): Def = {
val compilerForTesting = new ScalaCompilerForUnitTesting
val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = false)
val sourceApi = compilerForTesting.extractApiFromSrc(src)
val FooApi = sourceApi.definitions().find(_.name() == "Foo").get.asInstanceOf[ClassLike]
val fooMethodApi = FooApi.structure().declared().find(_.name == "foo").get
@ -66,7 +66,7 @@ class ExtractAPISpecification extends Specification {
| class Foo extends Namers
|}
|""".stripMargin
val compilerForTesting = new ScalaCompilerForUnitTesting
val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = false)
val apis = compilerForTesting.extractApisFromSrcs(reuseCompilerInstance = false)(List(src1, src2), List(src2))
val _ :: src2Api1 :: src2Api2 :: Nil = apis.toList
val namerApi1 = selectNamer(src2Api1)
@ -92,7 +92,7 @@ class ExtractAPISpecification extends Specification {
val srcC6 = "class C6 extends AnyRef with X { self: X with Y => }"
val srcC7 = "class C7 { _ => }"
val srcC8 = "class C8 { self => }"
val compilerForTesting = new ScalaCompilerForUnitTesting
val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = false)
val apis = compilerForTesting.extractApisFromSrcs(reuseCompilerInstance = true)(
List(srcX, srcY, srcC1, srcC2, srcC3, srcC4, srcC5, srcC6, srcC7, srcC8)
).map(x => collectFirstClass(x.definitions))

View File

@ -20,7 +20,7 @@ import ScalaCompilerForUnitTesting.ExtractedSourceDependencies
* Provides common functionality needed for unit tests that require compiling
* source code using Scala compiler.
*/
class ScalaCompilerForUnitTesting(nameHashing: Boolean = false) {
class ScalaCompilerForUnitTesting(nameHashing: Boolean, includeSynthToNameHashing: Boolean = false) {
/**
* Compiles given source code using Scala compiler and returns API representation
@ -127,7 +127,7 @@ class ScalaCompilerForUnitTesting(nameHashing: Boolean = false) {
private def compileSrcs(groupedSrcs: List[List[String]],
reuseCompilerInstance: Boolean): (Seq[File], TestCallback) = {
withTemporaryDirectory { temp =>
val analysisCallback = new TestCallback(nameHashing)
val analysisCallback = new TestCallback(nameHashing, includeSynthToNameHashing)
val classesDir = new File(temp, "classes")
classesDir.mkdir()

View File

@ -59,4 +59,8 @@ public interface AnalysisCallback
* Do not depend on it, please.
*/
boolean nameHashing();
/**
* Include synthetic methods into the dependency tracking by name hashing.
*/
boolean includeSynthToNameHashing();
}

View File

@ -5,7 +5,7 @@ import scala.collection.mutable.ArrayBuffer
import xsbti.api.SourceAPI
import xsbti.DependencyContext._
class TestCallback(override val nameHashing: Boolean = false) extends AnalysisCallback
class TestCallback(override val nameHashing: Boolean, override val includeSynthToNameHashing: Boolean) extends AnalysisCallback
{
val sourceDependencies = new ArrayBuffer[(File, File, DependencyContext)]
val binaryDependencies = new ArrayBuffer[(File, String, File, DependencyContext)]

View File

@ -0,0 +1,10 @@
[@eed3si9n]: https://github.com/eed3si9n
[@jsuereth]: https://github.com/jsuereth
[@dwijnand]: http://github.com/dwijnand
[@Duhemm]: http://github.com/Duhemm
[@gkossakowski]: https://github.com/gkossakowski
[2573]: https://github.com/sbt/sbt/issues/2537
### Bug fixes
- Provides a workaround flag `incOptions := incOptions.value.withIncludeSynthToNameHashing(true)` for name hashing not including synthetic methods. This will not be enabled by default in sbt 0.13. It can also enabled by passing `sbt.inc.include_synth=true` to JVM. [#2537][2573] by [@eed3si9h][@eed3si9h]

View File

@ -0,0 +1 @@
case class A(a: Int)

View File

@ -0,0 +1 @@
class B { def test(a: A) = a.copy() }

View File

@ -0,0 +1,5 @@
lazy val root = (project in file(".")).
settings(
scalaVersion := "2.11.7",
incOptions := incOptions.value.withIncludeSynthToNameHashing(true)
)

View File

@ -0,0 +1 @@
case class A(a: Int) { private def copy = ??? }

View File

@ -0,0 +1,5 @@
> compile
$ copy-file changes/A.scala A.scala
-> compile