Skip to content

Commit 1c3a245

Browse files
gregestrencopybara-github
authored andcommitted
Support select() on constraint_value for aliases.
This implements approach bazelbuild#4 of bazelbuild#13047 (comment). The basic change adds a new toolchain resolution mode: "resolve iff the target has a select()". It then sets alias() to that mode. We could remove this special casing if we ever ubiquitously provide platform info to *all* rules (bazelbuild#12899 (comment)). RELNOTES: alias() can now select() directly on constraint_value() Fixes bazelbuild#13047. Closes bazelbuild#14310. PiperOrigin-RevId: 411868223
1 parent 89ea68b commit 1c3a245

File tree

8 files changed

+111
-13
lines changed

8 files changed

+111
-13
lines changed

src/main/java/com/google/devtools/build/lib/packages/Rule.java

+26
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import com.google.devtools.build.lib.packages.ImplicitOutputsFunction.StarlarkImplicitOutputsFunction;
3636
import com.google.devtools.build.lib.packages.License.DistributionType;
3737
import com.google.devtools.build.lib.packages.Package.ConfigSettingVisibilityPolicy;
38+
import com.google.devtools.build.lib.packages.RuleClass.ToolchainResolutionMode;
3839
import java.util.ArrayList;
3940
import java.util.Collection;
4041
import java.util.HashSet;
@@ -942,6 +943,31 @@ public Collection<Label> getAspectLabelsSuperset(DependencyFilter predicate) {
942943
return labels.values();
943944
}
944945

946+
/**
947+
* Should this rule instance resolve toolchains?
948+
*
949+
* <p>This may happen for two reasons:
950+
*
951+
* <ol>
952+
* <li>The rule uses toolchains by definition ({@link
953+
* RuleClass.Builder#useToolchainResolution(ToolchainResolutionMode)}
954+
* <li>The rule instance has a select(), which means it may depend on target platform properties
955+
* that are only provided when toolchain resolution is enabled.
956+
* </ol>
957+
*/
958+
public boolean useToolchainResolution() {
959+
ToolchainResolutionMode mode = ruleClass.useToolchainResolution();
960+
if (mode.isActive()) {
961+
return true;
962+
} else if (mode == ToolchainResolutionMode.HAS_SELECT) {
963+
RawAttributeMapper attr = RawAttributeMapper.of(this);
964+
return (attr.has(RuleClass.CONFIG_SETTING_DEPS_ATTRIBUTE)
965+
&& !attr.get(RuleClass.CONFIG_SETTING_DEPS_ATTRIBUTE, BuildType.LABEL_LIST).isEmpty());
966+
} else {
967+
return false;
968+
}
969+
}
970+
945971
/**
946972
* @return The repository name.
947973
*/

src/main/java/com/google/devtools/build/lib/packages/RuleClass.java

+27-3
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,22 @@ public enum ToolchainResolutionMode {
221221
ENABLED,
222222
/** The rule should not use toolchain resolution. */
223223
DISABLED,
224+
/**
225+
* The rule instance uses toolchain resolution if it has a select().
226+
*
227+
* <p>This is for rules that don't intrinsically use toolchains but have select()s on {@link
228+
* com.google.devtools.build.lib.rules.platform.ConstraintValue}, which are part of the build's
229+
* platform. Such instances need to know what platform the build is targeting, which Bazel won't
230+
* provide unless toolchain resolution is enabled.
231+
*
232+
* <p>This is set statically in rule definitions on an opt-in basis. Bazel doesn't automatically
233+
* infer this for any target with a select().
234+
*
235+
* <p>Ultimately, we should remove this when <a
236+
* href="https://github.com/bazelbuild/bazel/issues/12899#issuecomment-767759147}#12899</a>is
237+
* addressed, so platforms are unconditionally provided for all rules.
238+
*/
239+
HAS_SELECT,
224240
/** The rule should inherit the value from its parent rules. */
225241
INHERIT;
226242

@@ -245,6 +261,7 @@ boolean isActive() {
245261
case ENABLED:
246262
return true;
247263
case DISABLED:
264+
case HAS_SELECT: // Not true for RuleClass, but Rule may enable it.
248265
return false;
249266
default:
250267
}
@@ -380,7 +397,7 @@ public RuleErrorException(String message, Throwable cause) {
380397
* normal dependency resolution because they're needed to determine other dependencies. So there's
381398
* no intrinsic reason why we need an extra attribute to store them.
382399
*
383-
* <p>There are three reasons why we still create this attribute:
400+
* <p>There are four reasons why we still create this attribute:
384401
*
385402
* <ol>
386403
* <li>Collecting them once in {@link #populateRuleAttributeValues} instead of multiple times in
@@ -390,6 +407,9 @@ public RuleErrorException(String message, Throwable cause) {
390407
* we need to make sure its coverage remains complete.
391408
* <li>Manual configuration trimming uses the normal dependency resolution process to work
392409
* correctly and config_setting keys are subject to this trimming.
410+
* <li>{@link Rule#useToolchainResolution() supports conditional toolchain resolution for
411+
* targets with non-empty select()s. This requirement would go away if platform info was
412+
* prepared for all rules regardless of toolchain needs.
393413
* </ol>
394414
*
395415
* <p>It should be possible to clean up these issues if we decide we don't want an artificial
@@ -2753,8 +2773,12 @@ public ImmutableSet<Label> getRequiredToolchains() {
27532773
return requiredToolchains;
27542774
}
27552775

2756-
public boolean useToolchainResolution() {
2757-
return this.useToolchainResolution.isActive();
2776+
/**
2777+
* Public callers should use {@link Rule#useToolchainResolution()}, which also takes into account
2778+
* target-specific information.
2779+
*/
2780+
ToolchainResolutionMode useToolchainResolution() {
2781+
return this.useToolchainResolution;
27582782
}
27592783

27602784
public boolean useToolchainTransition() {

src/main/java/com/google/devtools/build/lib/query2/cquery/ConfiguredTargetAccessor.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ private static ToolchainCollection<ToolchainContext> getToolchainContexts(
211211
}
212212

213213
Rule rule = ((Rule) target);
214-
if (!rule.getRuleClassObject().useToolchainResolution()) {
214+
if (!rule.useToolchainResolution()) {
215215
return null;
216216
}
217217

src/main/java/com/google/devtools/build/lib/rules/Alias.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,9 @@ public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment envi
8181
.canHaveAnyProvider()
8282
// Aliases themselves do not need toolchains or an execution platform, so this is fine.
8383
// The actual target will resolve platforms and toolchains with no issues regardless of
84-
// this setting.
85-
.useToolchainResolution(ToolchainResolutionMode.DISABLED)
84+
// this setting. The only time an alias directly needs the platform is when it has a
85+
// select() on a constraint_setting, so special-case enable those instances too.
86+
.useToolchainResolution(ToolchainResolutionMode.HAS_SELECT)
8687
.build();
8788
}
8889

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,7 @@ public static ComputedToolchainContexts computeUnloadedToolchainContexts(
485485
ExecGroupCollection.builder(defaultExecGroup, rule.getRuleClassObject().getExecGroups());
486486

487487
// Short circuit and end now if this target doesn't require toolchain resolution.
488-
if (!rule.getRuleClassObject().useToolchainResolution()) {
488+
if (!rule.useToolchainResolution()) {
489489
ComputedToolchainContexts result = new ComputedToolchainContexts();
490490
result.execGroupCollectionBuilder = execGroupCollectionBuilder;
491491
return result;

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

+7-5
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
import com.google.devtools.build.lib.packages.NoSuchTargetException;
3030
import com.google.devtools.build.lib.packages.NoSuchThingException;
3131
import com.google.devtools.build.lib.packages.Package;
32-
import com.google.devtools.build.lib.packages.RuleClass;
32+
import com.google.devtools.build.lib.packages.Rule;
3333
import com.google.devtools.build.lib.packages.Target;
3434
import com.google.devtools.build.lib.server.FailureDetails.Toolchain.Code;
3535
import com.google.devtools.build.skyframe.SkyFunction.Environment;
@@ -161,16 +161,18 @@ private static PlatformInfo findPlatformInfo(
161161
}
162162

163163
static boolean hasPlatformInfo(Target target) {
164+
Rule rule = target.getAssociatedRule();
164165
// If the rule uses toolchain resolution, it can't be used as a target or exec platform.
165-
if (target.getAssociatedRule() == null) {
166+
if (rule == null) {
166167
return false;
167168
}
168-
RuleClass ruleClass = target.getAssociatedRule().getRuleClassObject();
169-
if (ruleClass == null || ruleClass.useToolchainResolution()) {
169+
if (rule.useToolchainResolution()) {
170170
return false;
171171
}
172172

173-
return ruleClass.getAdvertisedProviders().advertises(PlatformInfo.PROVIDER.id());
173+
return rule.getRuleClassObject()
174+
.getAdvertisedProviders()
175+
.advertises(PlatformInfo.PROVIDER.id());
174176
}
175177

176178
/** Exception used when a platform label is not a valid platform. */

src/test/java/com/google/devtools/build/lib/analysis/ConfigurableAttributesTest.java

+43
Original file line numberDiff line numberDiff line change
@@ -1290,6 +1290,49 @@ public void nonToolchainResolvingTargetsCantSelectDirectlyOnConstraints() throws
12901290
assertContainsEvent("//conditions:apple is not a valid select() condition for //check:hello");
12911291
}
12921292

1293+
@Test
1294+
public void selectOnlyToolchainResolvingTargetsCanSelectDirectlyOnConstraints() throws Exception {
1295+
// Tests select()ing directly on a constraint_value when the rule uses toolchain resolution
1296+
// *only if it has a select()*. As of this test, alias() is the only rule that supports that
1297+
// (see Alias#useToolchainResolution(ToolchainResolutionMode.HAS_SELECT).
1298+
scratch.file(
1299+
"conditions/BUILD",
1300+
"constraint_setting(name = 'fruit')",
1301+
"constraint_value(name = 'apple', constraint_setting = 'fruit')",
1302+
"constraint_value(name = 'banana', constraint_setting = 'fruit')",
1303+
"platform(",
1304+
" name = 'apple_platform',",
1305+
" constraint_values = [':apple'],",
1306+
")");
1307+
scratch.file(
1308+
"check/defs.bzl",
1309+
"def _impl(ctx):",
1310+
" pass",
1311+
"simple_rule = rule(",
1312+
" implementation = _impl,",
1313+
" attrs = {}",
1314+
")");
1315+
scratch.file(
1316+
"check/BUILD",
1317+
"load('//check:defs.bzl', 'simple_rule')",
1318+
"filegroup(name = 'bdep', srcs = ['bfile'])",
1319+
"simple_rule(name = 'hello')",
1320+
"simple_rule(name = 'tere')",
1321+
"alias(",
1322+
" name = 'selectable_alias',",
1323+
" actual = select({",
1324+
" '//conditions:apple': ':hello',",
1325+
" '//conditions:banana': ':tere',",
1326+
" }))");
1327+
useConfiguration("--platforms=//conditions:apple_platform");
1328+
assertThat(
1329+
getConfiguredTarget("//check:selectable_alias")
1330+
.getActual()
1331+
.getLabel()
1332+
.getCanonicalForm())
1333+
.isEqualTo("//check:hello");
1334+
}
1335+
12931336
@Test
12941337
public void multipleMatchErrorWhenAliasResolvesToSameSetting() throws Exception {
12951338
scratch.file(

src/test/java/com/google/devtools/build/lib/query2/cquery/ProtoOutputFormatterCallbackTest.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,9 @@ public void testAlias_withSelect() throws Exception {
203203
myAliasRuleProto.forEach(
204204
configuredTarget -> depNames.add(configuredTarget.getTarget().getRule().getName()));
205205
assertThat(depNames)
206-
.containsExactly("//test:my_alias_rule", "//test:config1", "//test:target1");
206+
// The alias also includes platform info since aliases with select() trigger toolchain
207+
// resolution. We're not interested in those here.
208+
.containsAtLeast("//test:my_alias_rule", "//test:config1", "//test:target1");
207209
}
208210

209211
private MockRule getSimpleRule() {

0 commit comments

Comments
 (0)