Enable label-based Skylark loading. In particular, such labels may reference files in external repositories.

In addition:

- Cleaned up and refactored some tests to reflect the new loading behavior.

Deferred to future CLs:

- Updating Bazel Skylark documentation to reflect the new load form.

- Enabling command-line loading of Aspects via labels.

RELNOTES: Skylark load statements may now reference .bzl files via build labels, in addition to paths. In particular, such labels can be used to reference Skylark files in external repositories; e.g., load("@my_external_repo//some_pkg:some_file.bzl", ...). Path-based loads are now deprecated and may be disabled in the future. Caveats: Skylark files currently do not respect package visibility; i.e., all Skylark files are effectively public. Also, loads may not reference the special //external package.

--
MOS_MIGRATED_REVID=110786452
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/LoadStatement.java b/src/main/java/com/google/devtools/build/lib/syntax/LoadStatement.java
index 67ca431..d373c46 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/LoadStatement.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/LoadStatement.java
@@ -20,7 +20,6 @@
 import com.google.devtools.build.lib.syntax.compiler.DebugInfo;
 import com.google.devtools.build.lib.syntax.compiler.LoopLabels;
 import com.google.devtools.build.lib.syntax.compiler.VariableScope;
-import com.google.devtools.build.lib.vfs.PathFragment;
 
 import net.bytebuddy.implementation.bytecode.ByteCodeAppender;
 
@@ -33,8 +32,7 @@
 
   private final ImmutableMap<Identifier, String> symbols;
   private final ImmutableList<Identifier> cachedSymbols; // to save time
-  private final PathFragment importPath;
-  private final StringLiteral pathString;
+  private final SkylarkImport imp;
 
   /**
    * Constructs an import statement.
@@ -43,24 +41,24 @@
    * the bzl file that should be loaded. If aliasing is used, the value differs from its key's
    * {@code symbol.getName()}. Otherwise, both values are identical.
    */
-  LoadStatement(StringLiteral path, Map<Identifier, String> symbols) {
+  LoadStatement(SkylarkImport imp, Map<Identifier, String> symbols) {
+    this.imp = imp;
     this.symbols = ImmutableMap.copyOf(symbols);
     this.cachedSymbols = ImmutableList.copyOf(symbols.keySet());
-    this.importPath = new PathFragment(path.getValue() + ".bzl");
-    this.pathString = path;
   }
 
   public ImmutableList<Identifier> getSymbols() {
     return cachedSymbols;
   }
 
-  public PathFragment getImportPath() {
-    return importPath;
+  public SkylarkImport getImport() {
+    return imp;
   }
 
   @Override
   public String toString() {
-    return String.format("load(\"%s\", %s)", importPath, Joiner.on(", ").join(cachedSymbols));
+    return String.format(
+        "load(\"%s\", %s)", imp.getImportString(), Joiner.on(", ").join(cachedSymbols));
   }
 
   @Override
@@ -75,7 +73,7 @@
         }
         // The key is the original name that was used to define the symbol
         // in the loaded bzl file.
-        env.importSymbol(getImportPath(), current, entry.getValue());
+        env.importSymbol(imp.getImportString(), current, entry.getValue());
       } catch (Environment.NoSuchVariableException | Environment.LoadFailedException e) {
         throw new EvalException(getLocation(), e.getMessage());
       }
@@ -89,41 +87,11 @@
 
   @Override
   void validate(ValidationEnvironment env) throws EvalException {
-    validatePath();
     for (Identifier symbol : cachedSymbols) {
       env.declare(symbol.getName(), getLocation());
     }
   }
 
-  public StringLiteral getPath() {
-    return pathString;
-  }
-
-  /**
-   * Throws an exception if the path argument to load() starts with more than one forward
-   * slash ('/')
-   */
-  public void validatePath() throws EvalException {
-    String error = null;
-
-    if (pathString.getValue().isEmpty()) {
-      error = "Path argument to load() must not be empty.";
-    } else if (pathString.getValue().startsWith("//")) {
-      error =
-          "First argument of load() is a path, not a label. "
-          + "It should start with a single slash if it is an absolute path.";
-    } else if (!importPath.isAbsolute() && importPath.segmentCount() > 1) {
-      error = String.format(
-          "Path '%s' is not valid. "
-              + "It should either start with a slash or refer to a file in the current directory.",
-          importPath);
-    }
-
-    if (error != null) {
-      throw new EvalException(getLocation(), error);
-    }
-  }
-
   @Override
   ByteCodeAppender compile(
       VariableScope scope, Optional<LoopLabels> loopLabels, DebugInfo debugInfo) {
@@ -131,8 +99,4 @@
         "load statements should never appear in method bodies and"
             + " should never be compiled in general");
   }
-
-  public boolean isAbsolute() {
-    return getImportPath().isAbsolute();
-  }
 }