mirror of https://github.com/sbt/sbt.git
Make recompilation on macro definition optional.
Introduce a new incremental compiler option that controls incremental compiler's treatment of macro definitions and their clients. The current strategy is that whenever a source file containing a macro definition is touched it will cause recompilation of all direct dependencies of that file. That strategy has proven to be too conservative for some projects like Scala compiler of specs2 leading to too many source files being recompiled. We make this behavior optional by introducing a new option `recompileOnMacroDef` in `IncOptions` class. The default value is set to `true` which preserves the previous behavior.
This commit is contained in:
parent
a6f04cf53b
commit
39036e7c20
|
|
@ -46,42 +46,62 @@ final class IncOptions(
|
||||||
*/
|
*/
|
||||||
val 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. */
|
/** Creates a new ClassfileManager that will handle class file deletion and addition during a single incremental compilation run. */
|
||||||
val 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 {
|
) extends Product with Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 = {
|
def withTransitiveStep(transitiveStep: Int): IncOptions = {
|
||||||
new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize,
|
new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize,
|
||||||
apiDumpDirectory, newClassfileManager)
|
apiDumpDirectory, newClassfileManager, recompileOnMacroDef)
|
||||||
}
|
}
|
||||||
|
|
||||||
def withRecompileAllFraction(recompileAllFraction: Double): IncOptions = {
|
def withRecompileAllFraction(recompileAllFraction: Double): IncOptions = {
|
||||||
new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize,
|
new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize,
|
||||||
apiDumpDirectory, newClassfileManager)
|
apiDumpDirectory, newClassfileManager, recompileOnMacroDef)
|
||||||
}
|
}
|
||||||
|
|
||||||
def withRelationsDebug(relationsDebug: Boolean): IncOptions = {
|
def withRelationsDebug(relationsDebug: Boolean): IncOptions = {
|
||||||
new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize,
|
new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize,
|
||||||
apiDumpDirectory, newClassfileManager)
|
apiDumpDirectory, newClassfileManager, recompileOnMacroDef)
|
||||||
}
|
}
|
||||||
|
|
||||||
def withApiDebug(apiDebug: Boolean): IncOptions = {
|
def withApiDebug(apiDebug: Boolean): IncOptions = {
|
||||||
new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize,
|
new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize,
|
||||||
apiDumpDirectory, newClassfileManager)
|
apiDumpDirectory, newClassfileManager, recompileOnMacroDef)
|
||||||
}
|
}
|
||||||
|
|
||||||
def withApiDiffContextSize(apiDiffContextSize: Int): IncOptions = {
|
def withApiDiffContextSize(apiDiffContextSize: Int): IncOptions = {
|
||||||
new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize,
|
new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize,
|
||||||
apiDumpDirectory, newClassfileManager)
|
apiDumpDirectory, newClassfileManager, recompileOnMacroDef)
|
||||||
}
|
}
|
||||||
|
|
||||||
def withApiDumpDirectory(apiDumpDirectory: Option[File]): IncOptions = {
|
def withApiDumpDirectory(apiDumpDirectory: Option[File]): IncOptions = {
|
||||||
new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize,
|
new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize,
|
||||||
apiDumpDirectory, newClassfileManager)
|
apiDumpDirectory, newClassfileManager, recompileOnMacroDef)
|
||||||
}
|
}
|
||||||
|
|
||||||
def withNewClassfileManager(newClassfileManager: () => ClassfileManager): IncOptions = {
|
def withNewClassfileManager(newClassfileManager: () => ClassfileManager): IncOptions = {
|
||||||
new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize,
|
new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize,
|
||||||
apiDumpDirectory, newClassfileManager)
|
apiDumpDirectory, newClassfileManager, recompileOnMacroDef)
|
||||||
|
}
|
||||||
|
|
||||||
|
def withRecompileOnMacroDef(recompileOnMacroDef: Boolean): IncOptions = {
|
||||||
|
new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize,
|
||||||
|
apiDumpDirectory, newClassfileManager, recompileOnMacroDef)
|
||||||
}
|
}
|
||||||
|
|
||||||
//- EXPANDED CASE CLASS METHOD BEGIN -//
|
//- EXPANDED CASE CLASS METHOD BEGIN -//
|
||||||
|
|
@ -147,7 +167,8 @@ final class IncOptions(
|
||||||
}
|
}
|
||||||
|
|
||||||
object IncOptions extends Serializable {
|
object IncOptions extends Serializable {
|
||||||
val Default = new IncOptions(
|
private val recompileOnMacroDefDefault: Boolean = true
|
||||||
|
val Default = IncOptions(
|
||||||
// 1. recompile changed sources
|
// 1. recompile changed sources
|
||||||
// 2(3). recompile direct dependencies and transitive public inheritance dependencies of sources with API changes in 1(2).
|
// 2(3). recompile direct dependencies and transitive public inheritance dependencies of sources with API changes in 1(2).
|
||||||
// 4. further changes invalidate all dependencies transitively to avoid too many steps
|
// 4. further changes invalidate all dependencies transitively to avoid too many steps
|
||||||
|
|
@ -157,16 +178,24 @@ object IncOptions extends Serializable {
|
||||||
apiDebug = false,
|
apiDebug = false,
|
||||||
apiDiffContextSize = 5,
|
apiDiffContextSize = 5,
|
||||||
apiDumpDirectory = None,
|
apiDumpDirectory = None,
|
||||||
newClassfileManager = ClassfileManager.deleteImmediately
|
newClassfileManager = ClassfileManager.deleteImmediately,
|
||||||
|
recompileOnMacroDef = recompileOnMacroDefDefault
|
||||||
)
|
)
|
||||||
//- EXPANDED CASE CLASS METHOD BEGIN -//
|
//- EXPANDED CASE CLASS METHOD BEGIN -//
|
||||||
final override def toString(): String = "IncOptions"
|
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,
|
def apply(transitiveStep: Int, recompileAllFraction: Double, relationsDebug: Boolean, apiDebug: Boolean,
|
||||||
apiDiffContextSize: Int, apiDumpDirectory: Option[java.io.File],
|
apiDiffContextSize: Int, apiDumpDirectory: Option[java.io.File],
|
||||||
newClassfileManager: () => ClassfileManager): IncOptions = {
|
newClassfileManager: () => ClassfileManager): IncOptions = {
|
||||||
new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize,
|
new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize,
|
||||||
apiDumpDirectory, newClassfileManager)
|
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")
|
@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)] = {
|
def unapply(x$0: IncOptions): Option[(Int, Double, Boolean, Boolean, Int, Option[java.io.File], () => AnyRef)] = {
|
||||||
if (x$0 == null) None
|
if (x$0 == null) None
|
||||||
|
|
@ -187,7 +216,8 @@ object IncOptions extends Serializable {
|
||||||
val relationsDebugKey = "relationsDebug"
|
val relationsDebugKey = "relationsDebug"
|
||||||
val apiDebugKey = "apiDebug"
|
val apiDebugKey = "apiDebug"
|
||||||
val apiDumpDirectoryKey = "apiDumpDirectory"
|
val apiDumpDirectoryKey = "apiDumpDirectory"
|
||||||
val apiDiffContextSize = "apiDiffContextSize"
|
val apiDiffContextSizeKey = "apiDiffContextSize"
|
||||||
|
val recompileOnMacroDefKey = "recompileOnMacroDefKey"
|
||||||
|
|
||||||
def fromStringMap(m: java.util.Map[String, String]): IncOptions = {
|
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
|
// all the code below doesn't look like idiomatic Scala for a good reason: we are working with Java API
|
||||||
|
|
@ -208,7 +238,7 @@ object IncOptions extends Serializable {
|
||||||
if (m.containsKey(k)) m.get(k).toBoolean else Default.apiDebug
|
if (m.containsKey(k)) m.get(k).toBoolean else Default.apiDebug
|
||||||
}
|
}
|
||||||
def getApiDiffContextSize: Int = {
|
def getApiDiffContextSize: Int = {
|
||||||
val k = apiDiffContextSize
|
val k = apiDiffContextSizeKey
|
||||||
if (m.containsKey(k)) m.get(k).toInt else Default.apiDiffContextSize
|
if (m.containsKey(k)) m.get(k).toInt else Default.apiDiffContextSize
|
||||||
}
|
}
|
||||||
def getApiDumpDirectory: Option[java.io.File] = {
|
def getApiDumpDirectory: Option[java.io.File] = {
|
||||||
|
|
@ -217,9 +247,13 @@ object IncOptions extends Serializable {
|
||||||
Some(new java.io.File(m.get(k)))
|
Some(new java.io.File(m.get(k)))
|
||||||
else None
|
else None
|
||||||
}
|
}
|
||||||
|
def getRecompileOnMacroDef: Boolean = {
|
||||||
|
val k = recompileOnMacroDefKey
|
||||||
|
if (m.containsKey(k)) m.get(k).toBoolean else Default.recompileOnMacroDef
|
||||||
|
}
|
||||||
|
|
||||||
new IncOptions(getTransitiveStep, getRecompileAllFraction, getRelationsDebug, getApiDebug, getApiDiffContextSize,
|
new IncOptions(getTransitiveStep, getRecompileAllFraction, getRelationsDebug, getApiDebug, getApiDiffContextSize,
|
||||||
getApiDumpDirectory, ClassfileManager.deleteImmediately)
|
getApiDumpDirectory, ClassfileManager.deleteImmediately, getRecompileOnMacroDef)
|
||||||
}
|
}
|
||||||
|
|
||||||
def toStringMap(o: IncOptions): java.util.Map[String, String] = {
|
def toStringMap(o: IncOptions): java.util.Map[String, String] = {
|
||||||
|
|
@ -229,7 +263,8 @@ object IncOptions extends Serializable {
|
||||||
m.put(relationsDebugKey, o.relationsDebug.toString)
|
m.put(relationsDebugKey, o.relationsDebug.toString)
|
||||||
m.put(apiDebugKey, o.apiDebug.toString)
|
m.put(apiDebugKey, o.apiDebug.toString)
|
||||||
o.apiDumpDirectory.foreach(f => m.put(apiDumpDirectoryKey, f.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
|
m
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -142,7 +142,7 @@ object Incremental
|
||||||
{
|
{
|
||||||
val oldApis = lastSources.toSeq map oldAPI
|
val oldApis = lastSources.toSeq map oldAPI
|
||||||
val newApis = lastSources.toSeq map newAPI
|
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) {
|
if (apiDebug(options) && apiChanges.nonEmpty) {
|
||||||
logApiChanges(apiChanges, oldAPI, newAPI, log, options)
|
logApiChanges(apiChanges, oldAPI, newAPI, log, options)
|
||||||
|
|
@ -150,13 +150,13 @@ object Incremental
|
||||||
|
|
||||||
new APIChanges(apiChanges)
|
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.
|
// Clients of a modified source file (ie, one that doesn't satisfy `shortcutSameSource`) containing macros must be recompiled.
|
||||||
val hasMacro = a.hasMacro || b.hasMacro
|
val hasMacro = a.hasMacro || b.hasMacro
|
||||||
if (shortcutSameSource(a, b)) {
|
if (shortcutSameSource(a, b)) {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
if (hasMacro) {
|
if (hasMacro && options.recompileOnMacroDef) {
|
||||||
Some(APIChangeDueToMacroDefinition(src))
|
Some(APIChangeDueToMacroDefinition(src))
|
||||||
} else sameAPI(src, a, b, log)
|
} else sameAPI(src, a, b, log)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue