Add logic for skylark options to all BuildOptions mechanics (construction, diff-ing, reconstruction, serialization, etc). Get the new skylark information from an OptionsProvider (the same way native options are retrieved). Rename the <code>getOptions()</code> method in BuildOptions to <code>getNativeOptions</code> to better reflect this new information being stored in BuildOptions.
PiperOrigin-RevId: 209841664
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfiguration.java b/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfiguration.java
index 71d28c8..3f30da4 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfiguration.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfiguration.java
@@ -1096,7 +1096,7 @@
// "bazel-out/android-arm-linux-fastbuild/bin". That's pretty awkward to check here.
// && outputRoots.equals(other.outputRoots)
&& fragments.values().containsAll(other.fragments.values())
- && buildOptions.getOptions().containsAll(other.buildOptions.getOptions()));
+ && buildOptions.getNativeOptions().containsAll(other.buildOptions.getNativeOptions()));
}
/**
@@ -1117,7 +1117,7 @@
}
private int computeHashCode() {
- return Objects.hash(fragments, buildOptions.getOptions());
+ return Objects.hash(fragments, buildOptions.getNativeOptions());
}
public void describe(StringBuilder sb) {
@@ -1366,7 +1366,7 @@
}
return TransitiveOptionDetails.forOptionsWithDefaults(
- buildOptions.getOptions(), lateBoundDefaults);
+ buildOptions.getNativeOptions(), lateBoundDefaults);
}
private String buildMnemonic() {
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/config/BuildOptions.java b/src/main/java/com/google/devtools/build/lib/analysis/config/BuildOptions.java
index 33dd455..6efa6ac 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/config/BuildOptions.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/config/BuildOptions.java
@@ -24,6 +24,7 @@
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Multimap;
+import com.google.common.collect.Ordering;
import com.google.common.collect.Sets;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.InvocationPolicy;
@@ -70,6 +71,7 @@
public final class BuildOptions implements Cloneable, Serializable {
private static final Comparator<Class<? extends FragmentOptions>>
lexicalFragmentOptionsComparator = Comparator.comparing(Class::getName);
+ private static final Comparator<String> skylarkOptionsComparator = Ordering.natural();
private static final Logger logger = Logger.getLogger(BuildOptions.class.getName());
/**
@@ -115,7 +117,7 @@
builder.add(options);
}
}
- return builder.build();
+ return builder.addSkylarkOptions(skylarkOptionsMap).build();
}
/**
@@ -128,10 +130,13 @@
for (Class<? extends FragmentOptions> optionsClass : optionsList) {
builder.add(provider.getOptions(optionsClass));
}
- return builder.build();
+ return builder.addSkylarkOptions(provider.getSkylarkOptions()).build();
}
- /** Creates a BuildOptions class by taking the option values from command-line arguments. */
+ /**
+ * Creates a BuildOptions class by taking the option values from command-line arguments. Returns a
+ * BuildOptions class that only has native options.
+ */
@VisibleForTesting
public static BuildOptions of(List<Class<? extends FragmentOptions>> optionsList, String... args)
throws OptionsParsingException {
@@ -146,6 +151,14 @@
return builder.build();
}
+ /*
+ * Returns a BuildOptions class that only has skylark options.
+ */
+ @VisibleForTesting
+ public static BuildOptions of(Map<String, Object> skylarkOptions) {
+ return builder().addSkylarkOptions(skylarkOptions).build();
+ }
+
/** Returns the actual instance of a FragmentOptions class. */
public <T extends FragmentOptions> T get(Class<T> optionsClass) {
FragmentOptions options = fragmentOptionsMap.get(optionsClass);
@@ -185,6 +198,7 @@
for (FragmentOptions options : fragmentOptionsMap.values()) {
keyBuilder.append(options.cacheKey());
}
+ keyBuilder.append(OptionsBase.mapToCacheKey(skylarkOptionsMap));
return keyBuilder.toString();
}
@@ -203,20 +217,29 @@
}
/** Returns the options contained in this collection. */
- public Collection<FragmentOptions> getOptions() {
+ public Collection<FragmentOptions> getNativeOptions() {
return fragmentOptionsMap.values();
}
- /** Creates a copy of the BuildOptions object that contains copies of the FragmentOptions. */
+ public ImmutableMap<String, Object> getSkylarkOptions() {
+ return skylarkOptionsMap;
+ }
+
+ /**
+ * Creates a copy of the BuildOptions object that contains copies of the FragmentOptions and
+ * skylark options.
+ */
@Override
public BuildOptions clone() {
- ImmutableMap.Builder<Class<? extends FragmentOptions>, FragmentOptions> builder =
+ ImmutableMap.Builder<Class<? extends FragmentOptions>, FragmentOptions> nativeOptionsBuilder =
ImmutableMap.builder();
for (Map.Entry<Class<? extends FragmentOptions>, FragmentOptions> entry :
fragmentOptionsMap.entrySet()) {
- builder.put(entry.getKey(), entry.getValue().clone());
+ nativeOptionsBuilder.put(entry.getKey(), entry.getValue().clone());
}
- return new BuildOptions(builder.build());
+ return new BuildOptions(
+ nativeOptionsBuilder.build(),
+ ImmutableMap.copyOf(skylarkOptionsMap));
}
/**
@@ -242,6 +265,10 @@
fingerprint.addString(entry.getKey().getName());
fingerprint.addString(entry.getValue().cacheKey());
}
+ for (Map.Entry<String, Object> entry : skylarkOptionsMap.entrySet()) {
+ fingerprint.addString(entry.getKey());
+ fingerprint.addString(entry.getValue().toString());
+ }
byte[] computedFingerprint = fingerprint.digestAndReset();
hashCode = Arrays.hashCode(computedFingerprint);
this.fingerprint = computedFingerprint;
@@ -274,10 +301,15 @@
/** Maps options class definitions to FragmentOptions objects. */
private final ImmutableMap<Class<? extends FragmentOptions>, FragmentOptions> fragmentOptionsMap;
+ /** Maps skylark options names to skylark options values. */
+ private final ImmutableMap<String, Object> skylarkOptionsMap;
@AutoCodec.VisibleForSerialization
- BuildOptions(ImmutableMap<Class<? extends FragmentOptions>, FragmentOptions> fragmentOptionsMap) {
+ BuildOptions(
+ ImmutableMap<Class<? extends FragmentOptions>, FragmentOptions> fragmentOptionsMap,
+ ImmutableMap<String, Object> skylarkOptionsMap) {
this.fragmentOptionsMap = fragmentOptionsMap;
+ this.skylarkOptionsMap = skylarkOptionsMap;
}
public BuildOptions applyDiff(OptionsDiffForReconstruction optionsDiff) {
@@ -298,6 +330,20 @@
for (FragmentOptions extraSecondFragment : optionsDiff.extraSecondFragments) {
builder.add(extraSecondFragment);
}
+
+ Map<String, Object> skylarkOptions = new HashMap<>();
+ for (Map.Entry<String, Object> buildSettingAndValue : skylarkOptionsMap.entrySet()) {
+ String buildSetting = buildSettingAndValue.getKey();
+ if (optionsDiff.extraFirstSkylarkOptions.contains(buildSetting)) {
+ continue;
+ } else if (optionsDiff.differingSkylarkOptions.containsKey(buildSetting)) {
+ skylarkOptions.put(buildSetting, optionsDiff.differingSkylarkOptions.get(buildSetting));
+ } else {
+ skylarkOptions.put(buildSetting, skylarkOptionsMap.get(buildSetting));
+ }
+ }
+ skylarkOptions.putAll(optionsDiff.extraSecondSkylarkOptions);
+ builder.addSkylarkOptions(skylarkOptions);
return builder.build();
}
@@ -317,15 +363,23 @@
return this;
}
+ Builder addSkylarkOptions(Map<String, Object> options) {
+ builderSkylarkOptionsMap.putAll(options);
+ return this;
+ }
+
public BuildOptions build() {
return new BuildOptions(
- ImmutableSortedMap.copyOf(builderMap, lexicalFragmentOptionsComparator));
+ ImmutableSortedMap.copyOf(builderMap, lexicalFragmentOptionsComparator),
+ ImmutableSortedMap.copyOf(builderSkylarkOptionsMap, skylarkOptionsComparator));
}
private Map<Class<? extends FragmentOptions>, FragmentOptions> builderMap;
+ private Map<String, Object> builderSkylarkOptionsMap;
private Builder() {
builderMap = new HashMap<>();
+ builderSkylarkOptionsMap = new HashMap<>();
}
}
@@ -347,6 +401,11 @@
*/
public static OptionsDiff diff(
OptionsDiff diff, @Nullable BuildOptions first, @Nullable BuildOptions second) {
+ if (diff.hasSkylarkOptions) {
+ throw new IllegalStateException(
+ "OptionsDiff cannot handle multiple 'second' BuildOptions with skylark options "
+ + "and is trying to diff against a second BuildOptions with skylark options.");
+ }
if (first == null || second == null) {
throw new IllegalArgumentException("Cannot diff null BuildOptions");
}
@@ -357,13 +416,13 @@
// other.
ImmutableSet<Class<? extends FragmentOptions>> firstOptionClasses =
first
- .getOptions()
+ .getNativeOptions()
.stream()
.map(FragmentOptions::getClass)
.collect(ImmutableSet.toImmutableSet());
ImmutableSet<Class<? extends FragmentOptions>> secondOptionClasses =
second
- .getOptions()
+ .getNativeOptions()
.stream()
.map(FragmentOptions::getClass)
.collect(ImmutableSet.toImmutableSet());
@@ -389,6 +448,21 @@
}
}
}
+
+ // Compare skylark options for the two classes
+ Map<String, Object> skylarkFirst = first.getSkylarkOptions();
+ Map<String, Object> skylarkSecond = second.getSkylarkOptions();
+ diff.setHasSkylarkOptions(!skylarkFirst.isEmpty() || !skylarkSecond.isEmpty());
+ for (String buildSetting : Sets.union(skylarkFirst.keySet(), skylarkSecond.keySet())) {
+ if (skylarkFirst.get(buildSetting) == null) {
+ diff.addExtraSecondSkylarkOption(buildSetting, skylarkSecond.get(buildSetting));
+ } else if (skylarkSecond.get(buildSetting) == null) {
+ diff.addExtraFirstSkylarkOption(buildSetting);
+ } else if (!skylarkFirst.get(buildSetting).equals(skylarkSecond.get(buildSetting))) {
+ diff.putSkylarkDiff(
+ buildSetting, skylarkFirst.get(buildSetting), skylarkSecond.get(buildSetting));
+ }
+ }
return diff;
}
@@ -444,7 +518,10 @@
.sorted(Comparator.comparing(o -> o.getClass().getName()))
.collect(ImmutableList.toImmutableList()),
first.fingerprint,
- second.computeChecksum());
+ second.computeChecksum(),
+ diff.skylarkSecond,
+ diff.extraSkylarkOptionsFirst,
+ diff.extraSkylarkOptionsSecond);
}
/**
@@ -465,13 +542,15 @@
private final Set<Class<? extends FragmentOptions>> extraFirstFragments = new HashSet<>();
private final Set<FragmentOptions> extraSecondFragments = new HashSet<>();
- private void addExtraFirstFragment(Class<? extends FragmentOptions> options) {
- extraFirstFragments.add(options);
- }
+ private final Map<String, Object> skylarkFirst = new LinkedHashMap<>();
+ // TODO(b/112041323): This should also be multimap but we don't diff multiple times with
+ // skylark options anywhere yet so add that feature when necessary.
+ private final Map<String, Object> skylarkSecond = new LinkedHashMap<>();
- private void addExtraSecondFragment(FragmentOptions options) {
- extraSecondFragments.add(options);
- }
+ private final List<String> extraSkylarkOptionsFirst = new ArrayList<>();
+ private final Map<String, Object> extraSkylarkOptionsSecond = new HashMap<>();
+
+ private boolean hasSkylarkOptions = false;
@VisibleForTesting
Set<Class<? extends FragmentOptions>> getExtraFirstFragmentClassesForTesting() {
@@ -501,6 +580,51 @@
second.put(option, secondValue);
}
+ private void addExtraFirstFragment(Class<? extends FragmentOptions> options) {
+ extraFirstFragments.add(options);
+ }
+
+ private void addExtraSecondFragment(FragmentOptions options) {
+ extraSecondFragments.add(options);
+ }
+
+ private void putSkylarkDiff(String buildSetting, Object firstValue, Object secondValue) {
+ skylarkFirst.put(buildSetting, firstValue);
+ skylarkSecond.put(buildSetting, secondValue);
+ }
+
+ private void addExtraFirstSkylarkOption(String buildSetting) {
+ extraSkylarkOptionsFirst.add(buildSetting);
+ }
+
+ private void addExtraSecondSkylarkOption(String buildSetting, Object value) {
+ extraSkylarkOptionsSecond.put(buildSetting, value);
+ }
+
+ private void setHasSkylarkOptions(boolean hasSkylarkOptions) {
+ this.hasSkylarkOptions = hasSkylarkOptions;
+ }
+
+ @VisibleForTesting
+ Map<String, Object> getSkylarkFirstForTesting() {
+ return skylarkFirst;
+ }
+
+ @VisibleForTesting
+ Map<String, Object> getSkylarkSecondForTesting() {
+ return skylarkSecond;
+ }
+
+ @VisibleForTesting
+ List<String> getExtraSkylarkOptionsFirstForTesting() {
+ return extraSkylarkOptionsFirst;
+ }
+
+ @VisibleForTesting
+ Map<String, Object> getExtraSkylarkOptionsSecondForTesting() {
+ return extraSkylarkOptionsSecond;
+ }
+
/**
* Note: it's not enough for first and second to be empty, with trimming, they must also contain
* the same options classes.
@@ -510,7 +634,11 @@
&& second.isEmpty()
&& extraSecondFragments.isEmpty()
&& extraFirstFragments.isEmpty()
- && differingOptions.isEmpty();
+ && differingOptions.isEmpty()
+ && skylarkFirst.isEmpty()
+ && skylarkSecond.isEmpty()
+ && extraSkylarkOptionsFirst.isEmpty()
+ && extraSkylarkOptionsSecond.isEmpty();
}
public String prettyPrint() {
@@ -526,6 +654,8 @@
first.forEach(
(option, value) ->
toReturn.add(option.getOptionName() + ":" + value + " -> " + second.get(option)));
+ skylarkFirst.forEach(
+ (option, value) -> toReturn.add(option + ":" + value + skylarkSecond.get(option)));
return toReturn;
}
}
@@ -535,29 +665,46 @@
* another: the full fragments of the second one, the fragment classes of the first that should be
* omitted, and the values of any fields that should be changed.
*/
- public static class OptionsDiffForReconstruction {
+ public static final class OptionsDiffForReconstruction {
private final Map<Class<? extends FragmentOptions>, Map<String, Object>> differingOptions;
private final ImmutableSet<Class<? extends FragmentOptions>> extraFirstFragmentClasses;
private final ImmutableList<FragmentOptions> extraSecondFragments;
private final byte[] baseFingerprint;
private final String checksum;
+ private final Map<String, Object> differingSkylarkOptions;
+ private final List<String> extraFirstSkylarkOptions;
+ private final Map<String, Object> extraSecondSkylarkOptions;
+
OptionsDiffForReconstruction(
Map<Class<? extends FragmentOptions>, Map<String, Object>> differingOptions,
ImmutableSet<Class<? extends FragmentOptions>> extraFirstFragmentClasses,
ImmutableList<FragmentOptions> extraSecondFragments,
byte[] baseFingerprint,
- String checksum) {
+ String checksum,
+ Map<String, Object> differingSkylarkOptions,
+ List<String> extraFirstSkylarkOptions,
+ Map<String, Object> extraSecondSkylarkOptions) {
this.differingOptions = differingOptions;
this.extraFirstFragmentClasses = extraFirstFragmentClasses;
this.extraSecondFragments = extraSecondFragments;
this.baseFingerprint = baseFingerprint;
this.checksum = checksum;
+ this.differingSkylarkOptions = differingSkylarkOptions;
+ this.extraFirstSkylarkOptions = extraFirstSkylarkOptions;
+ this.extraSecondSkylarkOptions = extraSecondSkylarkOptions;
}
private static OptionsDiffForReconstruction getEmpty(byte[] baseFingerprint, String checksum) {
return new OptionsDiffForReconstruction(
- ImmutableMap.of(), ImmutableSet.of(), ImmutableList.of(), baseFingerprint, checksum);
+ ImmutableMap.of(),
+ ImmutableSet.of(),
+ ImmutableList.of(),
+ baseFingerprint,
+ checksum,
+ ImmutableMap.of(),
+ ImmutableList.of(),
+ ImmutableMap.of());
}
@Nullable
@@ -589,7 +736,10 @@
private boolean isEmpty() {
return differingOptions.isEmpty()
&& extraFirstFragmentClasses.isEmpty()
- && extraSecondFragments.isEmpty();
+ && extraSecondFragments.isEmpty()
+ && differingSkylarkOptions.isEmpty()
+ && extraFirstSkylarkOptions.isEmpty()
+ && extraSecondSkylarkOptions.isEmpty();
}
@Override
@@ -611,6 +761,9 @@
.add("differingOptions", differingOptions)
.add("extraFirstFragmentClasses", extraFirstFragmentClasses)
.add("extraSecondFragments", extraSecondFragments)
+ .add("differingSkylarkOptions", differingSkylarkOptions)
+ .add("extraFirstSkylarkOptions", extraFirstSkylarkOptions)
+ .add("extraSecondSkylarkOptions", extraSecondSkylarkOptions)
.toString();
}
@@ -643,6 +796,9 @@
context.serialize(diff.extraSecondFragments, bytesOut);
bytesOut.writeByteArrayNoTag(diff.baseFingerprint);
context.serialize(diff.checksum, bytesOut);
+ context.serialize(diff.differingSkylarkOptions, bytesOut);
+ context.serialize(diff.extraFirstSkylarkOptions, bytesOut);
+ context.serialize(diff.extraSecondSkylarkOptions, bytesOut);
bytesOut.flush();
byteStringOut.flush();
int optionsDiffSize = byteStringOut.size();
@@ -675,13 +831,19 @@
ImmutableList<FragmentOptions> extraSecondFragments = context.deserialize(codedInput);
byte[] baseFingerprint = codedInput.readByteArray();
String checksum = context.deserialize(codedInput);
+ Map<String, Object> differingSkylarkOptions = context.deserialize(codedInput);
+ List<String> extraFirstSkylarkOptions = context.deserialize(codedInput);
+ Map<String, Object> extraSecondSkylarkOptions = context.deserialize(codedInput);
diff =
new OptionsDiffForReconstruction(
differingOptions,
extraFirstFragmentClasses,
extraSecondFragments,
baseFingerprint,
- checksum);
+ checksum,
+ differingSkylarkOptions,
+ extraFirstSkylarkOptions,
+ extraSecondSkylarkOptions);
cache.putBytesFromOptionsDiff(diff, bytes);
}
return diff;
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/test/TestTrimmingTransitionFactory.java b/src/main/java/com/google/devtools/build/lib/analysis/test/TestTrimmingTransitionFactory.java
index ed8f625..5a35c16 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/test/TestTrimmingTransitionFactory.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/test/TestTrimmingTransitionFactory.java
@@ -52,7 +52,7 @@
return originalOptions;
}
BuildOptions.Builder builder = BuildOptions.builder();
- for (FragmentOptions options : originalOptions.getOptions()) {
+ for (FragmentOptions options : originalOptions.getNativeOptions()) {
if (!(options instanceof TestOptions)) {
builder.add(options);
}
diff --git a/src/main/java/com/google/devtools/common/options/OptionsBase.java b/src/main/java/com/google/devtools/common/options/OptionsBase.java
index 9496c65..ce66e66 100644
--- a/src/main/java/com/google/devtools/common/options/OptionsBase.java
+++ b/src/main/java/com/google/devtools/common/options/OptionsBase.java
@@ -91,8 +91,13 @@
*/
public final String cacheKey() {
StringBuilder result = new StringBuilder(getClass().getName()).append("{");
+ result.append(mapToCacheKey(asMap()));
+ return result.append("}").toString();
+ }
- for (Map.Entry<String, Object> entry : asMap().entrySet()) {
+ public static String mapToCacheKey(Map<String, Object> optionsMap) {
+ StringBuilder result = new StringBuilder();
+ for (Map.Entry<String, Object> entry : optionsMap.entrySet()) {
result.append(entry.getKey()).append("=");
Object value = entry.getValue();
@@ -110,15 +115,12 @@
}
result.append(", ");
}
-
- return result.append("}").toString();
+ return result.toString();
}
@Override
public final boolean equals(Object that) {
- return that != null &&
- this.getClass() == that.getClass() &&
- this.asMap().equals(((OptionsBase) that).asMap());
+ return that instanceof OptionsBase && this.asMap().equals(((OptionsBase) that).asMap());
}
@Override
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/config/BuildOptionsTest.java b/src/test/java/com/google/devtools/build/lib/analysis/config/BuildOptionsTest.java
index 099ded0..e897c11 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/config/BuildOptionsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/config/BuildOptionsTest.java
@@ -18,6 +18,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
import com.google.devtools.build.lib.analysis.config.BuildOptions.OptionsDiff;
import com.google.devtools.build.lib.analysis.config.BuildOptions.OptionsDiffForReconstruction;
import com.google.devtools.build.lib.rules.android.AndroidConfiguration;
@@ -34,6 +35,11 @@
/**
* A test for {@link BuildOptions}.
+ *
+ * Currently this tests native options and skylark options completely
+ * separately since these two types of options do not interact. In the future when we begin to
+ * migrate native options to skylark options, the format of this test class will need to accommodate
+ * that overlap.
*/
@RunWith(JUnit4.class)
public class BuildOptionsTest {
@@ -163,7 +169,7 @@
}
@Test
- public void applyDiff() throws Exception {
+ public void applyDiff_nativeOptions() throws Exception {
BuildOptions one = BuildOptions.of(TEST_OPTIONS, "--compilation_mode=opt", "cpu=k8");
BuildOptions two = BuildOptions.of(TEST_OPTIONS, "--compilation_mode=dbg", "cpu=k8");
BuildOptions reconstructedTwo = one.applyDiff(BuildOptions.diffForReconstruction(one, two));
@@ -178,6 +184,95 @@
.isEqualTo(one);
}
+ @Test
+ public void optionsDiff_sameSkylarkOptions() throws Exception {
+ String flagName = "//foo/flag";
+ String flagValue = "value";
+ BuildOptions one = BuildOptions.of(ImmutableMap.of(flagName, flagValue));
+ BuildOptions two = BuildOptions.of(ImmutableMap.of(flagName, flagValue));
+
+ assertThat(BuildOptions.diff(one, two).areSame()).isTrue();
+ }
+
+ @Test
+ public void optionsDiff_differentSkylarkOptions() throws Exception {
+ String flagName = "//bar/flag";
+ String flagValueOne = "valueOne";
+ String flagValueTwo = "valueTwo";
+ BuildOptions one = BuildOptions.of(ImmutableMap.of(flagName, flagValueOne));
+ BuildOptions two = BuildOptions.of(ImmutableMap.of(flagName, flagValueTwo));
+
+ OptionsDiff diff = BuildOptions.diff(one, two);
+
+ assertThat(diff.areSame()).isFalse();
+ assertThat(diff.getSkylarkFirstForTesting().keySet())
+ .isEqualTo(diff.getSkylarkSecondForTesting().keySet());
+ assertThat(diff.getSkylarkFirstForTesting().keySet()).containsExactly(flagName);
+ assertThat(diff.getSkylarkFirstForTesting().values()).containsExactly(flagValueOne);
+ assertThat(diff.getSkylarkSecondForTesting().values()).containsExactly(flagValueTwo);
+ }
+
+ @Test
+ public void optionsDiff_extraSkylarkOptions() throws Exception {
+ String flagNameOne = "//extra/flag/one";
+ String flagNameTwo = "//extra/flag/two";
+ String flagValue = "foo";
+ BuildOptions one = BuildOptions.of(ImmutableMap.of(flagNameOne, flagValue));
+ BuildOptions two = BuildOptions.of(ImmutableMap.of(flagNameTwo, flagValue));
+
+ OptionsDiff diff = BuildOptions.diff(one, two);
+
+ assertThat(diff.areSame()).isFalse();
+ assertThat(diff.getExtraSkylarkOptionsFirstForTesting()).containsExactly(flagNameOne);
+ assertThat(diff.getExtraSkylarkOptionsSecondForTesting().entrySet())
+ .containsExactly(Maps.immutableEntry(flagNameTwo, flagValue));
+ }
+
+ @Test
+ public void applyDiff_sameSkylarkOptions() throws Exception {
+ String flagName = "//foo/flag";
+ String flagValue = "value";
+ BuildOptions one = BuildOptions.of(ImmutableMap.of(flagName, flagValue));
+ BuildOptions two = BuildOptions.of(ImmutableMap.of(flagName, flagValue));
+
+ BuildOptions reconstructedTwo = one.applyDiff(BuildOptions.diffForReconstruction(one, two));
+
+ assertThat(reconstructedTwo).isEqualTo(two);
+ assertThat(reconstructedTwo).isNotSameAs(two);
+
+ BuildOptions reconstructedOne = one.applyDiff(BuildOptions.diffForReconstruction(one, one));
+
+ assertThat(reconstructedOne).isSameAs(one);
+ }
+
+ @Test
+ public void applyDiff_differentSkylarkOptions() throws Exception {
+ String flagName = "//bar/flag";
+ String flagValueOne = "valueOne";
+ String flagValueTwo = "valueTwo";
+ BuildOptions one = BuildOptions.of(ImmutableMap.of(flagName, flagValueOne));
+ BuildOptions two = BuildOptions.of(ImmutableMap.of(flagName, flagValueTwo));
+
+ BuildOptions reconstructedTwo = one.applyDiff(BuildOptions.diffForReconstruction(one, two));
+
+ assertThat(reconstructedTwo).isEqualTo(two);
+ assertThat(reconstructedTwo).isNotSameAs(two);
+ }
+
+ @Test
+ public void applyDiff_extraSkylarkOptions() throws Exception {
+ String flagNameOne = "//extra/flag/one";
+ String flagNameTwo = "//extra/flag/two";
+ String flagValue = "foo";
+ BuildOptions one = BuildOptions.of(ImmutableMap.of(flagNameOne, flagValue));
+ BuildOptions two = BuildOptions.of(ImmutableMap.of(flagNameTwo, flagValue));
+
+ BuildOptions reconstructedTwo = one.applyDiff(BuildOptions.diffForReconstruction(one, two));
+
+ assertThat(reconstructedTwo).isEqualTo(two);
+ assertThat(reconstructedTwo).isNotSameAs(two);
+ }
+
private static ImmutableList.Builder<Class<? extends FragmentOptions>> makeOptionsClassBuilder() {
return ImmutableList.<Class<? extends FragmentOptions>>builder()
.addAll(TEST_OPTIONS)