Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to Ignore scalac options change #548

Merged
merged 9 commits into from Jun 7, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions internal/compiler-interface/src/main/contraband/incremental.json
Expand Up @@ -177,6 +177,13 @@
"default": "xsbti.compile.IncOptions.defaultExternal()",
"doc": "External hooks that allows clients e.g. IDEs to interacts with compilation internals",
"since": "0.1.0"
},
{
"name": "ignoredScalacOptions",
"type": "String[]",
"default": "xsbti.compile.IncOptions.defaultIgnoredScalacOptions()",
"doc": "Array of regexes that will be used to determine if scalac options should be ignored if they change",
"since": "1.3.0"
}
],
"extra": [
Expand Down Expand Up @@ -232,6 +239,9 @@
"public static ExternalHooks defaultExternal() {",
" return new DefaultExternalHooks(java.util.Optional.empty(), java.util.Optional.empty());",
"}",
"public static String[] defaultIgnoredScalacOptions() {",
" return new String[0];",
"}",
"public static boolean defaultLogRecompileOnMacro() {",
" return true;",
"}"
Expand Down
Expand Up @@ -34,6 +34,7 @@ public class IncOptionsUtil {
public static final String USE_OPTIMIZED_SEALED = "useOptimizedSealed";
public static final String LOG_RECOMPILE_ON_MACRO = "logRecompileOnMacro";
public static final String CLASSFILE_MANAGER_TYPE_KEY = "classfileManagerType";
public static final String IGNORED_SCALAC_OPTIONS_KEY = "ignoredScalacOptions";
public static final String TRANSACTIONAL_MANAGER_TYPE = "transactionalManagerType";
public static final String TRANSACTIONAL_MANAGER_BASE_DIRECTORY = "transactionalManagerBaseDirectory";
public static final String DELETE_IMMEDIATELY_MANAGER_TYPE = "deleteImmediatelyManagerType";
Expand Down Expand Up @@ -139,6 +140,11 @@ public static IncOptions fromStringMap(Map<String, String> values, Logger logger
base = base.withUseOptimizedSealed(Boolean.parseBoolean(values.get(USE_OPTIMIZED_SEALED)));
}

if (values.containsKey(IGNORED_SCALAC_OPTIONS_KEY)) {
logger.debug(f0("IGNORED_SCALAC_OPTIONS_KEY value was read."));
base = base.withIgnoredScalacOptions(values.get(IGNORED_SCALAC_OPTIONS_KEY).split(" +"));
}

return base;
}
}
Expand Up @@ -36,18 +36,14 @@ object MiniSetupUtil {
/* *********************************************************************** */

/* Define first because `Equiv[CompileOrder.value]` dominates `Equiv[MiniSetup]`. */
implicit def equivCompileSetup(
implicit def equivCompileSetup(equivOpts: Equiv[MiniOptions])(
implicit equivOutput: Equiv[APIOutput],
equivOpts: Equiv[MiniOptions],
equivComp: Equiv[String]
): Equiv[MiniSetup] = {
new Equiv[MiniSetup] {
def equiv(a: MiniSetup, b: MiniSetup) = {
/* Hard-code these to use the `Equiv` defined in this class. For
* some reason, `Equiv[Nothing]` or an equivalent is getting injected
* into here now, and it's borking all our results. This fixes it. */
def sameOutput = MiniSetupUtil.equivOutput.equiv(a.output, b.output)
def sameOptions = MiniSetupUtil.equivOpts.equiv(a.options, b.options)
def sameOutput = equivOutput.equiv(a.output, b.output)
def sameOptions = equivOpts.equiv(a.options, b.options)
def sameCompiler = equivComp.equiv(a.compilerVersion, b.compilerVersion)
def sameOrder = a.order == b.order
def sameExtra = equivPairs.equiv(a.extra, b.extra)
Expand Down Expand Up @@ -107,10 +103,13 @@ object MiniSetupUtil {
}
}

implicit val equivOpts: Equiv[MiniOptions] = {
// for compatibility
val equivOpts: Equiv[MiniOptions] = equivOpts0(Equiv.fromFunction(_ sameElements _))

def equivOpts0(equivScalacOpts: Equiv[Array[String]]): Equiv[MiniOptions] = {
new Equiv[MiniOptions] {
def equiv(a: MiniOptions, b: MiniOptions) = {
(a.scalacOptions sameElements b.scalacOptions) &&
equivScalacOpts.equiv(a.scalacOptions, b.scalacOptions) &&
(a.javacOptions sameElements b.javacOptions)
}
}
Expand All @@ -127,4 +126,25 @@ object MiniSetupUtil {
def equiv(a: CompileOrder, b: CompileOrder) = a == b
}
}

def equivScalacOptions(ignoredRegexes: Array[String]): Equiv[Array[String]] = {
def groupWithParams(opts: Array[String]): Set[String] = {
def isParam(s: String) = !s.startsWith("-")
def recur(opts: List[String], res: Set[String]): Set[String] = opts match {
case opt :: param :: rest if isParam(param) => recur(rest, res + s"$opt $param")
case opt :: rest => recur(rest, res + opt)
case Nil => res
}
recur(opts.toList, Set.empty)
}
val ignoreRegex = ignoredRegexes.mkString("|").r
def dropIgnored(opts: Set[String]): Set[String] =
opts.filterNot(ignoreRegex.pattern.matcher(_).matches)

new Equiv[Array[String]] {
override def equiv(opts1: Array[String], opts2: Array[String]): Boolean = {
dropIgnored(groupWithParams(opts1)) == dropIgnored(groupWithParams(opts2))
}
}
}
}
@@ -0,0 +1,61 @@
package sbt.internal.inc

import MiniSetupUtil._

class MiniSetupUtilSpec extends UnitSpec {

it should "detect options change" in {
val equiv = equivScalacOptions(ignoredRegexes = Array())

val before = Array("-deprecation")
val after = Array[String]()

equiv.equiv(before, after) shouldBe false
}

it should "ignore options list different by specfied options (exact)" in {
val equiv = equivScalacOptions(ignoredRegexes = Array("-Xprint:typer", "-Xfatal-warnings"))

val before = Array("-Xprint:typer", "-deprecation")
val after = Array("-Xfatal-warnings", "-deprecation")

equiv.equiv(before, after) shouldBe true
}

it should "use regex to ignore options" in {
val equiv = equivScalacOptions(ignoredRegexes = Array("-Xprint:.*"))

val before = Array("-Xprint:typer")
val after = Array("-Xprint:typer", "-Xprint:refcheck", "-Xprint:parse")

equiv.equiv(before, after) shouldBe true
}

it should "combine options with their parameters with space for analysis" in {
val equiv = equivScalacOptions(ignoredRegexes = Array("-opt .*"))

val before = Array("-opt", "abc")
val after = Array("-opt", "def")

equiv.equiv(before, after) shouldBe true
}

it should "detect change for options with parameters" in {
val equiv = equivScalacOptions(ignoredRegexes = Array("-opt a.*"))

val before = Array("-opt", "abc")
val after = Array("-opt", "def")

equiv.equiv(before, after) shouldBe false
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are missing test case for option ordering

it should "ignore reordering of options" in {
val equiv = equivScalacOptions(ignoredRegexes = Array())

val before = Array("-a", "1", "-b", "2", "-c")
val after = Array("-b", "2", "-c", "-a", "1")

equiv.equiv(before, after) shouldBe true
}

}
Expand Up @@ -18,6 +18,7 @@ import sbt.util.InterfaceUtil
import xsbti._
import xsbti.compile.CompileOrder.Mixed
import xsbti.compile.{ ClasspathOptions => XClasspathOptions, JavaTools => XJavaTools, _ }
import sbt.internal.inc.MiniSetupUtil._

class IncrementalCompilerImpl extends IncrementalCompiler {

Expand Down Expand Up @@ -262,8 +263,9 @@ class IncrementalCompilerImpl extends IncrementalCompiler {
else {
val (analysis, changed) = compileInternal(
MixedAnalyzingCompiler(config)(logger),
MiniSetupUtil.equivCompileSetup,
MiniSetupUtil.equivPairs,
equivCompileSetup(
equivOpts0(equivScalacOptions(incrementalOptions.ignoredScalacOptions))),
equivPairs,
logger
)
CompileResult.of(analysis, config.currentSetup, changed)
Expand Down
@@ -0,0 +1 @@
class A
@@ -0,0 +1 @@
class B
@@ -0,0 +1 @@
class C
@@ -0,0 +1,3 @@
class A {
val a = 1
}
@@ -0,0 +1,2 @@
ignoredScalacOptions = -Xfatal-warnings
scalac.options = -deprecation -Xprint:typer
@@ -0,0 +1,2 @@
ignoredScalacOptions = -Xfatal-warnings
scalac.options = -Xfatal-warnings -Xprint:typer -deprecation
7 changes: 7 additions & 0 deletions zinc/src/sbt-test/source-dependencies/scalac-options/test
@@ -0,0 +1,7 @@
> compile

$ copy-file changes/A.scala A.scala
$ copy-file changes/incOptions.properties incOptions.properties

> compile
> checkRecompilations 1 A