diff --git a/src/main/java/com/google/devtools/build/lib/analysis/BUILD b/src/main/java/com/google/devtools/build/lib/analysis/BUILD
index 64993b41dd59e5..bcd2429c2a7d6b 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/analysis/BUILD
@@ -1655,6 +1655,7 @@ java_library(
":config/fragment_registry",
":config/invalid_configuration_exception",
":config/optioninfo",
+ ":config/options_diff",
":config/run_under",
":config/starlark_defined_config_transition",
":platform_options",
@@ -1954,6 +1955,21 @@ java_library(
],
)
+java_library(
+ name = "config/options_diff",
+ srcs = [
+ "config/OptionsDiff.java",
+ ],
+ deps = [
+ ":config/build_options",
+ ":config/fragment_options",
+ "//src/main/java/com/google/devtools/build/lib/cmdline",
+ "//src/main/java/com/google/devtools/build/lib/util",
+ "//src/main/java/com/google/devtools/common/options",
+ "//third_party:guava",
+ ],
+)
+
java_library(
name = "config/per_label_options",
srcs = ["config/PerLabelOptions.java"],
@@ -2480,6 +2496,7 @@ java_library(
":config/core_options",
":config/fragment_options",
":config/optioninfo",
+ ":config/options_diff",
":config/starlark_defined_config_transition",
":config/transitions/configuration_transition",
":test/test_configuration",
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 08065c4457bb31..bdb2a826038fa0 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
@@ -14,13 +14,11 @@
package com.google.devtools.build.lib.analysis.config;
-import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.ImmutableMap.toImmutableMap;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.MoreObjects;
-import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
@@ -28,8 +26,6 @@
import com.google.common.collect.Iterables;
import com.google.common.collect.MapDifference;
import com.google.common.collect.Maps;
-import com.google.common.collect.Multimap;
-import com.google.common.collect.Sets;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.skyframe.serialization.DeserializationContext;
import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec;
@@ -37,7 +33,6 @@
import com.google.devtools.build.lib.skyframe.serialization.SerializationException;
import com.google.devtools.build.lib.skyframe.serialization.autocodec.SerializationConstant;
import com.google.devtools.build.lib.util.Fingerprint;
-import com.google.devtools.build.lib.util.OrderedSetMultimap;
import com.google.devtools.common.options.OptionDefinition;
import com.google.devtools.common.options.OptionsBase;
import com.google.devtools.common.options.OptionsParser;
@@ -49,12 +44,9 @@
import com.google.protobuf.CodedInputStream;
import com.google.protobuf.CodedOutputStream;
import java.io.IOException;
-import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -481,232 +473,6 @@ public BuildOptions build() {
private Builder() {}
}
- /** Returns the difference between two BuildOptions in a new {@link BuildOptions.OptionsDiff}. */
- public static OptionsDiff diff(BuildOptions first, BuildOptions second) {
- return diff(new OptionsDiff(), first, second);
- }
-
- /**
- * Returns the difference between two BuildOptions in a pre-existing {@link
- * BuildOptions.OptionsDiff}.
- *
- *
In a single pass through this method, the method can only compare a single "first" {@link
- * BuildOptions} and single "second" BuildOptions; but an OptionsDiff instance can store the diff
- * between a single "first" BuildOptions and multiple "second" BuildOptions. Being able to
- * maintain a single OptionsDiff over multiple calls to diff is useful for, for example,
- * aggregating the difference between a single BuildOptions and the results of applying a {@link
- * com.google.devtools.build.lib.analysis.config.transitions.SplitTransition}) to it.
- */
- @SuppressWarnings("ReferenceEquality") // See comment above == comparison.
- public static OptionsDiff diff(OptionsDiff diff, BuildOptions first, BuildOptions second) {
- checkArgument(
- !diff.hasStarlarkOptions,
- "OptionsDiff cannot handle multiple 'second' BuildOptions with Starlark options and is"
- + " trying to diff against %s",
- diff);
- checkNotNull(first);
- checkNotNull(second);
- if (first.equals(second)) {
- return diff;
- }
-
- // Check and report if either class has been trimmed of an options class that exists in the
- // other.
- ImmutableSet> firstOptionClasses =
- first.getNativeOptions().stream()
- .map(FragmentOptions::getClass)
- .collect(ImmutableSet.toImmutableSet());
- ImmutableSet> secondOptionClasses =
- second.getNativeOptions().stream()
- .map(FragmentOptions::getClass)
- .collect(ImmutableSet.toImmutableSet());
- Sets.difference(firstOptionClasses, secondOptionClasses).forEach(diff::addExtraFirstFragment);
- Sets.difference(secondOptionClasses, firstOptionClasses).stream()
- .map(second::get)
- .forEach(diff::addExtraSecondFragment);
-
- // For fragments in common, report differences.
- for (Class extends FragmentOptions> clazz :
- Sets.intersection(firstOptionClasses, secondOptionClasses)) {
- FragmentOptions firstOptions = first.get(clazz);
- FragmentOptions secondOptions = second.get(clazz);
- // We avoid calling #equals because we are going to do a field-by-field comparison anyway.
- if (firstOptions == secondOptions) {
- continue;
- }
- for (OptionDefinition definition : OptionsParser.getOptionDefinitions(clazz)) {
- Object firstValue = firstOptions.getValueFromDefinition(definition);
- Object secondValue = secondOptions.getValueFromDefinition(definition);
- if (!Objects.equals(firstValue, secondValue)) {
- diff.addDiff(clazz, definition, firstValue, secondValue);
- }
- }
- }
-
- // Compare Starlark options for the two classes.
- Map starlarkFirst = first.starlarkOptionsMap;
- Map starlarkSecond = second.starlarkOptionsMap;
- for (Label buildSetting : Sets.union(starlarkFirst.keySet(), starlarkSecond.keySet())) {
- if (starlarkFirst.get(buildSetting) == null) {
- diff.addExtraSecondStarlarkOption(buildSetting, starlarkSecond.get(buildSetting));
- } else if (starlarkSecond.get(buildSetting) == null) {
- diff.addExtraFirstStarlarkOption(buildSetting);
- } else if (!starlarkFirst.get(buildSetting).equals(starlarkSecond.get(buildSetting))) {
- diff.putStarlarkDiff(
- buildSetting, starlarkFirst.get(buildSetting), starlarkSecond.get(buildSetting));
- }
- }
- return diff;
- }
-
- /**
- * A diff class for BuildOptions. Fields are meant to be populated and returned by {@link
- * BuildOptions#diff}.
- */
- public static final class OptionsDiff {
- private final Multimap, OptionDefinition> differingOptions =
- ArrayListMultimap.create();
- // The keyset for the {@link first} and {@link second} maps are identical and indicate which
- // specific options differ between the first and second built options.
- private final Map first = new LinkedHashMap<>();
- // Since this class can be used to track the result of transitions, {@link second} is a multimap
- // to be able to handle {@link SplitTransition}s.
- private final Multimap second = OrderedSetMultimap.create();
- // List of "extra" fragments for each BuildOption aka fragments that were trimmed off one
- // BuildOption but not the other.
- private final Set> extraFirstFragments = new HashSet<>();
- private final Set extraSecondFragments = new HashSet<>();
-
- private final Map starlarkFirst = new LinkedHashMap<>();
- // TODO(b/112041323): This should also be multimap but we don't diff multiple times with
- // Starlark options anywhere yet so add that feature when necessary.
- private final Map starlarkSecond = new LinkedHashMap<>();
-
- private final List extraStarlarkOptionsFirst = new ArrayList<>();
- private final Map extraStarlarkOptionsSecond = new HashMap<>();
-
- private boolean hasStarlarkOptions = false;
-
- @VisibleForTesting
- Set> getExtraFirstFragmentClassesForTesting() {
- return extraFirstFragments;
- }
-
- @VisibleForTesting
- Set getExtraSecondFragmentsForTesting() {
- return extraSecondFragments;
- }
-
- public Map getFirst() {
- return first;
- }
-
- public Multimap getSecond() {
- return second;
- }
-
- private void addDiff(
- Class extends FragmentOptions> fragmentOptionsClass,
- OptionDefinition option,
- Object firstValue,
- Object secondValue) {
- differingOptions.put(fragmentOptionsClass, option);
- first.put(option, firstValue);
- second.put(option, secondValue);
- }
-
- private void addExtraFirstFragment(Class extends FragmentOptions> options) {
- extraFirstFragments.add(options);
- }
-
- private void addExtraSecondFragment(FragmentOptions options) {
- extraSecondFragments.add(options);
- }
-
- private void putStarlarkDiff(Label buildSetting, Object firstValue, Object secondValue) {
- starlarkFirst.put(buildSetting, firstValue);
- starlarkSecond.put(buildSetting, secondValue);
- hasStarlarkOptions = true;
- }
-
- private void addExtraFirstStarlarkOption(Label buildSetting) {
- extraStarlarkOptionsFirst.add(buildSetting);
- hasStarlarkOptions = true;
- }
-
- private void addExtraSecondStarlarkOption(Label buildSetting, Object value) {
- extraStarlarkOptionsSecond.put(buildSetting, value);
- hasStarlarkOptions = true;
- }
-
- /**
- * Returns the labels of all starlark options that caused a difference between the first and
- * second options set.
- */
- public Set getChangedStarlarkOptions() {
- return ImmutableSet.builder()
- .addAll(starlarkFirst.keySet())
- .addAll(starlarkSecond.keySet())
- .addAll(extraStarlarkOptionsFirst)
- .addAll(extraStarlarkOptionsSecond.keySet())
- .build();
- }
-
- @VisibleForTesting
- Map getStarlarkFirstForTesting() {
- return starlarkFirst;
- }
-
- @VisibleForTesting
- Map getStarlarkSecondForTesting() {
- return starlarkSecond;
- }
-
- @VisibleForTesting
- List getExtraStarlarkOptionsFirstForTesting() {
- return extraStarlarkOptionsFirst;
- }
-
- @VisibleForTesting
- Map getExtraStarlarkOptionsSecondForTesting() {
- return extraStarlarkOptionsSecond;
- }
-
- /**
- * Note: it's not enough for first and second to be empty, with trimming, they must also contain
- * the same options classes.
- */
- boolean areSame() {
- return first.isEmpty()
- && second.isEmpty()
- && extraSecondFragments.isEmpty()
- && extraFirstFragments.isEmpty()
- && differingOptions.isEmpty()
- && starlarkFirst.isEmpty()
- && starlarkSecond.isEmpty()
- && extraStarlarkOptionsFirst.isEmpty()
- && extraStarlarkOptionsSecond.isEmpty();
- }
-
- public String prettyPrint() {
- StringBuilder toReturn = new StringBuilder();
- for (String diff : getPrettyPrintList()) {
- toReturn.append(diff).append(System.lineSeparator());
- }
- return toReturn.toString();
- }
-
- public List getPrettyPrintList() {
- List toReturn = new ArrayList<>();
- first.forEach(
- (option, value) ->
- toReturn.add(option.getOptionName() + ":" + value + " -> " + second.get(option)));
- starlarkFirst.forEach(
- (option, value) -> toReturn.add(option + ":" + value + starlarkSecond.get(option)));
- return toReturn;
- }
- }
-
@SuppressWarnings("unused") // Used reflectively.
private static final class Codec implements ObjectCodec {
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/config/OptionsDiff.java b/src/main/java/com/google/devtools/build/lib/analysis/config/OptionsDiff.java
new file mode 100644
index 00000000000000..4ed0b63ca80736
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/analysis/config/OptionsDiff.java
@@ -0,0 +1,263 @@
+// Copyright 2023 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.analysis.config;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.SetMultimap;
+import com.google.common.collect.Sets;
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.util.OrderedSetMultimap;
+import com.google.devtools.common.options.OptionDefinition;
+import com.google.devtools.common.options.OptionsParser;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * A diff class for BuildOptions. Fields are meant to be populated and returned by {@link
+ * OptionsDiff#diff}.
+ */
+public final class OptionsDiff {
+ /** Returns the difference between two BuildOptions in a new {@link OptionsDiff}. */
+ public static OptionsDiff diff(BuildOptions first, BuildOptions second) {
+ return diff(new OptionsDiff(), first, second);
+ }
+
+ /**
+ * Returns the difference between two BuildOptions in a pre-existing {@link OptionsDiff}.
+ *
+ * In a single pass through this method, the method can only compare a single "first" {@link
+ * BuildOptions} and single "second" BuildOptions; but an OptionsDiff instance can store the diff
+ * between a single "first" BuildOptions and multiple "second" BuildOptions. Being able to
+ * maintain a single OptionsDiff over multiple calls to diff is useful for, for example,
+ * aggregating the difference between a single BuildOptions and the results of applying a {@link
+ * com.google.devtools.build.lib.analysis.config.transitions.SplitTransition}) to it.
+ */
+ @SuppressWarnings("ReferenceEquality") // See comment above == comparison.
+ public static OptionsDiff diff(OptionsDiff diff, BuildOptions first, BuildOptions second) {
+ checkArgument(
+ !diff.hasStarlarkOptions,
+ "OptionsDiff cannot handle multiple 'second' BuildOptions with Starlark options and is"
+ + " trying to diff against %s",
+ diff);
+ checkNotNull(first);
+ checkNotNull(second);
+ if (first.equals(second)) {
+ return diff;
+ }
+
+ // Check and report if either class has been trimmed of an options class that exists in the
+ // other.
+ ImmutableSet> firstOptionClasses =
+ first.getNativeOptions().stream()
+ .map(FragmentOptions::getClass)
+ .collect(ImmutableSet.toImmutableSet());
+ ImmutableSet> secondOptionClasses =
+ second.getNativeOptions().stream()
+ .map(FragmentOptions::getClass)
+ .collect(ImmutableSet.toImmutableSet());
+ Sets.difference(firstOptionClasses, secondOptionClasses).forEach(diff::addExtraFirstFragment);
+ Sets.difference(secondOptionClasses, firstOptionClasses).stream()
+ .map(second::get)
+ .forEach(diff::addExtraSecondFragment);
+
+ // For fragments in common, report differences.
+ for (Class extends FragmentOptions> clazz :
+ Sets.intersection(firstOptionClasses, secondOptionClasses)) {
+ FragmentOptions firstOptions = first.get(clazz);
+ FragmentOptions secondOptions = second.get(clazz);
+ // We avoid calling #equals because we are going to do a field-by-field comparison anyway.
+ if (firstOptions == secondOptions) {
+ continue;
+ }
+ for (OptionDefinition definition : OptionsParser.getOptionDefinitions(clazz)) {
+ Object firstValue = firstOptions.getValueFromDefinition(definition);
+ Object secondValue = secondOptions.getValueFromDefinition(definition);
+ if (!Objects.equals(firstValue, secondValue)) {
+ diff.addDiff(clazz, definition, firstValue, secondValue);
+ }
+ }
+ }
+
+ // Compare Starlark options for the two classes.
+ ImmutableMap starlarkFirst = first.getStarlarkOptions();
+ ImmutableMap starlarkSecond = second.getStarlarkOptions();
+ for (Label buildSetting : Sets.union(starlarkFirst.keySet(), starlarkSecond.keySet())) {
+ if (starlarkFirst.get(buildSetting) == null) {
+ diff.addExtraSecondStarlarkOption(buildSetting, starlarkSecond.get(buildSetting));
+ } else if (starlarkSecond.get(buildSetting) == null) {
+ diff.addExtraFirstStarlarkOption(buildSetting);
+ } else if (!starlarkFirst.get(buildSetting).equals(starlarkSecond.get(buildSetting))) {
+ diff.putStarlarkDiff(
+ buildSetting, starlarkFirst.get(buildSetting), starlarkSecond.get(buildSetting));
+ }
+ }
+ return diff;
+ }
+
+ private final ListMultimap, OptionDefinition> differingOptions =
+ ArrayListMultimap.create();
+ // The keyset for the {@link first} and {@link second} maps are identical and indicate which
+ // specific options differ between the first and second built options.
+ private final Map first = new LinkedHashMap<>();
+ // Since this class can be used to track the result of transitions, {@link second} is a multimap
+ // to be able to handle {@link SplitTransition}s.
+ private final SetMultimap second = OrderedSetMultimap.create();
+ // List of "extra" fragments for each BuildOption aka fragments that were trimmed off one
+ // BuildOption but not the other.
+ private final Set> extraFirstFragments = new HashSet<>();
+ private final Set extraSecondFragments = new HashSet<>();
+
+ private final Map starlarkFirst = new LinkedHashMap<>();
+ // TODO(b/112041323): This should also be multimap but we don't diff multiple times with
+ // Starlark options anywhere yet so add that feature when necessary.
+ private final Map starlarkSecond = new LinkedHashMap<>();
+
+ private final List extraStarlarkOptionsFirst = new ArrayList<>();
+ private final Map extraStarlarkOptionsSecond = new HashMap<>();
+
+ private boolean hasStarlarkOptions = false;
+
+ @VisibleForTesting
+ Set> getExtraFirstFragmentClassesForTesting() {
+ return extraFirstFragments;
+ }
+
+ @VisibleForTesting
+ Set getExtraSecondFragmentsForTesting() {
+ return extraSecondFragments;
+ }
+
+ public Map getFirst() {
+ return first;
+ }
+
+ public Multimap getSecond() {
+ return second;
+ }
+
+ private void addDiff(
+ Class extends FragmentOptions> fragmentOptionsClass,
+ OptionDefinition option,
+ Object firstValue,
+ Object secondValue) {
+ differingOptions.put(fragmentOptionsClass, option);
+ first.put(option, firstValue);
+ second.put(option, secondValue);
+ }
+
+ private void addExtraFirstFragment(Class extends FragmentOptions> options) {
+ extraFirstFragments.add(options);
+ }
+
+ private void addExtraSecondFragment(FragmentOptions options) {
+ extraSecondFragments.add(options);
+ }
+
+ private void putStarlarkDiff(Label buildSetting, Object firstValue, Object secondValue) {
+ starlarkFirst.put(buildSetting, firstValue);
+ starlarkSecond.put(buildSetting, secondValue);
+ hasStarlarkOptions = true;
+ }
+
+ private void addExtraFirstStarlarkOption(Label buildSetting) {
+ extraStarlarkOptionsFirst.add(buildSetting);
+ hasStarlarkOptions = true;
+ }
+
+ private void addExtraSecondStarlarkOption(Label buildSetting, Object value) {
+ extraStarlarkOptionsSecond.put(buildSetting, value);
+ hasStarlarkOptions = true;
+ }
+
+ /**
+ * Returns the labels of all starlark options that caused a difference between the first and
+ * second options set.
+ */
+ public ImmutableSet getChangedStarlarkOptions() {
+ return ImmutableSet.builder()
+ .addAll(starlarkFirst.keySet())
+ .addAll(starlarkSecond.keySet())
+ .addAll(extraStarlarkOptionsFirst)
+ .addAll(extraStarlarkOptionsSecond.keySet())
+ .build();
+ }
+
+ @VisibleForTesting
+ Map getStarlarkFirstForTesting() {
+ return starlarkFirst;
+ }
+
+ @VisibleForTesting
+ Map getStarlarkSecondForTesting() {
+ return starlarkSecond;
+ }
+
+ @VisibleForTesting
+ List getExtraStarlarkOptionsFirstForTesting() {
+ return extraStarlarkOptionsFirst;
+ }
+
+ @VisibleForTesting
+ Map getExtraStarlarkOptionsSecondForTesting() {
+ return extraStarlarkOptionsSecond;
+ }
+
+ /**
+ * Note: it's not enough for first and second to be empty, with trimming, they must also contain
+ * the same options classes.
+ */
+ boolean areSame() {
+ return first.isEmpty()
+ && second.isEmpty()
+ && extraSecondFragments.isEmpty()
+ && extraFirstFragments.isEmpty()
+ && differingOptions.isEmpty()
+ && starlarkFirst.isEmpty()
+ && starlarkSecond.isEmpty()
+ && extraStarlarkOptionsFirst.isEmpty()
+ && extraStarlarkOptionsSecond.isEmpty();
+ }
+
+ public String prettyPrint() {
+ StringBuilder toReturn = new StringBuilder();
+ for (String diff : getPrettyPrintList()) {
+ toReturn.append(diff).append(System.lineSeparator());
+ }
+ return toReturn.toString();
+ }
+
+ public List getPrettyPrintList() {
+ List toReturn = new ArrayList<>();
+ first.forEach(
+ (option, value) ->
+ toReturn.add(option.getOptionName() + ":" + value + " -> " + second.get(option)));
+ starlarkFirst.forEach(
+ (option, value) -> toReturn.add(option + ":" + value + starlarkSecond.get(option)));
+ return toReturn;
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/config/OutputPathMnemonicComputer.java b/src/main/java/com/google/devtools/build/lib/analysis/config/OutputPathMnemonicComputer.java
index 794e1f6f0708ce..3565f83d5d6a49 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/config/OutputPathMnemonicComputer.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/config/OutputPathMnemonicComputer.java
@@ -302,7 +302,7 @@ public static String computeNameFragmentWithDiff(
// TODO(blaze-configurability-team): As a mild performance update, getFirst already includes
// details of the corresponding option. Could incorporate this instead of hashChosenOptions
// regenerating the OptionDefinitions and values.
- BuildOptions.OptionsDiff diff = BuildOptions.diff(toOptions, baselineOptions);
+ OptionsDiff diff = OptionsDiff.diff(toOptions, baselineOptions);
// Note: getFirst only excludes options trimmed between baselineOptions to toOptions and this is
// considered OK as a given Rule should not be being built with options of different
// trimmings. See longform note in {@link ConfiguredTargetKey} for details.
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/starlark/FunctionTransitionUtil.java b/src/main/java/com/google/devtools/build/lib/analysis/starlark/FunctionTransitionUtil.java
index 2917de13a8d23e..09427e7eecf80e 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/starlark/FunctionTransitionUtil.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/starlark/FunctionTransitionUtil.java
@@ -30,6 +30,7 @@
import com.google.devtools.build.lib.analysis.config.CoreOptions;
import com.google.devtools.build.lib.analysis.config.FragmentOptions;
import com.google.devtools.build.lib.analysis.config.OptionInfo;
+import com.google.devtools.build.lib.analysis.config.OptionsDiff;
import com.google.devtools.build.lib.analysis.config.StarlarkDefinedConfigTransition;
import com.google.devtools.build.lib.analysis.config.StarlarkDefinedConfigTransition.ValidationException;
import com.google.devtools.build.lib.analysis.test.TestConfiguration.TestOptions;
@@ -490,7 +491,7 @@ public static ImmutableSet getAffectedByStarlarkTransitionViaDiff(
return ImmutableSet.of();
}
- BuildOptions.OptionsDiff diff = BuildOptions.diff(toOptions, baselineOptions);
+ OptionsDiff diff = OptionsDiff.diff(toOptions, baselineOptions);
Stream diffNative =
diff.getFirst().keySet().stream()
.map(option -> COMMAND_LINE_OPTION_PREFIX + option.getOptionName());
diff --git a/src/main/java/com/google/devtools/build/lib/query2/BUILD b/src/main/java/com/google/devtools/build/lib/query2/BUILD
index d5bd7fdf50181d..08a14e51737d10 100644
--- a/src/main/java/com/google/devtools/build/lib/query2/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/query2/BUILD
@@ -40,6 +40,7 @@ java_library(
"//src/main/java/com/google/devtools/build/lib/analysis:config/config_matching_provider",
"//src/main/java/com/google/devtools/build/lib/analysis:config/core_options",
"//src/main/java/com/google/devtools/build/lib/analysis:config/fragment_options",
+ "//src/main/java/com/google/devtools/build/lib/analysis:config/options_diff",
"//src/main/java/com/google/devtools/build/lib/analysis:config/starlark_defined_config_transition",
"//src/main/java/com/google/devtools/build/lib/analysis:config/starlark_transition_cache",
"//src/main/java/com/google/devtools/build/lib/analysis:config/transitions/composing_transition",
diff --git a/src/main/java/com/google/devtools/build/lib/query2/cquery/TransitionsOutputFormatterCallback.java b/src/main/java/com/google/devtools/build/lib/query2/cquery/TransitionsOutputFormatterCallback.java
index b1d59927c0d87a..f3aa928a9ea57e 100644
--- a/src/main/java/com/google/devtools/build/lib/query2/cquery/TransitionsOutputFormatterCallback.java
+++ b/src/main/java/com/google/devtools/build/lib/query2/cquery/TransitionsOutputFormatterCallback.java
@@ -20,7 +20,7 @@
import com.google.devtools.build.lib.analysis.ConfiguredTarget;
import com.google.devtools.build.lib.analysis.config.BuildConfigurationValue;
import com.google.devtools.build.lib.analysis.config.BuildOptions;
-import com.google.devtools.build.lib.analysis.config.BuildOptions.OptionsDiff;
+import com.google.devtools.build.lib.analysis.config.OptionsDiff;
import com.google.devtools.build.lib.analysis.config.StarlarkTransitionCache;
import com.google.devtools.build.lib.analysis.config.transitions.ConfigurationTransition;
import com.google.devtools.build.lib.analysis.config.transitions.TransitionFactory;
@@ -123,7 +123,7 @@ public void processOutput(Iterable partialResult) throws Inter
}
OptionsDiff diff = new OptionsDiff();
for (BuildOptions options : dep.options()) {
- diff = BuildOptions.diff(diff, config.getOptions(), options);
+ diff = OptionsDiff.diff(diff, config.getOptions(), options);
}
diff.getPrettyPrintList().forEach(singleDiff -> addResult(" " + singleDiff));
}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/BUILD b/src/main/java/com/google/devtools/build/lib/skyframe/BUILD
index f3e45653594801..f874842989c293 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/BUILD
@@ -234,6 +234,7 @@ java_library(
"//src/main/java/com/google/devtools/build/lib/analysis:config/config_conditions",
"//src/main/java/com/google/devtools/build/lib/analysis:config/core_options",
"//src/main/java/com/google/devtools/build/lib/analysis:config/invalid_configuration_exception",
+ "//src/main/java/com/google/devtools/build/lib/analysis:config/options_diff",
"//src/main/java/com/google/devtools/build/lib/analysis:config/starlark_exec_transition_loader",
"//src/main/java/com/google/devtools/build/lib/analysis:config/starlark_transition_cache",
"//src/main/java/com/google/devtools/build/lib/analysis:config/toolchain_type_requirement",
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeBuildView.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeBuildView.java
index 79c53ce23ea60a..37368e05e306f0 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeBuildView.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeBuildView.java
@@ -62,10 +62,9 @@
import com.google.devtools.build.lib.analysis.TopLevelArtifactContext;
import com.google.devtools.build.lib.analysis.ViewCreationFailedException;
import com.google.devtools.build.lib.analysis.config.BuildConfigurationValue;
-import com.google.devtools.build.lib.analysis.config.BuildOptions;
-import com.google.devtools.build.lib.analysis.config.BuildOptions.OptionsDiff;
import com.google.devtools.build.lib.analysis.config.ConfigConditions;
import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException;
+import com.google.devtools.build.lib.analysis.config.OptionsDiff;
import com.google.devtools.build.lib.analysis.config.StarlarkTransitionCache;
import com.google.devtools.build.lib.analysis.test.AnalysisFailurePropagationException;
import com.google.devtools.build.lib.analysis.test.CoverageActionFinishedEvent;
@@ -216,7 +215,7 @@ private String describeConfigurationDifference(
}
OptionsDiff diff =
- BuildOptions.diff(this.configuration.getOptions(), configuration.getOptions());
+ OptionsDiff.diff(this.configuration.getOptions(), configuration.getOptions());
ImmutableSet nativeCacheInvalidatingDifferences =
getNativeCacheInvalidatingDifferences(configuration, diff);
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/BUILD b/src/test/java/com/google/devtools/build/lib/analysis/BUILD
index 06a5c46a5206b4..cf37909d3fb0a5 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/analysis/BUILD
@@ -88,6 +88,7 @@ java_library(
"//src/main/java/com/google/devtools/build/lib/analysis:config/fragment_options",
"//src/main/java/com/google/devtools/build/lib/analysis:config/fragment_registry",
"//src/main/java/com/google/devtools/build/lib/analysis:config/invalid_configuration_exception",
+ "//src/main/java/com/google/devtools/build/lib/analysis:config/options_diff",
"//src/main/java/com/google/devtools/build/lib/analysis:config/per_label_options",
"//src/main/java/com/google/devtools/build/lib/analysis:config/run_under",
"//src/main/java/com/google/devtools/build/lib/analysis:config/run_under_converter",
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 30a697d7660b32..346b59ffa31149 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
@@ -19,10 +19,8 @@
import com.google.common.collect.ImmutableClassToInstanceMap;
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.MapBackedChecksumCache;
import com.google.devtools.build.lib.analysis.config.BuildOptions.OptionsChecksumCache;
-import com.google.devtools.build.lib.analysis.config.BuildOptions.OptionsDiff;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.rules.android.AndroidConfiguration;
import com.google.devtools.build.lib.rules.cpp.CppOptions;
@@ -37,7 +35,6 @@
import com.google.devtools.common.options.OptionsParser;
import com.google.protobuf.ByteString;
import java.util.AbstractMap;
-import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -102,91 +99,6 @@ public void optionsEquality() throws Exception {
.isFalse();
}
- @Test
- public void optionsDiff() throws Exception {
- BuildOptions one = BuildOptions.of(BUILD_CONFIG_OPTIONS, "--compilation_mode=opt", "cpu=k8");
- BuildOptions two = BuildOptions.of(BUILD_CONFIG_OPTIONS, "--compilation_mode=dbg", "cpu=k8");
- BuildOptions three = BuildOptions.of(BUILD_CONFIG_OPTIONS, "--compilation_mode=dbg", "cpu=k8");
-
- OptionsDiff diffOneTwo = BuildOptions.diff(one, two);
- OptionsDiff diffTwoThree = BuildOptions.diff(two, three);
-
- assertThat(diffOneTwo.areSame()).isFalse();
- assertThat(diffOneTwo.getFirst().keySet()).isEqualTo(diffOneTwo.getSecond().keySet());
- assertThat(diffOneTwo.prettyPrint()).contains("opt");
- assertThat(diffOneTwo.prettyPrint()).contains("dbg");
-
- assertThat(diffTwoThree.areSame()).isTrue();
- }
-
- @Test
- public void optionsDiff_differentFragments() throws Exception {
- BuildOptions one = BuildOptions.of(ImmutableList.of(CppOptions.class));
- BuildOptions two = BuildOptions.of(BUILD_CONFIG_OPTIONS);
-
- OptionsDiff diff = BuildOptions.diff(one, two);
-
- assertThat(diff.areSame()).isFalse();
- assertThat(diff.getExtraFirstFragmentClassesForTesting()).containsExactly(CppOptions.class);
- assertThat(
- diff.getExtraSecondFragmentsForTesting().stream()
- .map(Object::getClass)
- .collect(Collectors.toSet()))
- .containsExactlyElementsIn(BUILD_CONFIG_OPTIONS);
- }
-
- @Test
- public void optionsDiff_nullOptionsThrow() throws Exception {
- BuildOptions options =
- BuildOptions.of(BUILD_CONFIG_OPTIONS, "--compilation_mode=opt", "cpu=k8");
- assertThrows(NullPointerException.class, () -> BuildOptions.diff(options, null));
- assertThrows(NullPointerException.class, () -> BuildOptions.diff(null, options));
- }
-
- @Test
- public void optionsDiff_sameStarlarkOptions() {
- Label flagName = Label.parseCanonicalUnchecked("//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_differentStarlarkOptions() {
- Label flagName = Label.parseCanonicalUnchecked("//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.getStarlarkFirstForTesting().keySet())
- .isEqualTo(diff.getStarlarkSecondForTesting().keySet());
- assertThat(diff.getStarlarkFirstForTesting().keySet()).containsExactly(flagName);
- assertThat(diff.getStarlarkFirstForTesting().values()).containsExactly(flagValueOne);
- assertThat(diff.getStarlarkSecondForTesting().values()).containsExactly(flagValueTwo);
- }
-
- @Test
- public void optionsDiff_extraStarlarkOptions() {
- Label flagNameOne = Label.parseCanonicalUnchecked("//extra/flag/one");
- Label flagNameTwo = Label.parseCanonicalUnchecked("//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.getExtraStarlarkOptionsFirstForTesting()).containsExactly(flagNameOne);
- assertThat(diff.getExtraStarlarkOptionsSecondForTesting().entrySet())
- .containsExactly(Maps.immutableEntry(flagNameTwo, flagValue));
- }
-
@Test
public void serialization() throws Exception {
new SerializationTester(
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/config/OptionsDiffTest.java b/src/test/java/com/google/devtools/build/lib/analysis/config/OptionsDiffTest.java
new file mode 100644
index 00000000000000..eef461bcaafa5f
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/analysis/config/OptionsDiffTest.java
@@ -0,0 +1,119 @@
+// Copyright 2023 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.analysis.config;
+
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.rules.cpp.CppOptions;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests of @link OptionsDiff}. */
+@RunWith(JUnit4.class)
+public class OptionsDiffTest {
+ private static final ImmutableList> BUILD_CONFIG_OPTIONS =
+ ImmutableList.of(CoreOptions.class);
+
+ @Test
+ public void diff() throws Exception {
+ BuildOptions one = BuildOptions.of(BUILD_CONFIG_OPTIONS, "--compilation_mode=opt", "cpu=k8");
+ BuildOptions two = BuildOptions.of(BUILD_CONFIG_OPTIONS, "--compilation_mode=dbg", "cpu=k8");
+ BuildOptions three = BuildOptions.of(BUILD_CONFIG_OPTIONS, "--compilation_mode=dbg", "cpu=k8");
+
+ OptionsDiff diffOneTwo = OptionsDiff.diff(one, two);
+ OptionsDiff diffTwoThree = OptionsDiff.diff(two, three);
+
+ assertThat(diffOneTwo.areSame()).isFalse();
+ assertThat(diffOneTwo.getFirst().keySet()).isEqualTo(diffOneTwo.getSecond().keySet());
+ assertThat(diffOneTwo.prettyPrint()).contains("opt");
+ assertThat(diffOneTwo.prettyPrint()).contains("dbg");
+
+ assertThat(diffTwoThree.areSame()).isTrue();
+ }
+
+ @Test
+ public void diff_differentFragments() throws Exception {
+ BuildOptions one = BuildOptions.of(ImmutableList.of(CppOptions.class));
+ BuildOptions two = BuildOptions.of(BUILD_CONFIG_OPTIONS);
+
+ OptionsDiff diff = OptionsDiff.diff(one, two);
+
+ assertThat(diff.areSame()).isFalse();
+ assertThat(diff.getExtraFirstFragmentClassesForTesting()).containsExactly(CppOptions.class);
+ assertThat(
+ diff.getExtraSecondFragmentsForTesting().stream()
+ .map(Object::getClass)
+ .collect(toImmutableList()))
+ .containsExactlyElementsIn(BUILD_CONFIG_OPTIONS);
+ }
+
+ @Test
+ public void biff_nullOptionsThrow() throws Exception {
+ BuildOptions options =
+ BuildOptions.of(BUILD_CONFIG_OPTIONS, "--compilation_mode=opt", "cpu=k8");
+ assertThrows(NullPointerException.class, () -> OptionsDiff.diff(options, null));
+ assertThrows(NullPointerException.class, () -> OptionsDiff.diff(null, options));
+ }
+
+ @Test
+ public void diff_sameStarlarkOptions() {
+ Label flagName = Label.parseCanonicalUnchecked("//foo/flag");
+ String flagValue = "value";
+ BuildOptions one = BuildOptions.of(ImmutableMap.of(flagName, flagValue));
+ BuildOptions two = BuildOptions.of(ImmutableMap.of(flagName, flagValue));
+
+ assertThat(OptionsDiff.diff(one, two).areSame()).isTrue();
+ }
+
+ @Test
+ public void diff_differentStarlarkOptions() {
+ Label flagName = Label.parseCanonicalUnchecked("//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 = OptionsDiff.diff(one, two);
+
+ assertThat(diff.areSame()).isFalse();
+ assertThat(diff.getStarlarkFirstForTesting().keySet())
+ .isEqualTo(diff.getStarlarkSecondForTesting().keySet());
+ assertThat(diff.getStarlarkFirstForTesting().keySet()).containsExactly(flagName);
+ assertThat(diff.getStarlarkFirstForTesting().values()).containsExactly(flagValueOne);
+ assertThat(diff.getStarlarkSecondForTesting().values()).containsExactly(flagValueTwo);
+ }
+
+ @Test
+ public void diff_extraStarlarkOptions() {
+ Label flagNameOne = Label.parseCanonicalUnchecked("//extra/flag/one");
+ Label flagNameTwo = Label.parseCanonicalUnchecked("//extra/flag/two");
+ String flagValue = "foo";
+ BuildOptions one = BuildOptions.of(ImmutableMap.of(flagNameOne, flagValue));
+ BuildOptions two = BuildOptions.of(ImmutableMap.of(flagNameTwo, flagValue));
+
+ OptionsDiff diff = OptionsDiff.diff(one, two);
+
+ assertThat(diff.areSame()).isFalse();
+ assertThat(diff.getExtraStarlarkOptionsFirstForTesting()).containsExactly(flagNameOne);
+ assertThat(diff.getExtraStarlarkOptionsSecondForTesting().entrySet())
+ .containsExactly(Maps.immutableEntry(flagNameTwo, flagValue));
+ }
+}