Enable load() statement in the WORKSPACE file.

RELNOTES[NEW]: Skylark macros are now enabled in WORKSPACE file.

Design document at https://docs.google.com/document/d/1jKbNXOVp2T1zJD_iRnVr8k5D0xZKgO8blMVDlXOksJg/preview

Fixes #337

--
MOS_MIGRATED_REVID=108860301
diff --git a/src/main/java/com/google/devtools/build/lib/packages/Package.java b/src/main/java/com/google/devtools/build/lib/packages/Package.java
index 579e060..a9ad155 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/Package.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/Package.java
@@ -688,6 +688,16 @@
     }
 
     /**
+     * Derive a LegacyBuilder from a normal Builder.
+     */
+    LegacyBuilder(Builder builder) {
+      super(builder.pkg);
+      if (builder.getFilename() != null) {
+        setFilename(builder.getFilename());
+      }
+    }
+
+    /**
      * Sets the globber used for this package's glob expansions.
      */
     LegacyBuilder setGlobber(Globber globber) {
@@ -717,8 +727,8 @@
     }
   }
 
-  public static Builder newExternalPackageBuilder(Path workspacePath, String runfilesPrefix) {
-    Builder b = new Builder(new Package(EXTERNAL_PACKAGE_IDENTIFIER, runfilesPrefix));
+  public static LegacyBuilder newExternalPackageBuilder(Path workspacePath, String runfilesPrefix) {
+    LegacyBuilder b = new LegacyBuilder(EXTERNAL_PACKAGE_IDENTIFIER, runfilesPrefix);
     b.setFilename(workspacePath);
     b.setMakeEnv(new MakeEnvironment.Builder());
     return b;
@@ -799,6 +809,11 @@
       return pkg.getPackageIdentifier();
     }
 
+    /** Determine if we are in the WORKSPACE file or not */
+    public boolean isWorkspace() {
+      return pkg.getPackageIdentifier().equals(EXTERNAL_PACKAGE_IDENTIFIER);
+    }
+
     /**
      * Sets the name of this package's BUILD file.
      */
diff --git a/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java b/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java
index 987b572..6093a40 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java
@@ -390,6 +390,7 @@
     this.environmentExtensions = ImmutableList.copyOf(environmentExtensions);
     this.packageArguments = createPackageArguments();
     this.nativeModule = newNativeModule();
+    this.workspaceNativeModule = WorkspaceFactory.newNativeModule(ruleClassProvider);
   }
 
   /**
@@ -1223,10 +1224,11 @@
   }
 
   private final ClassObject nativeModule;
+  private final ClassObject workspaceNativeModule;
 
   /** @return the Skylark struct to bind to "native" */
-  public ClassObject getNativeModule() {
-    return nativeModule;
+  public ClassObject getNativeModule(boolean workspace) {
+    return workspace ? workspaceNativeModule : nativeModule;
   }
 
   /**
diff --git a/src/main/java/com/google/devtools/build/lib/packages/RuleFactory.java b/src/main/java/com/google/devtools/build/lib/packages/RuleFactory.java
index d15994a..684289f 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/RuleFactory.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/RuleFactory.java
@@ -101,8 +101,7 @@
     } catch (LabelSyntaxException e) {
       throw new InvalidRuleException("illegal rule name: " + name + ": " + e.getMessage());
     }
-    boolean inWorkspaceFile =
-        location.getPath() != null && location.getPath().getBaseName().contains("WORKSPACE");
+    boolean inWorkspaceFile = pkgBuilder.isWorkspace();
     if (ruleClass.getWorkspaceOnly() && !inWorkspaceFile) {
       throw new RuleFactory.InvalidRuleException(
           ruleClass + " must be in the WORKSPACE file " + "(used by " + label + ")");
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 4527f5e..4a089c3 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
@@ -17,24 +17,29 @@
 import static com.google.devtools.build.lib.syntax.Runtime.NONE;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
 import com.google.devtools.build.lib.cmdline.LabelValidator;
 import com.google.devtools.build.lib.events.Event;
-import com.google.devtools.build.lib.events.Location;
 import com.google.devtools.build.lib.events.StoredEventHandler;
 import com.google.devtools.build.lib.packages.Package.Builder;
+import com.google.devtools.build.lib.packages.Package.LegacyBuilder;
 import com.google.devtools.build.lib.packages.PackageFactory.EnvironmentExtension;
 import com.google.devtools.build.lib.syntax.BaseFunction;
 import com.google.devtools.build.lib.syntax.BuildFileAST;
 import com.google.devtools.build.lib.syntax.BuiltinFunction;
+import com.google.devtools.build.lib.syntax.ClassObject;
 import com.google.devtools.build.lib.syntax.Environment;
+import com.google.devtools.build.lib.syntax.Environment.Extension;
 import com.google.devtools.build.lib.syntax.EvalException;
 import com.google.devtools.build.lib.syntax.FuncallExpression;
 import com.google.devtools.build.lib.syntax.FunctionSignature;
 import com.google.devtools.build.lib.syntax.Mutability;
 import com.google.devtools.build.lib.syntax.ParserInputSource;
+import com.google.devtools.build.lib.syntax.Runtime;
 import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.PathFragment;
 
 import java.io.File;
 import java.util.Map;
@@ -45,8 +50,17 @@
  * Parser for WORKSPACE files.  Fills in an ExternalPackage.Builder
  */
 public class WorkspaceFactory {
-  private final Builder builder;
-  private final Environment environment;
+  public static final String BIND = "bind";
+
+  private final LegacyBuilder builder;
+  private final StoredEventHandler localReporter;
+
+  private final Path installDir;
+  private final Path workspaceDir;
+  private final Mutability mutability;
+
+  private final ImmutableMap<String, BaseFunction> workspaceFunctions;
+  private final ImmutableList<EnvironmentExtension> environmentExtensions;
 
   /**
    * @param builder a builder for the Workspace
@@ -54,8 +68,10 @@
    * @param mutability the Mutability for the current evaluation context
    */
   public WorkspaceFactory(
-      Builder builder, RuleClassProvider ruleClassProvider,
-      ImmutableList<EnvironmentExtension> environmentExtensions, Mutability mutability) {
+      LegacyBuilder builder,
+      RuleClassProvider ruleClassProvider,
+      ImmutableList<EnvironmentExtension> environmentExtensions,
+      Mutability mutability) {
     this(builder, ruleClassProvider, environmentExtensions, mutability, null, null);
   }
 
@@ -69,62 +85,106 @@
    * @param workspaceDir the workspace directory
    */
   public WorkspaceFactory(
-      Builder builder,
+      LegacyBuilder builder,
       RuleClassProvider ruleClassProvider,
       ImmutableList<EnvironmentExtension> environmentExtensions,
       Mutability mutability,
       @Nullable Path installDir,
       @Nullable Path workspaceDir) {
     this.builder = builder;
-    this.environment = createWorkspaceEnv(builder, ruleClassProvider, environmentExtensions,
-        mutability, installDir, workspaceDir);
+    this.localReporter = new StoredEventHandler();
+    this.mutability = mutability;
+    this.installDir = installDir;
+    this.workspaceDir = workspaceDir;
+    this.environmentExtensions = environmentExtensions;
+    this.workspaceFunctions = createWorkspaceFunctions(ruleClassProvider);
   }
 
-  public void parse(ParserInputSource source)
-      throws InterruptedException {
-    StoredEventHandler localReporter = new StoredEventHandler();
-    BuildFileAST buildFileAST;
+  // State while parsing the WORKSPACE file.
+  // We store them so we can pause the parsing and load skylark imports from the
+  // WorkspaceFileFunction. Loading skylark imports require access to the .skyframe package
+  // which this package cannot depends on.
+  private BuildFileAST buildFileAST = null;
+  private Environment.Builder environmentBuilder = null;
+  private ParserInputSource source = null;
+
+  // Called by com.google.devtools.build.workspace.Resolver from //src/tools/generate_workspace.
+  public void parse(ParserInputSource source) throws InterruptedException {
+    // This method is split in 2 so WorkspaceFileFunction can call the two parts separately and
+    // do the Skylark load imports in between.
+    parseBuildFile(source);
+    execute();
+  }
+
+  public void parseBuildFile(ParserInputSource source) {
+    this.source = source;
     buildFileAST = BuildFileAST.parseBuildFile(source, localReporter, false);
     if (buildFileAST.containsErrors()) {
+      environmentBuilder = null;
       localReporter.handle(Event.error("WORKSPACE file could not be parsed"));
     } else {
-      if (!buildFileAST.exec(environment, localReporter)) {
+      environmentBuilder =
+          Environment.builder(mutability)
+              .setGlobals(Environment.BUILD)
+              .setEventHandler(localReporter);
+    }
+  }
+
+  public void execute() throws InterruptedException {
+    if (environmentBuilder != null) {
+      Environment workspaceEnv = environmentBuilder.setLoadingPhase().build();
+      addWorkspaceFunctions(workspaceEnv);
+      if (!buildFileAST.exec(workspaceEnv, localReporter)) {
         localReporter.handle(Event.error("Error evaluating WORKSPACE file " + source.getPath()));
       }
     }
+    environmentBuilder = null;
+    buildFileAST = null;
+    source = null;
 
     builder.addEvents(localReporter.getEvents());
     if (localReporter.hasErrors()) {
       builder.setContainsErrors();
     }
+    localReporter.clear();
+  }
+
+  public BuildFileAST getBuildFileAST() {
+    return buildFileAST;
+  }
+
+  public void setImportedExtensions(Map<PathFragment, Extension> importedExtensions) {
+    environmentBuilder.setImportedExtensions(importedExtensions);
   }
 
   // TODO(bazel-team): use @SkylarkSignature annotations on a BuiltinFunction.Factory
   // for signature + documentation of this and other functions in this file.
-  private static BuiltinFunction newWorkspaceNameFunction(final Builder builder) {
-    return new BuiltinFunction("workspace",
-        FunctionSignature.namedOnly("name"), BuiltinFunction.USE_LOC) {
-      public Object invoke(String name, Location loc) throws EvalException {
+  private static BuiltinFunction newWorkspaceNameFunction() {
+    return new BuiltinFunction(
+        "workspace", FunctionSignature.namedOnly("name"), BuiltinFunction.USE_AST_ENV) {
+      public Object invoke(String name, FuncallExpression ast, Environment env)
+          throws EvalException {
         String errorMessage = LabelValidator.validateTargetName(name);
         if (errorMessage != null) {
-          throw new EvalException(loc, errorMessage);
+          throw new EvalException(ast.getLocation(), errorMessage);
         }
-        builder.setWorkspaceName(name);
+
+        PackageFactory.getContext(env, ast).pkgBuilder.setWorkspaceName(name);
         return NONE;
       }
     };
   }
 
-  private static BuiltinFunction newBindFunction(
-      final RuleFactory ruleFactory, final Builder builder) {
+  private static BuiltinFunction newBindFunction(final RuleFactory ruleFactory) {
     return new BuiltinFunction(
-        "bind", FunctionSignature.namedOnly(1, "name", "actual"), BuiltinFunction.USE_LOC) {
-      public Object invoke(String name, String actual, Location loc)
+        "bind", FunctionSignature.namedOnly(1, "name", "actual"), BuiltinFunction.USE_AST_ENV) {
+      public Object invoke(String name, String actual, FuncallExpression ast, Environment env)
           throws EvalException, InterruptedException {
         Label nameLabel = null;
         try {
           nameLabel = Label.parseAbsolute("//external:" + name);
           try {
+            LegacyBuilder builder = PackageFactory.getContext(env, ast).pkgBuilder;
             RuleClass ruleClass = ruleFactory.getRuleClass("bind");
             builder
                 .externalPackageData()
@@ -133,16 +193,16 @@
                     ruleClass,
                     nameLabel,
                     actual == null ? null : Label.parseAbsolute(actual),
-                    loc);
+                    ast.getLocation());
           } catch (
               RuleFactory.InvalidRuleException | Package.NameConflictException
                       | LabelSyntaxException
                   e) {
-            throw new EvalException(loc, e.getMessage());
+            throw new EvalException(ast.getLocation(), e.getMessage());
           }
 
         } catch (LabelSyntaxException e) {
-          throw new EvalException(loc, e.getMessage());
+          throw new EvalException(ast.getLocation(), e.getMessage());
         }
         return NONE;
       }
@@ -154,12 +214,13 @@
    * specified package context.
    */
   private static BuiltinFunction newRuleFunction(
-      final RuleFactory ruleFactory, final Builder builder, final String ruleClassName) {
+      final RuleFactory ruleFactory, final String ruleClassName) {
     return new BuiltinFunction(
         ruleClassName, FunctionSignature.KWARGS, BuiltinFunction.USE_AST_ENV) {
       public Object invoke(Map<String, Object> kwargs, FuncallExpression ast, Environment env)
           throws EvalException, InterruptedException {
         try {
+          Builder builder = PackageFactory.getContext(env, ast).pkgBuilder;
           RuleClass ruleClass = ruleFactory.getRuleClass(ruleClassName);
           RuleClass bindRuleClass = ruleFactory.getRuleClass("bind");
           builder
@@ -175,22 +236,25 @@
     };
   }
 
-  private Environment createWorkspaceEnv(
-      Builder builder,
-      RuleClassProvider ruleClassProvider,
-      ImmutableList<EnvironmentExtension> environmentExtensions,
-      Mutability mutability,
-      Path installDir,
-      Path workspaceDir) {
-    Environment workspaceEnv = Environment.builder(mutability)
-        .setGlobals(Environment.BUILD)
-        .setLoadingPhase()
-        .build();
+  private static ImmutableMap<String, BaseFunction> createWorkspaceFunctions(
+      RuleClassProvider ruleClassProvider) {
+    ImmutableMap.Builder<String, BaseFunction> mapBuilder = ImmutableMap.builder();
     RuleFactory ruleFactory = new RuleFactory(ruleClassProvider);
+    mapBuilder.put(BIND, newBindFunction(ruleFactory));
+    for (String ruleClass : ruleFactory.getRuleClassNames()) {
+      if (!ruleClass.equals(BIND)) {
+        BaseFunction ruleFunction = newRuleFunction(ruleFactory, ruleClass);
+        mapBuilder.put(ruleClass, ruleFunction);
+      }
+    }
+    return mapBuilder.build();
+  }
+
+  private void addWorkspaceFunctions(Environment workspaceEnv) {
     try {
-      for (String ruleClass : ruleFactory.getRuleClassNames()) {
-        BaseFunction ruleFunction = newRuleFunction(ruleFactory, builder, ruleClass);
-        workspaceEnv.update(ruleClass, ruleFunction);
+      workspaceEnv.update("workspace", newWorkspaceNameFunction());
+      for (Map.Entry<String, BaseFunction> function : workspaceFunctions.entrySet()) {
+        workspaceEnv.update(function.getKey(), function.getValue());
       }
       if (installDir != null) {
         workspaceEnv.update("__embedded_dir__", installDir.getPathString());
@@ -200,16 +264,32 @@
       }
       File jreDirectory = new File(System.getProperty("java.home"));
       workspaceEnv.update("DEFAULT_SERVER_JAVABASE", jreDirectory.getParentFile().toString());
-      workspaceEnv.update("bind", newBindFunction(ruleFactory, builder));
-      workspaceEnv.update("workspace", newWorkspaceNameFunction(builder));
 
       for (EnvironmentExtension extension : environmentExtensions) {
         extension.updateWorkspace(workspaceEnv);
       }
-
-      return workspaceEnv;
+      workspaceEnv.setupDynamic(
+          PackageFactory.PKG_CONTEXT,
+          new PackageFactory.PackageContext(builder, null, localReporter));
     } catch (EvalException e) {
       throw new AssertionError(e);
     }
   }
+
+  private static ClassObject newNativeModule(
+      ImmutableMap<String, BaseFunction> workspaceFunctions) {
+    ImmutableMap.Builder<String, Object> builder = new ImmutableMap.Builder<>();
+    for (String nativeFunction : Runtime.getFunctionNames(SkylarkNativeModule.class)) {
+      builder.put(nativeFunction, Runtime.getFunction(SkylarkNativeModule.class, nativeFunction));
+    }
+    for (Map.Entry<String, BaseFunction> function : workspaceFunctions.entrySet()) {
+      builder.put(function.getKey(), function.getValue());
+    }
+
+    return new ClassObject.SkylarkClassObject(builder.build(), "no native function or rule '%s'");
+  }
+
+  public static ClassObject newNativeModule(RuleClassProvider ruleClassProvider) {
+    return newNativeModule(createWorkspaceFunctions(ruleClassProvider));
+  }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/repository/WorkspaceBaseRule.java b/src/main/java/com/google/devtools/build/lib/rules/repository/WorkspaceBaseRule.java
index 64bb1d5..581559c 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/repository/WorkspaceBaseRule.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/repository/WorkspaceBaseRule.java
@@ -14,6 +14,9 @@
 
 package com.google.devtools.build.lib.rules.repository;
 
+import static com.google.devtools.build.lib.packages.Attribute.attr;
+import static com.google.devtools.build.lib.syntax.Type.STRING;
+
 import com.google.devtools.build.lib.analysis.RuleDefinition;
 import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment;
 import com.google.devtools.build.lib.packages.RuleClass;
@@ -28,6 +31,9 @@
   public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) {
     return builder
         .exemptFromConstraintChecking("workspace rules aren't built for target environments")
+        .add(attr("generator_name", STRING).undocumented("internal"))
+        .add(attr("generator_function", STRING).undocumented("internal"))
+        .add(attr("generator_location", STRING).undocumented("internal"))
         .build();
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/AspectFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/AspectFunction.java
index d365738..97f6fdd 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/AspectFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/AspectFunction.java
@@ -73,7 +73,7 @@
       Environment env, Label extensionLabel, String skylarkValueName)
       throws ConversionException {
     
-    SkyKey importFileKey = SkylarkImportLookupValue.key(extensionLabel);
+    SkyKey importFileKey = SkylarkImportLookupValue.key(extensionLabel, false);
     SkylarkImportLookupValue skylarkImportLookupValue =
         (SkylarkImportLookupValue) env.getValue(importFileKey);
     if (skylarkImportLookupValue == null) {
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/PackageFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/PackageFunction.java
index 197d929..ce1b4c3 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/PackageFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/PackageFunction.java
@@ -539,7 +539,12 @@
               ImmutableList.<Label>of());
     } else {
       importResult =
-          fetchImportsFromBuildFile(buildFilePath, packageId, astAfterPreprocessing.ast, env);
+          fetchImportsFromBuildFile(
+              buildFilePath,
+              packageId,
+              astAfterPreprocessing.ast,
+              env,
+              skylarkImportLookupFunctionForInlining);
     }
 
     return importResult;
@@ -550,11 +555,12 @@
    * returns null.
    */
   @Nullable
-  private SkylarkImportResult fetchImportsFromBuildFile(
+  static SkylarkImportResult fetchImportsFromBuildFile(
       Path buildFilePath,
       PackageIdentifier packageId,
       BuildFileAST buildFileAST,
-      Environment env)
+      Environment env,
+      SkylarkImportLookupFunction skylarkImportLookupFunctionForInlining)
       throws PackageFunctionException, InterruptedException {
     ImmutableList<LoadStatement> loadStmts = buildFileAST.getImports();
     Map<PathFragment, Extension> importMap = Maps.newHashMapWithExpectedSize(loadStmts.size());
@@ -584,8 +590,9 @@
     // Look up and load the imports.
     ImmutableCollection<Label> importLabels = importPathMap.values();
     List<SkyKey> importLookupKeys = Lists.newArrayListWithExpectedSize(importLabels.size());
+    boolean inWorkspace = buildFilePath.getBaseName().endsWith("WORKSPACE");
     for (Label importLabel : importLabels) {
-      importLookupKeys.add(SkylarkImportLookupValue.key(importLabel));
+      importLookupKeys.add(SkylarkImportLookupValue.key(importLabel, inWorkspace));
     }
     Map<SkyKey, SkyValue> skylarkImportMap = Maps.newHashMapWithExpectedSize(importPathMap.size());
     boolean valuesMissing = false;
@@ -625,6 +632,7 @@
             skylarkImportMap.put(importLookupKey, skyValue);
           }
         }
+
       }
     } catch (SkylarkImportFailedException e) {
       env.getListener().handle(Event.error(Location.fromFile(buildFilePath), e.getMessage()));
@@ -644,7 +652,7 @@
     for (Entry<PathFragment, Label> importEntry : importPathMap.entrySet()) {
       PathFragment importPath = importEntry.getKey();
       Label importLabel = importEntry.getValue();
-      SkyKey keyForLabel = SkylarkImportLookupValue.key(importLabel);
+      SkyKey keyForLabel = SkylarkImportLookupValue.key(importLabel, inWorkspace);
       SkylarkImportLookupValue importLookupValue =
           (SkylarkImportLookupValue) skylarkImportMap.get(keyForLabel);
       importMap.put(importPath, importLookupValue.getEnvironmentExtension());
@@ -654,14 +662,14 @@
     return new SkylarkImportResult(importMap, transitiveClosureOfLabels(fileDependencies.build()));
   }
 
-  private ImmutableList<Label> transitiveClosureOfLabels(
+  private static ImmutableList<Label> transitiveClosureOfLabels(
       ImmutableList<SkylarkFileDependency> immediateDeps) {
     Set<Label> transitiveClosure = Sets.newHashSet();
     transitiveClosureOfLabels(immediateDeps, transitiveClosure);
     return ImmutableList.copyOf(transitiveClosure);
   }
 
-  private void transitiveClosureOfLabels(
+  private static void transitiveClosureOfLabels(
       ImmutableList<SkylarkFileDependency> immediateDeps, Set<Label> transitiveClosure) {
     for (SkylarkFileDependency dep : immediateDeps) {
       if (transitiveClosure.add(dep.getLabel())) {
@@ -954,16 +962,16 @@
    * Used to declare all the exception types that can be wrapped in the exception thrown by
    * {@link PackageFunction#compute}.
    */
-  private static class PackageFunctionException extends SkyFunctionException {
+  static class PackageFunctionException extends SkyFunctionException {
     public PackageFunctionException(NoSuchPackageException e, Transience transience) {
       super(e, transience);
     }
   }
 
   /** A simple value class to store the result of the Skylark imports.*/
-  private static final class SkylarkImportResult {
-    private final Map<PathFragment, Extension> importMap;
-    private final ImmutableList<Label> fileDependencies;
+  static final class SkylarkImportResult {
+    final Map<PathFragment, Extension> importMap;
+    final ImmutableList<Label> fileDependencies;
     private SkylarkImportResult(
         Map<PathFragment, Extension> importMap,
         ImmutableList<Label> fileDependencies) {
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupFunction.java
index 5eed6a6..3262e3d 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupFunction.java
@@ -32,6 +32,7 @@
 import com.google.devtools.build.lib.packages.PackageFactory;
 import com.google.devtools.build.lib.packages.RuleClassProvider;
 import com.google.devtools.build.lib.rules.SkylarkRuleClassFunctions;
+import com.google.devtools.build.lib.skyframe.SkylarkImportLookupValue.SkylarkImportLookupKey;
 import com.google.devtools.build.lib.syntax.BuildFileAST;
 import com.google.devtools.build.lib.syntax.Environment.Extension;
 import com.google.devtools.build.lib.syntax.EvalException;
@@ -77,9 +78,9 @@
   @Override
   public SkyValue compute(SkyKey skyKey, Environment env) throws SkyFunctionException,
       InterruptedException {
-    Label fileLabel = (Label) skyKey.argument();
+    SkylarkImportLookupKey key = (SkylarkImportLookupKey) skyKey.argument();
     try {
-      return computeInternal(fileLabel, env, null);
+      return computeInternal(key.importLabel, key.inWorkspace, env, null);
     } catch (InconsistentFilesystemException e) {
       throw new SkylarkImportLookupFunctionException(e, Transience.PERSISTENT);
     } catch (SkylarkImportFailedException e) {
@@ -91,22 +92,23 @@
       throws InconsistentFilesystemException,
           SkylarkImportFailedException,
           InterruptedException {
-    return computeWithInlineCallsInternal(
-        (Label) skyKey.argument(), env, new LinkedHashSet<Label>());
+    return computeWithInlineCallsInternal(skyKey, env, new LinkedHashSet<Label>());
   }
 
   private SkyValue computeWithInlineCallsInternal(
-      Label fileLabel, Environment env, Set<Label> visited)
-          throws InconsistentFilesystemException,
-              SkylarkImportFailedException,
-              InterruptedException {
-    return computeInternal(fileLabel, env, Preconditions.checkNotNull(visited, fileLabel));
+      SkyKey skyKey, Environment env, Set<Label> visited)
+      throws InconsistentFilesystemException, SkylarkImportFailedException, InterruptedException {
+    SkylarkImportLookupKey key = (SkylarkImportLookupKey) skyKey.argument();
+    return computeInternal(
+        key.importLabel,
+        key.inWorkspace,
+        env,
+        Preconditions.checkNotNull(visited, key.importLabel));
   }
 
-  SkyValue computeInternal(Label fileLabel, Environment env, @Nullable Set<Label> visited)
-      throws InconsistentFilesystemException,
-          SkylarkImportFailedException,
-          InterruptedException {
+  SkyValue computeInternal(
+      Label fileLabel, boolean inWorkspace, Environment env, @Nullable Set<Label> visited)
+      throws InconsistentFilesystemException, SkylarkImportFailedException, InterruptedException {
     PathFragment filePath = fileLabel.toPathFragment();
 
     // Load the AST corresponding to this file.
@@ -147,7 +149,7 @@
     List<SkyKey> importLookupKeys =
         Lists.newArrayListWithExpectedSize(importLabels.size());
     for (Label importLabel : importLabels) {
-      importLookupKeys.add(SkylarkImportLookupValue.key(importLabel));
+      importLookupKeys.add(SkylarkImportLookupValue.key(importLabel, inWorkspace));
     }
     Map<SkyKey, SkyValue> skylarkImportMap;
     boolean valuesMissing = false;
@@ -168,9 +170,7 @@
       }
       skylarkImportMap = Maps.newHashMapWithExpectedSize(loadStmts.size());
       for (SkyKey importLookupKey : importLookupKeys) {
-        SkyValue skyValue =
-            this.computeWithInlineCallsInternal(
-                (Label) importLookupKey.argument(), env, visited);
+        SkyValue skyValue = this.computeWithInlineCallsInternal(importLookupKey, env, visited);
         if (skyValue == null) {
           Preconditions.checkState(
               env.valuesMissing(), "no skylark import value for %s", importLookupKey);
@@ -194,7 +194,7 @@
     for (Entry<PathFragment, Label> importEntry : importPathMap.entrySet()) {
       PathFragment importPath = importEntry.getKey();
       Label importLabel = importEntry.getValue();
-      SkyKey keyForLabel = SkylarkImportLookupValue.key(importLabel);
+      SkyKey keyForLabel = SkylarkImportLookupValue.key(importLabel, inWorkspace);
       SkylarkImportLookupValue importLookupValue =
           (SkylarkImportLookupValue) skylarkImportMap.get(keyForLabel);
       importMap.put(importPath, importLookupValue.getEnvironmentExtension());
@@ -202,7 +202,8 @@
     }
     // Skylark UserDefinedFunction-s in that file will share this function definition Environment,
     // which will be frozen by the time it is returned by createExtension.
-    Extension extension = createExtension(ast, fileLabel, importMap, env);
+
+    Extension extension = createExtension(ast, fileLabel, importMap, env, inWorkspace);
 
     return new SkylarkImportLookupValue(
         extension, new SkylarkFileDependency(fileLabel, fileDependencies.build()));
@@ -363,8 +364,9 @@
       BuildFileAST ast,
       Label extensionLabel,
       Map<PathFragment, Extension> importMap,
-      Environment env)
-          throws SkylarkImportFailedException, InterruptedException {
+      Environment env,
+      boolean inWorkspace)
+      throws SkylarkImportFailedException, InterruptedException {
     StoredEventHandler eventHandler = new StoredEventHandler();
     // TODO(bazel-team): this method overestimates the changes which can affect the
     // Skylark RuleClass. For example changes to comments or unused functions can modify the hash.
@@ -373,9 +375,10 @@
     PathFragment extensionFile = extensionLabel.toPathFragment();
     try (Mutability mutability = Mutability.create("importing %s", extensionFile)) {
       com.google.devtools.build.lib.syntax.Environment extensionEnv =
-          ruleClassProvider.createSkylarkRuleClassEnvironment(
-              mutability, eventHandler, ast.getContentHashCode(), importMap)
-          .setupOverride("native", packageFactory.getNativeModule());
+          ruleClassProvider
+              .createSkylarkRuleClassEnvironment(
+                  mutability, eventHandler, ast.getContentHashCode(), importMap)
+              .setupOverride("native", packageFactory.getNativeModule(inWorkspace));
       ast.exec(extensionEnv, eventHandler);
       try {
         SkylarkRuleClassFunctions.exportRuleFunctionsAndAspects(extensionEnv, extensionLabel);
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupValue.java
index c298977..3574e2b 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupValue.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupValue.java
@@ -15,10 +15,14 @@
 
 import com.google.common.base.Preconditions;
 import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
 import com.google.devtools.build.lib.syntax.Environment.Extension;
 import com.google.devtools.build.skyframe.SkyKey;
 import com.google.devtools.build.skyframe.SkyValue;
 
+import java.io.Serializable;
+import java.util.Objects;
+
 /**
  * A value that represents a Skylark import lookup result. The lookup value corresponds to
  * exactly one Skylark file, identified by an absolute {@link Label} {@link SkyKey} argument. The
@@ -55,10 +59,41 @@
   }
 
   /**
-   * Returns a SkyKey to look up {@link Label} {@code importLabel}, which must be an absolute
-   * label.
+   * SkyKey for a Skylark import composed of the label of the Skylark extension and wether it is
+   * loaded from the WORKSPACE file or from a BUILD file.
    */
-  static SkyKey key(Label importLabel) {
-    return new SkyKey(SkyFunctions.SKYLARK_IMPORTS_LOOKUP, importLabel);  
+  @Immutable
+  public static final class SkylarkImportLookupKey implements Serializable {
+    public final Label importLabel;
+    public final boolean inWorkspace;
+
+    public SkylarkImportLookupKey(Label importLabel, boolean inWorkspace) {
+      Preconditions.checkNotNull(importLabel);
+      this.importLabel = importLabel;
+      this.inWorkspace = inWorkspace;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      if (this == obj) {
+        return true;
+      }
+      if (!(obj instanceof SkylarkImportLookupKey)) {
+        return false;
+      }
+      SkylarkImportLookupKey other = (SkylarkImportLookupKey) obj;
+      return importLabel.equals(other.importLabel)
+          && inWorkspace == other.inWorkspace;
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hash(importLabel, inWorkspace);
+    }
+  }
+
+  static SkyKey key(Label importLabel, boolean inWorkspace) {
+    return new SkyKey(
+        SkyFunctions.SKYLARK_IMPORTS_LOOKUP, new SkylarkImportLookupKey(importLabel, inWorkspace));
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkModuleCycleReporter.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkModuleCycleReporter.java
index 4da76f0..41f71d3 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkModuleCycleReporter.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkModuleCycleReporter.java
@@ -18,7 +18,6 @@
 import com.google.common.base.Predicate;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
-import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.events.Event;
 import com.google.devtools.build.lib.events.EventHandler;
 import com.google.devtools.build.skyframe.CycleInfo;
@@ -54,13 +53,16 @@
               .append(lastPathElement.argument()).append("/BUILD: ")
               .append("cycle in referenced extension files: ");
 
-      AbstractLabelCycleReporter.printCycle(cycleInfo.getCycle(), cycleMessage,
+      AbstractLabelCycleReporter.printCycle(
+          cycleInfo.getCycle(),
+          cycleMessage,
           new Function<SkyKey, String>() {
-        @Override
-        public String apply(SkyKey input) {
-          return ((Label) input.argument()).toString();
-        }
-      });
+            @Override
+            public String apply(SkyKey input) {
+              return ((SkylarkImportLookupValue.SkylarkImportLookupKey) input.argument())
+                  .importLabel.toString();
+            }
+          });
 
       // TODO(bazel-team): it would be nice to pass the Location of the load Statement in the
       // BUILD file.
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/WorkspaceFileFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/WorkspaceFileFunction.java
index 0f73697..345800d 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/WorkspaceFileFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/WorkspaceFileFunction.java
@@ -15,11 +15,12 @@
 package com.google.devtools.build.lib.skyframe;
 
 import com.google.devtools.build.lib.analysis.BlazeDirectories;
-import com.google.devtools.build.lib.packages.Package.Builder;
+import com.google.devtools.build.lib.cmdline.PackageIdentifier;
+import com.google.devtools.build.lib.packages.Package.LegacyBuilder;
 import com.google.devtools.build.lib.packages.PackageFactory;
 import com.google.devtools.build.lib.packages.RuleClassProvider;
 import com.google.devtools.build.lib.packages.WorkspaceFactory;
-import com.google.devtools.build.lib.syntax.EvalException;
+import com.google.devtools.build.lib.skyframe.PackageFunction.PackageFunctionException;
 import com.google.devtools.build.lib.syntax.Mutability;
 import com.google.devtools.build.lib.syntax.ParserInputSource;
 import com.google.devtools.build.lib.vfs.Path;
@@ -54,6 +55,8 @@
   @Override
   public SkyValue compute(SkyKey skyKey, Environment env) throws WorkspaceFileFunctionException,
       InterruptedException {
+    final Environment skyEnvironment = env;
+
     RootedPath workspaceRoot = (RootedPath) skyKey.argument();
     FileValue workspaceFileValue = (FileValue) env.getValue(FileValue.key(workspaceRoot));
     if (workspaceFileValue == null) {
@@ -61,7 +64,7 @@
     }
 
     Path repoWorkspace = workspaceRoot.getRoot().getRelative(workspaceRoot.getRelativePath());
-    Builder builder =
+    LegacyBuilder builder =
         com.google.devtools.build.lib.packages.Package.newExternalPackageBuilder(
             repoWorkspace, packageFactory.getRuleClassProvider().getRunfilesPrefix());
     try (Mutability mutability = Mutability.create("workspace %s", repoWorkspace)) {
@@ -73,15 +76,23 @@
               mutability,
               directories.getEmbeddedBinariesRoot(),
               directories.getWorkspace());
-      parser.parse(
-          ParserInputSource.create(
-              ruleClassProvider.getDefaultWorkspaceFile(), new PathFragment("DEFAULT.WORKSPACE")));
-      if (!workspaceFileValue.exists()) {
-        return new PackageValue(builder.build());
-      }
-
       try {
-        parser.parse(ParserInputSource.create(repoWorkspace, workspaceFileValue.getSize()));
+        PathFragment pathFragment = new PathFragment("/DEFAULT.WORKSPACE");
+        if (!parse(
+                ParserInputSource.create(ruleClassProvider.getDefaultWorkspaceFile(), pathFragment),
+                repoWorkspace, parser, skyEnvironment)) {
+          return null;
+        }
+        if (!workspaceFileValue.exists()) {
+          return new PackageValue(builder.build());
+        }
+
+        if (!parse(
+                ParserInputSource.create(repoWorkspace), repoWorkspace, parser, skyEnvironment)) {
+          return null;
+        }
+      } catch (PackageFunctionException e) {
+        throw new WorkspaceFileFunctionException(e, Transience.PERSISTENT);
       } catch (IOException e) {
         throw new WorkspaceFileFunctionException(e, Transience.TRANSIENT);
       }
@@ -90,17 +101,43 @@
     return new PackageValue(builder.build());
   }
 
+  private boolean loadSkylarkImports(Path repoWorkspace, WorkspaceFactory parser,
+      Environment skyEnvironment) throws PackageFunctionException, InterruptedException {
+    // Load skylark imports
+    PackageFunction.SkylarkImportResult importResult;
+    importResult = PackageFunction.fetchImportsFromBuildFile(repoWorkspace,
+            PackageIdentifier.createInDefaultRepo("external"),
+            parser.getBuildFileAST(),
+            skyEnvironment,
+            null);
+    if (importResult == null) {
+      return false;
+    }
+    parser.setImportedExtensions(importResult.importMap);
+    return true;
+  }
+
+  private boolean parse(ParserInputSource source, Path repoWorkspace, WorkspaceFactory parser,
+      Environment skyEnvironment) throws PackageFunctionException, InterruptedException {
+    parser.parseBuildFile(source);
+    if (!loadSkylarkImports(repoWorkspace, parser, skyEnvironment)) {
+      return false;
+    }
+    parser.execute();
+    return true;
+  }
+
   @Override
   public String extractTag(SkyKey skyKey) {
     return null;
   }
 
   private static final class WorkspaceFileFunctionException extends SkyFunctionException {
-    public WorkspaceFileFunctionException(IOException e, Transience transience) {
+    public WorkspaceFileFunctionException(Exception e, Transience transience) {
       super(e, transience);
     }
 
-    public WorkspaceFileFunctionException(EvalException e) {
+    public WorkspaceFileFunctionException(Exception e) {
       super(e, Transience.PERSISTENT);
     }
   }
diff --git a/src/test/java/com/google/devtools/build/lib/packages/ExternalPackageTest.java b/src/test/java/com/google/devtools/build/lib/packages/ExternalPackageTest.java
index 3d83246..5af665a 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/ExternalPackageTest.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/ExternalPackageTest.java
@@ -48,15 +48,12 @@
   public void testMultipleRulesWithSameName() throws Exception {
     Builder builder = Package.newExternalPackageBuilder(workspacePath, "TESTING");
 
-    // The WORKSPACE file allows rules to be overridden, but the TestRuleClassProvider doesn't
-    // provide WORKSPACE rules (new_local_repo et al). So for the test, we create an
-    // ExternalPackage with BUILD rules, even though these rules wouldn't ordinarily be added to
-    // ExternalPackage.
-    Location buildFile = Location.fromFile(getOutputPath().getRelative("BUILD"));
+    // The WORKSPACE file allows rules to be overridden.
+    Location buildFile = Location.fromFile(getOutputPath().getRelative("WORKSPACE"));
 
     // Add first rule.
     RuleClass ruleClass =
-        TestRuleClassProvider.getRuleClassProvider().getRuleClassMap().get("cc_library");
+        TestRuleClassProvider.getRuleClassProvider().getRuleClassMap().get("local_repository");
     RuleClass bindRuleClass =
         TestRuleClassProvider.getRuleClassProvider().getRuleClassMap().get("bind");
 
@@ -70,7 +67,8 @@
         .createAndAddRepositoryRule(builder, ruleClass, bindRuleClass, kwargs, ast);
 
     // Add another rule with the same name.
-    ruleClass = TestRuleClassProvider.getRuleClassProvider().getRuleClassMap().get("sh_test");
+    ruleClass =
+        TestRuleClassProvider.getRuleClassProvider().getRuleClassMap().get("new_local_repository");
     ast =
         new FuncallExpression(
             new Identifier(ruleClass.getName()), Lists.<Argument.Passed>newArrayList());
@@ -81,6 +79,6 @@
     Package pkg = builder.build();
 
     // Make sure the second rule "wins."
-    assertEquals("sh_test rule", pkg.getTarget("my-rule").getTargetKind());
+    assertEquals("new_local_repository rule", pkg.getTarget("my-rule").getTargetKind());
   }
 }
diff --git a/src/test/java/com/google/devtools/build/lib/packages/RuleFactoryTest.java b/src/test/java/com/google/devtools/build/lib/packages/RuleFactoryTest.java
index 9634a02..c09de9f 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/RuleFactoryTest.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/RuleFactoryTest.java
@@ -147,7 +147,7 @@
   public void testBuildRuleFailsInWorkspaceFile() throws Exception {
     Path myPkgPath = scratch.resolve("/foo/workspace/WORKSPACE");
     Package.Builder pkgBuilder =
-        new Package.Builder(PackageIdentifier.createInDefaultRepo("mypkg"), "TESTING")
+        new Package.Builder(Package.EXTERNAL_PACKAGE_IDENTIFIER, "TESTING")
             .setFilename(myPkgPath)
             .setMakeEnv(new MakeEnvironment.Builder());
 
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupFunctionTest.java
index 6304b9c..99999be 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupFunctionTest.java
@@ -121,7 +121,7 @@
   }
 
   private SkyKey key(String label) throws Exception {
-    return SkylarkImportLookupValue.key(Label.parseAbsoluteUnchecked(label));
+    return SkylarkImportLookupValue.key(Label.parseAbsoluteUnchecked(label), false);
   }
 
   private void checkLabel(String labelRequested, String labelFound) throws Exception {
@@ -134,7 +134,7 @@
   public void testSkylarkImportLookupNoBuildFile() throws Exception {
     scratch.file("pkg/ext.bzl", "");
     SkyKey skylarkImportLookupKey =
-        SkylarkImportLookupValue.key(Label.parseAbsoluteUnchecked("//pkg:ext.bzl"));
+        SkylarkImportLookupValue.key(Label.parseAbsoluteUnchecked("//pkg:ext.bzl"), false);
     EvaluationResult<SkylarkImportLookupValue> result =
         SkyframeExecutorTestUtils.evaluate(
             getSkyframeExecutor(), skylarkImportLookupKey, /*keepGoing=*/ false, reporter);
@@ -150,7 +150,7 @@
     scratch.file("pkg1/ext.bzl", "a = 1");
     scratch.file("pkg2/ext.bzl", "load('/pkg1/ext', 'a')");
     SkyKey skylarkImportLookupKey =
-        SkylarkImportLookupValue.key(Label.parseAbsoluteUnchecked("//pkg:ext.bzl"));
+        SkylarkImportLookupValue.key(Label.parseAbsoluteUnchecked("//pkg:ext.bzl"), false);
     EvaluationResult<SkylarkImportLookupValue> result =
         SkyframeExecutorTestUtils.evaluate(
             getSkyframeExecutor(), skylarkImportLookupKey, /*keepGoing=*/ false, reporter);
@@ -165,7 +165,7 @@
     scratch.file("pkg/BUILD", "");
     scratch.file("pkg/ext.bzl", "load('/pkg/oops\u0000', 'a')");
     SkyKey skylarkImportLookupKey =
-        SkylarkImportLookupValue.key(Label.parseAbsoluteUnchecked("//pkg:ext.bzl"));
+        SkylarkImportLookupValue.key(Label.parseAbsoluteUnchecked("//pkg:ext.bzl"), false);
     EvaluationResult<SkylarkImportLookupValue> result =
         SkyframeExecutorTestUtils.evaluate(
             getSkyframeExecutor(), skylarkImportLookupKey, /*keepGoing=*/ false, reporter);
@@ -180,7 +180,7 @@
     scratch.file("pkg/BUILD", "");
     scratch.file("pkg/ext.bzl", "load('oops\u0000', 'a')");
     SkyKey skylarkImportLookupKey =
-        SkylarkImportLookupValue.key(Label.parseAbsoluteUnchecked("//pkg:ext.bzl"));
+        SkylarkImportLookupValue.key(Label.parseAbsoluteUnchecked("//pkg:ext.bzl"), false);
     EvaluationResult<SkylarkImportLookupValue> result =
         SkyframeExecutorTestUtils.evaluate(
             getSkyframeExecutor(), skylarkImportLookupKey, /*keepGoing=*/ false, reporter);
diff --git a/src/test/shell/bazel/BUILD b/src/test/shell/bazel/BUILD
index a041d34..d882efe 100644
--- a/src/test/shell/bazel/BUILD
+++ b/src/test/shell/bazel/BUILD
@@ -172,6 +172,13 @@
 )
 
 sh_test(
+    name = "skylark_repository_test",
+    size = "large",
+    srcs = ["skylark_repository_test.sh"],
+    data = [":test-deps"],
+)
+
+sh_test(
     name = "runfiles_test",
     size = "medium",
     srcs = ["runfiles_test.sh"],
diff --git a/src/test/shell/bazel/skylark_repository_test.sh b/src/test/shell/bazel/skylark_repository_test.sh
new file mode 100755
index 0000000..bc1cd6e
--- /dev/null
+++ b/src/test/shell/bazel/skylark_repository_test.sh
@@ -0,0 +1,87 @@
+#!/bin/bash
+#
+# Copyright 2015 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.
+#
+# Test the local_repository binding
+#
+
+# Load test environment
+source $(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/test-setup.sh \
+  || { echo "test-setup.sh not found!" >&2; exit 1; }
+
+# Basic test.
+function test_macro_local_repository() {
+  create_new_workspace
+  repo2=$new_workspace_dir
+
+  mkdir -p carnivore
+  cat > carnivore/BUILD <<'EOF'
+genrule(
+    name = "mongoose",
+    cmd = "echo 'Tra-la!' > $@",
+    outs = ["moogoose.txt"],
+    visibility = ["//visibility:public"],
+)
+EOF
+  cd ${WORKSPACE_DIR}
+  pwd
+  cat > WORKSPACE <<EOF
+load('/test', 'macro')
+
+macro('$repo2')
+EOF
+
+  # Empty package for the .bzl file
+  echo -n >BUILD
+
+  # Our macro
+  cat >test.bzl <<EOF
+def macro(path):
+  print('bleh')
+  native.local_repository(name='endangered', path=path)
+  native.bind(name='mongoose', actual='@endangered//carnivore:mongoose')
+EOF
+  mkdir -p zoo
+  cat > zoo/BUILD <<'EOF'
+genrule(
+    name = "ball-pit1",
+    srcs = ["@endangered//carnivore:mongoose"],
+    outs = ["ball-pit1.txt"],
+    cmd = "cat $< >$@",
+)
+
+genrule(
+    name = "ball-pit2",
+    srcs = ["//external:mongoose"],
+    outs = ["ball-pit2.txt"],
+    cmd = "cat $< >$@",
+)
+EOF
+
+  bazel build //zoo:ball-pit1 >& $TEST_log || fail "Failed to build"
+  expect_log "bleh."
+  cat bazel-genfiles/zoo/ball-pit1.txt >$TEST_log
+  expect_log "Tra-la!"
+
+  bazel build //zoo:ball-pit2 >& $TEST_log || fail "Failed to build"
+  cat bazel-genfiles/zoo/ball-pit2.txt >$TEST_log
+  expect_log "Tra-la!"
+}
+
+function tear_down() {
+  true
+}
+
+run_suite "local repository tests"
diff --git a/src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/Resolver.java b/src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/Resolver.java
index 5c4fc8e..ecc5659 100644
--- a/src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/Resolver.java
+++ b/src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/Resolver.java
@@ -74,7 +74,7 @@
    */
   public Package parse(Path workspacePath) {
     resolver.addHeader(workspacePath.getPathString());
-    Package.Builder builder =
+    Package.LegacyBuilder builder =
         Package.newExternalPackageBuilder(workspacePath, ruleClassProvider.getRunfilesPrefix());
     try (Mutability mutability = Mutability.create("External Package %s", workspacePath)) {
       new WorkspaceFactory(builder, ruleClassProvider, environmentExtensions, mutability)