mirror of https://github.com/sbt/sbt.git
Merge pull request #976 from gkossakowski/macro-handling
Make recompilation on macro definition optional
This commit is contained in:
commit
bf0ef95356
|
|
@ -5,10 +5,15 @@ package sbt.inc
|
|||
/**
|
||||
* Represents all configuration options for the incremental compiler itself and
|
||||
* not the underlying Java/Scala compiler.
|
||||
*
|
||||
* NOTE: This class used to be a case class but due to problems with retaining
|
||||
* binary compatibility while new fields are added it has been expanded to a
|
||||
* regular class. All compiler-generated methods for a case class has been
|
||||
* defined explicitly.
|
||||
*/
|
||||
final case class IncOptions(
|
||||
final class IncOptions(
|
||||
/** After which step include whole transitive closure of invalidated source files. */
|
||||
transitiveStep: Int,
|
||||
val transitiveStep: Int,
|
||||
/**
|
||||
* What's the fraction of invalidated source files when we switch to recompiling
|
||||
* all files and giving up incremental compilation altogether. That's useful in
|
||||
|
|
@ -16,9 +21,9 @@ final case class IncOptions(
|
|||
* in multiple steps is high. Multi-step incremental recompilation is slower
|
||||
* than recompiling everything in one step.
|
||||
*/
|
||||
recompileAllFraction: Double,
|
||||
val recompileAllFraction: Double,
|
||||
/** Print very detailed information about relations, such as dependencies between source files. */
|
||||
relationsDebug: Boolean,
|
||||
val relationsDebug: Boolean,
|
||||
/**
|
||||
* Enable tools for debugging API changes. At the moment this option is unused but in the
|
||||
* future it will enable for example:
|
||||
|
|
@ -26,25 +31,143 @@ final case class IncOptions(
|
|||
* - diffing textual API representation which helps understanding what kind of changes
|
||||
* to APIs are visible to the incremental compiler
|
||||
*/
|
||||
apiDebug: Boolean,
|
||||
val apiDebug: Boolean,
|
||||
/**
|
||||
* Controls context size (in lines) displayed when diffs are produced for textual API
|
||||
* representation.
|
||||
*
|
||||
* This option is used only when `apiDebug == true`.
|
||||
*/
|
||||
apiDiffContextSize: Int,
|
||||
val apiDiffContextSize: Int,
|
||||
/**
|
||||
* The directory where we dump textual representation of APIs. This method might be called
|
||||
* only if apiDebug returns true. This is unused option at the moment as the needed functionality
|
||||
* is not implemented yet.
|
||||
*/
|
||||
apiDumpDirectory: Option[java.io.File],
|
||||
val apiDumpDirectory: Option[java.io.File],
|
||||
/** Creates a new ClassfileManager that will handle class file deletion and addition during a single incremental compilation run. */
|
||||
newClassfileManager: () => ClassfileManager
|
||||
)
|
||||
val newClassfileManager: () => ClassfileManager,
|
||||
/**
|
||||
* Determines whether incremental compiler should recompile all dependencies of a file
|
||||
* that contains a macro definition.
|
||||
*/
|
||||
val recompileOnMacroDef: Boolean
|
||||
) extends Product with Serializable {
|
||||
|
||||
object IncOptions {
|
||||
/**
|
||||
* Secondary constructor introduced to make IncOptions to be binary compatible with version that didn't have
|
||||
* `recompileOnMacroDef` filed defined.
|
||||
*/
|
||||
def this(transitiveStep: Int, recompileAllFraction: Double, relationsDebug: Boolean, apiDebug: Boolean,
|
||||
apiDiffContextSize: Int, apiDumpDirectory: Option[java.io.File], newClassfileManager: () => ClassfileManager) = {
|
||||
this(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize,
|
||||
apiDumpDirectory, newClassfileManager, IncOptions.recompileOnMacroDefDefault)
|
||||
}
|
||||
|
||||
def withTransitiveStep(transitiveStep: Int): IncOptions = {
|
||||
new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize,
|
||||
apiDumpDirectory, newClassfileManager, recompileOnMacroDef)
|
||||
}
|
||||
|
||||
def withRecompileAllFraction(recompileAllFraction: Double): IncOptions = {
|
||||
new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize,
|
||||
apiDumpDirectory, newClassfileManager, recompileOnMacroDef)
|
||||
}
|
||||
|
||||
def withRelationsDebug(relationsDebug: Boolean): IncOptions = {
|
||||
new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize,
|
||||
apiDumpDirectory, newClassfileManager, recompileOnMacroDef)
|
||||
}
|
||||
|
||||
def withApiDebug(apiDebug: Boolean): IncOptions = {
|
||||
new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize,
|
||||
apiDumpDirectory, newClassfileManager, recompileOnMacroDef)
|
||||
}
|
||||
|
||||
def withApiDiffContextSize(apiDiffContextSize: Int): IncOptions = {
|
||||
new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize,
|
||||
apiDumpDirectory, newClassfileManager, recompileOnMacroDef)
|
||||
}
|
||||
|
||||
def withApiDumpDirectory(apiDumpDirectory: Option[File]): IncOptions = {
|
||||
new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize,
|
||||
apiDumpDirectory, newClassfileManager, recompileOnMacroDef)
|
||||
}
|
||||
|
||||
def withNewClassfileManager(newClassfileManager: () => ClassfileManager): IncOptions = {
|
||||
new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize,
|
||||
apiDumpDirectory, newClassfileManager, recompileOnMacroDef)
|
||||
}
|
||||
|
||||
def withRecompileOnMacroDef(recompileOnMacroDef: Boolean): IncOptions = {
|
||||
new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize,
|
||||
apiDumpDirectory, newClassfileManager, recompileOnMacroDef)
|
||||
}
|
||||
|
||||
//- EXPANDED CASE CLASS METHOD BEGIN -//
|
||||
@deprecated("Use `with$nameOfTheField` copying methods instead.", "0.13.2")
|
||||
def copy(transitiveStep: Int = this.transitiveStep, recompileAllFraction: Double = this.recompileAllFraction,
|
||||
relationsDebug: Boolean = this.relationsDebug, apiDebug: Boolean = this.apiDebug,
|
||||
apiDiffContextSize: Int = this.apiDiffContextSize,
|
||||
apiDumpDirectory: Option[java.io.File] = this.apiDumpDirectory,
|
||||
newClassfileManager: () => ClassfileManager = this.newClassfileManager): IncOptions = {
|
||||
new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize,
|
||||
apiDumpDirectory, newClassfileManager)
|
||||
}
|
||||
|
||||
@deprecated("Methods generated for case class will be removed in the future.", "0.13.2")
|
||||
override def productPrefix: String = "IncOptions"
|
||||
|
||||
@deprecated("Methods generated for case class will be removed in the future.", "0.13.2")
|
||||
def productArity: Int = 7
|
||||
|
||||
@deprecated("Methods generated for case class will be removed in the future.", "0.13.2")
|
||||
def productElement(x$1: Int): Any = x$1 match {
|
||||
case 0 => IncOptions.this.transitiveStep
|
||||
case 1 => IncOptions.this.recompileAllFraction
|
||||
case 2 => IncOptions.this.relationsDebug
|
||||
case 3 => IncOptions.this.apiDebug
|
||||
case 4 => IncOptions.this.apiDiffContextSize
|
||||
case 5 => IncOptions.this.apiDumpDirectory
|
||||
case 6 => IncOptions.this.newClassfileManager
|
||||
case _ => throw new IndexOutOfBoundsException(x$1.toString())
|
||||
}
|
||||
|
||||
@deprecated("Methods generated for case class will be removed in the future.", "0.13.2")
|
||||
override def productIterator: Iterator[Any] = scala.runtime.ScalaRunTime.typedProductIterator[Any](IncOptions.this)
|
||||
|
||||
@deprecated("Methods generated for case class will be removed in the future.", "0.13.2")
|
||||
def canEqual(x$1: Any): Boolean = x$1.isInstanceOf[IncOptions]
|
||||
|
||||
override def hashCode(): Int = {
|
||||
import scala.runtime.Statics
|
||||
var acc: Int = -889275714
|
||||
acc = Statics.mix(acc, transitiveStep)
|
||||
acc = Statics.mix(acc, Statics.doubleHash(recompileAllFraction))
|
||||
acc = Statics.mix(acc, if (relationsDebug) 1231 else 1237)
|
||||
acc = Statics.mix(acc, if (apiDebug) 1231 else 1237)
|
||||
acc = Statics.mix(acc, apiDiffContextSize)
|
||||
acc = Statics.mix(acc, Statics.anyHash(apiDumpDirectory))
|
||||
acc = Statics.mix(acc, Statics.anyHash(newClassfileManager))
|
||||
Statics.finalizeHash(acc, 7)
|
||||
}
|
||||
|
||||
override def toString(): String = scala.runtime.ScalaRunTime._toString(IncOptions.this)
|
||||
|
||||
override def equals(x$1: Any): Boolean = {
|
||||
this.eq(x$1.asInstanceOf[Object]) || (x$1.isInstanceOf[IncOptions] && ({
|
||||
val IncOptions$1: IncOptions = x$1.asInstanceOf[IncOptions]
|
||||
transitiveStep == IncOptions$1.transitiveStep && recompileAllFraction == IncOptions$1.recompileAllFraction &&
|
||||
relationsDebug == IncOptions$1.relationsDebug && apiDebug == IncOptions$1.apiDebug &&
|
||||
apiDiffContextSize == IncOptions$1.apiDiffContextSize && apiDumpDirectory == IncOptions$1.apiDumpDirectory &&
|
||||
newClassfileManager == IncOptions$1.newClassfileManager
|
||||
}))
|
||||
}
|
||||
//- EXPANDED CASE CLASS METHOD END -//
|
||||
}
|
||||
|
||||
object IncOptions extends Serializable {
|
||||
private val recompileOnMacroDefDefault: Boolean = true
|
||||
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).
|
||||
|
|
@ -55,18 +178,46 @@ object IncOptions {
|
|||
apiDebug = false,
|
||||
apiDiffContextSize = 5,
|
||||
apiDumpDirectory = None,
|
||||
newClassfileManager = ClassfileManager.deleteImmediately
|
||||
newClassfileManager = ClassfileManager.deleteImmediately,
|
||||
recompileOnMacroDef = recompileOnMacroDefDefault
|
||||
)
|
||||
//- EXPANDED CASE CLASS METHOD BEGIN -//
|
||||
final override def toString(): String = "IncOptions"
|
||||
@deprecated("Use overloaded variant of `apply` with complete list of arguments instead.", "0.13.2")
|
||||
def apply(transitiveStep: Int, recompileAllFraction: Double, relationsDebug: Boolean, apiDebug: Boolean,
|
||||
apiDiffContextSize: Int, apiDumpDirectory: Option[java.io.File],
|
||||
newClassfileManager: () => ClassfileManager): IncOptions = {
|
||||
new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize,
|
||||
apiDumpDirectory, newClassfileManager)
|
||||
}
|
||||
def apply(transitiveStep: Int, recompileAllFraction: Double, relationsDebug: Boolean, apiDebug: Boolean,
|
||||
apiDiffContextSize: Int, apiDumpDirectory: Option[java.io.File],
|
||||
newClassfileManager: () => ClassfileManager, recompileOnMacroDef: Boolean): IncOptions = {
|
||||
new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize,
|
||||
apiDumpDirectory, newClassfileManager, recompileOnMacroDef)
|
||||
}
|
||||
@deprecated("Methods generated for case class will be removed in the future.", "0.13.2")
|
||||
def unapply(x$0: IncOptions): Option[(Int, Double, Boolean, Boolean, Int, Option[java.io.File], () => AnyRef)] = {
|
||||
if (x$0 == null) None
|
||||
else Some.apply[(Int, Double, Boolean, Boolean, Int, Option[java.io.File], () => AnyRef)](
|
||||
Tuple7.apply[Int, Double, Boolean, Boolean, Int, Option[java.io.File], () => AnyRef](
|
||||
x$0.transitiveStep, x$0.recompileAllFraction, x$0.relationsDebug, x$0.apiDebug, x$0.apiDiffContextSize,
|
||||
x$0.apiDumpDirectory, x$0.newClassfileManager))
|
||||
}
|
||||
private def readResolve(): Object = IncOptions
|
||||
//- EXPANDED CASE CLASS METHOD END -//
|
||||
|
||||
def defaultTransactional(tempDir: File): IncOptions = setTransactional(Default, tempDir)
|
||||
def setTransactional(opts: IncOptions, tempDir: File): IncOptions =
|
||||
opts.copy(newClassfileManager = ClassfileManager.transactional(tempDir))
|
||||
|
||||
val transitiveStepKey = "transitiveStep"
|
||||
val recompileAllFractionKey = "recompileAllFraction"
|
||||
val relationsDebugKey = "relationsDebug"
|
||||
val apiDebugKey = "apiDebug"
|
||||
val apiDumpDirectoryKey = "apiDumpDirectory"
|
||||
val apiDiffContextSize = "apiDiffContextSize"
|
||||
private val transitiveStepKey = "transitiveStep"
|
||||
private val recompileAllFractionKey = "recompileAllFraction"
|
||||
private val relationsDebugKey = "relationsDebug"
|
||||
private val apiDebugKey = "apiDebug"
|
||||
private val apiDumpDirectoryKey = "apiDumpDirectory"
|
||||
private val apiDiffContextSizeKey = "apiDiffContextSize"
|
||||
private val recompileOnMacroDefKey = "recompileOnMacroDefKey"
|
||||
|
||||
def fromStringMap(m: java.util.Map[String, String]): IncOptions = {
|
||||
// all the code below doesn't look like idiomatic Scala for a good reason: we are working with Java API
|
||||
|
|
@ -87,7 +238,7 @@ object IncOptions {
|
|||
if (m.containsKey(k)) m.get(k).toBoolean else Default.apiDebug
|
||||
}
|
||||
def getApiDiffContextSize: Int = {
|
||||
val k = apiDiffContextSize
|
||||
val k = apiDiffContextSizeKey
|
||||
if (m.containsKey(k)) m.get(k).toInt else Default.apiDiffContextSize
|
||||
}
|
||||
def getApiDumpDirectory: Option[java.io.File] = {
|
||||
|
|
@ -96,9 +247,13 @@ object IncOptions {
|
|||
Some(new java.io.File(m.get(k)))
|
||||
else None
|
||||
}
|
||||
def getRecompileOnMacroDef: Boolean = {
|
||||
val k = recompileOnMacroDefKey
|
||||
if (m.containsKey(k)) m.get(k).toBoolean else Default.recompileOnMacroDef
|
||||
}
|
||||
|
||||
IncOptions(getTransitiveStep, getRecompileAllFraction, getRelationsDebug, getApiDebug, getApiDiffContextSize,
|
||||
getApiDumpDirectory, ClassfileManager.deleteImmediately)
|
||||
new IncOptions(getTransitiveStep, getRecompileAllFraction, getRelationsDebug, getApiDebug, getApiDiffContextSize,
|
||||
getApiDumpDirectory, ClassfileManager.deleteImmediately, getRecompileOnMacroDef)
|
||||
}
|
||||
|
||||
def toStringMap(o: IncOptions): java.util.Map[String, String] = {
|
||||
|
|
@ -108,7 +263,8 @@ object IncOptions {
|
|||
m.put(relationsDebugKey, o.relationsDebug.toString)
|
||||
m.put(apiDebugKey, o.apiDebug.toString)
|
||||
o.apiDumpDirectory.foreach(f => m.put(apiDumpDirectoryKey, f.toString))
|
||||
m.put(apiDiffContextSize, o.apiDiffContextSize.toString)
|
||||
m.put(apiDiffContextSizeKey, o.apiDiffContextSize.toString)
|
||||
m.put(recompileOnMacroDefKey, o.recompileOnMacroDef.toString)
|
||||
m
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@ object Incremental
|
|||
{
|
||||
val oldApis = lastSources.toSeq map oldAPI
|
||||
val newApis = lastSources.toSeq map newAPI
|
||||
val apiChanges = (lastSources, oldApis, newApis).zipped.flatMap { (src, oldApi, newApi) => sameSource(src, oldApi, newApi, log) }
|
||||
val apiChanges = (lastSources, oldApis, newApis).zipped.flatMap { (src, oldApi, newApi) => sameSource(src, oldApi, newApi, log, options) }
|
||||
|
||||
if (apiDebug(options) && apiChanges.nonEmpty) {
|
||||
logApiChanges(apiChanges, oldAPI, newAPI, log, options)
|
||||
|
|
@ -150,13 +150,13 @@ object Incremental
|
|||
|
||||
new APIChanges(apiChanges)
|
||||
}
|
||||
def sameSource[T](src: T, a: Source, b: Source, log: Logger): Option[APIChange[T]] = {
|
||||
def sameSource[T](src: T, a: Source, b: Source, log: Logger, options: IncOptions): Option[APIChange[T]] = {
|
||||
// Clients of a modified source file (ie, one that doesn't satisfy `shortcutSameSource`) containing macros must be recompiled.
|
||||
val hasMacro = a.hasMacro || b.hasMacro
|
||||
if (shortcutSameSource(a, b)) {
|
||||
None
|
||||
} else {
|
||||
if (hasMacro) {
|
||||
if (hasMacro && options.recompileOnMacroDef) {
|
||||
Some(APIChangeDueToMacroDefinition(src))
|
||||
} else sameAPI(src, a, b, log)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue