Allow repository rules to provide default bindings for external labels.

The idea is that an android_sdk_repository rule would by default bind @external:android/sdk to itself, thus avoiding an unnecessary roundtrip through //tools/android:sdk . If we then also eventually bind that external label to something, we can avoid having the //tools/android:sdk rule altogether.

--
MOS_MIGRATED_REVID=96285812
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AndroidSdkRepositoryRule.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AndroidSdkRepositoryRule.java
index 200de57..ae6a0f9 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AndroidSdkRepositoryRule.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/android/AndroidSdkRepositoryRule.java
@@ -17,13 +17,21 @@
 import static com.google.devtools.build.lib.packages.Type.INTEGER;
 import static com.google.devtools.build.lib.packages.Type.STRING;
 
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableMap;
 import com.google.devtools.build.lib.analysis.RuleDefinition;
 import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment;
 import com.google.devtools.build.lib.bazel.rules.workspace.WorkspaceBaseRule;
 import com.google.devtools.build.lib.bazel.rules.workspace.WorkspaceConfiguredTargetFactory;
+import com.google.devtools.build.lib.packages.Rule;
 import com.google.devtools.build.lib.packages.RuleClass;
 import com.google.devtools.build.lib.packages.RuleClass.Builder;
 import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType;
+import com.google.devtools.build.lib.syntax.Label;
+
+import java.util.Map;
+
+import javax.annotation.Nullable;
 
 /**
  * Definition of the {@code android_sdk} repository rule.
@@ -31,11 +39,22 @@
 public class AndroidSdkRepositoryRule implements RuleDefinition {
   public static final String NAME = "android_sdk_repository";
 
+  public static final Function<? super Rule, Map<String, Label>> BINDINGS_FUNCTION =
+      new Function< Rule, Map<String, Label>>() {
+        @Nullable
+        @Override
+        public Map<String, Label> apply(Rule rule) {
+          return ImmutableMap.of(
+              "android/sdk", Label.parseAbsoluteUnchecked("@" + rule.getName() + "//:sdk"));
+        }
+      };
+
   @Override
   public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) {
     return builder
         .setUndocumented()
         .setWorkspaceOnly()
+        .setExternalBindingsFunction(BINDINGS_FUNCTION)
         .add(attr("path", STRING).mandatory().nonconfigurable("WORKSPACE rule"))
         .add(attr("build_tools_version", STRING).mandatory().nonconfigurable("WORKSPACE rule"))
         .add(attr("api_level", INTEGER).mandatory().nonconfigurable("WORKSPACE rule"))
diff --git a/src/main/java/com/google/devtools/build/lib/packages/ExternalPackage.java b/src/main/java/com/google/devtools/build/lib/packages/ExternalPackage.java
index f90d2a6..9a20f7b 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/ExternalPackage.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/ExternalPackage.java
@@ -220,6 +220,11 @@
           ast.getLocation());
       addEvents(eventHandler.getEvents());
       repositoryMap.put(RepositoryName.create("@" + tempRule.getName()), tempRule);
+      for (Map.Entry<String, Label> entry :
+          ruleClass.getExternalBindingsFunction().apply(tempRule).entrySet()) {
+          Label nameLabel = Label.parseAbsolute("//external:" + entry.getKey());
+          addBinding(nameLabel, new Binding(entry.getValue(), tempRule.getLocation()));
+      }
       return this;
     }
 
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 d2c1f56..05ad52a 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
@@ -20,10 +20,13 @@
 import static com.google.devtools.build.lib.packages.Type.LABEL_LIST;
 
 import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Function;
+import com.google.common.base.Functions;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Ordering;
 import com.google.devtools.build.lib.events.EventHandler;
@@ -91,6 +94,8 @@
  */
 @Immutable
 public final class RuleClass {
+  public static final Function<? super Rule, Map<String, Label>> NO_EXTERNAL_BINDINGS =
+        Functions.<Map<String, Label>>constant(ImmutableMap.<String, Label>of());
   /**
    * A constraint for the package name of the Rule instances.
    */
@@ -428,6 +433,8 @@
     private Predicate<String> preferredDependencyPredicate = Predicates.alwaysFalse();
     private List<Class<?>> advertisedProviders = new ArrayList<>();
     private BaseFunction configuredTargetFunction = null;
+    private Function<? super Rule, Map<String, Label>> externalBindingsFunction =
+        NO_EXTERNAL_BINDINGS;
     private SkylarkEnvironment ruleDefinitionEnvironment = null;
     private Set<Class<?>> configurationFragments = new LinkedHashSet<>();
     private boolean failIfMissingConfigurationFragment;
@@ -510,10 +517,13 @@
           == (configuredTargetFactory == null && configuredTargetFunction == null));
       Preconditions.checkState(skylarkExecutable == (configuredTargetFunction != null));
       Preconditions.checkState(skylarkExecutable == (ruleDefinitionEnvironment != null));
+      Preconditions.checkState(workspaceOnly || externalBindingsFunction == NO_EXTERNAL_BINDINGS);
+
       return new RuleClass(name, skylarkExecutable, documented, publicByDefault, binaryOutput,
           workspaceOnly, outputsDefaultExecutable, implicitOutputsFunction, configurator,
           configuredTargetFactory, validityPredicate, preferredDependencyPredicate,
           ImmutableSet.copyOf(advertisedProviders), configuredTargetFunction,
+          externalBindingsFunction,
           ruleDefinitionEnvironment, configurationFragments, failIfMissingConfigurationFragment,
           supportsConstraintChecking, attributes.values().toArray(new Attribute[0]));
     }
@@ -688,6 +698,11 @@
       return this;
     }
 
+    public Builder setExternalBindingsFunction(Function<? super Rule, Map<String, Label>> func) {
+      this.externalBindingsFunction = func;
+      return this;
+    }
+
     /**
      *  Sets the rule definition environment. Meant for Skylark usage.
      */
@@ -841,6 +856,11 @@
   @Nullable private final BaseFunction configuredTargetFunction;
 
   /**
+   * Returns the extra bindings a workspace function adds to the WORKSPACE file.
+   */
+  private final Function<? super Rule, Map<String, Label>> externalBindingsFunction;
+
+  /**
    * The Skylark rule definition environment of this RuleClass.
    * Null for non Skylark executable RuleClasses.
    */
@@ -900,6 +920,7 @@
       PredicateWithMessage<Rule> validityPredicate, Predicate<String> preferredDependencyPredicate,
       ImmutableSet<Class<?>> advertisedProviders,
       @Nullable BaseFunction configuredTargetFunction,
+      Function<? super Rule, Map<String, Label>> externalBindingsFunction,
       @Nullable SkylarkEnvironment ruleDefinitionEnvironment,
       Set<Class<?>> allowedConfigurationFragments, boolean failIfMissingConfigurationFragment,
       boolean supportsConstraintChecking,
@@ -917,6 +938,7 @@
     this.preferredDependencyPredicate = preferredDependencyPredicate;
     this.advertisedProviders = advertisedProviders;
     this.configuredTargetFunction = configuredTargetFunction;
+    this.externalBindingsFunction = externalBindingsFunction;
     this.ruleDefinitionEnvironment = ruleDefinitionEnvironment;
     // Do not make a defensive copy as builder does that already
     this.attributes = attributes;
@@ -1512,6 +1534,14 @@
   }
 
   /**
+   * Returns a function that computes the external bindings a repository function contributes to
+   * the WORKSPACE file.
+   */
+  public Function<? super Rule, Map<String, Label>> getExternalBindingsFunction() {
+    return externalBindingsFunction;
+  }
+
+  /**
    * Returns this RuleClass's rule definition environment.
    */
   @Nullable public SkylarkEnvironment getRuleDefinitionEnvironment() {