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"