From b77b0e161ea4e660c318370b947d1b6010b3fad8 Mon Sep 17 00:00:00 2001 From: Grzegorz Kossakowski Date: Tue, 12 Nov 2013 21:31:28 +0100 Subject: [PATCH 1/4] Desugar case class IncOptions in binary compatible way. Expand case class `IncOptions` in binary compatible way so we can have better control of methods like `unapply` when new fields are added. Great precaution has been taken to ensure that this commit doesn't break binary compatibility. I took a dump of javap output before and after this change for both the class and it's companion object. The diff is presented below: diff -u ~/inc-options-before ~/inc-options-after --- /Users/grek/inc-options-before 2013-11-03 14:48:45.000000000 +0100 +++ /Users/grek/inc-options-after 2013-11-03 15:53:10.000000000 +0100 @@ -9,7 +9,11 @@ public static java.lang.String transitiveStepKey(); public static sbt.inc.IncOptions setTransactional(sbt.inc.IncOptions, java.io.File); public static sbt.inc.IncOptions defaultTransactional(java.io.File); + public static scala.Option unapply(sbt.inc.IncOptions); + public static sbt.inc.IncOptions apply(int, double, boolean, boolean, int, scala.Option, scala.Function0); public static sbt.inc.IncOptions Default(); + public static scala.Function1 tupled(); + public static scala.Function1 curried(); public int transitiveStep(); public double recompileAllFraction(); public boolean relationsDebug(); diff -u inc-options-module-before inc-options-module-after --- inc-options-module-before 2013-11-03 14:48:55.000000000 +0100 +++ inc-options-module-after 2013-11-12 21:00:41.000000000 +0100 @@ -3,6 +3,9 @@ public static final sbt.inc.IncOptions$ MODULE$; public static {}; public sbt.inc.IncOptions Default(); + public final java.lang.String toString(); + public sbt.inc.IncOptions apply(int, double, boolean, boolean, int, scala.Option, scala.Function0); + public scala.Option unapply(sbt.inc.IncOptions); public sbt.inc.IncOptions defaultTransactional(java.io.File); public sbt.inc.IncOptions setTransactional(sbt.inc.IncOptions, java.io.File); public java.lang.String transitiveStepKey(); @@ -13,7 +16,5 @@ public java.lang.String apiDiffContextSize(); public sbt.inc.IncOptions fromStringMap(java.util.Map); public java.util.Map toStringMap(sbt.inc.IncOptions); - public sbt.inc.IncOptions apply(int, double, boolean, boolean, int, scala.Option, scala.Function0); - public scala.Option unapply(sbt.inc.IncOptions); } The first diff shows that there are just more static forwarders defined for top-level companion object and that is binary compatible change. The second diff shows that there are just a few minor differences in order in which `unapply`, `apply` and bridge method for `apply` are defined. Also, there's a new `toString` declaration. All those changes are binary compatible. All methods that are generated for a case class are marked as deprecated and will be removed in the future. --- .../src/main/scala/sbt/inc/IncOptions.scala | 110 ++++++++++++++++-- 1 file changed, 98 insertions(+), 12 deletions(-) diff --git a/compile/inc/src/main/scala/sbt/inc/IncOptions.scala b/compile/inc/src/main/scala/sbt/inc/IncOptions.scala index 81b6910b0..c5ebc649a 100644 --- a/compile/inc/src/main/scala/sbt/inc/IncOptions.scala +++ b/compile/inc/src/main/scala/sbt/inc/IncOptions.scala @@ -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,26 +31,88 @@ 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 +) extends Product with Serializable { -object IncOptions { - val Default = IncOptions( + //- EXPANDED CASE CLASS METHOD BEGIN -// + @deprecated("Methods generated for case class will be removed in the future.", "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 { + val Default = new IncOptions( // 1. recompile changed sources // 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 @@ -57,6 +124,25 @@ object IncOptions { apiDumpDirectory = None, newClassfileManager = ClassfileManager.deleteImmediately ) + //- EXPANDED CASE CLASS METHOD BEGIN -// + final override def toString(): String = "IncOptions" + 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) + } + @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)) @@ -97,7 +183,7 @@ object IncOptions { else None } - IncOptions(getTransitiveStep, getRecompileAllFraction, getRelationsDebug, getApiDebug, getApiDiffContextSize, + new IncOptions(getTransitiveStep, getRecompileAllFraction, getRelationsDebug, getApiDebug, getApiDiffContextSize, getApiDumpDirectory, ClassfileManager.deleteImmediately) } From a6f04cf53b4ca64566a24591920f1769d710cb52 Mon Sep 17 00:00:00 2001 From: Grzegorz Kossakowski Date: Mon, 4 Nov 2013 17:55:55 +0100 Subject: [PATCH 2/4] Add specialized copy methods to IncOptions class. Add methods that allow one to set a new value to one of the fields of IncOptions class. These methods are meant to be an alternative to copy method that is hard to keep binary compatible when new fields are added to the class. Each copying method is related to one field of the class so when new fields are added existing methods (and their signatures) are unaffected. --- .../src/main/scala/sbt/inc/IncOptions.scala | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/compile/inc/src/main/scala/sbt/inc/IncOptions.scala b/compile/inc/src/main/scala/sbt/inc/IncOptions.scala index c5ebc649a..800eae4b3 100644 --- a/compile/inc/src/main/scala/sbt/inc/IncOptions.scala +++ b/compile/inc/src/main/scala/sbt/inc/IncOptions.scala @@ -49,8 +49,43 @@ final class IncOptions( val newClassfileManager: () => ClassfileManager ) extends Product with Serializable { + def withTransitiveStep(transitiveStep: Int): IncOptions = { + new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, + apiDumpDirectory, newClassfileManager) + } + + def withRecompileAllFraction(recompileAllFraction: Double): IncOptions = { + new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, + apiDumpDirectory, newClassfileManager) + } + + def withRelationsDebug(relationsDebug: Boolean): IncOptions = { + new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, + apiDumpDirectory, newClassfileManager) + } + + def withApiDebug(apiDebug: Boolean): IncOptions = { + new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, + apiDumpDirectory, newClassfileManager) + } + + def withApiDiffContextSize(apiDiffContextSize: Int): IncOptions = { + new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, + apiDumpDirectory, newClassfileManager) + } + + def withApiDumpDirectory(apiDumpDirectory: Option[File]): IncOptions = { + new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, + apiDumpDirectory, newClassfileManager) + } + + def withNewClassfileManager(newClassfileManager: () => ClassfileManager): IncOptions = { + new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, + apiDumpDirectory, newClassfileManager) + } + //- EXPANDED CASE CLASS METHOD BEGIN -// - @deprecated("Methods generated for case class will be removed in the future.", "0.13.2") + @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, From 39036e7c2097c5597df5e66a9d4923dd5154a510 Mon Sep 17 00:00:00 2001 From: Grzegorz Kossakowski Date: Tue, 12 Nov 2013 14:04:51 +0100 Subject: [PATCH 3/4] 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. --- .../src/main/scala/sbt/inc/IncOptions.scala | 65 ++++++++++++++----- .../src/main/scala/sbt/inc/Incremental.scala | 6 +- 2 files changed, 53 insertions(+), 18 deletions(-) diff --git a/compile/inc/src/main/scala/sbt/inc/IncOptions.scala b/compile/inc/src/main/scala/sbt/inc/IncOptions.scala index 800eae4b3..0441eb6c5 100644 --- a/compile/inc/src/main/scala/sbt/inc/IncOptions.scala +++ b/compile/inc/src/main/scala/sbt/inc/IncOptions.scala @@ -46,42 +46,62 @@ final class IncOptions( */ val apiDumpDirectory: Option[java.io.File], /** 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 { + /** + * 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) + apiDumpDirectory, newClassfileManager, recompileOnMacroDef) } def withRecompileAllFraction(recompileAllFraction: Double): IncOptions = { new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, - apiDumpDirectory, newClassfileManager) + apiDumpDirectory, newClassfileManager, recompileOnMacroDef) } def withRelationsDebug(relationsDebug: Boolean): IncOptions = { new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, - apiDumpDirectory, newClassfileManager) + apiDumpDirectory, newClassfileManager, recompileOnMacroDef) } def withApiDebug(apiDebug: Boolean): IncOptions = { new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, - apiDumpDirectory, newClassfileManager) + apiDumpDirectory, newClassfileManager, recompileOnMacroDef) } def withApiDiffContextSize(apiDiffContextSize: Int): IncOptions = { new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, - apiDumpDirectory, newClassfileManager) + apiDumpDirectory, newClassfileManager, recompileOnMacroDef) } def withApiDumpDirectory(apiDumpDirectory: Option[File]): IncOptions = { new IncOptions(transitiveStep, recompileAllFraction, relationsDebug, apiDebug, apiDiffContextSize, - apiDumpDirectory, newClassfileManager) + apiDumpDirectory, newClassfileManager, recompileOnMacroDef) } def withNewClassfileManager(newClassfileManager: () => ClassfileManager): IncOptions = { 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 -// @@ -147,7 +167,8 @@ final class IncOptions( } object IncOptions extends Serializable { - val Default = new IncOptions( + 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). // 4. further changes invalidate all dependencies transitively to avoid too many steps @@ -157,17 +178,25 @@ object IncOptions extends Serializable { 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) } - @deprecated("Methods generated for case class will be removed in the future.", "0.13.2") + 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)]( @@ -187,7 +216,8 @@ object IncOptions extends Serializable { val relationsDebugKey = "relationsDebug" val apiDebugKey = "apiDebug" val apiDumpDirectoryKey = "apiDumpDirectory" - val apiDiffContextSize = "apiDiffContextSize" + val apiDiffContextSizeKey = "apiDiffContextSize" + 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 @@ -208,7 +238,7 @@ object IncOptions extends Serializable { 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] = { @@ -217,9 +247,13 @@ object IncOptions extends Serializable { 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 + } new IncOptions(getTransitiveStep, getRecompileAllFraction, getRelationsDebug, getApiDebug, getApiDiffContextSize, - getApiDumpDirectory, ClassfileManager.deleteImmediately) + getApiDumpDirectory, ClassfileManager.deleteImmediately, getRecompileOnMacroDef) } 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(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 } } diff --git a/compile/inc/src/main/scala/sbt/inc/Incremental.scala b/compile/inc/src/main/scala/sbt/inc/Incremental.scala index 321a0b1c9..74ad517ac 100644 --- a/compile/inc/src/main/scala/sbt/inc/Incremental.scala +++ b/compile/inc/src/main/scala/sbt/inc/Incremental.scala @@ -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) } From 698e24da114010940b9b8bb8102d5da806e3d228 Mon Sep 17 00:00:00 2001 From: Grzegorz Kossakowski Date: Tue, 12 Nov 2013 21:39:18 +0100 Subject: [PATCH 4/4] Mark fields storing keys in IncOptions as private. It was an omission in the original commit that introduced them and didn't mark them as private. They are purely an implementation detail and should be hidden. We hiding them now. --- .../inc/src/main/scala/sbt/inc/IncOptions.scala | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/compile/inc/src/main/scala/sbt/inc/IncOptions.scala b/compile/inc/src/main/scala/sbt/inc/IncOptions.scala index 0441eb6c5..7077d2291 100644 --- a/compile/inc/src/main/scala/sbt/inc/IncOptions.scala +++ b/compile/inc/src/main/scala/sbt/inc/IncOptions.scala @@ -211,13 +211,13 @@ object IncOptions extends Serializable { 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 apiDiffContextSizeKey = "apiDiffContextSize" - val recompileOnMacroDefKey = "recompileOnMacroDefKey" + 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