Make the "actual" argument of bind() optional and do not point //external:android/sdk anywhere so that no Android-specific package is loaded when the user doesn't use an android_sdk_repository rule.

To this end, SkyframePackageLoaderWithValueEnvironment.getLoadedTarget() doesn't resolve //external: labels anymore. This was only needed for JVM resolution, which was dealt with by adding and extra RedirectChaser.followRedirect() call to JvmConfigurationLoader. One hack less.

On the flip side, BazelConfigurationCollection.collectTransitiveClosure() grew a hack to handle bind(), but that method is awful enough as it is anyway.

--
MOS_MIGRATED_REVID=97307779
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelConfigurationCollection.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelConfigurationCollection.java
index d6dd55e..61ef8f4 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelConfigurationCollection.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelConfigurationCollection.java
@@ -250,6 +250,13 @@
           }
         }
       }
+
+      if (rule.getRuleClass().equals("bind")) {
+        Label actual = AggregatingAttributeMapper.of(rule).get("actual", Type.LABEL);
+        if (actual != null) {
+          collectTransitiveClosure(loadedPackageProvider, reachableLabels, actual);
+        }
+      }
     }
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/android/android.WORKSPACE b/src/main/java/com/google/devtools/build/lib/bazel/rules/android/android.WORKSPACE
index a3f4130..17b8ac1 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/android/android.WORKSPACE
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/android/android.WORKSPACE
@@ -12,4 +12,4 @@
 bind(name = "android/shuffle_jars", actual = "//tools/android:shuffle_jars")
 bind(name = "android/merge_dexzips", actual = "//tools/android:merge_dexzips")
 bind(name = "android/debug_keystore", actual = "//tools/android:debug_keystore")
-bind(name = "android/sdk", actual = "//tools/android:sdk")
+bind(name = "android/sdk")
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 9a20f7b..5f5c173 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
@@ -166,6 +166,10 @@
     private void resolveLabel(final Label virtual, Binding binding)
         throws NoSuchBindingException {
       Label actual = binding.getActual();
+      if (actual == null) {
+        return;
+      }
+
       Label tortoise = virtual;
       Label hare = actual;
       boolean moveTortoise = true;
@@ -202,7 +206,9 @@
       // Bound rules don't have a name field, but this works because we don't want more than one
       // with the same virtual name.
       attributes.put("name", virtual.getName());
-      attributes.put("actual", actual);
+      if (actual != null) {
+        attributes.put("actual", actual);
+      }
       StoredEventHandler handler = new StoredEventHandler();
       Rule rule = RuleFactory.createAndAddRule(this, klass, attributes, handler, null, location);
       rule.setVisibility(ConstantRuleVisibility.PUBLIC);
diff --git a/src/main/java/com/google/devtools/build/lib/packages/WorkspaceFactory.java b/src/main/java/com/google/devtools/build/lib/packages/WorkspaceFactory.java
index a304c6d..ddb1471 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/WorkspaceFactory.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/WorkspaceFactory.java
@@ -91,13 +91,14 @@
 
   private static BuiltinFunction newBindFunction(final Builder builder) {
     return new BuiltinFunction("bind",
-        FunctionSignature.namedOnly("name", "actual"), BuiltinFunction.USE_LOC) {
+        FunctionSignature.namedOnly(1, "name", "actual"), BuiltinFunction.USE_LOC) {
       public Object invoke(String name, String actual, Location loc)
           throws EvalException, InterruptedException {
         Label nameLabel = null;
         try {
           nameLabel = Label.parseAbsolute("//external:" + name);
-          builder.addBinding(nameLabel, new Binding(Label.parseAbsolute(actual), loc));
+          Binding binding = new Binding(actual == null ? null : Label.parseAbsolute(actual), loc);
+          builder.addBinding(nameLabel, binding);
         } catch (Label.SyntaxException e) {
           throw new EvalException(loc, e.getMessage());
         }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidConfiguration.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidConfiguration.java
index 1eb3a16..e7892bc 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidConfiguration.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidConfiguration.java
@@ -18,7 +18,6 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Multimap;
 import com.google.devtools.build.lib.Constants;
-import com.google.devtools.build.lib.analysis.RedirectChaser;
 import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
 import com.google.devtools.build.lib.analysis.config.BuildConfiguration.Fragment;
 import com.google.devtools.build.lib.analysis.config.BuildConfiguration.LabelConverter;
@@ -151,9 +150,7 @@
     @Override
     public Fragment create(ConfigurationEnvironment env, BuildOptions buildOptions)
         throws InvalidConfigurationException {
-      Options options = buildOptions.get(Options.class);
-      Label sdk = RedirectChaser.followRedirects(env, options.realSdk(), "android_sdk");
-      return new AndroidConfiguration(options, sdk);
+      return new AndroidConfiguration(buildOptions.get(Options.class));
     }
 
     @Override
@@ -176,8 +173,8 @@
   private final boolean useJackForDexing;
   private final boolean jackSanityChecks;
 
-  AndroidConfiguration(Options options, Label sdk) {
-    this.sdk = sdk;
+  AndroidConfiguration(Options options) {
+    this.sdk = options.realSdk();
     this.strictDeps = options.strictDeps;
     this.legacyNativeSupport = options.legacyNativeSupport;
     this.cpu = options.cpu;
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JvmConfigurationLoader.java b/src/main/java/com/google/devtools/build/lib/rules/java/JvmConfigurationLoader.java
index a19461a..9ff7651 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/JvmConfigurationLoader.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/JvmConfigurationLoader.java
@@ -112,6 +112,12 @@
         List<Label> labels = javaHomeAttributes.get("srcs", Type.LABEL_LIST);
         for (Label jvmLabel : labels) {
           if (jvmLabel.getName().endsWith("-" + cpu)) {
+            jvmLabel = RedirectChaser.followRedirects(
+                lookup, jvmLabel, "Architecture-specific JDK");
+            if (jvmLabel == null) {
+              return null;
+            }
+
             Target jvmTarget = lookup.getTarget(jvmLabel);
             if (jvmTarget == null) {
               return null;
diff --git a/src/main/java/com/google/devtools/build/lib/rules/workspace/Bind.java b/src/main/java/com/google/devtools/build/lib/rules/workspace/Bind.java
index 49f829a..e87fe30 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/workspace/Bind.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/workspace/Bind.java
@@ -25,6 +25,8 @@
 import com.google.devtools.build.lib.analysis.RuleContext;
 import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
 import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
+import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
+import com.google.devtools.build.lib.collect.nestedset.Order;
 import com.google.devtools.build.lib.packages.Target;
 import com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory;
 import com.google.devtools.build.lib.syntax.ClassObject;
@@ -56,7 +58,7 @@
 
     @Override
     public <P extends TransitiveInfoProvider> P getProvider(Class<P> provider) {
-      return configuredTarget.getProvider(provider);
+      return configuredTarget == null ? null : configuredTarget.getProvider(provider);
     }
 
     @Override
@@ -66,17 +68,19 @@
 
     @Override
     public Object get(String providerKey) {
-      return configuredTarget.get(providerKey);
+      return configuredTarget == null ? null : configuredTarget.get(providerKey);
     }
 
     @Override
     public UnmodifiableIterator<TransitiveInfoProvider> iterator() {
-      return configuredTarget.iterator();
+      return configuredTarget == null
+          ? ImmutableList.<TransitiveInfoProvider>of().iterator()
+          : configuredTarget.iterator();
     }
 
     @Override
     public Target getTarget() {
-      return configuredTarget.getTarget();
+      return configuredTarget == null ? null : configuredTarget.getTarget();
     }
 
     @Override
@@ -94,20 +98,22 @@
         // A shortcut for files to build in Skylark. FileConfiguredTarget and RunleConfiguredTarget
         // always has FileProvider and Error- and PackageGroupConfiguredTarget-s shouldn't be
         // accessible in Skylark.
-        return SkylarkNestedSet.of(
-            Artifact.class, getProvider(FileProvider.class).getFilesToBuild());
+        return SkylarkNestedSet.of(Artifact.class, configuredTarget == null
+            ? NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER)
+            : getProvider(FileProvider.class).getFilesToBuild());
       }
-      return configuredTarget.get(name);
+      return configuredTarget == null ? null : configuredTarget.get(name);
     }
 
     @SuppressWarnings("cast")
     @Override
     public ImmutableCollection<String> getKeys() {
-      return new ImmutableList.Builder<String>()
-          .add("label", "files")
-          .addAll(configuredTarget.getProvider(RuleConfiguredTarget.SkylarkProviders.class)
-              .getKeys())
-          .build();
+      ImmutableList.Builder<String> result = ImmutableList.<String>builder().add("label", "files");
+      if (configuredTarget != null) {
+          result.addAll(
+              configuredTarget.getProvider(RuleConfiguredTarget.SkylarkProviders.class).getKeys());
+      }
+      return result.build();
     }
 
     @Override
@@ -121,5 +127,4 @@
   public ConfiguredTarget create(RuleContext ruleContext) throws InterruptedException {
     return new BindConfiguredTarget(ruleContext);
   }
-
 }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/workspace/BindRule.java b/src/main/java/com/google/devtools/build/lib/rules/workspace/BindRule.java
index ce2aa2e..8db1f69 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/workspace/BindRule.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/workspace/BindRule.java
@@ -37,6 +37,12 @@
         ${SYNOPSIS}
 
         <p>This target must exist, but can be any type of rule (including bind).</p>
+
+        <p>If this attribute is omitted, rules referring to this target in <code>//external</code>
+        will simply not see this dependency edge. Note that this is different from omitting the
+        <code>bind</code> rule completely: it is an error if an <code>//external</code> dependency
+        does not have an associated <code>bind</code> rule.
+        </p>
         <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
         .add(attr("actual", LABEL).allowedFileTypes(FileTypeSet.ANY_FILE))
         .setWorkspaceOnly()
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java
index dff91d6..c0f7908 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java
@@ -15,6 +15,7 @@
 
 import com.google.common.base.Function;
 import com.google.common.base.Preconditions;
+import com.google.common.base.Verify;
 import com.google.common.collect.ArrayListMultimap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
@@ -248,8 +249,14 @@
       Dependency dep = entry.getValue();
       SkyKey depKey = TO_KEYS.apply(dep);
       ConfiguredTarget depConfiguredTarget = depConfiguredTargetMap.get(depKey);
-      result.put(entry.getKey(),
-          RuleConfiguredTarget.mergeAspects(depConfiguredTarget, depAspectMap.get(depKey)));
+      Verify.verify(depConfiguredTarget != null);
+      if (depConfiguredTarget.getTarget() != null) {
+        // This condition will be false if this is a //external label defined by a bind() rule
+        // without an actual= attribute. In this case, we pretend that the dependency does not
+        // exist.
+        result.put(entry.getKey(),
+            RuleConfiguredTarget.mergeAspects(depConfiguredTarget, depAspectMap.get(depKey)));
+      }
     }
 
     return result;
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframePackageLoaderWithValueEnvironment.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframePackageLoaderWithValueEnvironment.java
index 9478daf..e467ae0 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframePackageLoaderWithValueEnvironment.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframePackageLoaderWithValueEnvironment.java
@@ -18,7 +18,6 @@
 import com.google.devtools.build.lib.analysis.config.BuildOptions;
 import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException;
 import com.google.devtools.build.lib.analysis.config.PackageProviderForConfigurations;
-import com.google.devtools.build.lib.packages.ExternalPackage;
 import com.google.devtools.build.lib.packages.NoSuchPackageException;
 import com.google.devtools.build.lib.packages.NoSuchTargetException;
 import com.google.devtools.build.lib.packages.Package;
@@ -78,12 +77,6 @@
   public Target getLoadedTarget(Label label) throws NoSuchPackageException,
       NoSuchTargetException {
     Package pkg = getLoadedPackage(label.getPackageIdentifier());
-    if (ExternalPackage.isExternal(pkg)) {
-      label = ((ExternalPackage) pkg).getActualLabel(label);
-      if (label != null) {
-        pkg = getLoadedPackage(label.getPackageIdentifier());
-      }
-    }
     return pkg == null ? null : pkg.getTarget(label.getName());
   }
 
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java b/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java
index 0e4b323..8a66f0f 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java
@@ -53,7 +53,8 @@
         "filegroup(name='langtools', srcs=['jdk/lib/tools.jar'])",
         "filegroup(name='bootclasspath', srcs=['jdk/jre/lib/rt.jar'])",
         "filegroup(name='extdir', srcs=glob(['jdk/jre/lib/ext/*']))",
-        "filegroup(name='java', srcs = ['jdk/jre/bin/java'])",
+        // "dummy" is needed so that RedirectChaser stops here
+        "filegroup(name='java', srcs = ['jdk/jre/bin/java', 'dummy'])",
         "exports_files(['JavaBuilder_deploy.jar','SingleJar_deploy.jar',",
         "               'JavaBuilderCanary_deploy.jar', 'ijar'])");
     config.create("tools/cpp/BUILD",