Skip to content

Commit 9c230f1

Browse files
aragoscopybara-github
authored andcommitted
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 #6426 RELNOTES: None. PiperOrigin-RevId: 238298127
1 parent 09c6b7a commit 9c230f1

File tree

6 files changed

+482
-50
lines changed

6 files changed

+482
-50
lines changed

src/main/java/com/google/devtools/build/lib/analysis/PlatformConfigurationLoader.java

+2-30
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,11 @@
1616

1717
import com.google.common.collect.ImmutableList;
1818
import com.google.common.collect.ImmutableSet;
19-
import com.google.common.collect.Iterables;
2019
import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
2120
import com.google.devtools.build.lib.analysis.config.BuildOptions;
2221
import com.google.devtools.build.lib.analysis.config.ConfigurationFragmentFactory;
2322
import com.google.devtools.build.lib.analysis.config.FragmentOptions;
2423
import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException;
25-
import com.google.devtools.build.lib.cmdline.Label;
2624

2725
/** A loader that creates {@link PlatformConfiguration} instances based on command-line options. */
2826
public class PlatformConfigurationLoader implements ConfigurationFragmentFactory {
@@ -35,36 +33,10 @@ public ImmutableSet<Class<? extends FragmentOptions>> requiredOptions() {
3533
public PlatformConfiguration create(BuildOptions buildOptions)
3634
throws InvalidConfigurationException {
3735
PlatformOptions platformOptions = buildOptions.get(PlatformOptions.class);
38-
39-
// Handle default values for the host and target platform.
40-
// TODO(https://github.com/bazelbuild/bazel/issues/6849): After migration, set the defaults
41-
// directly.
42-
Label hostPlatform;
43-
if (platformOptions.hostPlatform != null) {
44-
hostPlatform = platformOptions.hostPlatform;
45-
} else if (platformOptions.autoConfigureHostPlatform) {
46-
// Use the auto-configured host platform.
47-
hostPlatform = PlatformOptions.DEFAULT_HOST_PLATFORM;
48-
} else {
49-
// Use the legacy host platform.
50-
hostPlatform = PlatformOptions.LEGACY_DEFAULT_HOST_PLATFORM;
51-
}
52-
53-
Label targetPlatform;
54-
if (!platformOptions.platforms.isEmpty()) {
55-
targetPlatform = Iterables.getFirst(platformOptions.platforms, null);
56-
} else if (platformOptions.autoConfigureHostPlatform) {
57-
// Default to the host platform, whatever it is.
58-
targetPlatform = hostPlatform;
59-
} else {
60-
// Use the legacy target platform
61-
targetPlatform = PlatformOptions.LEGACY_DEFAULT_TARGET_PLATFORM;
62-
}
63-
6436
return new PlatformConfiguration(
65-
hostPlatform,
37+
platformOptions.computeHostPlatform(),
6638
ImmutableList.copyOf(platformOptions.extraExecutionPlatforms),
67-
targetPlatform,
39+
platformOptions.computeTargetPlatform(),
6840
ImmutableList.copyOf(platformOptions.extraToolchains),
6941
ImmutableList.copyOf(platformOptions.enabledToolchainTypes));
7042
}

src/main/java/com/google/devtools/build/lib/analysis/PlatformOptions.java

+52-17
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package com.google.devtools.build.lib.analysis;
1616

1717
import com.google.common.collect.ImmutableList;
18+
import com.google.common.collect.Iterables;
1819
import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
1920
import com.google.devtools.build.lib.analysis.config.BuildConfiguration.LabelListConverter;
2021
import com.google.devtools.build.lib.analysis.config.FragmentOptions;
@@ -101,23 +102,22 @@ public class PlatformOptions extends FragmentOptions {
101102
public List<String> extraToolchains;
102103

103104
@Option(
104-
name = "toolchain_resolution_override",
105-
allowMultiple = true,
106-
defaultValue = "",
107-
documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
108-
effectTags = {
109-
OptionEffectTag.AFFECTS_OUTPUTS,
110-
OptionEffectTag.CHANGES_INPUTS,
111-
OptionEffectTag.LOADING_AND_ANALYSIS
112-
},
113-
deprecationWarning =
114-
"toolchain_resolution_override is now a no-op and will be removed in"
115-
+ " an upcoming release",
116-
help =
117-
"Override toolchain resolution for a toolchain type with a specific toolchain. "
118-
+ "Example: --toolchain_resolution_override=@io_bazel_rules_go//:toolchain="
119-
+ "@io_bazel_rules_go//:linux-arm64-toolchain"
120-
)
105+
name = "toolchain_resolution_override",
106+
allowMultiple = true,
107+
defaultValue = "",
108+
documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
109+
effectTags = {
110+
OptionEffectTag.AFFECTS_OUTPUTS,
111+
OptionEffectTag.CHANGES_INPUTS,
112+
OptionEffectTag.LOADING_AND_ANALYSIS
113+
},
114+
deprecationWarning =
115+
"toolchain_resolution_override is now a no-op and will be removed in"
116+
+ " an upcoming release",
117+
help =
118+
"Override toolchain resolution for a toolchain type with a specific toolchain. "
119+
+ "Example: --toolchain_resolution_override=@io_bazel_rules_go//:toolchain="
120+
+ "@io_bazel_rules_go//:linux-arm64-toolchain")
121121
public List<String> toolchainResolutionOverrides;
122122

123123
@Option(
@@ -184,4 +184,39 @@ public PlatformOptions getHost() {
184184
host.useToolchainResolutionForJavaRules = this.useToolchainResolutionForJavaRules;
185185
return host;
186186
}
187+
188+
/** Returns the intended target platform value based on options defined in this fragment. */
189+
public Label computeTargetPlatform() {
190+
// Handle default values for the host and target platform.
191+
// TODO(https://github.com/bazelbuild/bazel/issues/6849): After migration, set the defaults
192+
// directly.
193+
194+
if (!platforms.isEmpty()) {
195+
return Iterables.getFirst(platforms, null);
196+
} else if (autoConfigureHostPlatform) {
197+
// Default to the host platform, whatever it is.
198+
return computeHostPlatform();
199+
} else {
200+
// Use the legacy target platform
201+
return LEGACY_DEFAULT_TARGET_PLATFORM;
202+
}
203+
}
204+
205+
/** Returns the intended host platform value based on options defined in this fragment. */
206+
public Label computeHostPlatform() {
207+
// Handle default values for the host and target platform.
208+
// TODO(https://github.com/bazelbuild/bazel/issues/6849): After migration, set the defaults
209+
// directly.
210+
211+
Label hostPlatform;
212+
if (this.hostPlatform != null) {
213+
return this.hostPlatform;
214+
} else if (autoConfigureHostPlatform) {
215+
// Use the auto-configured host platform.
216+
return DEFAULT_HOST_PLATFORM;
217+
} else {
218+
// Use the legacy host platform.
219+
return LEGACY_DEFAULT_HOST_PLATFORM;
220+
}
221+
}
187222
}

src/main/java/com/google/devtools/build/lib/analysis/config/BuildOptions.java

+5
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,11 @@ public Collection<FragmentOptions> getNativeOptions() {
192192
return fragmentOptionsMap.values();
193193
}
194194

195+
/** Returns the set of fragment classes contained in these options. */
196+
public Set<Class<? extends FragmentOptions>> getFragmentClasses() {
197+
return fragmentOptionsMap.keySet();
198+
}
199+
195200
public ImmutableMap<Label, Object> getStarlarkOptions() {
196201
return skylarkOptionsMap;
197202
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
// Copyright 2019 The Bazel Authors. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.devtools.build.lib.skyframe;
16+
17+
import com.google.common.base.Preconditions;
18+
import com.google.common.collect.ImmutableList;
19+
import com.google.common.collect.Interner;
20+
import com.google.common.collect.Iterables;
21+
import com.google.devtools.build.lib.analysis.PlatformOptions;
22+
import com.google.devtools.build.lib.analysis.config.BuildOptions;
23+
import com.google.devtools.build.lib.cmdline.Label;
24+
import com.google.devtools.build.lib.concurrent.BlazeInterners;
25+
import com.google.devtools.build.lib.concurrent.ThreadSafety;
26+
import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
27+
import com.google.devtools.build.lib.vfs.RootedPath;
28+
import com.google.devtools.build.skyframe.SkyFunctionName;
29+
import com.google.devtools.build.skyframe.SkyKey;
30+
import com.google.devtools.build.skyframe.SkyValue;
31+
import com.google.devtools.common.options.OptionsParser;
32+
import com.google.devtools.common.options.OptionsParsingException;
33+
import com.google.devtools.common.options.OptionsParsingResult;
34+
import java.util.Collection;
35+
import java.util.List;
36+
import java.util.Map;
37+
import java.util.Objects;
38+
39+
/**
40+
* Stores contents of a platforms/flags mapping file for transforming one {@link
41+
* BuildConfigurationValue.Key} into another.
42+
*
43+
* <p>See <a href=https://docs.google.com/document/d/1Vg_tPgiZbSrvXcJ403vZVAGlsWhH9BUDrAxMOYnO0Ls>
44+
* the design</a> for more details on how the mapping can be defined and the desired logic on how it
45+
* is applied to configuration keys.
46+
*/
47+
public final class PlatformMappingValue implements SkyValue {
48+
49+
/** Key for {@link PlatformMappingValue} based on the location of the mapping file. */
50+
@ThreadSafety.Immutable
51+
@AutoCodec
52+
public static final class Key implements SkyKey {
53+
private static final Interner<Key> interner = BlazeInterners.newWeakInterner();
54+
55+
private final RootedPath path;
56+
57+
private Key(RootedPath path) {
58+
this.path = path;
59+
}
60+
61+
@AutoCodec.VisibleForSerialization
62+
@AutoCodec.Instantiator
63+
static Key create(RootedPath path) {
64+
return interner.intern(new Key(path));
65+
}
66+
67+
@Override
68+
public SkyFunctionName functionName() {
69+
return SkyFunctions.PLATFORM_MAPPING;
70+
}
71+
72+
@Override
73+
public boolean equals(Object o) {
74+
if (this == o) {
75+
return true;
76+
}
77+
if (o == null || getClass() != o.getClass()) {
78+
return false;
79+
}
80+
Key key = (Key) o;
81+
return Objects.equals(path, key.path);
82+
}
83+
84+
@Override
85+
public int hashCode() {
86+
return Objects.hash(path);
87+
}
88+
89+
@Override
90+
public String toString() {
91+
return "PlatformMappingValue.Key{" + "path=" + path + '}';
92+
}
93+
}
94+
95+
private final Map<Label, Collection<String>> platformsToFlags;
96+
private final Map<Collection<String>, Label> flagsToPlatforms;
97+
98+
/**
99+
* Creates a new mapping value which will match on the given platforms (if a target platform is
100+
* set on the key to be mapped), otherwise on the set of flags.
101+
*
102+
* @param platformsToFlags mapping from target platform label to the command line style flags that
103+
* should be parsed & modified if that platform is set
104+
* @param flagsToPlatforms mapping from a collection of command line style flags to a target
105+
* platform that should be set if the flags match the mapped options
106+
*/
107+
PlatformMappingValue(
108+
Map<Label, Collection<String>> platformsToFlags,
109+
Map<Collection<String>, Label> flagsToPlatforms) {
110+
this.platformsToFlags = platformsToFlags;
111+
this.flagsToPlatforms = flagsToPlatforms;
112+
}
113+
114+
/**
115+
* Maps one {@link BuildConfigurationValue.Key} to another by way of mappings provided in a file.
116+
*
117+
* <p>The <a href=https://docs.google.com/document/d/1Vg_tPgiZbSrvXcJ403vZVAGlsWhH9BUDrAxMOYnO0Ls>
118+
* full design</a> contains the details for the mapping logic but in short:
119+
*
120+
* <ol>
121+
* <li>If a target platform is set on the original then mappings from platform to flags will be
122+
* applied.
123+
* <li>If no target platform is set then mappings from flags to platforms will be applied.
124+
* <li>If no matching flags to platforms mapping was found, the default target platform will be
125+
* used.
126+
* </ol>
127+
*
128+
* @param original the key representing the configuration to be mapped
129+
* @param defaultBuildOptions build options as set by default in this server
130+
* @return the mapped key if any mapping matched the original or else the original
131+
* @throws OptionsParsingException if any of the user configured flags cannot be parsed
132+
* @throws IllegalArgumentException if the original does not contain a {@link PlatformOptions}
133+
* fragment
134+
*/
135+
public BuildConfigurationValue.Key map(
136+
BuildConfigurationValue.Key original, BuildOptions defaultBuildOptions)
137+
throws OptionsParsingException {
138+
BuildOptions.OptionsDiffForReconstruction originalDiff = original.getOptionsDiff();
139+
BuildOptions originalOptions = defaultBuildOptions.applyDiff(originalDiff);
140+
141+
Preconditions.checkArgument(
142+
originalOptions.contains(PlatformOptions.class),
143+
"When using platform mappings, all configurations must contain platform options");
144+
145+
BuildOptions modifiedOptions = null;
146+
147+
if (!originalOptions.get(PlatformOptions.class).platforms.isEmpty()) {
148+
List<Label> platforms = originalOptions.get(PlatformOptions.class).platforms;
149+
150+
Preconditions.checkArgument(
151+
platforms.size() == 1,
152+
"Platform mapping only supports a single target platform but found %s",
153+
platforms);
154+
155+
Label targetPlatform = Iterables.getOnlyElement(platforms);
156+
if (!platformsToFlags.containsKey(targetPlatform)) {
157+
// This can happen if the user has set the platform and any other flags that would normally
158+
// be mapped from it on the command line instead of relying on the mapping.
159+
return original;
160+
}
161+
162+
OptionsParsingResult parsingResult =
163+
parse(platformsToFlags.get(targetPlatform), defaultBuildOptions);
164+
modifiedOptions = originalOptions.applyParsingResult(parsingResult);
165+
} else {
166+
boolean mappingFound = false;
167+
for (Map.Entry<Collection<String>, Label> flagsToPlatform : flagsToPlatforms.entrySet()) {
168+
if (originalOptions.matches(parse(flagsToPlatform.getKey(), defaultBuildOptions))) {
169+
modifiedOptions = originalOptions.clone();
170+
modifiedOptions.get(PlatformOptions.class).platforms =
171+
ImmutableList.of(flagsToPlatform.getValue());
172+
mappingFound = true;
173+
break;
174+
}
175+
}
176+
177+
if (!mappingFound) {
178+
Label targetPlatform = originalOptions.get(PlatformOptions.class).computeTargetPlatform();
179+
modifiedOptions = originalOptions.clone();
180+
modifiedOptions.get(PlatformOptions.class).platforms = ImmutableList.of(targetPlatform);
181+
}
182+
}
183+
184+
return BuildConfigurationValue.key(
185+
original.getFragments(),
186+
BuildOptions.diffForReconstruction(defaultBuildOptions, modifiedOptions));
187+
}
188+
189+
private OptionsParsingResult parse(Iterable<String> args, BuildOptions defaultBuildOptions)
190+
throws OptionsParsingException {
191+
OptionsParser parser = OptionsParser.newOptionsParser(defaultBuildOptions.getFragmentClasses());
192+
parser.parse(ImmutableList.copyOf(args));
193+
// TODO(schmitt): Parse starlark options as well.
194+
return parser;
195+
}
196+
}

src/main/java/com/google/devtools/build/lib/skyframe/SkyFunctions.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,7 @@
2121
import com.google.devtools.build.skyframe.SkyFunctionName;
2222
import com.google.devtools.build.skyframe.SkyKey;
2323

24-
/**
25-
* Value types in Skyframe.
26-
*/
24+
/** Value types in Skyframe. */
2725
public final class SkyFunctions {
2826
public static final SkyFunctionName PRECOMPUTED =
2927
SkyFunctionName.createNonHermetic("PRECOMPUTED");
@@ -120,6 +118,8 @@ public final class SkyFunctions {
120118
public static final SkyFunctionName BUILD_INFO = SkyFunctionName.createHermetic("BUILD_INFO");
121119
public static final SkyFunctionName WORKSPACE_NAME =
122120
SkyFunctionName.createHermetic("WORKSPACE_NAME");
121+
static final SkyFunctionName PLATFORM_MAPPING =
122+
SkyFunctionName.createHermetic("PLATFORM_MAPPING");
123123
static final SkyFunctionName COVERAGE_REPORT = SkyFunctionName.createHermetic("COVERAGE_REPORT");
124124
public static final SkyFunctionName REPOSITORY = SkyFunctionName.createHermetic("REPOSITORY");
125125
public static final SkyFunctionName REPOSITORY_DIRECTORY =

0 commit comments

Comments
 (0)