mirror of https://github.com/sbt/sbt.git
Do not rely on filename of package objects
Add a new field in `Source` that indicates whether this `Source` represents a package object. This field is used in the incrmental compiler to perform recompilation of package objects rather than relying on the file name being `package.scala`. Mark pending test `source-dependencies/package-object-name` as passing.
This commit is contained in:
parent
09bf5255ad
commit
b7b1f2453d
|
|
@ -17,6 +17,26 @@ object APIUtil {
|
|||
|
||||
def isScalaSourceName(name: String): Boolean = name.endsWith(".scala")
|
||||
|
||||
def hasPackageObject(s: SourceAPI): Boolean =
|
||||
{
|
||||
val check = new HasPackageObject
|
||||
check.visitAPI(s)
|
||||
check.hasPackageObject
|
||||
}
|
||||
|
||||
private[this] class HasPackageObject extends Visit {
|
||||
var hasPackageObject = false
|
||||
|
||||
private def isPackageObject(c: ClassLike): Boolean = {
|
||||
import xsbti.api.DefinitionType.{ Module, PackageModule }
|
||||
c.definitionType == PackageModule || (c.definitionType == Module && c.name.endsWith(".package"))
|
||||
}
|
||||
|
||||
override def visitClass0(c: ClassLike): Unit = {
|
||||
hasPackageObject ||= isPackageObject(c)
|
||||
}
|
||||
}
|
||||
|
||||
def hasMacro(s: SourceAPI): Boolean =
|
||||
{
|
||||
val check = new HasMacro
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@ trait APIs {
|
|||
|
||||
def internal: Map[File, Source]
|
||||
def external: Map[String, Source]
|
||||
|
||||
def hasPackageObject(src: File): Boolean
|
||||
}
|
||||
object APIs {
|
||||
def apply(internal: Map[File, Source], external: Map[String, Source]): APIs = new MAPIs(internal, external)
|
||||
|
|
@ -46,7 +48,7 @@ object APIs {
|
|||
val emptyAPI = new xsbti.api.SourceAPI(Array(), Array())
|
||||
val emptyCompilation = new xsbti.api.Compilation(-1, Array())
|
||||
val emptyNameHashes = new xsbti.api._internalOnly_NameHashes(Array.empty, Array.empty)
|
||||
val emptySource = new xsbti.api.Source(emptyCompilation, Array(), emptyAPI, 0, emptyNameHashes, false)
|
||||
val emptySource = new xsbti.api.Source(emptyCompilation, Array(), emptyAPI, 0, emptyNameHashes, false, false)
|
||||
def getAPI[T](map: Map[T, Source], src: T): Source = map.getOrElse(src, emptySource)
|
||||
}
|
||||
|
||||
|
|
@ -71,6 +73,8 @@ private class MAPIs(val internal: Map[File, Source], val external: Map[String, S
|
|||
def internalAPI(src: File) = getAPI(internal, src)
|
||||
def externalAPI(ext: String) = getAPI(external, ext)
|
||||
|
||||
override def hasPackageObject(src: File): Boolean = internalAPI(src).hasPackageObject
|
||||
|
||||
override def equals(other: Any): Boolean = other match {
|
||||
case o: MAPIs => {
|
||||
def areEqual[T](x: Map[T, Source], y: Map[T, Source])(implicit ord: math.Ordering[T]) = {
|
||||
|
|
|
|||
|
|
@ -110,6 +110,8 @@ private final class AnalysisCallback(internalMap: File => Option[File], external
|
|||
private[this] val binaryClassName = new HashMap[File, String]
|
||||
// source files containing a macro def.
|
||||
private[this] val macroSources = Set[File]()
|
||||
// source files defining a package object
|
||||
private[this] val packageObjectSources = Set[File]()
|
||||
|
||||
private def add[A, B](map: Map[A, Set[B]], a: A, b: B): Unit =
|
||||
map.getOrElseUpdate(a, new HashSet[B]) += b
|
||||
|
|
@ -186,7 +188,10 @@ private final class AnalysisCallback(internalMap: File => Option[File], external
|
|||
|
||||
def api(sourceFile: File, source: SourceAPI): Unit = {
|
||||
import xsbt.api.{ APIUtil, HashAPI }
|
||||
if (APIUtil.isScalaSourceName(sourceFile.getName) && APIUtil.hasMacro(source)) macroSources += sourceFile
|
||||
if (APIUtil.isScalaSourceName(sourceFile.getName)) {
|
||||
if (APIUtil.hasMacro(source)) macroSources += sourceFile
|
||||
if (APIUtil.hasPackageObject(source)) packageObjectSources += sourceFile
|
||||
}
|
||||
publicNameHashes(sourceFile) = {
|
||||
if (nameHashing)
|
||||
(new xsbt.api.NameHashing).nameHashes(source)
|
||||
|
|
@ -218,7 +223,8 @@ private final class AnalysisCallback(internalMap: File => Option[File], external
|
|||
val hash = stamp match { case h: Hash => h.value; case _ => new Array[Byte](0) }
|
||||
// TODO store this in Relations, rather than Source.
|
||||
val hasMacro: Boolean = macroSources.contains(src)
|
||||
val s = new xsbti.api.Source(compilation, hash, api._2, api._1, publicNameHashes(src), hasMacro)
|
||||
val hasPackageObject = packageObjectSources.contains(src)
|
||||
val s = new xsbti.api.Source(compilation, hash, api._2, api._1, publicNameHashes(src), hasMacro, hasPackageObject)
|
||||
val info = SourceInfos.makeInfo(getOrNil(reporteds, src), getOrNil(unreporteds, src))
|
||||
val binaries = binaryDeps.getOrElse(src, Nil: Iterable[File])
|
||||
val prods = classes.getOrElse(src, Nil: Iterable[(File, String)])
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import xsbti.api.Source
|
|||
private final class IncrementalAntStyle(log: Logger, options: IncOptions) extends IncrementalCommon(log, options) {
|
||||
|
||||
/** Ant-style mode doesn't do anything special with package objects */
|
||||
override protected def invalidatedPackageObjects(invalidated: Set[File], relations: Relations): Set[File] = Set.empty
|
||||
override protected def invalidatedPackageObjects(invalidated: Set[File], relations: Relations, apis: APIs): Set[File] = Set.empty
|
||||
|
||||
/** In Ant-style mode we don't need to compare APIs because we don't perform any invalidation */
|
||||
override protected def sameAPI[T](src: T, a: Source, b: Source): Option[APIChange[T]] = None
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ private[inc] abstract class IncrementalCommon(log: Logger, options: IncOptions)
|
|||
else {
|
||||
val wrappedLog = new Incremental.PrefixingLogger("[inv] ")(log)
|
||||
def debug(s: => String) = if (incDebug(options)) wrappedLog.debug(s) else ()
|
||||
val withPackageObjects = invalidatedRaw ++ invalidatedPackageObjects(invalidatedRaw, previous.relations)
|
||||
val withPackageObjects = invalidatedRaw ++ invalidatedPackageObjects(invalidatedRaw, previous.relations, previous.apis)
|
||||
val invalidated = expand(withPackageObjects, allSources)
|
||||
val pruned = Incremental.prune(invalidated, previous, classfileManager)
|
||||
debug("********* Pruned: \n" + pruned.relations + "\n*********")
|
||||
|
|
@ -54,7 +54,7 @@ private[inc] abstract class IncrementalCommon(log: Logger, options: IncOptions)
|
|||
} else invalidated
|
||||
}
|
||||
|
||||
protected def invalidatedPackageObjects(invalidated: Set[File], relations: Relations): Set[File]
|
||||
protected def invalidatedPackageObjects(invalidated: Set[File], relations: Relations, apis: APIs): Set[File]
|
||||
|
||||
/**
|
||||
* Logs API changes using debug-level logging. The API are obtained using the APIDiff class.
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ private final class IncrementalDefaultImpl(log: Logger, options: IncOptions) ext
|
|||
|
||||
// Package objects are fragile: if they inherit from an invalidated source, get "class file needed by package is missing" error
|
||||
// This might be too conservative: we probably only need package objects for packages of invalidated sources.
|
||||
override protected def invalidatedPackageObjects(invalidated: Set[File], relations: Relations): Set[File] =
|
||||
invalidated flatMap relations.publicInherited.internal.reverse filter { _.getName == "package.scala" }
|
||||
override protected def invalidatedPackageObjects(invalidated: Set[File], relations: Relations, apis: APIs): Set[File] =
|
||||
invalidated flatMap relations.publicInherited.internal.reverse filter apis.hasPackageObject
|
||||
|
||||
override protected def sameAPI[T](src: T, a: Source, b: Source): Option[SourceAPIChange[T]] = {
|
||||
if (SameAPI(a, b))
|
||||
|
|
|
|||
|
|
@ -17,10 +17,8 @@ private final class IncrementalNameHashing(log: Logger, options: IncOptions) ext
|
|||
|
||||
// Package objects are fragile: if they inherit from an invalidated source, get "class file needed by package is missing" error
|
||||
// This might be too conservative: we probably only need package objects for packages of invalidated sources.
|
||||
override protected def invalidatedPackageObjects(invalidated: Set[File], relations: Relations): Set[File] =
|
||||
transitiveDeps(invalidated)(relations.inheritance.internal.reverse).filter {
|
||||
_.getName == "package.scala"
|
||||
}
|
||||
override protected def invalidatedPackageObjects(invalidated: Set[File], relations: Relations, apis: APIs): Set[File] =
|
||||
transitiveDeps(invalidated)(relations.inheritance.internal.reverse) filter apis.hasPackageObject
|
||||
|
||||
override protected def sameAPI[T](src: T, a: Source, b: Source): Option[APIChange[T]] = {
|
||||
if (SameAPI(a, b))
|
||||
|
|
|
|||
|
|
@ -110,8 +110,9 @@ object TestCaseGenerators {
|
|||
hash <- Gen.containerOfN[Array, Byte](hashLen, arbitrary[Byte])
|
||||
apiHash <- arbitrary[Int]
|
||||
hasMacro <- arbitrary[Boolean]
|
||||
hasPackageObject <- arbitrary[Boolean]
|
||||
nameHashes <- genNameHashes(defns)
|
||||
} yield new Source(new Compilation(startTime, Array()), hash, new SourceAPI(Array(), Array(defns map makeDefinition: _*)), apiHash, nameHashes, hasMacro)
|
||||
} yield new Source(new Compilation(startTime, Array()), hash, new SourceAPI(Array(), Array(defns map makeDefinition: _*)), apiHash, nameHashes, hasMacro, hasPackageObject)
|
||||
|
||||
def genSources(all_defns: Seq[Seq[String]]): Gen[Seq[Source]] = Gen.sequence[List, Source](all_defns.map(genSource))
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ Source
|
|||
apiHash: Int
|
||||
_internalOnly_nameHashes: _internalOnly_NameHashes
|
||||
hasMacro: Boolean
|
||||
hasPackageObject: Boolean
|
||||
|
||||
_internalOnly_NameHashes
|
||||
regularMembers: _internalOnly_NameHash*
|
||||
|
|
|
|||
Loading…
Reference in New Issue