Skip tests that aren't CPU-compatible with the current build.

We don't yet have a SKIPPED test status. So for now we report
"NO STATUS".

Also moved top-level constraint checking to its own file,
since its logic is getting more complicated.

PiperOrigin-RevId: 162790879
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/BuildView.java b/src/main/java/com/google/devtools/build/lib/analysis/BuildView.java
index 5d88451..5396334 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/BuildView.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/BuildView.java
@@ -45,6 +45,7 @@
 import com.google.devtools.build.lib.analysis.config.DynamicTransitionMapper;
 import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException;
 import com.google.devtools.build.lib.analysis.config.PatchTransition;
+import com.google.devtools.build.lib.analysis.constraints.TopLevelConstraintSemantics;
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.cmdline.PackageIdentifier;
 import com.google.devtools.build.lib.collect.nestedset.NestedSet;
@@ -335,8 +336,9 @@
    * Return value for {@link BuildView#update} and {@code BuildTool.prepareToBuild}.
    */
   public static final class AnalysisResult {
-    private final ImmutableList<ConfiguredTarget> targetsToBuild;
+    private final ImmutableSet<ConfiguredTarget> targetsToBuild;
     @Nullable private final ImmutableList<ConfiguredTarget> targetsToTest;
+    private final ImmutableSet<ConfiguredTarget> targetsToSkip;
     @Nullable private final String error;
     private final ActionGraph actionGraph;
     private final ImmutableSet<Artifact> artifactsToBuild;
@@ -351,6 +353,7 @@
         Collection<ConfiguredTarget> targetsToBuild,
         Collection<AspectValue> aspects,
         Collection<ConfiguredTarget> targetsToTest,
+        Collection<ConfiguredTarget> targetsToSkip,
         @Nullable String error,
         ActionGraph actionGraph,
         Collection<Artifact> artifactsToBuild,
@@ -359,9 +362,10 @@
         TopLevelArtifactContext topLevelContext,
         ImmutableMap<PackageIdentifier, Path> packageRoots,
         String workspaceName) {
-      this.targetsToBuild = ImmutableList.copyOf(targetsToBuild);
+      this.targetsToBuild = ImmutableSet.copyOf(targetsToBuild);
       this.aspects = ImmutableList.copyOf(aspects);
       this.targetsToTest = targetsToTest == null ? null : ImmutableList.copyOf(targetsToTest);
+      this.targetsToSkip = ImmutableSet.copyOf(targetsToSkip);
       this.error = error;
       this.actionGraph = actionGraph;
       this.artifactsToBuild = ImmutableSet.copyOf(artifactsToBuild);
@@ -375,7 +379,7 @@
     /**
      * Returns configured targets to build.
      */
-    public Collection<ConfiguredTarget> getTargetsToBuild() {
+    public ImmutableSet<ConfiguredTarget> getTargetsToBuild() {
       return targetsToBuild;
     }
 
@@ -406,6 +410,16 @@
       return targetsToTest;
     }
 
+    /**
+     * Returns the configured targets that should not be executed because they're not
+     * platform-compatible with the current build.
+     *
+     * <p>For example: tests that aren't intended for the designated CPU.
+     */
+    public ImmutableSet<ConfiguredTarget> getTargetsToSkip() {
+      return targetsToSkip;
+    }
+
     public ImmutableSet<Artifact> getAdditionalArtifactsToBuild() {
       return artifactsToBuild;
     }
@@ -591,13 +605,20 @@
       LOG.info(msg);
     }
 
+    Set<ConfiguredTarget> targetsToSkip =
+        TopLevelConstraintSemantics.checkTargetEnvironmentRestrictions(
+            skyframeAnalysisResult.getConfiguredTargets(),
+            skyframeExecutor.getPackageManager(),
+            eventHandler);
+
     AnalysisResult result =
         createResult(
             eventHandler,
             loadingResult,
             topLevelOptions,
             viewOptions,
-            skyframeAnalysisResult);
+            skyframeAnalysisResult,
+            targetsToSkip);
     LOG.info("Finished analysis");
     return result;
   }
@@ -607,17 +628,19 @@
       LoadingResult loadingResult,
       TopLevelArtifactContext topLevelOptions,
       BuildView.Options viewOptions,
-      SkyframeAnalysisResult skyframeAnalysisResult)
+      SkyframeAnalysisResult skyframeAnalysisResult,
+      Set<ConfiguredTarget> targetsToSkip)
       throws InterruptedException {
     Collection<Target> testsToRun = loadingResult.getTestsToRun();
-    Collection<ConfiguredTarget> configuredTargets = skyframeAnalysisResult.getConfiguredTargets();
+    Set<ConfiguredTarget> configuredTargets =
+        Sets.newLinkedHashSet(skyframeAnalysisResult.getConfiguredTargets());
     Collection<AspectValue> aspects = skyframeAnalysisResult.getAspects();
 
-    Collection<ConfiguredTarget> allTargetsToTest = null;
+    Set<ConfiguredTarget> allTargetsToTest = null;
     if (testsToRun != null) {
       // Determine the subset of configured targets that are meant to be run as tests.
-      allTargetsToTest =
-          Lists.newArrayList(filterTestsByTargets(configuredTargets, Sets.newHashSet(testsToRun)));
+      allTargetsToTest = Sets.newLinkedHashSet(
+          filterTestsByTargets(configuredTargets, Sets.newHashSet(testsToRun)));
     }
 
     Set<Artifact> artifactsToBuild = new HashSet<>();
@@ -682,6 +705,7 @@
         configuredTargets,
         aspects,
         allTargetsToTest,
+        targetsToSkip,
         error,
         actionGraph,
         artifactsToBuild,
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/constraints/TopLevelConstraintSemantics.java b/src/main/java/com/google/devtools/build/lib/analysis/constraints/TopLevelConstraintSemantics.java
new file mode 100644
index 0000000..6fd2a9b
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/analysis/constraints/TopLevelConstraintSemantics.java
@@ -0,0 +1,190 @@
+// 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.analysis.constraints;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Verify;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Multimap;
+import com.google.devtools.build.lib.analysis.ConfiguredTarget;
+import com.google.devtools.build.lib.analysis.OutputFileConfiguredTarget;
+import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
+import com.google.devtools.build.lib.analysis.ViewCreationFailedException;
+import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.events.ExtendedEventHandler;
+import com.google.devtools.build.lib.packages.EnvironmentGroup;
+import com.google.devtools.build.lib.packages.NoSuchPackageException;
+import com.google.devtools.build.lib.packages.NoSuchTargetException;
+import com.google.devtools.build.lib.packages.Target;
+import com.google.devtools.build.lib.pkgcache.PackageManager;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.annotation.Nullable;
+
+/**
+ * Constraint semantics that apply to top-level targets.
+ *
+ * <p>Top-level targets are "special" because they have no parents that can assert expected
+ * environment compatibility. So these expectations have to be declared by other means.
+ *
+ * <p>For all other targets see {@link ConstraintSemantics}.
+ */
+public class TopLevelConstraintSemantics {
+
+  /**
+   * Checks that if this is an environment-restricted build, all top-level targets support
+   * expected top-level environments. Expected top-level environments can be declared explicitly
+   * through {@code --target_environment} or implicitly through
+   * {@code --experimental_auto_cpu_environment_group}. For the latter, top-level targets must
+   * be compatible with the build's target configuration CPU.
+   *
+   * <p>If any target doesn't support an explicitly expected environment declared through
+   * {@link BuildConfiguration.Options#targetEnvironments}, the entire build fails with an error.
+   *
+   * <p>If any target doesn't support an implicitly expected environment declared through
+   * {@link BuildConfiguration.Options#autoCpuEnvironmentGroup}, the target is skipped during
+   * execution while remaining targets execute as normal.
+   *
+   * @param topLevelTargets the build's top-level targets
+   * @param packageManager object for retrieving loaded targets
+   * @param eventHandler the build's event handler
+   *
+   * @return the set of bad top-level targets.
+   * @throws ViewCreationFailedException if any target doesn't support an explicitly expected
+   * environment declared through {@link BuildConfiguration.Options#targetEnvironments}
+   */
+  public static Set<ConfiguredTarget> checkTargetEnvironmentRestrictions(
+      Iterable<ConfiguredTarget> topLevelTargets, PackageManager packageManager,
+      ExtendedEventHandler eventHandler)
+      throws ViewCreationFailedException, InterruptedException {
+    ImmutableSet.Builder<ConfiguredTarget> badTargets = ImmutableSet.builder();
+    // Maps targets that are missing *explicitly* required environments to the set of environments
+    // they're missing. These targets trigger a ViewCreationFailedException, which halts the build.
+    // Targets with missing *implicitly* required environments don't belong here, since the build
+    // continues while skipping them.
+    Multimap<ConfiguredTarget, Label> exceptionInducingTargets = ArrayListMultimap.create();
+    for (ConfiguredTarget topLevelTarget : topLevelTargets) {
+      BuildConfiguration config = topLevelTarget.getConfiguration();
+      boolean failBuildIfTargetIsBad = true;
+      if (config == null) {
+        // TODO(bazel-team): support file targets (they should apply package-default constraints).
+        continue;
+      } else if (!config.enforceConstraints()) {
+        continue;
+      }
+
+      List<Label> targetEnvironments = config.getTargetEnvironments();
+      if (targetEnvironments.isEmpty()) {
+        try {
+          targetEnvironments = autoConfigureTargetEnvironments(config,
+              config.getAutoCpuEnvironmentGroup(), packageManager, eventHandler);
+          failBuildIfTargetIsBad = false;
+        } catch (NoSuchPackageException
+            | NoSuchTargetException
+            | ConstraintSemantics.EnvironmentLookupException e) {
+          throw new ViewCreationFailedException("invalid target environment", e);
+        }
+      }
+
+      if (targetEnvironments.isEmpty()) {
+        continue;
+      }
+
+      // Parse and collect this configuration's environments.
+      EnvironmentCollection.Builder builder = new EnvironmentCollection.Builder();
+      for (Label envLabel : targetEnvironments) {
+        try {
+          Target env = packageManager.getTarget(eventHandler, envLabel);
+          builder.put(ConstraintSemantics.getEnvironmentGroup(env), envLabel);
+        } catch (NoSuchPackageException | NoSuchTargetException
+            | ConstraintSemantics.EnvironmentLookupException e) {
+          throw new ViewCreationFailedException("invalid target environment", e);
+        }
+      }
+      EnvironmentCollection expectedEnvironments = builder.build();
+
+      // Now check the target against those environments.
+      TransitiveInfoCollection asProvider;
+      if (topLevelTarget instanceof OutputFileConfiguredTarget) {
+        asProvider = ((OutputFileConfiguredTarget) topLevelTarget).getGeneratingRule();
+      } else {
+        asProvider = topLevelTarget;
+      }
+      SupportedEnvironmentsProvider provider =
+          Verify.verifyNotNull(asProvider.getProvider(SupportedEnvironmentsProvider.class));
+      Collection<Label> missingEnvironments =
+          ConstraintSemantics.getUnsupportedEnvironments(
+              provider.getRefinedEnvironments(), expectedEnvironments);
+      if (!missingEnvironments.isEmpty()) {
+        badTargets.add(topLevelTarget);
+        if (failBuildIfTargetIsBad) {
+          exceptionInducingTargets.putAll(topLevelTarget, missingEnvironments);
+        }
+      }
+    }
+
+    if (!exceptionInducingTargets.isEmpty()) {
+      throw new ViewCreationFailedException(getBadTargetsUserMessage(exceptionInducingTargets));
+    }
+    return ImmutableSet.copyOf(badTargets.build());
+  }
+
+  /**
+   * Helper method for {@link #checkTargetEnvironmentRestrictions} that populates inferred
+   * expected environments.
+   */
+  private static List<Label> autoConfigureTargetEnvironments(BuildConfiguration config,
+      @Nullable Label environmentGroupLabel, PackageManager packageManager,
+      ExtendedEventHandler eventHandler)
+      throws InterruptedException, NoSuchTargetException, NoSuchPackageException,
+      ConstraintSemantics.EnvironmentLookupException {
+    if (environmentGroupLabel == null) {
+      return ImmutableList.of();
+    }
+
+    EnvironmentGroup environmentGroup = (EnvironmentGroup)
+        packageManager.getTarget(eventHandler, environmentGroupLabel);
+
+    ImmutableList.Builder<Label> targetEnvironments = new ImmutableList.Builder<>();
+    for (Label environmentLabel : environmentGroup.getEnvironments()) {
+      if (environmentLabel.getName().equals(config.getCpu())) {
+        targetEnvironments.add(environmentLabel);
+      }
+    }
+
+    return targetEnvironments.build();
+  }
+
+  /**
+   * Prepares a user-friendly error message for a list of targets missing support for required
+   * environments.
+   */
+  private static String getBadTargetsUserMessage(Multimap<ConfiguredTarget, Label> badTargets) {
+    StringBuilder msg = new StringBuilder();
+    msg.append("This is a restricted-environment build.");
+    for (Map.Entry<ConfiguredTarget, Collection<Label>> entry : badTargets.asMap().entrySet()) {
+      msg.append(String.format("\n - %s does not support required environment%s %s.",
+          entry.getKey().getLabel(),
+          entry.getValue().size() == 1 ? "" : "s",
+          Joiner.on(", ").join(entry.getValue())));
+    }
+    return msg.toString();
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/buildtool/BuildResult.java b/src/main/java/com/google/devtools/build/lib/buildtool/BuildResult.java
index 8bbc62c..741ef98 100644
--- a/src/main/java/com/google/devtools/build/lib/buildtool/BuildResult.java
+++ b/src/main/java/com/google/devtools/build/lib/buildtool/BuildResult.java
@@ -14,6 +14,7 @@
 
 package com.google.devtools.build.lib.buildtool;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.MoreObjects;
 import com.google.devtools.build.lib.analysis.ConfiguredTarget;
 import com.google.devtools.build.lib.analysis.config.BuildConfigurationCollection;
@@ -42,6 +43,7 @@
   private Collection<ConfiguredTarget> actualTargets;
   private Collection<ConfiguredTarget> testTargets;
   private Collection<ConfiguredTarget> successfulTargets;
+  private Collection<ConfiguredTarget> skippedTargets;
 
   public BuildResult(long startTimeMillis) {
     this.startTimeMillis = startTimeMillis;
@@ -207,6 +209,22 @@
     return successfulTargets;
   }
 
+  /**
+   * See {@link #getSkippedTargets()}.
+   */
+  void setSkippedTargets(Collection<ConfiguredTarget> skippedTargets) {
+    this.skippedTargets = skippedTargets;
+  }
+
+  /**
+   * Returns the set of targets which were skipped (Blaze didn't attempt to execute them)
+   * because they're not compatible with the build's target platform.
+   */
+  @VisibleForTesting
+  public Collection<ConfiguredTarget> getSkippedTargets() {
+    return skippedTargets;
+  }
+
   /** For debugging. */
   @Override
   public String toString() {
diff --git a/src/main/java/com/google/devtools/build/lib/buildtool/BuildResultPrinter.java b/src/main/java/com/google/devtools/build/lib/buildtool/BuildResultPrinter.java
index 2d036ee..b43863d 100644
--- a/src/main/java/com/google/devtools/build/lib/buildtool/BuildResultPrinter.java
+++ b/src/main/java/com/google/devtools/build/lib/buildtool/BuildResultPrinter.java
@@ -56,6 +56,7 @@
       BuildRequest request,
       BuildResult result,
       Collection<ConfiguredTarget> configuredTargets,
+      Collection<ConfiguredTarget> configuredTargetsToSkip,
       Collection<AspectValue> aspects) {
     // NOTE: be careful what you print!  We don't want to create a consistency
     // problem where the summary message and the exit code disagree.  The logic
@@ -72,6 +73,9 @@
       (successfulTargets.contains(target) ? succeeded : failed).add(target);
     }
 
+    // TODO(bazel-team): convert these to a new "SKIPPED" status when ready: b/62191890.
+    failed.addAll(configuredTargetsToSkip);
+
     // Suppress summary if --show_result value is exceeded:
     if (succeeded.size() + failed.size() + aspectsToPrint.size()
         > request.getBuildOptions().maxResultTargets) {
diff --git a/src/main/java/com/google/devtools/build/lib/buildtool/BuildTool.java b/src/main/java/com/google/devtools/build/lib/buildtool/BuildTool.java
index f9bab78..a65c0ae 100644
--- a/src/main/java/com/google/devtools/build/lib/buildtool/BuildTool.java
+++ b/src/main/java/com/google/devtools/build/lib/buildtool/BuildTool.java
@@ -14,11 +14,8 @@
 package com.google.devtools.build.lib.buildtool;
 
 import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Joiner;
 import com.google.common.base.Stopwatch;
 import com.google.common.base.Throwables;
-import com.google.common.base.Verify;
-import com.google.common.collect.ImmutableList;
 import com.google.devtools.build.lib.actions.BuildFailedException;
 import com.google.devtools.build.lib.actions.TestExecException;
 import com.google.devtools.build.lib.analysis.AnalysisPhaseCompleteEvent;
@@ -29,39 +26,28 @@
 import com.google.devtools.build.lib.analysis.LicensesProvider;
 import com.google.devtools.build.lib.analysis.LicensesProvider.TargetLicense;
 import com.google.devtools.build.lib.analysis.MakeEnvironmentEvent;
-import com.google.devtools.build.lib.analysis.OutputFileConfiguredTarget;
 import com.google.devtools.build.lib.analysis.StaticallyLinkedMarkerProvider;
-import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
 import com.google.devtools.build.lib.analysis.ViewCreationFailedException;
 import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
 import com.google.devtools.build.lib.analysis.config.BuildConfigurationCollection;
 import com.google.devtools.build.lib.analysis.config.BuildOptions;
 import com.google.devtools.build.lib.analysis.config.DefaultsPackage;
 import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException;
-import com.google.devtools.build.lib.analysis.constraints.ConstraintSemantics;
-import com.google.devtools.build.lib.analysis.constraints.ConstraintSemantics.EnvironmentLookupException;
-import com.google.devtools.build.lib.analysis.constraints.EnvironmentCollection;
-import com.google.devtools.build.lib.analysis.constraints.SupportedEnvironmentsProvider;
 import com.google.devtools.build.lib.buildtool.BuildRequest.BuildRequestOptions;
 import com.google.devtools.build.lib.buildtool.buildevent.BuildCompleteEvent;
 import com.google.devtools.build.lib.buildtool.buildevent.BuildInterruptedEvent;
 import com.google.devtools.build.lib.buildtool.buildevent.BuildStartingEvent;
 import com.google.devtools.build.lib.buildtool.buildevent.TestFilteringCompleteEvent;
-import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.cmdline.TargetParsingException;
 import com.google.devtools.build.lib.collect.nestedset.NestedSet;
 import com.google.devtools.build.lib.events.Event;
 import com.google.devtools.build.lib.events.OutputFilter;
 import com.google.devtools.build.lib.events.Reporter;
-import com.google.devtools.build.lib.packages.EnvironmentGroup;
 import com.google.devtools.build.lib.packages.InputFile;
 import com.google.devtools.build.lib.packages.License;
 import com.google.devtools.build.lib.packages.License.DistributionType;
-import com.google.devtools.build.lib.packages.NoSuchPackageException;
-import com.google.devtools.build.lib.packages.NoSuchTargetException;
 import com.google.devtools.build.lib.packages.Target;
 import com.google.devtools.build.lib.packages.TargetUtils;
-import com.google.devtools.build.lib.pkgcache.LoadedPackageProvider;
 import com.google.devtools.build.lib.pkgcache.LoadingCallback;
 import com.google.devtools.build.lib.pkgcache.LoadingFailedException;
 import com.google.devtools.build.lib.pkgcache.LoadingPhaseRunner;
@@ -76,12 +62,10 @@
 import com.google.devtools.build.lib.util.RegexFilter;
 import com.google.devtools.common.options.OptionsParsingException;
 import java.util.Collection;
-import java.util.List;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 import java.util.logging.Logger;
 import java.util.regex.Pattern;
-import javax.annotation.Nullable;
 
 /**
  * Provides the bulk of the implementation of the 'blaze build' command.
@@ -223,9 +207,6 @@
         result.setActualTargets(analysisResult.getTargetsToBuild());
         result.setTestTargets(analysisResult.getTargetsToTest());
 
-        LoadedPackageProvider bridge =
-            new LoadedPackageProvider(env.getPackageManager(), env.getReporter());
-        checkTargetEnvironmentRestrictions(analysisResult.getTargetsToBuild(), bridge);
         reportTargets(analysisResult);
 
         // Execution phase.
@@ -291,104 +272,6 @@
     }
   }
 
-  /**
-   * Checks that if this is an environment-restricted build, all top-level targets support the
-   * expected environments.
-   *
-   * @param topLevelTargets the build's top-level targets
-   * @throws ViewCreationFailedException if constraint enforcement is on, the build declares
-   *     environment-restricted top level configurations, and any top-level target doesn't support
-   *     the expected environments
-   */
-  private static void checkTargetEnvironmentRestrictions(
-      Iterable<ConfiguredTarget> topLevelTargets, LoadedPackageProvider packageManager)
-      throws ViewCreationFailedException, InterruptedException {
-    for (ConfiguredTarget topLevelTarget : topLevelTargets) {
-      BuildConfiguration config = topLevelTarget.getConfiguration();
-      if (config == null) {
-        // TODO(bazel-team): support file targets (they should apply package-default constraints).
-        continue;
-      } else if (!config.enforceConstraints()) {
-        continue;
-      }
-
-      List<Label> targetEnvironments = config.getTargetEnvironments();
-      if (targetEnvironments.isEmpty()) {
-        try {
-          targetEnvironments =
-              autoConfigureTargetEnvironments(
-                  packageManager, config, config.getAutoCpuEnvironmentGroup());
-        } catch (NoSuchPackageException
-            | NoSuchTargetException
-            | ConstraintSemantics.EnvironmentLookupException e) {
-          throw new ViewCreationFailedException("invalid target environment", e);
-        }
-      }
-
-      if (targetEnvironments.isEmpty()) {
-        continue;
-      }
-
-      // Parse and collect this configuration's environments.
-      EnvironmentCollection.Builder builder = new EnvironmentCollection.Builder();
-      for (Label envLabel : targetEnvironments) {
-        try {
-          Target env = packageManager.getLoadedTarget(envLabel);
-          builder.put(ConstraintSemantics.getEnvironmentGroup(env), envLabel);
-        } catch (NoSuchPackageException | NoSuchTargetException
-            | ConstraintSemantics.EnvironmentLookupException e) {
-          throw new ViewCreationFailedException("invalid target environment", e);
-        }
-      }
-      EnvironmentCollection expectedEnvironments = builder.build();
-
-      // Now check the target against those environments.
-      TransitiveInfoCollection asProvider;
-      if (topLevelTarget instanceof OutputFileConfiguredTarget) {
-        asProvider = ((OutputFileConfiguredTarget) topLevelTarget).getGeneratingRule();
-      } else {
-        asProvider = topLevelTarget;
-      }
-      SupportedEnvironmentsProvider provider =
-          Verify.verifyNotNull(asProvider.getProvider(SupportedEnvironmentsProvider.class));
-      Collection<Label> missingEnvironments =
-          ConstraintSemantics.getUnsupportedEnvironments(
-              provider.getRefinedEnvironments(), expectedEnvironments);
-      if (!missingEnvironments.isEmpty()) {
-        throw new ViewCreationFailedException(
-            String.format(
-                "This is a restricted-environment build. %s does not support"
-                    + " required environment%s %s",
-                topLevelTarget.getLabel(),
-                missingEnvironments.size() == 1 ? "" : "s",
-                Joiner.on(", ").join(missingEnvironments)));
-      }
-    }
-  }
-
-  private static List<Label> autoConfigureTargetEnvironments(
-      LoadedPackageProvider packageManager,
-      BuildConfiguration config,
-      @Nullable Label environmentGroupLabel)
-      throws InterruptedException, NoSuchTargetException, NoSuchPackageException,
-          EnvironmentLookupException {
-    if (environmentGroupLabel == null) {
-      return ImmutableList.of();
-    }
-
-    EnvironmentGroup environmentGroup =
-        (EnvironmentGroup) packageManager.getLoadedTarget(environmentGroupLabel);
-
-    ImmutableList.Builder<Label> targetEnvironments = new ImmutableList.Builder<>();
-    for (Label environmentLabel : environmentGroup.getEnvironments()) {
-      if (environmentLabel.getName().equals(config.getCpu())) {
-        targetEnvironments.add(environmentLabel);
-      }
-    }
-
-    return targetEnvironments.build();
-  }
-
   private void reportExceptionError(Exception e) {
     if (e.getMessage() != null) {
       getReporter().handle(Event.error(e.getMessage()));
diff --git a/src/main/java/com/google/devtools/build/lib/buildtool/ExecutionTool.java b/src/main/java/com/google/devtools/build/lib/buildtool/ExecutionTool.java
index ef87660..953ae16 100644
--- a/src/main/java/com/google/devtools/build/lib/buildtool/ExecutionTool.java
+++ b/src/main/java/com/google/devtools/build/lib/buildtool/ExecutionTool.java
@@ -430,6 +430,7 @@
           analysisResult.getParallelTests(),
           analysisResult.getExclusiveTests(),
           analysisResult.getTargetsToBuild(),
+          analysisResult.getTargetsToSkip(),
           analysisResult.getAspects(),
           executor,
           builtTargets,
@@ -467,9 +468,10 @@
       try (AutoProfiler p = AutoProfiler.profiled("Show results", ProfilerTask.INFO)) {
         buildResult.setSuccessfulTargets(
             determineSuccessfulTargets(configuredTargets, builtTargets, timer));
+        buildResult.setSkippedTargets(analysisResult.getTargetsToSkip());
         BuildResultPrinter buildResultPrinter = new BuildResultPrinter(env);
-        buildResultPrinter.showBuildResult(
-            request, buildResult, configuredTargets, analysisResult.getAspects());
+        buildResultPrinter.showBuildResult(request, buildResult, configuredTargets,
+            analysisResult.getTargetsToSkip(), analysisResult.getAspects());
       }
 
       try (AutoProfiler p = AutoProfiler.profiled("Show artifacts", ProfilerTask.INFO)) {
diff --git a/src/main/java/com/google/devtools/build/lib/buildtool/SkyframeBuilder.java b/src/main/java/com/google/devtools/build/lib/buildtool/SkyframeBuilder.java
index c8cde04..ab8bcee 100644
--- a/src/main/java/com/google/devtools/build/lib/buildtool/SkyframeBuilder.java
+++ b/src/main/java/com/google/devtools/build/lib/buildtool/SkyframeBuilder.java
@@ -18,6 +18,7 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Range;
+import com.google.common.collect.Sets;
 import com.google.devtools.build.lib.actions.ActionCacheChecker;
 import com.google.devtools.build.lib.actions.ActionExecutionException;
 import com.google.devtools.build.lib.actions.ActionExecutionStatusReporter;
@@ -97,7 +98,8 @@
       Set<Artifact> artifacts,
       Set<ConfiguredTarget> parallelTests,
       Set<ConfiguredTarget> exclusiveTests,
-      Collection<ConfiguredTarget> targetsToBuild,
+      Set<ConfiguredTarget> targetsToBuild,
+      Set<ConfiguredTarget> targetsToSkip,
       Collection<AspectValue> aspects,
       Executor executor,
       Set<ConfiguredTarget> builtTargets,
@@ -139,6 +141,10 @@
         executionProgressReceiver, statusReporter);
     watchdog.start();
 
+    targetsToBuild = Sets.difference(targetsToBuild, targetsToSkip);
+    parallelTests = Sets.difference(parallelTests, targetsToSkip);
+    exclusiveTests = Sets.difference(exclusiveTests, targetsToSkip);
+
     try {
       result =
           skyframeExecutor.buildArtifacts(
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/Builder.java b/src/main/java/com/google/devtools/build/lib/skyframe/Builder.java
index d25341a..1b88199 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/Builder.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/Builder.java
@@ -24,10 +24,8 @@
 import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadCompatible;
 import com.google.devtools.build.lib.events.Reporter;
 import com.google.devtools.build.lib.util.AbruptExitException;
-
 import java.util.Collection;
 import java.util.Set;
-
 import javax.annotation.Nullable;
 
 /**
@@ -56,6 +54,8 @@
    *        artifacts.
    * @param exclusiveTests are executed one at a time, only after all other tasks have completed
    * @param targetsToBuild Set of targets which will be built
+   * @param targetsToSkip Set of targets which should be skipped (they still show up in build
+   *        results, but with a "SKIPPED" status and without the cost of any actual build work)
    * @param aspects Set of aspects that will be built
    * @param executor an opaque application-specific value that will be
    *        passed down to the execute() method of any Action executed during
@@ -65,10 +65,10 @@
    *        valid even if a future action throws ActionExecutionException
    * @param lastExecutionTimeRange If not null, the start/finish time of the last build that
    *        run the execution phase.
-   * @param topLevelArtifactContext contains the the options which determine the artifacts to build
+   * @param topLevelArtifactContext contains the options which determine the artifacts to build
    *        for the top-level targets.
    * @throws BuildFailedException if there were problems establishing the action execution
-   *         environment, if the the metadata of any file  during the build could not be obtained,
+   *         environment, if the metadata of any file  during the build could not be obtained,
    *         if any input files are missing, or if an action fails during execution
    * @throws InterruptedException if there was an asynchronous stop request
    * @throws TestExecException if any test fails
@@ -79,7 +79,8 @@
       Set<Artifact> artifacts,
       Set<ConfiguredTarget> parallelTests,
       Set<ConfiguredTarget> exclusiveTests,
-      Collection<ConfiguredTarget> targetsToBuild,
+      Set<ConfiguredTarget> targetsToBuild,
+      Set<ConfiguredTarget> targetsToSkip,
       Collection<AspectValue> aspects,
       Executor executor,
       Set<ConfiguredTarget> builtTargets,
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/SkyframeAwareActionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/SkyframeAwareActionTest.java
index 940c894..c6ac94a 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/SkyframeAwareActionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/SkyframeAwareActionTest.java
@@ -378,6 +378,7 @@
         null,
         null,
         null,
+        null,
         executor,
         null,
         false,
@@ -406,6 +407,7 @@
         null,
         null,
         null,
+        null,
         executor,
         null,
         false,
@@ -781,6 +783,7 @@
         null,
         null,
         null,
+        null,
         executor,
         null,
         false,
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 64ee5df..0357d70 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
@@ -236,7 +236,8 @@
           Set<Artifact> artifacts,
           Set<ConfiguredTarget> parallelTests,
           Set<ConfiguredTarget> exclusiveTests,
-          Collection<ConfiguredTarget> targetsToBuild,
+          Set<ConfiguredTarget> targetsToBuild,
+          Set<ConfiguredTarget> targetsToSkip,
           Collection<AspectValue> aspects,
           Executor executor,
           Set<ConfiguredTarget> builtTargets,
@@ -384,6 +385,7 @@
           null,
           null,
           null,
+          null,
           executor,
           builtArtifacts, /*explain=*/
           false,