PiperOrigin-RevId: 156331430
diff --git a/src/main/java/com/google/devtools/build/lib/BUILD b/src/main/java/com/google/devtools/build/lib/BUILD
index 07a5641..3e753e6 100644
--- a/src/main/java/com/google/devtools/build/lib/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/BUILD
@@ -477,6 +477,8 @@
         ":vfs",
         "//src/main/java/com/google/devtools/build/lib/buildeventstream/proto:build_event_stream_java_proto",
         "//src/main/java/com/google/devtools/build/lib/cmdline",
+        "//src/main/java/com/google/devtools/build/skyframe",
+        "//src/main/java/com/google/devtools/build/skyframe:skyframe-objects",
         "//src/main/java/com/google/devtools/common/options",
         "//src/main/protobuf:build_java_proto",
         "//third_party:guava",
@@ -606,6 +608,27 @@
 )
 
 java_library(
+    name = "BazelPackageLoader",
+    srcs = glob(["skyframe/packages/*.java"]),
+    deps = [
+        ":bazel-rules",
+        ":build-base",
+        "//src/main/java/com/google/devtools/build/lib:clock",
+        "//src/main/java/com/google/devtools/build/lib:events",
+        "//src/main/java/com/google/devtools/build/lib:io",
+        "//src/main/java/com/google/devtools/build/lib:packages-internal",
+        "//src/main/java/com/google/devtools/build/lib:preconditions",
+        "//src/main/java/com/google/devtools/build/lib:vfs",
+        "//src/main/java/com/google/devtools/build/lib/cmdline",
+        "//src/main/java/com/google/devtools/build/skyframe",
+        "//src/main/java/com/google/devtools/build/skyframe:skyframe-objects",
+        "//src/main/java/com/google/devtools/common/options",
+        "//src/main/protobuf:invocation_policy_java_proto",
+        "//third_party:guava",
+    ],
+)
+
+java_library(
     name = "bazel-rules",
     srcs = glob(
         [
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 03416f6..5381c82 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
@@ -346,41 +346,42 @@
 
   private final Package.Builder.Helper packageBuilderHelper;
 
-  /** Factory for {@link PackageFactory} instances. Intended to only be used by unit tests. */
+  /** Builder for {@link PackageFactory} instances. Intended to only be used by unit tests. */
   @VisibleForTesting
-  public abstract static class FactoryForTesting {
-    public final PackageFactory create(RuleClassProvider ruleClassProvider, FileSystem fs) {
-      return create(ruleClassProvider, null, ImmutableList.<EnvironmentExtension>of(), fs);
+  public abstract static class BuilderForTesting {
+    protected final String version = "test";
+    protected Iterable<EnvironmentExtension> environmentExtensions = ImmutableList.of();
+    protected Map<String, String> platformSetRegexps = null;
+    protected Function<RuleClass, AttributeContainer> attributeContainerFactory =
+        AttributeContainer.ATTRIBUTE_CONTAINER_FACTORY;
+    protected boolean doChecksForTesting = true;
+
+    public BuilderForTesting setEnvironmentExtensions(
+        Iterable<EnvironmentExtension> environmentExtensions) {
+      this.environmentExtensions = environmentExtensions;
+      return this;
     }
 
-    public final PackageFactory create(
-        RuleClassProvider ruleClassProvider,
-        EnvironmentExtension environmentExtension,
-        FileSystem fs) {
-      return create(ruleClassProvider, null, ImmutableList.of(environmentExtension), fs);
+    public BuilderForTesting setPlatformSetRegexps(Map<String, String> platformSetRegexps) {
+      this.platformSetRegexps = platformSetRegexps;
+      return this;
     }
 
-    public final PackageFactory create(
-        RuleClassProvider ruleClassProvider,
-        Map<String, String> platformSetRegexps,
-        Iterable<EnvironmentExtension> environmentExtensions,
-        FileSystem fs) {
-      return create(
-          ruleClassProvider,
-          platformSetRegexps,
-          AttributeContainer.ATTRIBUTE_CONTAINER_FACTORY,
-          environmentExtensions,
-          "test",
-          fs);
+    public BuilderForTesting disableChecks() {
+      this.doChecksForTesting = false;
+      return this;
     }
 
-    protected abstract PackageFactory create(
-        RuleClassProvider ruleClassProvider,
-        Map<String, String> platformSetRegexps,
-        Function<RuleClass, AttributeContainer> attributeContainerFactory,
-        Iterable<EnvironmentExtension> environmentExtensions,
-        String version,
-        FileSystem fs);
+    public abstract PackageFactory build(RuleClassProvider ruleClassProvider, FileSystem fs);
+  }
+
+  /**
+   * Factory for {@link PackageFactory.BuilderForTesting} instances. Intended to only be used by
+   * unit tests.
+   */
+  @VisibleForTesting
+  public abstract static class BuilderFactoryForTesting {
+    public abstract BuilderForTesting builder();
   }
 
   /**
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 00da7e7..ce2ec24 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
@@ -1297,7 +1297,7 @@
     }
   }
 
-  static boolean isDefaultsPackage(PackageIdentifier packageIdentifier) {
+  public static boolean isDefaultsPackage(PackageIdentifier packageIdentifier) {
     return packageIdentifier.getRepository().isMain()
         && packageIdentifier.getPackageFragment().equals(DEFAULTS_PACKAGE_NAME);
   }
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/PrecomputedValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/PrecomputedValue.java
index 78c525f..ebdbdf0 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/PrecomputedValue.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/PrecomputedValue.java
@@ -59,7 +59,7 @@
       this.supplier = supplier;
     }
 
-    void inject(Injectable injectable) {
+    public void inject(Injectable injectable) {
       injectable.inject(precomputed.key, new PrecomputedValue(supplier.get()));
     }
 
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/packages/AbstractPackageLoader.java b/src/main/java/com/google/devtools/build/lib/skyframe/packages/AbstractPackageLoader.java
new file mode 100644
index 0000000..d0b771d
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/packages/AbstractPackageLoader.java
@@ -0,0 +1,329 @@
+// Copyright 2017 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.skyframe.packages;
+
+import static com.google.common.base.Throwables.throwIfInstanceOf;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.eventbus.EventBus;
+import com.google.devtools.build.lib.analysis.BlazeDirectories;
+import com.google.devtools.build.lib.cmdline.PackageIdentifier;
+import com.google.devtools.build.lib.events.Reporter;
+import com.google.devtools.build.lib.packages.AttributeContainer;
+import com.google.devtools.build.lib.packages.BuildFileContainsErrorsException;
+import com.google.devtools.build.lib.packages.CachingPackageLocator;
+import com.google.devtools.build.lib.packages.ConstantRuleVisibility;
+import com.google.devtools.build.lib.packages.NoSuchPackageException;
+import com.google.devtools.build.lib.packages.Package;
+import com.google.devtools.build.lib.packages.PackageFactory;
+import com.google.devtools.build.lib.packages.PackageFactory.EnvironmentExtension;
+import com.google.devtools.build.lib.packages.Preprocessor;
+import com.google.devtools.build.lib.packages.RuleClassProvider;
+import com.google.devtools.build.lib.pkgcache.PathPackageLocator;
+import com.google.devtools.build.lib.skyframe.ASTFileLookupFunction;
+import com.google.devtools.build.lib.skyframe.BlacklistedPackagePrefixesFunction;
+import com.google.devtools.build.lib.skyframe.ContainingPackageLookupFunction;
+import com.google.devtools.build.lib.skyframe.DirectoryListingFunction;
+import com.google.devtools.build.lib.skyframe.DirectoryListingStateFunction;
+import com.google.devtools.build.lib.skyframe.ExternalFilesHelper;
+import com.google.devtools.build.lib.skyframe.ExternalFilesHelper.ExternalFileAction;
+import com.google.devtools.build.lib.skyframe.ExternalPackageFunction;
+import com.google.devtools.build.lib.skyframe.FileFunction;
+import com.google.devtools.build.lib.skyframe.FileStateFunction;
+import com.google.devtools.build.lib.skyframe.FileSymlinkCycleUniquenessFunction;
+import com.google.devtools.build.lib.skyframe.FileSymlinkInfiniteExpansionUniquenessFunction;
+import com.google.devtools.build.lib.skyframe.GlobFunction;
+import com.google.devtools.build.lib.skyframe.PackageFunction;
+import com.google.devtools.build.lib.skyframe.PackageFunction.CacheEntryWithGlobDeps;
+import com.google.devtools.build.lib.skyframe.PackageLookupFunction;
+import com.google.devtools.build.lib.skyframe.PackageValue;
+import com.google.devtools.build.lib.skyframe.PrecomputedFunction;
+import com.google.devtools.build.lib.skyframe.PrecomputedValue;
+import com.google.devtools.build.lib.skyframe.SkyFunctions;
+import com.google.devtools.build.lib.skyframe.SkylarkImportLookupFunction;
+import com.google.devtools.build.lib.skyframe.WorkspaceASTFunction;
+import com.google.devtools.build.lib.skyframe.WorkspaceFileFunction;
+import com.google.devtools.build.lib.skyframe.WorkspaceNameFunction;
+import com.google.devtools.build.lib.syntax.SkylarkSemanticsOptions;
+import com.google.devtools.build.lib.util.BlazeClock;
+import com.google.devtools.build.lib.util.Preconditions;
+import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor;
+import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.PathFragment;
+import com.google.devtools.build.lib.vfs.UnixGlob;
+import com.google.devtools.build.skyframe.BuildDriver;
+import com.google.devtools.build.skyframe.Differencer;
+import com.google.devtools.build.skyframe.ErrorInfo;
+import com.google.devtools.build.skyframe.EvaluationResult;
+import com.google.devtools.build.skyframe.ImmutableDiff;
+import com.google.devtools.build.skyframe.InMemoryMemoizingEvaluator;
+import com.google.devtools.build.skyframe.Injectable;
+import com.google.devtools.build.skyframe.MemoizingEvaluator;
+import com.google.devtools.build.skyframe.SequentialBuildDriver;
+import com.google.devtools.build.skyframe.SkyFunction;
+import com.google.devtools.build.skyframe.SkyFunctionName;
+import com.google.devtools.build.skyframe.SkyKey;
+import com.google.devtools.build.skyframe.SkyValue;
+import com.google.devtools.build.skyframe.Version;
+import com.google.devtools.build.skyframe.WalkableGraph;
+import com.google.devtools.common.options.Options;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+import javax.annotation.Nullable;
+
+/**
+ * Abstract base class of a {@link PackageLoader} implementation that has no incrementality or
+ * caching.
+ */
+public abstract class AbstractPackageLoader implements PackageLoader {
+  private final ImmutableDiff preinjectedDiff;
+  private final Differencer preinjectedDifferencer = new Differencer() {
+    @Override
+    public Diff getDiff(WalkableGraph fromGraph, Version fromVersion, Version toVersion)
+        throws InterruptedException {
+      return preinjectedDiff;
+    }
+  };
+  private final Reporter reporter;
+  protected final RuleClassProvider ruleClassProvider;
+  protected final ImmutableMap<SkyFunctionName, SkyFunction> extraSkyFunctions;
+  protected final AtomicReference<PathPackageLocator> pkgLocatorRef;
+  protected final ExternalFilesHelper externalFilesHelper;
+  protected final AtomicReference<ImmutableSet<PackageIdentifier>> deletedPackagesRef =
+      new AtomicReference<>(ImmutableSet.<PackageIdentifier>of());
+  protected final CachingPackageLocator packageManager;
+  protected final BlazeDirectories directories;
+
+  /** Abstract base class of a builder for {@link PackageLoader} instances. */
+  public abstract static class Builder {
+    protected final Path workspaceDir;
+    protected RuleClassProvider ruleClassProvider = getDefaultRuleClassProvider();
+    protected Reporter reporter = new Reporter(new EventBus());
+    protected ImmutableMap<SkyFunctionName, SkyFunction> extraSkyFunctions = ImmutableMap.of();
+    protected ImmutableList<PrecomputedValue.Injected> extraPrecomputedValues = ImmutableList.of();
+    protected String defaultsPackageContents = getDefaultDefaulsPackageContents();
+
+    protected Builder(Path workspaceDir) {
+      this.workspaceDir = workspaceDir;
+    }
+
+    public Builder setRuleClassProvider(RuleClassProvider ruleClassProvider) {
+      this.ruleClassProvider = ruleClassProvider;
+      return this;
+    }
+
+    public Builder setDefaultsPackageContents(String defaultsPackageContents) {
+      this.defaultsPackageContents = defaultsPackageContents;
+      return this;
+    }
+
+    public Builder setReporter(Reporter reporter) {
+      this.reporter = reporter;
+      return this;
+    }
+
+    public Builder setExtraSkyFunctions(
+        ImmutableMap<SkyFunctionName, SkyFunction> extraSkyFunctions) {
+      this.extraSkyFunctions = extraSkyFunctions;
+      return this;
+    }
+
+    public Builder setExtraPrecomputedValues(
+        ImmutableList<PrecomputedValue.Injected> extraPrecomputedValues) {
+      this.extraPrecomputedValues = extraPrecomputedValues;
+      return this;
+    }
+
+    public abstract PackageLoader build();
+
+    protected abstract RuleClassProvider getDefaultRuleClassProvider();
+
+    protected abstract String getDefaultDefaulsPackageContents();
+  }
+
+  protected AbstractPackageLoader(Builder builder) {
+    Path workspaceDir = builder.workspaceDir;
+    PathPackageLocator pkgLocator =
+        new PathPackageLocator(null, ImmutableList.of(workspaceDir));
+    this.ruleClassProvider = builder.ruleClassProvider;
+    this.reporter = builder.reporter;
+    this.extraSkyFunctions = builder.extraSkyFunctions;
+    this.pkgLocatorRef = new AtomicReference<>(pkgLocator);
+
+    // The 'installBase' and 'outputBase' directories won't be meaningfully used by
+    // WorkspaceFileFunction, so we pass in a dummy Path.
+    // TODO(nharmata): Refactor WorkspaceFileFunction to make this a non-issue.
+    Path devNull = workspaceDir.getFileSystem().getPath("/dev/null");
+    this.directories = new BlazeDirectories(/*installBase=*/devNull,
+        /*outputBase=*/devNull, /*workspace=*/workspaceDir, "blaze");
+    this.externalFilesHelper = new ExternalFilesHelper(
+        pkgLocatorRef,
+        ExternalFileAction.DEPEND_ON_EXTERNAL_PKG_FOR_EXTERNAL_REPO_PATHS,
+        directories);
+    this.packageManager = new CachingPackageLocator() {
+      @Override
+      @Nullable
+      public Path getBuildFileForPackage(PackageIdentifier packageName) {
+        return pkgLocatorRef.get().getPackageBuildFileNullable(packageName,
+            UnixGlob.DEFAULT_SYSCALLS_REF);
+      }
+    };
+    this.preinjectedDiff = makePreinjectedDiff(
+        pkgLocator,
+        builder.defaultsPackageContents,
+        builder.extraPrecomputedValues,
+        directories);
+  }
+
+  private static ImmutableDiff makePreinjectedDiff(
+      PathPackageLocator pkgLocator,
+      String defaultsPackageContents,
+      ImmutableList<PrecomputedValue.Injected> extraPrecomputedValues,
+      BlazeDirectories directories) {
+    final Map<SkyKey, SkyValue> valuesToInject = new HashMap<>();
+    Injectable injectable =
+        new Injectable() {
+          @Override
+          public void inject(Map<SkyKey, ? extends SkyValue> values) {
+            valuesToInject.putAll(values);
+          }
+
+          @Override
+          public void inject(SkyKey key, SkyValue value) {
+            valuesToInject.put(key, value);
+          }
+        };
+    for (PrecomputedValue.Injected injected : extraPrecomputedValues) {
+      injected.inject(injectable);
+    }
+    PrecomputedValue.PATH_PACKAGE_LOCATOR.set(injectable, pkgLocator);
+    PrecomputedValue.DEFAULT_VISIBILITY.set(injectable, ConstantRuleVisibility.PRIVATE);
+    PrecomputedValue.SKYLARK_SEMANTICS.set(
+        injectable,
+        Options.getDefaults(SkylarkSemanticsOptions.class));
+    PrecomputedValue.DEFAULTS_PACKAGE_CONTENTS.set(injectable, defaultsPackageContents);
+    PrecomputedValue.BLACKLISTED_PACKAGE_PREFIXES_FILE.set(injectable, PathFragment.EMPTY_FRAGMENT);
+    PrecomputedValue.BLAZE_DIRECTORIES.set(injectable, directories);
+    return new ImmutableDiff(ImmutableList.<SkyKey>of(), valuesToInject);
+  }
+
+  /**
+   * Returns a {@link Package} instance, if any, representing the Blaze package specified by
+   * {@code pkgId}. Note that the returned {@link Package} instance may be in error (see
+   * {@link Package#containsErrors}), e.g. if there was syntax error in the package's BUILD file.
+   *
+   * @throws InterruptedException if the package loading was interrupted.
+   * @throws NoSuchPackageException if there was a non-recoverable error loading the package, e.g.
+   *     an io error reading the BUILD file.
+   */
+  @Override
+  public Package loadPackage(PackageIdentifier pkgId) throws NoSuchPackageException,
+      InterruptedException {
+    SkyKey key = PackageValue.key(pkgId);
+    EvaluationResult<PackageValue> result =
+        makeFreshDriver()
+            .evaluate(ImmutableList.of(key), /*keepGoing=*/ true, /*numThreads=*/ 1, reporter);
+    if (result.hasError()) {
+      ErrorInfo error = result.getError();
+      if (!Iterables.isEmpty(error.getCycleInfo())) {
+        throw new BuildFileContainsErrorsException(
+            pkgId, "Cycle encountered while loading package " + pkgId);
+      }
+      Throwable e = Preconditions.checkNotNull(error.getException());
+      throwIfInstanceOf(e, NoSuchPackageException.class);
+      throw new IllegalStateException("Unexpected Exception type from PackageValue for '"
+          + pkgId + "'' with root causes: " + Iterables.toString(error.getRootCauses()), e);
+    }
+    return result.get(key).getPackage();
+  }
+
+  private BuildDriver makeFreshDriver() {
+    return new SequentialBuildDriver(
+        InMemoryMemoizingEvaluator.SUPPLIER.create(
+            makeFreshSkyFunctions(),
+            preinjectedDifferencer,
+            /*progressReceiver=*/ null,
+            new MemoizingEvaluator.EmittedEventState(),
+            /*keepEdges=*/ false));
+  }
+
+  protected abstract String getName();
+  protected abstract ImmutableList<EnvironmentExtension> getEnvironmentExtensions();
+  protected abstract PackageLookupFunction makePackageLookupFunction();
+  protected abstract ImmutableMap<SkyFunctionName, SkyFunction> getExtraExtraSkyFunctions();
+
+  protected final ImmutableMap<SkyFunctionName, SkyFunction> makeFreshSkyFunctions() {
+    AtomicReference<TimestampGranularityMonitor> tsgm =
+        new AtomicReference<>(new TimestampGranularityMonitor(BlazeClock.instance()));
+    Cache<PackageIdentifier, CacheEntryWithGlobDeps<Package.Builder>> packageFunctionCache =
+        CacheBuilder.newBuilder().build();
+    Cache<PackageIdentifier, CacheEntryWithGlobDeps<Preprocessor.AstAfterPreprocessing>> astCache =
+        CacheBuilder.newBuilder().build();
+    PackageFactory pkgFactory = new PackageFactory(
+        ruleClassProvider,
+        null,
+        AttributeContainer.ATTRIBUTE_CONTAINER_FACTORY,
+        getEnvironmentExtensions(),
+        getName(),
+        Package.Builder.DefaultHelper.INSTANCE);
+    ImmutableMap.Builder<SkyFunctionName, SkyFunction> builder = ImmutableMap.builder();
+    builder
+        .put(SkyFunctions.PRECOMPUTED, new PrecomputedFunction())
+        .put(SkyFunctions.FILE_STATE, new FileStateFunction(tsgm, externalFilesHelper))
+        .put(
+            SkyFunctions.DIRECTORY_LISTING_STATE,
+            new DirectoryListingStateFunction(externalFilesHelper))
+        .put(SkyFunctions.FILE_SYMLINK_CYCLE_UNIQUENESS, new FileSymlinkCycleUniquenessFunction())
+        .put(
+            SkyFunctions.FILE_SYMLINK_INFINITE_EXPANSION_UNIQUENESS,
+            new FileSymlinkInfiniteExpansionUniquenessFunction())
+        .put(SkyFunctions.FILE, new FileFunction(pkgLocatorRef))
+        .put(SkyFunctions.DIRECTORY_LISTING, new DirectoryListingFunction())
+        .put(SkyFunctions.PACKAGE_LOOKUP, makePackageLookupFunction())
+        .put(SkyFunctions.BLACKLISTED_PACKAGE_PREFIXES, new BlacklistedPackagePrefixesFunction())
+        .put(SkyFunctions.CONTAINING_PACKAGE_LOOKUP, new ContainingPackageLookupFunction())
+        .put(SkyFunctions.AST_FILE_LOOKUP, new ASTFileLookupFunction(ruleClassProvider))
+        .put(
+            SkyFunctions.SKYLARK_IMPORTS_LOOKUP,
+            new SkylarkImportLookupFunction(ruleClassProvider, pkgFactory))
+        .put(SkyFunctions.WORKSPACE_NAME, new WorkspaceNameFunction())
+        .put(SkyFunctions.WORKSPACE_AST, new WorkspaceASTFunction(ruleClassProvider))
+        .put(
+            SkyFunctions.WORKSPACE_FILE,
+            new WorkspaceFileFunction(ruleClassProvider, pkgFactory, directories))
+        .put(SkyFunctions.EXTERNAL_PACKAGE, new ExternalPackageFunction())
+        .put(SkyFunctions.GLOB, new GlobFunction(/*alwaysUseDirListing=*/ false))
+        .put(
+            SkyFunctions.PACKAGE,
+            new PackageFunction(
+                pkgFactory,
+                packageManager,
+                /*showLoadingProgress=*/ new AtomicBoolean(false),
+                packageFunctionCache,
+                astCache,
+                /*numPackagesLoaded=*/ new AtomicInteger(0),
+                null))
+        .putAll(extraSkyFunctions)
+        .putAll(getExtraExtraSkyFunctions());
+    return builder.build();
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/packages/BazelPackageLoader.java b/src/main/java/com/google/devtools/build/lib/skyframe/packages/BazelPackageLoader.java
new file mode 100644
index 0000000..62335dd
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/packages/BazelPackageLoader.java
@@ -0,0 +1,92 @@
+// Copyright 2017 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.skyframe.packages;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.devtools.build.lib.bazel.rules.BazelRuleClassProvider;
+import com.google.devtools.build.lib.packages.PackageFactory.EnvironmentExtension;
+import com.google.devtools.build.lib.packages.RuleClassProvider;
+import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.InvocationPolicy;
+import com.google.devtools.build.lib.skyframe.LocalRepositoryLookupFunction;
+import com.google.devtools.build.lib.skyframe.PackageLookupFunction;
+import com.google.devtools.build.lib.skyframe.PackageLookupFunction.CrossRepositoryLabelViolationStrategy;
+import com.google.devtools.build.lib.skyframe.PackageLookupValue.BuildFileName;
+import com.google.devtools.build.lib.skyframe.SkyFunctions;
+import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.skyframe.SkyFunction;
+import com.google.devtools.build.skyframe.SkyFunctionName;
+
+/**
+ * Concrete implementation of {@link PackageLoader} that uses skyframe under the covers, but with
+ * no caching or incrementality.
+ */
+public class BazelPackageLoader extends AbstractPackageLoader {
+  /** Returns a fresh {@link Builder} instance. */
+  public static Builder builder(Path workspaceDir) {
+    return new Builder(workspaceDir);
+  }
+
+  /** Builder for {@link BazelPackageLoader} instances. */
+  public static class Builder extends AbstractPackageLoader.Builder {
+    private Builder(Path workspaceDir) {
+      super(workspaceDir);
+    }
+
+    @Override
+    public BazelPackageLoader build() {
+      return new BazelPackageLoader(this);
+    }
+
+    @Override
+    protected RuleClassProvider getDefaultRuleClassProvider() {
+      return BazelRuleClassProvider.create();
+    }
+
+    @Override
+    protected String getDefaultDefaulsPackageContents() {
+      return BazelRuleClassProvider.create().getDefaultsPackageContent(
+          InvocationPolicy.getDefaultInstance());
+    }
+  }
+
+  private BazelPackageLoader(Builder builder) {
+    super(builder);
+  }
+
+  @Override
+  protected String getName() {
+    return "BazelPackageLoader";
+  }
+
+  @Override
+  protected ImmutableList<EnvironmentExtension> getEnvironmentExtensions() {
+    return ImmutableList.of();
+  }
+
+  @Override
+  protected PackageLookupFunction makePackageLookupFunction() {
+    return new PackageLookupFunction(
+        deletedPackagesRef,
+        CrossRepositoryLabelViolationStrategy.ERROR,
+        ImmutableList.of(BuildFileName.BUILD_DOT_BAZEL, BuildFileName.BUILD));
+  }
+
+  @Override
+  protected ImmutableMap<SkyFunctionName, SkyFunction> getExtraExtraSkyFunctions() {
+    return ImmutableMap.<SkyFunctionName, SkyFunction>of(
+        SkyFunctions.LOCAL_REPOSITORY_LOOKUP, new LocalRepositoryLookupFunction());
+    // TODO(nharmata): Add support for external repositories.
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/packages/PackageFactoryBuilderWithSkyframeForTesting.java b/src/main/java/com/google/devtools/build/lib/skyframe/packages/PackageFactoryBuilderWithSkyframeForTesting.java
new file mode 100644
index 0000000..a23d251
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/packages/PackageFactoryBuilderWithSkyframeForTesting.java
@@ -0,0 +1,43 @@
+// Copyright 2017 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.skyframe.packages;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.devtools.build.lib.packages.PackageFactory;
+import com.google.devtools.build.lib.skyframe.PrecomputedValue;
+import com.google.devtools.build.skyframe.SkyFunction;
+import com.google.devtools.build.skyframe.SkyFunctionName;
+
+/**
+ * A {@link PackageFactory.BuilderForTesting} that also allows specification of some skyframe
+ * details.
+ */
+public abstract class PackageFactoryBuilderWithSkyframeForTesting
+    extends PackageFactory.BuilderForTesting {
+  protected ImmutableMap<SkyFunctionName, SkyFunction> extraSkyFunctions = ImmutableMap.of();
+  protected ImmutableList<PrecomputedValue.Injected> extraPrecomputedValues = ImmutableList.of();
+
+  public PackageFactoryBuilderWithSkyframeForTesting setExtraSkyFunctions(
+      ImmutableMap<SkyFunctionName, SkyFunction> extraSkyFunctions) {
+    this.extraSkyFunctions = extraSkyFunctions;
+    return this;
+  }
+
+  public PackageFactoryBuilderWithSkyframeForTesting setExtraPrecomputeValues(
+      Iterable<PrecomputedValue.Injected> extraPrecomputedValues) {
+    this.extraPrecomputedValues = ImmutableList.copyOf(extraPrecomputedValues);
+    return this;
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/packages/PackageLoader.java b/src/main/java/com/google/devtools/build/lib/skyframe/packages/PackageLoader.java
new file mode 100644
index 0000000..f8f08c6
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/packages/PackageLoader.java
@@ -0,0 +1,24 @@
+// Copyright 2017 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.skyframe.packages;
+
+import com.google.devtools.build.lib.cmdline.PackageIdentifier;
+import com.google.devtools.build.lib.packages.NoSuchPackageException;
+import com.google.devtools.build.lib.packages.Package;
+
+/** A standalone library for performing Bazel package loading. */
+public interface PackageLoader {
+  /** Loads and returns the specified package. */
+  Package loadPackage(PackageIdentifier pkgId) throws NoSuchPackageException, InterruptedException;
+}
diff --git a/src/test/java/com/google/devtools/build/lib/BUILD b/src/test/java/com/google/devtools/build/lib/BUILD
index 302276a..adcbbf9 100644
--- a/src/test/java/com/google/devtools/build/lib/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/BUILD
@@ -64,6 +64,7 @@
     srcs = glob(["testutil/*.java"]),
     visibility = ["//visibility:public"],
     deps = [
+        "//src/main/java/com/google/devtools/build/lib:BazelPackageLoader",
         "//src/main/java/com/google/devtools/build/lib:bazel-main",
         "//src/main/java/com/google/devtools/build/lib:bazel-rules",
         "//src/main/java/com/google/devtools/build/lib:build-base",
@@ -339,6 +340,7 @@
         ":foundations_testutil",
         ":packages_testutil",
         ":testutil",
+        "//src/main/java/com/google/devtools/build/lib:BazelPackageLoader",
         "//src/main/java/com/google/devtools/build/lib:android-rules",
         "//src/main/java/com/google/devtools/build/lib:bazel-main",
         "//src/main/java/com/google/devtools/build/lib:bazel-rules",
@@ -581,6 +583,7 @@
         ":foundations_testutil",
         ":testutil",
         "//src/main/java/com/google/devtools/build/docgen:docgen_javalib",
+        "//src/main/java/com/google/devtools/build/lib:BazelPackageLoader",
         "//src/main/java/com/google/devtools/build/lib:bazel-main",
         "//src/main/java/com/google/devtools/build/lib:bazel-rules",
         "//src/main/java/com/google/devtools/build/lib:build-base",
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisMock.java b/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisMock.java
index 57a6ac4..70e3ef0 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisMock.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisMock.java
@@ -24,7 +24,6 @@
 import com.google.devtools.build.lib.bazel.rules.android.AndroidSdkRepositoryFunction;
 import com.google.devtools.build.lib.bazel.rules.android.AndroidSdkRepositoryRule;
 import com.google.devtools.build.lib.flags.InvocationPolicyEnforcer;
-import com.google.devtools.build.lib.packages.PackageFactory;
 import com.google.devtools.build.lib.packages.util.LoadingMock;
 import com.google.devtools.build.lib.packages.util.MockCcSupport;
 import com.google.devtools.build.lib.packages.util.MockToolsConfig;
@@ -34,11 +33,11 @@
 import com.google.devtools.build.lib.rules.repository.RepositoryFunction;
 import com.google.devtools.build.lib.rules.repository.RepositoryLoaderFunction;
 import com.google.devtools.build.lib.skyframe.SkyFunctions;
+import com.google.devtools.build.lib.skyframe.packages.PackageFactoryBuilderWithSkyframeForTesting;
 import com.google.devtools.build.lib.testutil.TestConstants;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.skyframe.SkyFunction;
 import com.google.devtools.build.skyframe.SkyFunctionName;
-
 import java.io.IOException;
 import java.lang.reflect.Field;
 import java.util.Collection;
@@ -68,8 +67,8 @@
   }
 
   @Override
-  public PackageFactory.FactoryForTesting getPackageFactoryForTesting() {
-    return TestConstants.PACKAGE_FACTORY_FACTORY_FOR_TESTING;
+  public PackageFactoryBuilderWithSkyframeForTesting getPackageFactoryBuilderForTesting() {
+    return super.getPackageFactoryBuilderForTesting().setExtraSkyFunctions(getSkyFunctions());
   }
 
   @Override
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisTestCase.java b/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisTestCase.java
index 7f88ad3..0fb75bf 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisTestCase.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisTestCase.java
@@ -13,7 +13,6 @@
 // limitations under the License.
 package com.google.devtools.build.lib.analysis.util;
 
-
 import com.google.common.base.Predicates;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
@@ -173,8 +172,8 @@
             ruleClassProvider.getConfigurationFragments()));
     PackageFactory pkgFactory =
         analysisMock
-            .getPackageFactoryForTesting()
-            .create(ruleClassProvider, scratch.getFileSystem());
+            .getPackageFactoryBuilderForTesting()
+            .build(ruleClassProvider, scratch.getFileSystem());
     BinTools binTools = BinTools.forUnitTesting(directories, analysisMock.getEmbeddedTools());
     skyframeExecutor =
         SequencedSkyframeExecutor.create(
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java b/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java
index 1c88d6d..77fcd43 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewTestCase.java
@@ -216,14 +216,17 @@
     configurationFactory =
         analysisMock.createConfigurationFactory(ruleClassProvider.getConfigurationFragments());
 
+    ImmutableList<PrecomputedValue.Injected> extraPrecomputedValues = ImmutableList.of(
+        PrecomputedValue.injected(
+            RepositoryDelegatorFunction.REPOSITORY_OVERRIDES,
+            ImmutableMap.<RepositoryName, PathFragment>of()));
     pkgFactory =
         analysisMock
-            .getPackageFactoryForTesting()
-            .create(
-                ruleClassProvider,
-                getPlatformSetRegexps(),
-                getEnvironmentExtensions(),
-                scratch.getFileSystem());
+            .getPackageFactoryBuilderForTesting()
+            .setExtraPrecomputeValues(extraPrecomputedValues)
+            .setEnvironmentExtensions(getEnvironmentExtensions())
+            .setPlatformSetRegexps(getPlatformSetRegexps())
+            .build(ruleClassProvider, scratch.getFileSystem());
     tsgm = new TimestampGranularityMonitor(BlazeClock.instance());
     skyframeExecutor =
         SequencedSkyframeExecutor.create(
@@ -240,9 +243,7 @@
             analysisMock.getProductName(),
             CrossRepositoryLabelViolationStrategy.ERROR,
             ImmutableList.of(BuildFileName.BUILD_DOT_BAZEL, BuildFileName.BUILD));
-    skyframeExecutor.injectExtraPrecomputedValues(ImmutableList.of(PrecomputedValue.injected(
-        RepositoryDelegatorFunction.REPOSITORY_OVERRIDES,
-        ImmutableMap.<RepositoryName, PathFragment>of())));
+    skyframeExecutor.injectExtraPrecomputedValues(extraPrecomputedValues);
     packageCacheOptions.defaultVisibility = ConstantRuleVisibility.PUBLIC;
     packageCacheOptions.showLoadingProgress = true;
     packageCacheOptions.globbingThreads = 7;
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/util/ConfigurationTestCase.java b/src/test/java/com/google/devtools/build/lib/analysis/util/ConfigurationTestCase.java
index 9cfc8fd..0f61da4 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/util/ConfigurationTestCase.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/util/ConfigurationTestCase.java
@@ -98,8 +98,8 @@
         new BlazeDirectories(outputBase, outputBase, rootDirectory, analysisMock.getProductName());
     pkgFactory =
         analysisMock
-            .getPackageFactoryForTesting()
-            .create(ruleClassProvider, scratch.getFileSystem());
+            .getPackageFactoryBuilderForTesting()
+            .build(ruleClassProvider, scratch.getFileSystem());
     AnalysisTestUtil.DummyWorkspaceStatusActionFactory workspaceStatusActionFactory =
         new AnalysisTestUtil.DummyWorkspaceStatusActionFactory(directories);
 
diff --git a/src/test/java/com/google/devtools/build/lib/packages/util/LoadingMock.java b/src/test/java/com/google/devtools/build/lib/packages/util/LoadingMock.java
index f08e502..217c375 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/util/LoadingMock.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/util/LoadingMock.java
@@ -15,7 +15,7 @@
 
 import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider;
 import com.google.devtools.build.lib.flags.InvocationPolicyEnforcer;
-import com.google.devtools.build.lib.packages.PackageFactory;
+import com.google.devtools.build.lib.skyframe.packages.PackageFactoryBuilderWithSkyframeForTesting;
 import com.google.devtools.build.lib.testutil.TestConstants;
 import com.google.devtools.build.lib.testutil.TestRuleClassProvider;
 
@@ -29,8 +29,9 @@
     return TestConstants.PRODUCT_NAME;
   }
 
-  public PackageFactory.FactoryForTesting getPackageFactoryForTesting() {
-    return TestConstants.PACKAGE_FACTORY_FACTORY_FOR_TESTING;
+  public PackageFactoryBuilderWithSkyframeForTesting getPackageFactoryBuilderForTesting() {
+    return (PackageFactoryBuilderWithSkyframeForTesting)
+        TestConstants.PACKAGE_FACTORY_BUILDER_FACTORY_FOR_TESTING.builder();
   }
 
   public ConfiguredRuleClassProvider createRuleClassProvider() {
diff --git a/src/test/java/com/google/devtools/build/lib/packages/util/PackageLoadingTestCase.java b/src/test/java/com/google/devtools/build/lib/packages/util/PackageLoadingTestCase.java
index 975160d..3ebda8e 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/util/PackageLoadingTestCase.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/util/PackageLoadingTestCase.java
@@ -90,8 +90,9 @@
     }
     packageFactory =
         loadingMock
-            .getPackageFactoryForTesting()
-            .create(ruleClassProvider, null, getEnvironmentExtensions(), scratch.getFileSystem());
+            .getPackageFactoryBuilderForTesting()
+            .setEnvironmentExtensions(getEnvironmentExtensions())
+            .build(ruleClassProvider, scratch.getFileSystem());
     skyframeExecutor = createSkyframeExecutor();
     setUpSkyframe();
   }
diff --git a/src/test/java/com/google/devtools/build/lib/pkgcache/BuildFileModificationTest.java b/src/test/java/com/google/devtools/build/lib/pkgcache/BuildFileModificationTest.java
index 91297b5..1633111 100644
--- a/src/test/java/com/google/devtools/build/lib/pkgcache/BuildFileModificationTest.java
+++ b/src/test/java/com/google/devtools/build/lib/pkgcache/BuildFileModificationTest.java
@@ -81,8 +81,8 @@
     skyframeExecutor =
         SequencedSkyframeExecutor.create(
             analysisMock
-                .getPackageFactoryForTesting()
-                .create(ruleClassProvider, scratch.getFileSystem()),
+                .getPackageFactoryBuilderForTesting()
+                .build(ruleClassProvider, scratch.getFileSystem()),
             directories,
             null, /* BinTools */
             null, /* workspaceStatusActionFactory */
diff --git a/src/test/java/com/google/devtools/build/lib/pkgcache/IncrementalLoadingTest.java b/src/test/java/com/google/devtools/build/lib/pkgcache/IncrementalLoadingTest.java
index 8e57f6c..def12d9 100644
--- a/src/test/java/com/google/devtools/build/lib/pkgcache/IncrementalLoadingTest.java
+++ b/src/test/java/com/google/devtools/build/lib/pkgcache/IncrementalLoadingTest.java
@@ -468,8 +468,8 @@
       skyframeExecutor =
           SequencedSkyframeExecutor.create(
               loadingMock
-                  .getPackageFactoryForTesting()
-                  .create(loadingMock.createRuleClassProvider(), fs),
+                  .getPackageFactoryBuilderForTesting()
+                  .build(loadingMock.createRuleClassProvider(), fs),
               new BlazeDirectories(
                   fs.getPath("/install"),
                   fs.getPath("/output"),
diff --git a/src/test/java/com/google/devtools/build/lib/pkgcache/LoadingPhaseRunnerTest.java b/src/test/java/com/google/devtools/build/lib/pkgcache/LoadingPhaseRunnerTest.java
index 9de3dec..eb114ea 100644
--- a/src/test/java/com/google/devtools/build/lib/pkgcache/LoadingPhaseRunnerTest.java
+++ b/src/test/java/com/google/devtools/build/lib/pkgcache/LoadingPhaseRunnerTest.java
@@ -604,7 +604,7 @@
 
       ConfiguredRuleClassProvider ruleClassProvider = analysisMock.createRuleClassProvider();
       PackageFactory pkgFactory =
-          analysisMock.getPackageFactoryForTesting().create(ruleClassProvider, fs);
+          analysisMock.getPackageFactoryBuilderForTesting().build(ruleClassProvider, fs);
       PackageCacheOptions options = Options.getDefaults(PackageCacheOptions.class);
       storedErrors = new StoredEventHandler();
       BlazeDirectories directories =
diff --git a/src/test/java/com/google/devtools/build/lib/pkgcache/PackageCacheTest.java b/src/test/java/com/google/devtools/build/lib/pkgcache/PackageCacheTest.java
index d31fa52..3b7908e 100644
--- a/src/test/java/com/google/devtools/build/lib/pkgcache/PackageCacheTest.java
+++ b/src/test/java/com/google/devtools/build/lib/pkgcache/PackageCacheTest.java
@@ -36,6 +36,7 @@
 import com.google.devtools.build.lib.packages.NoSuchPackageException;
 import com.google.devtools.build.lib.packages.NoSuchTargetException;
 import com.google.devtools.build.lib.packages.Package;
+import com.google.devtools.build.lib.packages.PackageFactory;
 import com.google.devtools.build.lib.packages.Rule;
 import com.google.devtools.build.lib.packages.Target;
 import com.google.devtools.build.lib.skyframe.DiffAwareness;
@@ -76,15 +77,22 @@
 
   @Before
   public final void initializeSkyframeExecutor() throws Exception {
+    initializeSkyframeExecutor(/*doPackageLoadingChecks=*/ true);
+  }
+
+  private void initializeSkyframeExecutor(boolean doPackageLoadingChecks) throws Exception {
     analysisMock = AnalysisMock.get();
     ruleClassProvider = analysisMock.createRuleClassProvider();
     BlazeDirectories directories =
         new BlazeDirectories(outputBase, outputBase, rootDirectory, analysisMock.getProductName());
+    PackageFactory.BuilderForTesting packageFactoryBuilder =
+        analysisMock.getPackageFactoryBuilderForTesting();
+    if (!doPackageLoadingChecks) {
+      packageFactoryBuilder.disableChecks();
+    }
     skyframeExecutor =
         SequencedSkyframeExecutor.create(
-            analysisMock
-                .getPackageFactoryForTesting()
-                .create(ruleClassProvider, scratch.getFileSystem()),
+            packageFactoryBuilder.build(ruleClassProvider, scratch.getFileSystem()),
             directories,
             null, /* BinTools */
             null, /* workspaceStatusActionFactory */
@@ -484,6 +492,8 @@
 
   @Test
   public void testDeletedPackages() throws Exception {
+    // PackageLoader doesn't support --deleted_packages.
+    initializeSkyframeExecutor(/*doPackageLoadingChecks=*/ false);
     reporter.removeHandler(failFastHandler);
     setUpCacheWithTwoRootLocator();
     createBuildFile(rootDir1, "c", "d/x");
diff --git a/src/test/java/com/google/devtools/build/lib/rules/repository/RepositoryDelegatorTest.java b/src/test/java/com/google/devtools/build/lib/rules/repository/RepositoryDelegatorTest.java
index 449cf0c..849a3b7 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/repository/RepositoryDelegatorTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/repository/RepositoryDelegatorTest.java
@@ -106,7 +106,7 @@
                     SkyFunctions.WORKSPACE_FILE,
                     new WorkspaceFileFunction(
                         TestRuleClassProvider.getRuleClassProvider(),
-                        TestConstants.PACKAGE_FACTORY_FACTORY_FOR_TESTING.create(
+                        TestConstants.PACKAGE_FACTORY_BUILDER_FACTORY_FOR_TESTING.builder().build(
                             TestRuleClassProvider.getRuleClassProvider(), root.getFileSystem()),
                         directories))
                 .put(SkyFunctions.LOCAL_REPOSITORY_LOOKUP, new LocalRepositoryLookupFunction())
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/ArtifactFunctionTestCase.java b/src/test/java/com/google/devtools/build/lib/skyframe/ArtifactFunctionTestCase.java
index 491ecc1..c01285e 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/ArtifactFunctionTestCase.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/ArtifactFunctionTestCase.java
@@ -108,7 +108,7 @@
                     SkyFunctions.WORKSPACE_FILE,
                     new WorkspaceFileFunction(
                         TestRuleClassProvider.getRuleClassProvider(),
-                        TestConstants.PACKAGE_FACTORY_FACTORY_FOR_TESTING.create(
+                        TestConstants.PACKAGE_FACTORY_BUILDER_FACTORY_FOR_TESTING.builder().build(
                             TestRuleClassProvider.getRuleClassProvider(), root.getFileSystem()),
                         directories))
                 .put(SkyFunctions.EXTERNAL_PACKAGE, new ExternalPackageFunction())
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/FileFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/FileFunctionTest.java
index 8e1ff34..8be17f2 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/FileFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/FileFunctionTest.java
@@ -157,7 +157,7 @@
                     SkyFunctions.WORKSPACE_FILE,
                     new WorkspaceFileFunction(
                         TestRuleClassProvider.getRuleClassProvider(),
-                        TestConstants.PACKAGE_FACTORY_FACTORY_FOR_TESTING.create(
+                        TestConstants.PACKAGE_FACTORY_BUILDER_FACTORY_FOR_TESTING.builder().build(
                             TestRuleClassProvider.getRuleClassProvider(), fs),
                         directories))
                 .put(SkyFunctions.EXTERNAL_PACKAGE, new ExternalPackageFunction())
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/FilesystemValueCheckerTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/FilesystemValueCheckerTest.java
index 2c92706..74e7c77 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/FilesystemValueCheckerTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/FilesystemValueCheckerTest.java
@@ -128,7 +128,7 @@
         new WorkspaceASTFunction(TestRuleClassProvider.getRuleClassProvider()));
     skyFunctions.put(SkyFunctions.WORKSPACE_FILE,
         new WorkspaceFileFunction(TestRuleClassProvider.getRuleClassProvider(),
-            TestConstants.PACKAGE_FACTORY_FACTORY_FOR_TESTING.create(
+            TestConstants.PACKAGE_FACTORY_BUILDER_FACTORY_FOR_TESTING.builder().build(
                 TestRuleClassProvider.getRuleClassProvider(), fs),
             directories));
     skyFunctions.put(SkyFunctions.EXTERNAL_PACKAGE, new ExternalPackageFunction());
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/LocalRepositoryLookupFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/LocalRepositoryLookupFunctionTest.java
index a2d5178..c8c5bae 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/LocalRepositoryLookupFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/LocalRepositoryLookupFunctionTest.java
@@ -24,6 +24,7 @@
 import com.google.devtools.build.lib.cmdline.RepositoryName;
 import com.google.devtools.build.lib.events.NullEventHandler;
 import com.google.devtools.build.lib.packages.PackageFactory;
+import com.google.devtools.build.lib.packages.PackageFactory.EnvironmentExtension;
 import com.google.devtools.build.lib.packages.RuleClassProvider;
 import com.google.devtools.build.lib.pkgcache.PathPackageLocator;
 import com.google.devtools.build.lib.skyframe.ExternalFilesHelper.ExternalFileAction;
@@ -94,10 +95,12 @@
         new WorkspaceFileFunction(
             ruleClassProvider,
             analysisMock
-                .getPackageFactoryForTesting()
-                .create(
+                .getPackageFactoryBuilderForTesting()
+                .setEnvironmentExtensions(
+                    ImmutableList.<EnvironmentExtension>of(
+                        new PackageFactory.EmptyEnvironmentExtension()))
+                .build(
                     ruleClassProvider,
-                    new PackageFactory.EmptyEnvironmentExtension(),
                     scratch.getFileSystem()),
             directories));
     skyFunctions.put(SkyFunctions.EXTERNAL_PACKAGE, new ExternalPackageFunction());
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/PackageLookupFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/PackageLookupFunctionTest.java
index 9fe0880..ff5b2bd 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/PackageLookupFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/PackageLookupFunctionTest.java
@@ -31,6 +31,7 @@
 import com.google.devtools.build.lib.events.NullEventHandler;
 import com.google.devtools.build.lib.packages.BuildFileNotFoundException;
 import com.google.devtools.build.lib.packages.PackageFactory;
+import com.google.devtools.build.lib.packages.PackageFactory.EnvironmentExtension;
 import com.google.devtools.build.lib.packages.RuleClassProvider;
 import com.google.devtools.build.lib.pkgcache.PathPackageLocator;
 import com.google.devtools.build.lib.rules.repository.LocalRepositoryFunction;
@@ -117,10 +118,12 @@
         new WorkspaceFileFunction(
             ruleClassProvider,
             analysisMock
-                .getPackageFactoryForTesting()
-                .create(
+                .getPackageFactoryBuilderForTesting()
+                .setEnvironmentExtensions(
+                    ImmutableList.<EnvironmentExtension>of(
+                        new PackageFactory.EmptyEnvironmentExtension()))
+                .build(
                     ruleClassProvider,
-                    new PackageFactory.EmptyEnvironmentExtension(),
                     scratch.getFileSystem()),
             directories));
     skyFunctions.put(SkyFunctions.EXTERNAL_PACKAGE, new ExternalPackageFunction());
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/RecursiveFilesystemTraversalFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/RecursiveFilesystemTraversalFunctionTest.java
index d2ddae0..2aa91f6 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/RecursiveFilesystemTraversalFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/RecursiveFilesystemTraversalFunctionTest.java
@@ -120,8 +120,8 @@
         new WorkspaceFileFunction(
             ruleClassProvider,
             analysisMock
-                .getPackageFactoryForTesting()
-                .create(ruleClassProvider, scratch.getFileSystem()),
+                .getPackageFactoryBuilderForTesting()
+                .build(ruleClassProvider, scratch.getFileSystem()),
             directories));
     skyFunctions.put(SkyFunctions.EXTERNAL_PACKAGE, new ExternalPackageFunction());
     skyFunctions.put(SkyFunctions.LOCAL_REPOSITORY_LOOKUP, new LocalRepositoryLookupFunction());
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/TimestampBuilderTestCase.java b/src/test/java/com/google/devtools/build/lib/skyframe/TimestampBuilderTestCase.java
index 901fa9f..2104ec5 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/TimestampBuilderTestCase.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/TimestampBuilderTestCase.java
@@ -205,7 +205,7 @@
                     SkyFunctions.WORKSPACE_FILE,
                     new WorkspaceFileFunction(
                         TestRuleClassProvider.getRuleClassProvider(),
-                        TestConstants.PACKAGE_FACTORY_FACTORY_FOR_TESTING.create(
+                        TestConstants.PACKAGE_FACTORY_BUILDER_FACTORY_FOR_TESTING.builder().build(
                             TestRuleClassProvider.getRuleClassProvider(), scratch.getFileSystem()),
                         directories))
                 .put(SkyFunctions.EXTERNAL_PACKAGE, new ExternalPackageFunction())
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/packages/AbstractPackageLoaderTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/packages/AbstractPackageLoaderTest.java
new file mode 100644
index 0000000..40deb50
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/packages/AbstractPackageLoaderTest.java
@@ -0,0 +1,118 @@
+// Copyright 2017 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.skyframe.packages;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.devtools.build.lib.testutil.MoreAsserts.assertContainsEvent;
+import static com.google.devtools.build.lib.testutil.MoreAsserts.assertNoEvents;
+import static org.junit.Assert.fail;
+
+import com.google.common.base.Joiner;
+import com.google.common.eventbus.EventBus;
+import com.google.devtools.build.lib.cmdline.PackageIdentifier;
+import com.google.devtools.build.lib.events.Reporter;
+import com.google.devtools.build.lib.events.StoredEventHandler;
+import com.google.devtools.build.lib.packages.NoSuchPackageException;
+import com.google.devtools.build.lib.packages.Package;
+import com.google.devtools.build.lib.vfs.FileSystem;
+import com.google.devtools.build.lib.vfs.FileSystemUtils;
+import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.PathFragment;
+import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
+import org.junit.Before;
+import org.junit.Test;
+
+/** Abstract base class of a unit test for a {@link AbstractPackageLoader} implementation. */
+public abstract class AbstractPackageLoaderTest {
+  private Path pkgRoot;
+  protected StoredEventHandler handler;
+  protected PackageLoader pkgLoader;
+
+  @Before
+  public final void init() throws Exception {
+    FileSystem fs = new InMemoryFileSystem();
+    pkgRoot = fs.getRootDirectory().getChild("pkgRoot");
+    FileSystemUtils.createDirectoryAndParents(pkgRoot);
+    Reporter reporter = new Reporter(new EventBus());
+    handler = new StoredEventHandler();
+    reporter.addHandler(handler);
+    pkgLoader = makeFreshBuilder(pkgRoot).setReporter(reporter).build();
+  }
+
+  protected abstract AbstractPackageLoader.Builder makeFreshBuilder(Path pkgRoot);
+
+  @Test
+  public void simpleNoPackage() throws Exception {
+    PackageIdentifier pkgId = PackageIdentifier.createInMainRepo(PathFragment.create("nope"));
+    try {
+      pkgLoader.loadPackage(pkgId);
+      fail();
+    } catch (NoSuchPackageException expected) {
+      assertThat(expected)
+          .hasMessageThat()
+          .isEqualTo("no such package 'nope': BUILD file not found on package path");
+    }
+    assertNoEvents(handler.getEvents());
+  }
+
+  @Test
+  public void simpleBadPackage() throws Exception {
+    file("bad/BUILD", "invalidBUILDsyntax");
+    PackageIdentifier pkgId = PackageIdentifier.createInMainRepo(PathFragment.create("bad"));
+    Package badPkg = pkgLoader.loadPackage(pkgId);
+    assertThat(badPkg.containsErrors()).isTrue();
+    assertContainsEvent(badPkg.getEvents(), "invalidBUILDsyntax");
+    assertContainsEvent(handler.getEvents(), "invalidBUILDsyntax");
+  }
+
+  @Test
+  public void simpleGoodPackage() throws Exception {
+    file("good/BUILD", "sh_library(name = 'good')");
+    PackageIdentifier pkgId = PackageIdentifier.createInMainRepo(PathFragment.create("good"));
+    Package goodPkg = pkgLoader.loadPackage(pkgId);
+    assertThat(goodPkg.containsErrors()).isFalse();
+    assertThat(
+        goodPkg.getTarget("good").getAssociatedRule().getRuleClass()).isEqualTo("sh_library");
+    assertNoEvents(goodPkg.getEvents());
+    assertNoEvents(handler.getEvents());
+  }
+
+  @Test
+  public void simpleGoodPackage_Skylark() throws Exception {
+    file("good/good.bzl",
+        "def f(x):",
+        "  native.sh_library(name = x)");
+    file("good/BUILD",
+        "load('//good:good.bzl', 'f')",
+        "f('good')");
+    PackageIdentifier pkgId = PackageIdentifier.createInMainRepo(PathFragment.create("good"));
+    Package goodPkg = pkgLoader.loadPackage(pkgId);
+    assertThat(goodPkg.containsErrors()).isFalse();
+    assertThat(
+        goodPkg.getTarget("good").getAssociatedRule().getRuleClass()).isEqualTo("sh_library");
+    assertNoEvents(goodPkg.getEvents());
+    assertNoEvents(handler.getEvents());
+  }
+
+  protected Path path(String rootRelativePath) {
+    return pkgRoot.getRelative(PathFragment.create(rootRelativePath));
+  }
+
+  protected Path file(String fileName, String... contents) throws Exception {
+    Path path = path(fileName);
+    FileSystemUtils.createDirectoryAndParents(path.getParentDirectory());
+    FileSystemUtils.writeContentAsLatin1(path, Joiner.on("\n").join(contents));
+    return path;
+  }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/packages/BUILD b/src/test/java/com/google/devtools/build/lib/skyframe/packages/BUILD
new file mode 100644
index 0000000..89f75b3
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/packages/BUILD
@@ -0,0 +1,25 @@
+filegroup(
+    name = "srcs",
+    srcs = glob(["**"]),
+    visibility = ["//src/test/java/com/google/devtools/build/lib:__pkg__"],
+)
+
+java_test(
+    name = "BazelPackageLoaderTest",
+    srcs = [
+        "AbstractPackageLoaderTest.java",
+        "BazelPackageLoaderTest.java",
+    ],
+    deps = [
+        "//src/main/java/com/google/devtools/build/lib:BazelPackageLoader",
+        "//src/main/java/com/google/devtools/build/lib:events",
+        "//src/main/java/com/google/devtools/build/lib:inmemoryfs",
+        "//src/main/java/com/google/devtools/build/lib:packages",
+        "//src/main/java/com/google/devtools/build/lib:vfs",
+        "//src/test/java/com/google/devtools/build/lib:testutil",
+        "//third_party:guava",
+        "//third_party:jsr305",
+        "//third_party:junit4",
+        "//third_party:truth",
+    ],
+)
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/packages/BazelPackageLoaderTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/packages/BazelPackageLoaderTest.java
new file mode 100644
index 0000000..e6f6e4d
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/packages/BazelPackageLoaderTest.java
@@ -0,0 +1,32 @@
+// Copyright 2017 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.skyframe.packages;
+
+import com.google.devtools.build.lib.vfs.Path;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Simple tests for {@link BazelPackageLoader}.
+ *
+ * <p>Bazel's unit and integration tests do sanity checks with {@link BazelPackageLoader} under the
+ * covers, so we get pretty exhaustive correctness tests for free.
+ */
+@RunWith(JUnit4.class)
+public final class BazelPackageLoaderTest extends AbstractPackageLoaderTest {
+  @Override
+  protected BazelPackageLoader.Builder makeFreshBuilder(Path pkgRoot) {
+    return BazelPackageLoader.builder(pkgRoot);
+  }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/testutil/BazelPackageBuilderHelperForTesting.java b/src/test/java/com/google/devtools/build/lib/testutil/BazelPackageBuilderHelperForTesting.java
new file mode 100644
index 0000000..21f20cf
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/testutil/BazelPackageBuilderHelperForTesting.java
@@ -0,0 +1,108 @@
+// Copyright 2017 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.testutil;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.cmdline.PackageIdentifier;
+import com.google.devtools.build.lib.packages.NoSuchPackageException;
+import com.google.devtools.build.lib.packages.Package;
+import com.google.devtools.build.lib.packages.RuleClassProvider;
+import com.google.devtools.build.lib.packages.Target;
+import com.google.devtools.build.lib.skyframe.PackageFunction;
+import com.google.devtools.build.lib.skyframe.packages.BazelPackageLoader;
+import com.google.devtools.build.lib.skyframe.packages.PackageLoader;
+import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.PathFragment;
+
+/**
+ * A Package.Builder.Helper for use in tests that a sanity check with {@link BazelPackageLoader} for
+ * each loaded package, for the sake of getting pretty nice test coverage.
+ */
+public class BazelPackageBuilderHelperForTesting implements Package.Builder.Helper {
+  private final RuleClassProvider ruleClassProvider;
+
+  public BazelPackageBuilderHelperForTesting(RuleClassProvider ruleClassProvider) {
+    this.ruleClassProvider = ruleClassProvider;
+  }
+
+  @Override
+  public Package createFreshPackage(PackageIdentifier packageId, String runfilesPrefix) {
+    return Package.Builder.DefaultHelper.INSTANCE.createFreshPackage(packageId, runfilesPrefix);
+  }
+
+  @Override
+  public void onLoadingComplete(Package pkg) {
+    sanityCheckBazelPackageLoader(pkg, ruleClassProvider);
+  }
+
+  private static final Function<Target, Label> TARGET_TO_LABEL =
+      new Function<Target, Label>() {
+        @Override
+        public Label apply(Target input) {
+          return input.getLabel();
+        }
+      };
+
+  // This is synchronized because some Skylark internals aren't thread safe.
+  private synchronized void sanityCheckBazelPackageLoader(
+      Package pkg,
+      RuleClassProvider ruleClassProvider) {
+    PackageIdentifier pkgId = pkg.getPackageIdentifier();
+    if (pkgId.equals(Label.EXTERNAL_PACKAGE_IDENTIFIER)
+        || !pkg.getPackageIdentifier().getRepository().isMain()
+        || PackageFunction.isDefaultsPackage(pkg.getPackageIdentifier())) {
+      // TODO(nharmata): Support these packages.
+      return;
+    }
+    int numNameSegments = pkg.getNameFragment().segmentCount();
+    PathFragment fullFilenameFragment = pkg.getFilename().asFragment();
+    int numFullFilenameFragmentSegments = fullFilenameFragment.segmentCount();
+    Path workspaceRoot = pkg.getFilename().getFileSystem().getPath(
+        fullFilenameFragment.subFragment(
+            0,
+            numFullFilenameFragmentSegments - (numNameSegments + 1)));
+    PackageLoader packageLoader = BazelPackageLoader.builder(workspaceRoot)
+        .setRuleClassProvider(ruleClassProvider)
+        .build();
+    Package newlyLoadedPkg;
+    try {
+      newlyLoadedPkg = packageLoader.loadPackage(pkg.getPackageIdentifier());
+    } catch (InterruptedException e) {
+      return;
+    } catch (NoSuchPackageException e) {
+      throw new IllegalStateException(e);
+    }
+    ImmutableSet<Label> targetsInPkg =
+        ImmutableSet.copyOf(Iterables.transform(pkg.getTargets(), TARGET_TO_LABEL));
+    ImmutableSet<Label> targetsInNewlyLoadedPkg =
+        ImmutableSet.copyOf(Iterables.transform(newlyLoadedPkg.getTargets(), TARGET_TO_LABEL));
+    if (!targetsInPkg.equals(targetsInNewlyLoadedPkg)) {
+      throw new IllegalStateException(String.format(
+          "The Package for %s had a different set of targets (<targetsInPkg> - "
+          + "<targetsInNewlyLoadedPkg> = %s, <targetsInNewlyLoadedPkg> - <targetsInPkg> = %s) when "
+          + "loaded normally during execution of the current test than it did when loaded via "
+          + "BazelPackageLoader (done automatically by the BazelPackageBuilderHelperForTesting "
+          + "hook). This either means: (i) Skyframe package loading semantics have diverged from "
+          + "BazelPackageLoader semantics (ii) The test in question is doing something that "
+          + "confuses BazelPackageBuilderHelperForTesting.",
+          pkgId,
+          Sets.difference(targetsInPkg, targetsInNewlyLoadedPkg),
+          Sets.difference(targetsInNewlyLoadedPkg, targetsInPkg)));
+    }
+  }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/testutil/PackageFactoryBuilderFactoryForBazelUnitTests.java b/src/test/java/com/google/devtools/build/lib/testutil/PackageFactoryBuilderFactoryForBazelUnitTests.java
new file mode 100644
index 0000000..04e8e03
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/testutil/PackageFactoryBuilderFactoryForBazelUnitTests.java
@@ -0,0 +1,56 @@
+// Copyright 2016 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.testutil;
+
+import com.google.devtools.build.lib.packages.Package;
+import com.google.devtools.build.lib.packages.PackageFactory;
+import com.google.devtools.build.lib.packages.RuleClassProvider;
+import com.google.devtools.build.lib.skyframe.packages.PackageFactoryBuilderWithSkyframeForTesting;
+import com.google.devtools.build.lib.vfs.FileSystem;
+
+/**
+ * A {@link PackageFactory.BuilderFactoryForTesting} implementation that injects a
+ * {@link BazelPackageBuilderHelperForTesting}.
+ */
+class PackageFactoryBuilderFactoryForBazelUnitTests
+    extends PackageFactory.BuilderFactoryForTesting {
+  static final PackageFactoryBuilderFactoryForBazelUnitTests INSTANCE =
+      new PackageFactoryBuilderFactoryForBazelUnitTests();
+
+  private PackageFactoryBuilderFactoryForBazelUnitTests() {
+  }
+
+  @Override
+  public PackageFactoryBuilderWithSkyframeForTesting builder() {
+    return new PackageFactoryBuilderForBazelUnitTests();
+  }
+
+  private static class PackageFactoryBuilderForBazelUnitTests
+      extends PackageFactoryBuilderWithSkyframeForTesting {
+    @Override
+    public PackageFactory build(RuleClassProvider ruleClassProvider, FileSystem fs) {
+      Package.Builder.Helper packageBuilderHelperForTesting = doChecksForTesting
+          ? new BazelPackageBuilderHelperForTesting(ruleClassProvider)
+          : Package.Builder.DefaultHelper.INSTANCE;
+      return new PackageFactory(
+          ruleClassProvider,
+          platformSetRegexps,
+          attributeContainerFactory,
+          environmentExtensions,
+          version,
+          packageBuilderHelperForTesting);
+    }
+  }
+}
+
diff --git a/src/test/java/com/google/devtools/build/lib/testutil/PackageFactoryFactoryForBazelUnitTests.java b/src/test/java/com/google/devtools/build/lib/testutil/PackageFactoryFactoryForBazelUnitTests.java
deleted file mode 100644
index 43dea59..0000000
--- a/src/test/java/com/google/devtools/build/lib/testutil/PackageFactoryFactoryForBazelUnitTests.java
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2016 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.testutil;
-
-import com.google.common.base.Function;
-import com.google.devtools.build.lib.packages.AttributeContainer;
-import com.google.devtools.build.lib.packages.Package;
-import com.google.devtools.build.lib.packages.PackageFactory;
-import com.google.devtools.build.lib.packages.PackageFactory.EnvironmentExtension;
-import com.google.devtools.build.lib.packages.RuleClass;
-import com.google.devtools.build.lib.packages.RuleClassProvider;
-import com.google.devtools.build.lib.vfs.FileSystem;
-
-import java.util.Map;
-
-class PackageFactoryFactoryForBazelUnitTests extends PackageFactory.FactoryForTesting {
-  static final PackageFactoryFactoryForBazelUnitTests INSTANCE =
-      new PackageFactoryFactoryForBazelUnitTests();
-
-  private PackageFactoryFactoryForBazelUnitTests() {
-  }
-
-  @Override
-  protected PackageFactory create(
-      RuleClassProvider ruleClassProvider,
-      Map<String, String> platformSetRegexps,
-      Function<RuleClass, AttributeContainer> attributeContainerFactory,
-      Iterable<EnvironmentExtension> environmentExtensions,
-      String version,
-      FileSystem fs) {
-    return new PackageFactory(
-        ruleClassProvider,
-        platformSetRegexps,
-        attributeContainerFactory,
-        environmentExtensions,
-        version,
-        Package.Builder.DefaultHelper.INSTANCE);
-  }
-}
-
diff --git a/src/test/java/com/google/devtools/build/lib/testutil/TestConstants.java b/src/test/java/com/google/devtools/build/lib/testutil/TestConstants.java
index 52cf1ba..480a5d3 100644
--- a/src/test/java/com/google/devtools/build/lib/testutil/TestConstants.java
+++ b/src/test/java/com/google/devtools/build/lib/testutil/TestConstants.java
@@ -15,7 +15,7 @@
 package com.google.devtools.build.lib.testutil;
 
 import com.google.common.collect.ImmutableList;
-import com.google.devtools.build.lib.packages.PackageFactory;
+import com.google.devtools.build.lib.packages.PackageFactory.BuilderFactoryForTesting;
 import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.InvocationPolicy;
 
 /**
@@ -83,6 +83,6 @@
   public static final InvocationPolicy TEST_INVOCATION_POLICY =
       InvocationPolicy.getDefaultInstance();
 
-  public static final PackageFactory.FactoryForTesting PACKAGE_FACTORY_FACTORY_FOR_TESTING =
-      PackageFactoryFactoryForBazelUnitTests.INSTANCE;
+  public static final BuilderFactoryForTesting PACKAGE_FACTORY_BUILDER_FACTORY_FOR_TESTING =
+      PackageFactoryBuilderFactoryForBazelUnitTests.INSTANCE;
 }