aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/analysis/constraints/TopLevelConstraintSemantics.java
diff options
context:
space:
mode:
authorGravatar gregce <gregce@google.com>2017-07-22 00:11:13 +0200
committerGravatar Jakob Buchgraber <buchgr@google.com>2017-07-24 09:51:17 +0200
commit45ae096f3897e164a28a0cb1da9e20c9f523fc9c (patch)
tree0c0f2836738f67f2b34825fdc2b800e66e9438c9 /src/main/java/com/google/devtools/build/lib/analysis/constraints/TopLevelConstraintSemantics.java
parent129c93051e1c1aeb5df8991d8e4de6607f315130 (diff)
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
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/analysis/constraints/TopLevelConstraintSemantics.java')
-rw-r--r--src/main/java/com/google/devtools/build/lib/analysis/constraints/TopLevelConstraintSemantics.java190
1 files changed, 190 insertions, 0 deletions
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 0000000000..6fd2a9b7fb
--- /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();
+ }
+}