Transformation for build configurations based on a platform/flags mapping.
Introduces a new SkyValue which stores the information obtained from a mapping file (parser yet to be written) and provides logic to transform a build configuration (key) based on that.
Step 3/N towards the platforms mapping functionality for https://github.com/bazelbuild/bazel/issues/6426
RELNOTES: None.
PiperOrigin-RevId: 238298127
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/PlatformConfigurationLoader.java b/src/main/java/com/google/devtools/build/lib/analysis/PlatformConfigurationLoader.java
index ce34005..adad85a 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/PlatformConfigurationLoader.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/PlatformConfigurationLoader.java
@@ -16,13 +16,11 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
import com.google.devtools.build.lib.analysis.config.BuildOptions;
import com.google.devtools.build.lib.analysis.config.ConfigurationFragmentFactory;
import com.google.devtools.build.lib.analysis.config.FragmentOptions;
import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException;
-import com.google.devtools.build.lib.cmdline.Label;
/** A loader that creates {@link PlatformConfiguration} instances based on command-line options. */
public class PlatformConfigurationLoader implements ConfigurationFragmentFactory {
@@ -35,36 +33,10 @@
public PlatformConfiguration create(BuildOptions buildOptions)
throws InvalidConfigurationException {
PlatformOptions platformOptions = buildOptions.get(PlatformOptions.class);
-
- // Handle default values for the host and target platform.
- // TODO(https://github.com/bazelbuild/bazel/issues/6849): After migration, set the defaults
- // directly.
- Label hostPlatform;
- if (platformOptions.hostPlatform != null) {
- hostPlatform = platformOptions.hostPlatform;
- } else if (platformOptions.autoConfigureHostPlatform) {
- // Use the auto-configured host platform.
- hostPlatform = PlatformOptions.DEFAULT_HOST_PLATFORM;
- } else {
- // Use the legacy host platform.
- hostPlatform = PlatformOptions.LEGACY_DEFAULT_HOST_PLATFORM;
- }
-
- Label targetPlatform;
- if (!platformOptions.platforms.isEmpty()) {
- targetPlatform = Iterables.getFirst(platformOptions.platforms, null);
- } else if (platformOptions.autoConfigureHostPlatform) {
- // Default to the host platform, whatever it is.
- targetPlatform = hostPlatform;
- } else {
- // Use the legacy target platform
- targetPlatform = PlatformOptions.LEGACY_DEFAULT_TARGET_PLATFORM;
- }
-
return new PlatformConfiguration(
- hostPlatform,
+ platformOptions.computeHostPlatform(),
ImmutableList.copyOf(platformOptions.extraExecutionPlatforms),
- targetPlatform,
+ platformOptions.computeTargetPlatform(),
ImmutableList.copyOf(platformOptions.extraToolchains),
ImmutableList.copyOf(platformOptions.enabledToolchainTypes));
}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/PlatformOptions.java b/src/main/java/com/google/devtools/build/lib/analysis/PlatformOptions.java
index 7681871..788dc3a 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/PlatformOptions.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/PlatformOptions.java
@@ -15,6 +15,7 @@
package com.google.devtools.build.lib.analysis;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration.LabelListConverter;
import com.google.devtools.build.lib.analysis.config.FragmentOptions;
@@ -101,23 +102,22 @@
public List<String> extraToolchains;
@Option(
- name = "toolchain_resolution_override",
- allowMultiple = true,
- defaultValue = "",
- documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
- effectTags = {
- OptionEffectTag.AFFECTS_OUTPUTS,
- OptionEffectTag.CHANGES_INPUTS,
- OptionEffectTag.LOADING_AND_ANALYSIS
- },
- deprecationWarning =
- "toolchain_resolution_override is now a no-op and will be removed in"
- + " an upcoming release",
- help =
- "Override toolchain resolution for a toolchain type with a specific toolchain. "
- + "Example: --toolchain_resolution_override=@io_bazel_rules_go//:toolchain="
- + "@io_bazel_rules_go//:linux-arm64-toolchain"
- )
+ name = "toolchain_resolution_override",
+ allowMultiple = true,
+ defaultValue = "",
+ documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
+ effectTags = {
+ OptionEffectTag.AFFECTS_OUTPUTS,
+ OptionEffectTag.CHANGES_INPUTS,
+ OptionEffectTag.LOADING_AND_ANALYSIS
+ },
+ deprecationWarning =
+ "toolchain_resolution_override is now a no-op and will be removed in"
+ + " an upcoming release",
+ help =
+ "Override toolchain resolution for a toolchain type with a specific toolchain. "
+ + "Example: --toolchain_resolution_override=@io_bazel_rules_go//:toolchain="
+ + "@io_bazel_rules_go//:linux-arm64-toolchain")
public List<String> toolchainResolutionOverrides;
@Option(
@@ -184,4 +184,39 @@
host.useToolchainResolutionForJavaRules = this.useToolchainResolutionForJavaRules;
return host;
}
+
+ /** Returns the intended target platform value based on options defined in this fragment. */
+ public Label computeTargetPlatform() {
+ // Handle default values for the host and target platform.
+ // TODO(https://github.com/bazelbuild/bazel/issues/6849): After migration, set the defaults
+ // directly.
+
+ if (!platforms.isEmpty()) {
+ return Iterables.getFirst(platforms, null);
+ } else if (autoConfigureHostPlatform) {
+ // Default to the host platform, whatever it is.
+ return computeHostPlatform();
+ } else {
+ // Use the legacy target platform
+ return LEGACY_DEFAULT_TARGET_PLATFORM;
+ }
+ }
+
+ /** Returns the intended host platform value based on options defined in this fragment. */
+ public Label computeHostPlatform() {
+ // Handle default values for the host and target platform.
+ // TODO(https://github.com/bazelbuild/bazel/issues/6849): After migration, set the defaults
+ // directly.
+
+ Label hostPlatform;
+ if (this.hostPlatform != null) {
+ return this.hostPlatform;
+ } else if (autoConfigureHostPlatform) {
+ // Use the auto-configured host platform.
+ return DEFAULT_HOST_PLATFORM;
+ } else {
+ // Use the legacy host platform.
+ return LEGACY_DEFAULT_HOST_PLATFORM;
+ }
+ }
}
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 7775d33..a47b2d6 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
@@ -192,6 +192,11 @@
return fragmentOptionsMap.values();
}
+ /** Returns the set of fragment classes contained in these options. */
+ public Set<Class<? extends FragmentOptions>> getFragmentClasses() {
+ return fragmentOptionsMap.keySet();
+ }
+
public ImmutableMap<Label, Object> getStarlarkOptions() {
return skylarkOptionsMap;
}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/PlatformMappingValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/PlatformMappingValue.java
new file mode 100644
index 0000000..4ad16cb
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/PlatformMappingValue.java
@@ -0,0 +1,196 @@
+// Copyright 2019 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.skyframe;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Interner;
+import com.google.common.collect.Iterables;
+import com.google.devtools.build.lib.analysis.PlatformOptions;
+import com.google.devtools.build.lib.analysis.config.BuildOptions;
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.concurrent.BlazeInterners;
+import com.google.devtools.build.lib.concurrent.ThreadSafety;
+import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
+import com.google.devtools.build.lib.vfs.RootedPath;
+import com.google.devtools.build.skyframe.SkyFunctionName;
+import com.google.devtools.build.skyframe.SkyKey;
+import com.google.devtools.build.skyframe.SkyValue;
+import com.google.devtools.common.options.OptionsParser;
+import com.google.devtools.common.options.OptionsParsingException;
+import com.google.devtools.common.options.OptionsParsingResult;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Stores contents of a platforms/flags mapping file for transforming one {@link
+ * BuildConfigurationValue.Key} into another.
+ *
+ * <p>See <a href=https://docs.google.com/document/d/1Vg_tPgiZbSrvXcJ403vZVAGlsWhH9BUDrAxMOYnO0Ls>
+ * the design</a> for more details on how the mapping can be defined and the desired logic on how it
+ * is applied to configuration keys.
+ */
+public final class PlatformMappingValue implements SkyValue {
+
+ /** Key for {@link PlatformMappingValue} based on the location of the mapping file. */
+ @ThreadSafety.Immutable
+ @AutoCodec
+ public static final class Key implements SkyKey {
+ private static final Interner<Key> interner = BlazeInterners.newWeakInterner();
+
+ private final RootedPath path;
+
+ private Key(RootedPath path) {
+ this.path = path;
+ }
+
+ @AutoCodec.VisibleForSerialization
+ @AutoCodec.Instantiator
+ static Key create(RootedPath path) {
+ return interner.intern(new Key(path));
+ }
+
+ @Override
+ public SkyFunctionName functionName() {
+ return SkyFunctions.PLATFORM_MAPPING;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Key key = (Key) o;
+ return Objects.equals(path, key.path);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(path);
+ }
+
+ @Override
+ public String toString() {
+ return "PlatformMappingValue.Key{" + "path=" + path + '}';
+ }
+ }
+
+ private final Map<Label, Collection<String>> platformsToFlags;
+ private final Map<Collection<String>, Label> flagsToPlatforms;
+
+ /**
+ * Creates a new mapping value which will match on the given platforms (if a target platform is
+ * set on the key to be mapped), otherwise on the set of flags.
+ *
+ * @param platformsToFlags mapping from target platform label to the command line style flags that
+ * should be parsed & modified if that platform is set
+ * @param flagsToPlatforms mapping from a collection of command line style flags to a target
+ * platform that should be set if the flags match the mapped options
+ */
+ PlatformMappingValue(
+ Map<Label, Collection<String>> platformsToFlags,
+ Map<Collection<String>, Label> flagsToPlatforms) {
+ this.platformsToFlags = platformsToFlags;
+ this.flagsToPlatforms = flagsToPlatforms;
+ }
+
+ /**
+ * Maps one {@link BuildConfigurationValue.Key} to another by way of mappings provided in a file.
+ *
+ * <p>The <a href=https://docs.google.com/document/d/1Vg_tPgiZbSrvXcJ403vZVAGlsWhH9BUDrAxMOYnO0Ls>
+ * full design</a> contains the details for the mapping logic but in short:
+ *
+ * <ol>
+ * <li>If a target platform is set on the original then mappings from platform to flags will be
+ * applied.
+ * <li>If no target platform is set then mappings from flags to platforms will be applied.
+ * <li>If no matching flags to platforms mapping was found, the default target platform will be
+ * used.
+ * </ol>
+ *
+ * @param original the key representing the configuration to be mapped
+ * @param defaultBuildOptions build options as set by default in this server
+ * @return the mapped key if any mapping matched the original or else the original
+ * @throws OptionsParsingException if any of the user configured flags cannot be parsed
+ * @throws IllegalArgumentException if the original does not contain a {@link PlatformOptions}
+ * fragment
+ */
+ public BuildConfigurationValue.Key map(
+ BuildConfigurationValue.Key original, BuildOptions defaultBuildOptions)
+ throws OptionsParsingException {
+ BuildOptions.OptionsDiffForReconstruction originalDiff = original.getOptionsDiff();
+ BuildOptions originalOptions = defaultBuildOptions.applyDiff(originalDiff);
+
+ Preconditions.checkArgument(
+ originalOptions.contains(PlatformOptions.class),
+ "When using platform mappings, all configurations must contain platform options");
+
+ BuildOptions modifiedOptions = null;
+
+ if (!originalOptions.get(PlatformOptions.class).platforms.isEmpty()) {
+ List<Label> platforms = originalOptions.get(PlatformOptions.class).platforms;
+
+ Preconditions.checkArgument(
+ platforms.size() == 1,
+ "Platform mapping only supports a single target platform but found %s",
+ platforms);
+
+ Label targetPlatform = Iterables.getOnlyElement(platforms);
+ if (!platformsToFlags.containsKey(targetPlatform)) {
+ // This can happen if the user has set the platform and any other flags that would normally
+ // be mapped from it on the command line instead of relying on the mapping.
+ return original;
+ }
+
+ OptionsParsingResult parsingResult =
+ parse(platformsToFlags.get(targetPlatform), defaultBuildOptions);
+ modifiedOptions = originalOptions.applyParsingResult(parsingResult);
+ } else {
+ boolean mappingFound = false;
+ for (Map.Entry<Collection<String>, Label> flagsToPlatform : flagsToPlatforms.entrySet()) {
+ if (originalOptions.matches(parse(flagsToPlatform.getKey(), defaultBuildOptions))) {
+ modifiedOptions = originalOptions.clone();
+ modifiedOptions.get(PlatformOptions.class).platforms =
+ ImmutableList.of(flagsToPlatform.getValue());
+ mappingFound = true;
+ break;
+ }
+ }
+
+ if (!mappingFound) {
+ Label targetPlatform = originalOptions.get(PlatformOptions.class).computeTargetPlatform();
+ modifiedOptions = originalOptions.clone();
+ modifiedOptions.get(PlatformOptions.class).platforms = ImmutableList.of(targetPlatform);
+ }
+ }
+
+ return BuildConfigurationValue.key(
+ original.getFragments(),
+ BuildOptions.diffForReconstruction(defaultBuildOptions, modifiedOptions));
+ }
+
+ private OptionsParsingResult parse(Iterable<String> args, BuildOptions defaultBuildOptions)
+ throws OptionsParsingException {
+ OptionsParser parser = OptionsParser.newOptionsParser(defaultBuildOptions.getFragmentClasses());
+ parser.parse(ImmutableList.copyOf(args));
+ // TODO(schmitt): Parse starlark options as well.
+ return parser;
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyFunctions.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyFunctions.java
index ea7724a..8bfa5df 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyFunctions.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyFunctions.java
@@ -21,9 +21,7 @@
import com.google.devtools.build.skyframe.SkyFunctionName;
import com.google.devtools.build.skyframe.SkyKey;
-/**
- * Value types in Skyframe.
- */
+/** Value types in Skyframe. */
public final class SkyFunctions {
public static final SkyFunctionName PRECOMPUTED =
SkyFunctionName.createNonHermetic("PRECOMPUTED");
@@ -120,6 +118,8 @@
public static final SkyFunctionName BUILD_INFO = SkyFunctionName.createHermetic("BUILD_INFO");
public static final SkyFunctionName WORKSPACE_NAME =
SkyFunctionName.createHermetic("WORKSPACE_NAME");
+ static final SkyFunctionName PLATFORM_MAPPING =
+ SkyFunctionName.createHermetic("PLATFORM_MAPPING");
static final SkyFunctionName COVERAGE_REPORT = SkyFunctionName.createHermetic("COVERAGE_REPORT");
public static final SkyFunctionName REPOSITORY = SkyFunctionName.createHermetic("REPOSITORY");
public static final SkyFunctionName REPOSITORY_DIRECTORY =
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/PlatformMappingValueTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/PlatformMappingValueTest.java
new file mode 100644
index 0000000..af50470
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/PlatformMappingValueTest.java
@@ -0,0 +1,224 @@
+// Copyright 2019 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.skyframe;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.devtools.build.lib.analysis.PlatformOptions.LEGACY_DEFAULT_TARGET_PLATFORM;
+import static com.google.devtools.build.lib.testutil.MoreAsserts.assertThrows;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.devtools.build.lib.analysis.PlatformConfiguration;
+import com.google.devtools.build.lib.analysis.PlatformOptions;
+import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
+import com.google.devtools.build.lib.analysis.config.BuildOptions;
+import com.google.devtools.build.lib.analysis.config.CompilationMode;
+import com.google.devtools.build.lib.analysis.config.FragmentOptions;
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.common.options.OptionsParsingException;
+import java.util.Collection;
+import java.util.Set;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link PlatformMappingValue}. */
+@RunWith(JUnit4.class)
+public class PlatformMappingValueTest {
+
+ // We don't actually care about the contents of this set other than that it is passed intact
+ // through the mapping logic. The platform fragment in it is purely an example, it could be any
+ // set of fragments.
+ private static final Set<Class<? extends BuildConfiguration.Fragment>> PLATFORM_FRAGMENT_CLASS =
+ ImmutableSet.of(PlatformConfiguration.class);
+
+ private static final ImmutableList<Class<? extends FragmentOptions>>
+ BUILD_CONFIG_PLATFORM_OPTIONS =
+ ImmutableList.of(BuildConfiguration.Options.class, PlatformOptions.class);
+
+ private static final Label PLATFORM1 = Label.parseAbsoluteUnchecked("//platforms:one");
+ private static final Label PLATFORM2 = Label.parseAbsoluteUnchecked("//platforms:two");
+
+ private static final BuildOptions DEFAULT_BUILD_CONFIG_PLATFORM_OPTIONS =
+ getDefaultBuildConfigPlatformOptions();
+ private static final BuildOptions.OptionsDiffForReconstruction EMPTY_DIFF =
+ BuildOptions.diffForReconstruction(
+ DEFAULT_BUILD_CONFIG_PLATFORM_OPTIONS, DEFAULT_BUILD_CONFIG_PLATFORM_OPTIONS);
+
+ @Test
+ public void testMapNoMappings() throws OptionsParsingException {
+ PlatformMappingValue mappingValue =
+ new PlatformMappingValue(ImmutableMap.of(), ImmutableMap.of());
+
+ BuildConfigurationValue.Key key =
+ BuildConfigurationValue.key(PLATFORM_FRAGMENT_CLASS, EMPTY_DIFF);
+
+ BuildConfigurationValue.Key mapped =
+ mappingValue.map(key, DEFAULT_BUILD_CONFIG_PLATFORM_OPTIONS);
+
+ assertThat(toMappedOptions(mapped).get(PlatformOptions.class).platforms)
+ .containsExactly(LEGACY_DEFAULT_TARGET_PLATFORM);
+ }
+
+ @Test
+ public void testMapPlatformToFlags() throws Exception {
+ ImmutableMap<Label, Collection<String>> platformsToFlags =
+ ImmutableMap.of(PLATFORM1, ImmutableList.of("--cpu=one", "--compilation_mode=dbg"));
+
+ PlatformMappingValue mappingValue =
+ new PlatformMappingValue(platformsToFlags, ImmutableMap.of());
+
+ BuildOptions modifiedOptions = DEFAULT_BUILD_CONFIG_PLATFORM_OPTIONS.clone();
+ modifiedOptions.get(PlatformOptions.class).platforms = ImmutableList.of(PLATFORM1);
+
+ BuildConfigurationValue.Key mapped =
+ mappingValue.map(keyForOptions(modifiedOptions), DEFAULT_BUILD_CONFIG_PLATFORM_OPTIONS);
+
+ assertThat(mapped.getFragments()).isEqualTo(PLATFORM_FRAGMENT_CLASS);
+
+ assertThat(toMappedOptions(mapped).get(BuildConfiguration.Options.class).cpu).isEqualTo("one");
+ }
+
+ @Test
+ public void testMapFlagsToPlatform() throws Exception {
+ ImmutableMap<Collection<String>, Label> flagsToPlatforms =
+ ImmutableMap.of(ImmutableList.of("--cpu=one", "--compilation_mode=dbg"), PLATFORM1);
+
+ PlatformMappingValue mappingValue =
+ new PlatformMappingValue(ImmutableMap.of(), flagsToPlatforms);
+
+ BuildOptions modifiedOptions = DEFAULT_BUILD_CONFIG_PLATFORM_OPTIONS.clone();
+ modifiedOptions.get(BuildConfiguration.Options.class).cpu = "one";
+ modifiedOptions.get(BuildConfiguration.Options.class).compilationMode = CompilationMode.DBG;
+
+ BuildConfigurationValue.Key mapped =
+ mappingValue.map(keyForOptions(modifiedOptions), DEFAULT_BUILD_CONFIG_PLATFORM_OPTIONS);
+
+ assertThat(mapped.getFragments()).isEqualTo(PLATFORM_FRAGMENT_CLASS);
+
+ assertThat(toMappedOptions(mapped).get(PlatformOptions.class).platforms)
+ .containsExactly(PLATFORM1);
+ }
+
+ @Test
+ public void testMapFlagsToPlatformPriority() throws Exception {
+ ImmutableMap<Collection<String>, Label> flagsToPlatforms =
+ ImmutableMap.of(
+ ImmutableList.of("--cpu=foo", "--compilation_mode=dbg"), PLATFORM1,
+ ImmutableList.of("--cpu=foo"), PLATFORM2);
+
+ PlatformMappingValue mappingValue =
+ new PlatformMappingValue(ImmutableMap.of(), flagsToPlatforms);
+
+ BuildOptions modifiedOptions = DEFAULT_BUILD_CONFIG_PLATFORM_OPTIONS.clone();
+ modifiedOptions.get(BuildConfiguration.Options.class).cpu = "foo";
+
+ BuildConfigurationValue.Key mapped =
+ mappingValue.map(keyForOptions(modifiedOptions), DEFAULT_BUILD_CONFIG_PLATFORM_OPTIONS);
+
+ assertThat(toMappedOptions(mapped).get(PlatformOptions.class).platforms)
+ .containsExactly(PLATFORM2);
+ }
+
+ @Test
+ public void testMapFlagsToPlatformNoneMatching() throws Exception {
+ ImmutableMap<Collection<String>, Label> flagsToPlatforms =
+ ImmutableMap.of(ImmutableList.of("--cpu=foo", "--compilation_mode=dbg"), PLATFORM1);
+
+ PlatformMappingValue mappingValue =
+ new PlatformMappingValue(ImmutableMap.of(), flagsToPlatforms);
+
+ BuildOptions modifiedOptions = DEFAULT_BUILD_CONFIG_PLATFORM_OPTIONS.clone();
+ modifiedOptions.get(BuildConfiguration.Options.class).cpu = "bar";
+
+ BuildConfigurationValue.Key mapped =
+ mappingValue.map(keyForOptions(modifiedOptions), DEFAULT_BUILD_CONFIG_PLATFORM_OPTIONS);
+
+ assertThat(toMappedOptions(mapped).get(PlatformOptions.class).platforms)
+ .containsExactly(LEGACY_DEFAULT_TARGET_PLATFORM);
+ }
+
+ @Test
+ public void testMapNoPlatformOptions() throws Exception {
+ ImmutableMap<Collection<String>, Label> flagsToPlatforms =
+ ImmutableMap.of(ImmutableList.of("--cpu=one"), PLATFORM1);
+
+ PlatformMappingValue mappingValue =
+ new PlatformMappingValue(ImmutableMap.of(), flagsToPlatforms);
+
+ BuildOptions options = BuildOptions.of(ImmutableList.of(BuildConfiguration.Options.class));
+
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> mappingValue.map(keyForOptions(options), DEFAULT_BUILD_CONFIG_PLATFORM_OPTIONS));
+ }
+
+ @Test
+ public void testMapNoMappingIfPlatformIsSetButNotMatching() throws Exception {
+ ImmutableMap<Label, Collection<String>> platformsToFlags =
+ ImmutableMap.of(PLATFORM1, ImmutableList.of("--cpu=one", "--compilation_mode=dbg"));
+ ImmutableMap<Collection<String>, Label> flagsToPlatforms =
+ ImmutableMap.of(ImmutableList.of("--cpu=one"), PLATFORM1);
+
+ BuildOptions modifiedOptions = DEFAULT_BUILD_CONFIG_PLATFORM_OPTIONS.clone();
+ modifiedOptions.get(BuildConfiguration.Options.class).cpu = "one";
+ modifiedOptions.get(PlatformOptions.class).platforms = ImmutableList.of(PLATFORM2);
+
+ PlatformMappingValue mappingValue =
+ new PlatformMappingValue(platformsToFlags, flagsToPlatforms);
+
+ BuildConfigurationValue.Key mapped =
+ mappingValue.map(keyForOptions(modifiedOptions), DEFAULT_BUILD_CONFIG_PLATFORM_OPTIONS);
+
+ assertThat(keyForOptions(modifiedOptions)).isEqualTo(mapped);
+ }
+
+ @Test
+ public void testMapNoMappingIfPlatformIsSetAndNoPlatformMapping() throws Exception {
+ ImmutableMap<Collection<String>, Label> flagsToPlatforms =
+ ImmutableMap.of(ImmutableList.of("--cpu=one"), PLATFORM1);
+
+ BuildOptions modifiedOptions = DEFAULT_BUILD_CONFIG_PLATFORM_OPTIONS.clone();
+ modifiedOptions.get(BuildConfiguration.Options.class).cpu = "one";
+ modifiedOptions.get(PlatformOptions.class).platforms = ImmutableList.of(PLATFORM2);
+
+ PlatformMappingValue mappingValue =
+ new PlatformMappingValue(ImmutableMap.of(), flagsToPlatforms);
+
+ BuildConfigurationValue.Key mapped =
+ mappingValue.map(keyForOptions(modifiedOptions), DEFAULT_BUILD_CONFIG_PLATFORM_OPTIONS);
+
+ assertThat(keyForOptions(modifiedOptions)).isEqualTo(mapped);
+ }
+
+ private BuildOptions toMappedOptions(BuildConfigurationValue.Key mapped) {
+ return DEFAULT_BUILD_CONFIG_PLATFORM_OPTIONS.applyDiff(mapped.getOptionsDiff());
+ }
+
+ private static BuildOptions getDefaultBuildConfigPlatformOptions() {
+ try {
+ return BuildOptions.of(BUILD_CONFIG_PLATFORM_OPTIONS);
+ } catch (OptionsParsingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private BuildConfigurationValue.Key keyForOptions(BuildOptions modifiedOptions) {
+ BuildOptions.OptionsDiffForReconstruction diff =
+ BuildOptions.diffForReconstruction(DEFAULT_BUILD_CONFIG_PLATFORM_OPTIONS, modifiedOptions);
+
+ return BuildConfigurationValue.key(PLATFORM_FRAGMENT_CLASS, diff);
+ }
+}