Allow starlark rules to be build settings.

For skylark rules that are declared build settings, we auto-add a nonconfigurable build_setting_default attribute which must be set on a per target basis. We also add access to the build setting value via the rule context object. All of this functionality is hidden behind the --experimental_build_setting_api flag which by default is flipped off.

This includes:
- Add a build_setting parameter to starlark rule construction.
- If a starlark rule sets the build_setting parameter, auto-add a mandatory 'build_setting_default' attribute of the same type.
- Allow the value of a build setting skylark rules to be accessed via ctx.build_setting_value.
- Get rid of BuildSettingDescriptor since we don't need both it and BuildSetting.
- Add the --experimental_build_setting_api flag to SkylarkSemantics flags.

Work towards issue #5577

PiperOrigin-RevId: 219537101
diff --git a/src/main/java/com/google/devtools/build/lib/packages/BuildSetting.java b/src/main/java/com/google/devtools/build/lib/packages/BuildSetting.java
new file mode 100644
index 0000000..506aaab
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/packages/BuildSetting.java
@@ -0,0 +1,47 @@
+// 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.packages;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.devtools.build.lib.skylarkbuildapi.SkylarkConfigApi.BuildSettingApi;
+import com.google.devtools.build.lib.skylarkinterface.SkylarkPrinter;
+import com.google.devtools.build.lib.syntax.Type;
+
+/**
+ * Metadata of a build setting rule's properties. This describes the build setting's type (for
+ * example, 'int' or 'string'), and whether the build setting corresponds to a command line flag.
+ */
+public class BuildSetting implements BuildSettingApi {
+  private final boolean isFlag;
+  private final Type<?> type;
+
+  public BuildSetting(boolean isFlag, Type<?> type) {
+    this.isFlag = isFlag;
+    this.type = type;
+  }
+
+  public Type<?> getType() {
+    return type;
+  }
+
+  @VisibleForTesting
+  public boolean isFlag() {
+    return isFlag;
+  }
+
+  @Override
+  public void repr(SkylarkPrinter printer) {
+    printer.append("<build_setting." + type + ">");
+  }
+}
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 fba2b88..56ceb24 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
@@ -57,6 +57,7 @@
 import com.google.devtools.build.lib.syntax.SkylarkList;
 import com.google.devtools.build.lib.syntax.Type;
 import com.google.devtools.build.lib.syntax.Type.ConversionException;
+import com.google.devtools.build.lib.util.FileTypeSet;
 import com.google.devtools.build.lib.util.StringUtil;
 import com.google.devtools.build.lib.vfs.PathFragment;
 import java.util.ArrayList;
@@ -111,6 +112,8 @@
  *       tree. All targets appearing in these attributes appears beneath the ".runfiles" tree; in
  *       addition, "deps" may have rule-specific semantics.
  * </ul>
+ *
+ * TODO(bazel-team): Consider breaking up this class in more manageable subclasses.
  */
 // Non-final only for mocking in tests. Do not subclass!
 @Immutable
@@ -607,6 +610,12 @@
       }
     }
 
+    /**
+     * Name of default attribute implicitly added to all Skylark RuleClasses that are {@code
+     * build_setting}s.
+     */
+    public static final String SKYLARK_BUILD_SETTING_DEFAULT_ATTR_NAME = "build_setting_default";
+
     /** 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());
@@ -641,6 +650,7 @@
     private Predicate<String> preferredDependencyPredicate = Predicates.alwaysFalse();
     private AdvertisedProviderSet.Builder advertisedProviders = AdvertisedProviderSet.builder();
     private BaseFunction configuredTargetFunction = null;
+    private BuildSetting buildSetting = null;
     private Function<? super Rule, Map<String, Label>> externalBindingsFunction =
         NO_EXTERNAL_BINDINGS;
     private Function<? super Rule, ? extends Set<String>> optionReferenceFunction =
@@ -770,6 +780,17 @@
       if (skylark) {
         assertRuleClassProperStarlarkDefinedTransitionUsage();
       }
+      if (buildSetting != null) {
+        Type<?> type = buildSetting.getType();
+        Attribute.Builder<?> attrBuilder =
+            attr(SKYLARK_BUILD_SETTING_DEFAULT_ATTR_NAME, type)
+                .nonconfigurable("Build setting defaults are referenced during analysis.")
+                .mandatory();
+        if (BuildType.isLabelType(type)) {
+          attrBuilder.allowedFileTypes(FileTypeSet.ANY_FILE);
+        }
+        this.add(attrBuilder);
+      }
 
       return new RuleClass(
           name,
@@ -803,7 +824,8 @@
           executionPlatformConstraintsAllowed,
           executionPlatformConstraints,
           outputFileKind,
-          attributes.values());
+          attributes.values(),
+          buildSetting);
     }
 
     private void assertSkylarkRuleClassHasImplementationFunction() {
@@ -1142,6 +1164,11 @@
       return this;
     }
 
+    public Builder setBuildSetting(BuildSetting buildSetting) {
+      this.buildSetting = buildSetting;
+      return this;
+    }
+
     public Builder setExternalBindingsFunction(Function<? super Rule, Map<String, Label>> func) {
       this.externalBindingsFunction = func;
       return this;
@@ -1418,6 +1445,12 @@
   @Nullable private final BaseFunction configuredTargetFunction;
 
   /**
+   * The BuildSetting associated with this rule. Null for all RuleClasses except Skylark-defined
+   * rules that pass {@code build_setting} to their {@code rule()} declaration.
+   */
+  @Nullable private final BuildSetting buildSetting;
+
+  /**
    * Returns the extra bindings a workspace function adds to the WORKSPACE file.
    */
   private final Function<? super Rule, Map<String, Label>> externalBindingsFunction;
@@ -1506,7 +1539,8 @@
       ExecutionPlatformConstraintsAllowed executionPlatformConstraintsAllowed,
       Set<Label> executionPlatformConstraints,
       OutputFile.Kind outputFileKind,
-      Collection<Attribute> attributes) {
+      Collection<Attribute> attributes,
+      @Nullable BuildSetting buildSetting) {
     this.name = name;
     this.key = key;
     this.type = type;
@@ -1541,6 +1575,7 @@
     this.supportsPlatforms = supportsPlatforms;
     this.executionPlatformConstraintsAllowed = executionPlatformConstraintsAllowed;
     this.executionPlatformConstraints = ImmutableSet.copyOf(executionPlatformConstraints);
+    this.buildSetting = buildSetting;
 
     // Create the index and collect non-configurable attributes.
     int index = 0;
@@ -2307,6 +2342,11 @@
     return configuredTargetFunction;
   }
 
+  @Nullable
+  public BuildSetting getBuildSetting() {
+    return buildSetting;
+  }
+
   /**
    * Returns a function that computes the external bindings a repository function contributes to
    * the WORKSPACE file.
diff --git a/src/main/java/com/google/devtools/build/lib/packages/SkylarkSemanticsOptions.java b/src/main/java/com/google/devtools/build/lib/packages/SkylarkSemanticsOptions.java
index c57e823..55ae637 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/SkylarkSemanticsOptions.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/SkylarkSemanticsOptions.java
@@ -72,6 +72,16 @@
   public boolean experimentalAnalysisTestingImprovements;
 
   @Option(
+      name = "experimental_build_setting_api",
+      defaultValue = "false",
+      documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
+      effectTags = OptionEffectTag.BUILD_FILE_SEMANTICS,
+      help =
+          "If set to true, allows access to value of build setting rules via "
+              + "ctx.build_setting_value.")
+  public boolean experimentalBuildSettingApi;
+
+  @Option(
       name = "experimental_cc_skylark_api_enabled_packages",
       converter = CommaSeparatedOptionListConverter.class,
       defaultValue = "",
@@ -520,6 +530,7 @@
     return SkylarkSemantics.builder()
         // <== Add new options here in alphabetic order ==>
         .experimentalAnalysisTestingImprovements(experimentalAnalysisTestingImprovements)
+        .experimentalBuildSettingApi(experimentalBuildSettingApi)
         .experimentalCcSkylarkApiEnabledPackages(experimentalCcSkylarkApiEnabledPackages)
         .experimentalEnableAndroidMigrationApis(experimentalEnableAndroidMigrationApis)
         .experimentalEnableRepoMapping(experimentalEnableRepoMapping)