Create the native version of label_setting and label_flag.
See the javadoc of LabelBuildSettings for explanation on why these are defined natively instead of in starlark (at least for V1 of SBC). TL;DR - free-for-all implementation functions for label-typed build settings are dangerous since we actually return providers of the targets that correspond to those labels.
context: https://docs.google.com/document/d/1vc8v-kXjvgZOdQdnxPTaV0rrLxtP2XwnD2tAZlYJOqw/edit#bookmark=id.a6x3vcng0anx
PiperOrigin-RevId: 226210154
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/GenericRules.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/GenericRules.java
index 2b81f74..107faf5 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/GenericRules.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/GenericRules.java
@@ -19,6 +19,8 @@
import com.google.devtools.build.lib.analysis.constraints.EnvironmentRule;
import com.google.devtools.build.lib.bazel.rules.common.BazelFilegroupRule;
import com.google.devtools.build.lib.rules.Alias.AliasRule;
+import com.google.devtools.build.lib.rules.LabelBuildSettings.LabelBuildFlagRule;
+import com.google.devtools.build.lib.rules.LabelBuildSettings.LabelBuildSettingRule;
import com.google.devtools.build.lib.rules.core.CoreRules;
import com.google.devtools.build.lib.rules.genquery.GenQueryRule;
import com.google.devtools.build.lib.rules.test.TestSuiteRule;
@@ -43,6 +45,8 @@
builder.addRuleDefinition(new BazelFilegroupRule());
builder.addRuleDefinition(new TestSuiteRule());
builder.addRuleDefinition(new GenQueryRule());
+ builder.addRuleDefinition(new LabelBuildSettingRule());
+ builder.addRuleDefinition(new LabelBuildFlagRule());
try {
builder.addWorkspaceFilePrefix(
diff --git a/src/main/java/com/google/devtools/build/lib/packages/RuleClass.java b/src/main/java/com/google/devtools/build/lib/packages/RuleClass.java
index 6a93d24..28184d6 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/RuleClass.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/RuleClass.java
@@ -14,6 +14,7 @@
package com.google.devtools.build.lib.packages;
+import static com.google.devtools.build.lib.packages.Attribute.ANY_RULE;
import static com.google.devtools.build.lib.packages.Attribute.attr;
import static com.google.devtools.build.lib.packages.BuildType.LABEL_LIST;
import static com.google.devtools.build.lib.syntax.Type.BOOLEAN;
@@ -616,6 +617,9 @@
*/
public static final String SKYLARK_BUILD_SETTING_DEFAULT_ATTR_NAME = "build_setting_default";
+ public static final String BUILD_SETTING_DEFAULT_NONCONFIGURABLE =
+ "Build setting defaults are referenced during analysis.";
+
/** List of required attributes for normal rules, name and type. */
public static final ImmutableList<Attribute> REQUIRED_ATTRIBUTES_FOR_NORMAL_RULES =
ImmutableList.of(attr("tags", Type.STRING_LIST).build());
@@ -784,10 +788,11 @@
Type<?> type = buildSetting.getType();
Attribute.Builder<?> attrBuilder =
attr(SKYLARK_BUILD_SETTING_DEFAULT_ATTR_NAME, type)
- .nonconfigurable("Build setting defaults are referenced during analysis.")
+ .nonconfigurable(BUILD_SETTING_DEFAULT_NONCONFIGURABLE)
.mandatory();
if (BuildType.isLabelType(type)) {
attrBuilder.allowedFileTypes(FileTypeSet.ANY_FILE);
+ attrBuilder.allowedRuleClasses(ANY_RULE);
}
this.add(attrBuilder);
}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/LabelBuildSettings.java b/src/main/java/com/google/devtools/build/lib/rules/LabelBuildSettings.java
new file mode 100644
index 0000000..90029aa
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/LabelBuildSettings.java
@@ -0,0 +1,108 @@
+// Copyright 2018 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.rules;
+
+import static com.google.devtools.build.lib.packages.Attribute.attr;
+import static com.google.devtools.build.lib.packages.BuildType.LABEL;
+import static com.google.devtools.build.lib.packages.RuleClass.Builder.SKYLARK_BUILD_SETTING_DEFAULT_ATTR_NAME;
+
+import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment;
+import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.packages.Attribute.LabelLateBoundDefault;
+import com.google.devtools.build.lib.packages.BuildSetting;
+import com.google.devtools.build.lib.packages.RuleClass;
+import com.google.devtools.build.lib.rules.LateBoundAlias.CommonAliasRule;
+import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
+import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec.VisibleForSerialization;
+
+/**
+ * Native implementation of label setting and flags.
+ *
+ * <p>While most build settings are completely defined in starlark, we're natively defining
+ * label-typed ones because:
+ *
+ * <ul>
+ * <li>they're essentially special Alias targets
+ * <li>we don't have a known use case where you'd want to manipulate a label-typed build setting
+ * in its implementation section.
+ * </ul>
+ *
+ * <p>Once we do have (2), we can consider switching over to starlark implementation. The dangers
+ * there involve the implementation function returning a label we've never seen before in the build.
+ * And since label-typed build settings actually return the providers of the targets they point to,
+ * we'd have to be able to load and configure potentially arbitrary labels on the fly. This is not
+ * possible today and could easily introduce large performance issues.
+ */
+public class LabelBuildSettings {
+ @AutoCodec @VisibleForSerialization
+ // TODO(b/65746853): find a way to do this without passing the entire BuildConfiguration
+ static final LabelLateBoundDefault<BuildConfiguration> ACTUAL =
+ LabelLateBoundDefault.fromTargetConfiguration(
+ BuildConfiguration.class,
+ null,
+ (rule, attributes, configuration) -> {
+ if (rule == null || configuration == null) {
+ return attributes.get(SKYLARK_BUILD_SETTING_DEFAULT_ATTR_NAME, LABEL);
+ }
+ Object commandLineValue =
+ configuration.getOptions().getStarlarkOptions().get(rule.getLabel());
+ return commandLineValue == null
+ ? attributes.get(SKYLARK_BUILD_SETTING_DEFAULT_ATTR_NAME, LABEL)
+ : (Label) commandLineValue;
+ });
+
+ private static RuleClass buildRuleClass(RuleClass.Builder builder, boolean flag) {
+ return builder
+ .removeAttribute("licenses")
+ .removeAttribute("distribs")
+ .add(attr(":alias", LABEL).value(ACTUAL))
+ .setBuildSetting(new BuildSetting(flag, LABEL))
+ .canHaveAnyProvider()
+ .supportsPlatforms(false)
+ .build();
+ }
+
+ /**
+ * Rule definition of label_setting TODO(b/110417082): documentation when other documentation on
+ * SBC exists and we can point to it.
+ */
+ public static class LabelBuildSettingRule extends CommonAliasRule<BuildConfiguration> {
+
+ public LabelBuildSettingRule() {
+ super("label_setting", env -> ACTUAL, BuildConfiguration.class);
+ }
+
+ @Override
+ public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment environment) {
+ return buildRuleClass(builder, false);
+ }
+ }
+
+ /**
+ * Rule definition of label_flag TODO(b/110417082): documentation when other documentation on SBC
+ * exists and we can point to it.
+ */
+ public static class LabelBuildFlagRule extends CommonAliasRule<BuildConfiguration> {
+
+ public LabelBuildFlagRule() {
+ super("label_flag", env -> ACTUAL, BuildConfiguration.class);
+ }
+
+ @Override
+ public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment environment) {
+ return buildRuleClass(builder, true);
+ }
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/rules/LabelBuildSettingTest.java b/src/test/java/com/google/devtools/build/lib/rules/LabelBuildSettingTest.java
new file mode 100644
index 0000000..916fd0c
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/rules/LabelBuildSettingTest.java
@@ -0,0 +1,119 @@
+// Copyright 2018 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.rules;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.devtools.build.lib.analysis.ConfiguredTarget;
+import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
+import com.google.devtools.build.lib.cmdline.Label;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@link LabelBuildSettings} rules. */
+@RunWith(JUnit4.class)
+public class LabelBuildSettingTest extends BuildViewTestCase {
+
+ private void writeRulesBzl(String type) throws Exception {
+ setSkylarkSemanticsOptions("--experimental_build_setting_api=True");
+
+ scratch.file(
+ "test/rules.bzl",
+ "def _my_rule_impl(ctx):",
+ " return struct(value = ctx.attr._label_setting[SimpleRuleInfo].value)",
+ "",
+ "my_rule = rule(",
+ " implementation = _my_rule_impl,",
+ " attrs = {",
+ " '_label_setting': attr.label(default = Label('//test:my_label_" + type + "')),",
+ " },",
+ ")",
+ "",
+ "SimpleRuleInfo = provider(fields = ['value'])",
+ "",
+ "def _simple_rule_impl(ctx):",
+ " return [SimpleRuleInfo(value = ctx.attr.value)]",
+ "",
+ "simple_rule = rule(",
+ " implementation = _simple_rule_impl,",
+ " attrs = {",
+ " 'value':attr.string(),",
+ " },",
+ ")");
+ }
+
+ @Test
+ public void testLabelSetting() throws Exception {
+ String testing = "setting";
+ writeRulesBzl(testing);
+ scratch.file(
+ "test/BUILD",
+ "load('//test:rules.bzl', 'my_rule', 'simple_rule')",
+ "",
+ "my_rule(name = 'my_rule')",
+ "simple_rule(name = 'default', value = 'default_value')",
+ "simple_rule(name = 'command_line', value = 'command_line_value')",
+ "label_setting(name = 'my_label_" + testing + "', build_setting_default = ':default')");
+
+ scratch.file("a/BUILD", "cc_library(name='a', srcs=['a.cc'])", "alias(name='b', actual='a')");
+
+ ConfiguredTarget b = getConfiguredTarget("//test:my_rule");
+ assertThat(b.get("value")).isEqualTo("default_value");
+ }
+
+ @Test
+ public void testLabelFlag_default() throws Exception {
+ String testing = "flag";
+ writeRulesBzl(testing);
+ scratch.file(
+ "test/BUILD",
+ "load('//test:rules.bzl', 'my_rule', 'simple_rule')",
+ "",
+ "my_rule(name = 'my_rule')",
+ "simple_rule(name = 'default', value = 'default_value')",
+ "simple_rule(name = 'command_line', value = 'command_line_value')",
+ "label_flag(name = 'my_label_" + testing + "', build_setting_default = ':default')");
+
+ scratch.file("a/BUILD", "cc_library(name='a', srcs=['a.cc'])", "alias(name='b', actual='a')");
+
+ ConfiguredTarget b = getConfiguredTarget("//test:my_rule");
+ assertThat(b.get("value")).isEqualTo("default_value");
+ }
+
+ @Test
+ public void testLabelFlag_set() throws Exception {
+ String testing = "flag";
+ writeRulesBzl(testing);
+ scratch.file(
+ "test/BUILD",
+ "load('//test:rules.bzl', 'my_rule', 'simple_rule')",
+ "",
+ "my_rule(name = 'my_rule')",
+ "simple_rule(name = 'default', value = 'default_value')",
+ "simple_rule(name = 'command_line', value = 'command_line_value')",
+ "label_flag(name = 'my_label_" + testing + "', build_setting_default = ':default')");
+
+ scratch.file("a/BUILD", "cc_library(name='a', srcs=['a.cc'])", "alias(name='b', actual='a')");
+
+ useConfiguration(
+ ImmutableMap.of(
+ "//test:my_label_flag", Label.parseAbsoluteUnchecked("//test:command_line")));
+
+ ConfiguredTarget b = getConfiguredTarget("//test:my_rule");
+ assertThat(b.get("value")).isEqualTo("command_line_value");
+ }
+}
diff --git a/src/test/shell/integration/starlark_configurations_test.sh b/src/test/shell/integration/starlark_configurations_test.sh
index 984d626..e0b2e59 100755
--- a/src/test/shell/integration/starlark_configurations_test.sh
+++ b/src/test/shell/integration/starlark_configurations_test.sh
@@ -223,5 +223,59 @@
expect_log "type=cowabunga"
}
+# Test that label-typed build setting has access to providers of the target it points to.
+function test_label_flag() {
+ local -r pkg=$FUNCNAME
+ mkdir -p $pkg
+
+ cat > $pkg/BUILD <<EOF
+load("//$pkg:rules.bzl", "my_rule", "simple_rule")
+
+my_rule(name = "my_rule")
+
+simple_rule(name = "default", value = "default_val")
+
+simple_rule(name = "command_line", value = "command_line_val")
+
+label_flag(
+ name = "my_label_build_setting",
+ build_setting_default = ":default"
+)
+EOF
+
+ cat > $pkg/rules.bzl <<EOF
+def _impl(ctx):
+ _setting = ctx.attr._label_flag[SimpleRuleInfo].value
+ print("value=" + _setting)
+
+my_rule = rule(
+ implementation = _impl,
+ attrs = {
+ "_label_flag": attr.label(default = Label("//$pkg:my_label_build_setting")),
+ },
+)
+
+SimpleRuleInfo = provider(fields = ['value'])
+
+def _simple_rule_impl(ctx):
+ return [SimpleRuleInfo(value = ctx.attr.value)]
+
+simple_rule = rule(
+ implementation = _simple_rule_impl,
+ attrs = {
+ "value":attr.string(),
+ },
+)
+EOF
+
+ bazel build //$pkg:my_rule > output 2>"$TEST_log" || fail "Expected success"
+
+ expect_log "value=default_val"
+
+ bazel build //$pkg:my_rule --//$pkg:my_label_build_setting=//$pkg:command_line > output \
+ 2>"$TEST_log" || fail "Expected success"
+
+ expect_log "value=command_line_val"
+}
run_suite "${PRODUCT_NAME} starlark configurations tests"