aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google
diff options
context:
space:
mode:
authorGravatar Greg Estren <gregce@google.com>2015-03-05 21:46:34 +0000
committerGravatar Ulf Adams <ulfjack@google.com>2015-03-06 09:16:40 +0000
commit42bece1e02afbf55b29fe7fea154e9de4e91871d (patch)
tree0cc403255ae655f3eb12ddef3c6db240f35030a9 /src/main/java/com/google
parent24b69a72cbb785e4aa2f74f2fb5b3aef8d72e88a (diff)
Add --target_environment flag. This declares the environment (or set of environments)
the build is being done for. In other words: blaze build //foo:all --target_environment=//buildenv/target:gce declares that this build targets GCE, so all top-level targets must also support GCE. This essentially allows constraint enforcement to apply to top-level targets, too. So users can protect against accidentally building targets in configurations they're not meant to work with. -- MOS_MIGRATED_REVID=87862252
Diffstat (limited to 'src/main/java/com/google')
-rw-r--r--src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfiguration.java155
-rw-r--r--src/main/java/com/google/devtools/build/lib/analysis/constraints/ConstraintSemantics.java111
-rw-r--r--src/main/java/com/google/devtools/build/lib/analysis/constraints/Environment.java17
-rw-r--r--src/main/java/com/google/devtools/build/lib/analysis/constraints/EnvironmentCollection.java13
-rw-r--r--src/main/java/com/google/devtools/build/lib/buildtool/BuildTool.java60
5 files changed, 236 insertions, 120 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfiguration.java b/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfiguration.java
index 967074a9db..35edea0032 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfiguration.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfiguration.java
@@ -411,9 +411,9 @@ public final class BuildConfiguration implements Serializable {
}
@Option(name = "cpu",
- defaultValue = "null",
- category = "semantics",
- help = "The target CPU.")
+ defaultValue = "null",
+ category = "semantics",
+ help = "The target CPU.")
public String cpu;
@Option(name = "min_param_file_size",
@@ -433,15 +433,15 @@ public final class BuildConfiguration implements Serializable {
defaultValue = "true",
category = "undocumented",
help = "Flag to help transition from allowing to disallowing runtime_deps on neverlink"
- + " Java archives. The depot needs to be cleaned up to roll this out by default.")
+ + " Java archives. The depot needs to be cleaned up to roll this out by default.")
public boolean allowRuntimeDepsOnNeverLink;
@Option(name = "strict_filesets",
- defaultValue = "false",
- category = "semantics",
- help = "If this option is enabled, filesets crossing package boundaries are reported "
- + "as errors. It does not work when check_fileset_dependencies_recursively is "
- + "disabled.")
+ defaultValue = "false",
+ category = "semantics",
+ help = "If this option is enabled, filesets crossing package boundaries are reported "
+ + "as errors. It does not work when check_fileset_dependencies_recursively is "
+ + "disabled.")
public boolean strictFilesets;
// Plugins are build using the host config. To avoid cycles we just don't propagate
@@ -449,19 +449,19 @@ public final class BuildConfiguration implements Serializable {
// host tools, we can improve this by (for example) creating a compiler configuration that is
// used only for building plugins.
@Option(name = "plugin",
- converter = LabelConverter.class,
- allowMultiple = true,
- defaultValue = "",
- category = "flags",
- help = "Plugins to use in the build. Currently works with java_plugin.")
+ converter = LabelConverter.class,
+ allowMultiple = true,
+ defaultValue = "",
+ category = "flags",
+ help = "Plugins to use in the build. Currently works with java_plugin.")
public List<Label> pluginList;
@Option(name = "plugin_copt",
- converter = PluginOptionConverter.class,
- allowMultiple = true,
- category = "flags",
- defaultValue = ":",
- help = "Plugin options")
+ converter = PluginOptionConverter.class,
+ allowMultiple = true,
+ category = "flags",
+ defaultValue = ":",
+ help = "Plugin options")
public List<Map.Entry<String, String>> pluginCoptList;
@Option(name = "stamp",
@@ -518,9 +518,9 @@ public final class BuildConfiguration implements Serializable {
public String shortName;
@Option(name = "platform_suffix",
- defaultValue = "null",
- category = "misc",
- help = "Specifies a suffix to be added to the configuration directory.")
+ defaultValue = "null",
+ category = "misc",
+ help = "Specifies a suffix to be added to the configuration directory.")
public String platformSuffix;
@Option(name = "test_env",
@@ -540,9 +540,9 @@ public final class BuildConfiguration implements Serializable {
defaultValue = "false",
category = "testing",
help = "If specified, Bazel will instrument code (using offline instrumentation where "
- + "possible) and will collect coverage information during tests. Only targets that "
- + " match --instrumentation_filter will be affected. Usually this option should "
- + " not be specified directly - 'bazel coverage' command should be used instead."
+ + "possible) and will collect coverage information during tests. Only targets that "
+ + " match --instrumentation_filter will be affected. Usually this option should "
+ + " not be specified directly - 'bazel coverage' command should be used instead."
)
public boolean collectCodeCoverage;
@@ -562,13 +562,13 @@ public final class BuildConfiguration implements Serializable {
category = "testing",
abbrev = 't', // it's useful to toggle this on/off quickly
help = "If 'auto', Bazel will only rerun a test if any of the following conditions apply: "
- + "(1) Bazel detects changes in the test or its dependencies "
- + "(2) the test is marked as external "
- + "(3) multiple test runs were requested with --runs_per_test"
- + "(4) the test failed"
- + "If 'yes', the caching behavior will be the same as 'auto' except that "
- + "it may cache test failures and test runs with --runs_per_test."
- + "If 'no', all tests will be always executed.")
+ + "(1) Bazel detects changes in the test or its dependencies "
+ + "(2) the test is marked as external "
+ + "(3) multiple test runs were requested with --runs_per_test"
+ + "(4) the test failed"
+ + "If 'yes', the caching behavior will be the same as 'auto' except that "
+ + "it may cache test failures and test runs with --runs_per_test."
+ + "If 'no', all tests will be always executed.")
public TriState cacheTestResults;
@Deprecated
@@ -609,10 +609,10 @@ public final class BuildConfiguration implements Serializable {
public List<PerLabelOptions> runsPerTest;
@Option(name = "build_runfile_links",
- defaultValue = "true",
- category = "strategy",
- help = "If true, build runfiles symlink forests for all targets. "
- + "If false, write only manifests when possible.")
+ defaultValue = "true",
+ category = "strategy",
+ help = "If true, build runfiles symlink forests for all targets. "
+ + "If false, write only manifests when possible.")
public boolean buildRunfiles;
@Option(name = "test_arg",
@@ -631,13 +631,13 @@ public final class BuildConfiguration implements Serializable {
defaultValue = "null",
category = "testing",
help = "Specifies a filter to forward to the test framework. Used to limit "
- + "the tests run. Note that this does not affect which targets are built.")
+ + "the tests run. Note that this does not affect which targets are built.")
public String testFilter;
@Option(name = "check_fileset_dependencies_recursively",
- defaultValue = "true",
- category = "semantics",
- help = "If false, fileset targets will, whenever possible, create "
+ defaultValue = "true",
+ category = "semantics",
+ help = "If false, fileset targets will, whenever possible, create "
+ "symlinks to directories instead of creating one symlink for each "
+ "file inside the directory. Disabling this will significantly "
+ "speed up fileset builds, but targets that depend on filesets will "
@@ -646,23 +646,23 @@ public final class BuildConfiguration implements Serializable {
public boolean checkFilesetDependenciesRecursively;
@Option(name = "run_under",
- category = "run",
- defaultValue = "null",
- converter = RunUnderConverter.class,
- help = "Prefix to insert in front of command before running. "
- + "Examples:\n"
- + "\t--run_under=valgrind\n"
- + "\t--run_under=strace\n"
- + "\t--run_under='strace -c'\n"
- + "\t--run_under='valgrind --quiet --num-callers=20'\n"
- + "\t--run_under=//package:target\n"
- + "\t--run_under='//package:target --options'\n")
+ category = "run",
+ defaultValue = "null",
+ converter = RunUnderConverter.class,
+ help = "Prefix to insert in front of command before running. "
+ + "Examples:\n"
+ + "\t--run_under=valgrind\n"
+ + "\t--run_under=strace\n"
+ + "\t--run_under='strace -c'\n"
+ + "\t--run_under='valgrind --quiet --num-callers=20'\n"
+ + "\t--run_under=//package:target\n"
+ + "\t--run_under='//package:target --options'\n")
public RunUnder runUnder;
@Option(name = "distinct_host_configuration",
- defaultValue = "true",
- category = "strategy",
- help = "Build all the tools used during the build for a distinct configuration from "
+ defaultValue = "true",
+ category = "strategy",
+ help = "Build all the tools used during the build for a distinct configuration from "
+ "that used for the target program. By default, the same configuration is used "
+ "for host and target programs, but this may cause undesirable rebuilds of tool "
+ "such as the protocol compiler (and then everything downstream) whenever a minor "
@@ -676,9 +676,9 @@ public final class BuildConfiguration implements Serializable {
public boolean useDistinctHostConfiguration;
@Option(name = "check_visibility",
- defaultValue = "true",
- category = "checking",
- help = "If disabled, visibility errors are demoted to warnings.")
+ defaultValue = "true",
+ category = "checking",
+ help = "If disabled, visibility errors are demoted to warnings.")
public boolean checkVisibility;
// Moved from viewOptions to here because license information is very expensive to serialize.
@@ -688,8 +688,8 @@ public final class BuildConfiguration implements Serializable {
defaultValue = "false",
category = "checking",
help = "Check that licensing constraints imposed by dependent packages "
- + "do not conflict with distribution modes of the targets being built. "
- + "By default, licenses are not checked.")
+ + "do not conflict with distribution modes of the targets being built. "
+ + "By default, licenses are not checked.")
public boolean checkLicenses;
@Option(name = "experimental_enforce_constraints",
@@ -700,11 +700,11 @@ public final class BuildConfiguration implements Serializable {
public boolean enforceConstraints;
@Option(name = "experimental_action_listener",
- allowMultiple = true,
- defaultValue = "",
- category = "experimental",
- converter = LabelConverter.class,
- help = "Use action_listener to attach an extra_action to existing build actions.")
+ allowMultiple = true,
+ defaultValue = "",
+ category = "experimental",
+ converter = LabelConverter.class,
+ help = "Use action_listener to attach an extra_action to existing build actions.")
public List<Label> actionListeners;
@Option(name = "is host configuration",
@@ -724,12 +724,23 @@ public final class BuildConfiguration implements Serializable {
defaultValue = "",
category = "flags",
help = "The given features will be enabled or disabled by default for all packages. "
- + "Specifying -<feature> will disable the feature globally. "
- + "Negative features always override positive ones. "
- + "This flag is used to enable rolling out default feature changes without a "
- + "Blaze release.")
+ + "Specifying -<feature> will disable the feature globally. "
+ + "Negative features always override positive ones. "
+ + "This flag is used to enable rolling out default feature changes without a "
+ + "Blaze release.")
public List<String> defaultFeatures;
+ @Option(name = "target_environment",
+ converter = LabelConverter.class,
+ allowMultiple = true,
+ defaultValue = "",
+ category = "flags",
+ help = "Declares this build's target environment. Must be a label reference to an "
+ + "\"environment\" rule. If specified, all top-level targets must be "
+ + "compatible with this environment."
+ )
+ public List<Label> targetEnvironments;
+
@Override
public FragmentOptions getHost(boolean fallback) {
Options host = (Options) getDefault();
@@ -1940,4 +1951,12 @@ public final class BuildConfiguration implements Serializable {
public List<String> getDefaultFeatures() {
return options.defaultFeatures;
}
+
+ /**
+ * Returns the "top-level" environment space, i.e. the set of environments all top-level
+ * targets must be compatible with. An empty value implies no restrictions.
+ */
+ public List<Label> getTargetEnvironments() {
+ return options.targetEnvironments;
+ }
}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/constraints/ConstraintSemantics.java b/src/main/java/com/google/devtools/build/lib/analysis/constraints/ConstraintSemantics.java
index 14ac2bc6fa..103c796a2e 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/constraints/ConstraintSemantics.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/constraints/ConstraintSemantics.java
@@ -26,6 +26,7 @@ import com.google.devtools.build.lib.packages.AttributeMap;
import com.google.devtools.build.lib.packages.EnvironmentGroup;
import com.google.devtools.build.lib.packages.Rule;
import com.google.devtools.build.lib.packages.RuleClass;
+import com.google.devtools.build.lib.packages.Target;
import com.google.devtools.build.lib.packages.Type;
import com.google.devtools.build.lib.syntax.Label;
@@ -277,6 +278,38 @@ public class ConstraintSemantics {
}
/**
+ * Exception indicating errors finding/parsing environments or their containing groups.
+ */
+ public static class EnvironmentLookupException extends Exception {
+ private EnvironmentLookupException(String message) {
+ super(message);
+ }
+ }
+
+ /**
+ * Returns the environment group that owns the given environment. Both must belong to
+ * the same package.
+ *
+ * @throws EnvironmentLookupException if the input is not an {@link EnvironmentRule} or no
+ * matching group is found
+ */
+ public static EnvironmentGroup getEnvironmentGroup(Target envTarget)
+ throws EnvironmentLookupException {
+ if (!(envTarget instanceof Rule)
+ || !((Rule) envTarget).getRuleClass().equals(EnvironmentRule.RULE_NAME)) {
+ throw new EnvironmentLookupException(
+ envTarget.getLabel() + " is not a valid environment definition");
+ }
+ for (EnvironmentGroup group : envTarget.getPackage().getTargets(EnvironmentGroup.class)) {
+ if (group.getEnvironments().contains(envTarget.getLabel())) {
+ return group;
+ }
+ }
+ throw new EnvironmentLookupException(
+ "cannot find the group for environment " + envTarget.getLabel());
+ }
+
+ /**
* Returns the set of environments this rule supports, applying the logic described in
* {@link ConstraintSemantics}.
*
@@ -395,14 +428,12 @@ public class ConstraintSemantics {
* Performs constraint checking on the given rule's dependencies and reports any errors.
*
* @param ruleContext the rule to analyze
- * @param supportedEnvironments the rule's supported environments, as defined by the return
+ * @param ruleEnvironments the rule's supported environments, as defined by the return
* value of {@link #getSupportedEnvironments}. In particular, for any environment group that's
* not in this collection, the rule is assumed to support the defaults for that group.
*/
public static void checkConstraints(RuleContext ruleContext,
- EnvironmentCollection supportedEnvironments) {
-
- Set<EnvironmentGroup> knownGroups = supportedEnvironments.getGroups();
+ EnvironmentCollection ruleEnvironments) {
for (TransitiveInfoCollection dependency : getAllPrerequisites(ruleContext)) {
SupportedEnvironmentsProvider depProvider =
@@ -412,43 +443,53 @@ public class ConstraintSemantics {
// opt them into constraint checking, but for now just pass them by.
continue;
}
- Collection<Label> depEnvironments = depProvider.getEnvironments().getEnvironments();
- Set<EnvironmentGroup> groupsKnownToDep = depProvider.getEnvironments().getGroups();
-
- // Environments we support that the dependency does not support.
- Set<Label> disallowedEnvironments = new LinkedHashSet<>();
-
- // For every environment we support, either the dependency must also support it OR it must be
- // a default for a group the dependency doesn't know about.
- for (EnvironmentWithGroup supportedEnv : supportedEnvironments.getGroupedEnvironments()) {
- EnvironmentGroup group = supportedEnv.group();
- Label environment = supportedEnv.environment();
- if (!depEnvironments.contains(environment)
- && (groupsKnownToDep.contains(group) || !group.isDefault(environment))) {
- disallowedEnvironments.add(environment);
- }
+ Collection<Label> unsupportedEnvironments =
+ getUnsupportedEnvironments(depProvider.getEnvironments(), ruleEnvironments);
+
+ if (!unsupportedEnvironments.isEmpty()) {
+ ruleContext.ruleError("dependency " + dependency.getLabel()
+ + " doesn't support expected environment"
+ + (unsupportedEnvironments.size() == 1 ? "" : "s")
+ + ": " + Joiner.on(", ").join(unsupportedEnvironments));
}
+ }
+ }
- // For any environment group we don't know about, we implicitly support its defaults. Check
- // that the dep does, too.
- for (EnvironmentGroup depGroup : groupsKnownToDep) {
- if (!knownGroups.contains(depGroup)) {
- for (Label defaultEnv : depGroup.getDefaults()) {
- if (!depEnvironments.contains(defaultEnv)) {
- disallowedEnvironments.add(defaultEnv);
- }
- }
- }
+ /**
+ * Given a collection of environments and a collection of expected environments, returns the
+ * missing environments that would cause constraint expectations to be violated. Includes
+ * the effects of environment group defaults.
+ */
+ public static Collection<Label> getUnsupportedEnvironments(
+ EnvironmentCollection actualEnvironments, EnvironmentCollection expectedEnvironments) {
+
+ Set<Label> missingEnvironments = new LinkedHashSet<>();
+
+ // For each expected environment, it must either be a supported environment OR a default
+ // for a group the supported environment set doesn't know about.
+ for (EnvironmentWithGroup expectedEnv : expectedEnvironments.getGroupedEnvironments()) {
+ EnvironmentGroup group = expectedEnv.group();
+ Label environment = expectedEnv.environment();
+ if (!actualEnvironments.getEnvironments().contains(environment)
+ && (actualEnvironments.getGroups().contains(group) || !group.isDefault(environment))) {
+ missingEnvironments.add(environment);
}
+ }
- // Report errors on bad environments.
- if (!disallowedEnvironments.isEmpty()) {
- ruleContext.ruleError("dependency " + dependency.getLabel()
- + " doesn't support expected environment"
- + (disallowedEnvironments.size() == 1 ? "" : "s")
- + ": " + Joiner.on(", ").join(disallowedEnvironments));
+ // For any environment group not referenced by the expected environments, its defaults are
+ // implicitly applied. We can ignore it if it's also missing from the supported environments
+ // (since in that case the same defaults apply), otherwise have to check.
+ for (EnvironmentGroup group : actualEnvironments.getGroups()) {
+ if (!expectedEnvironments.getGroups().contains(group)) {
+ for (Label defaultEnv : group.getDefaults()) {
+ if (!actualEnvironments.getEnvironments().contains(defaultEnv)) {
+ missingEnvironments.add(defaultEnv);
+ }
+ }
}
}
+
+ return missingEnvironments;
}
/**
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/constraints/Environment.java b/src/main/java/com/google/devtools/build/lib/analysis/constraints/Environment.java
index 912ed723fe..ab91f454ea 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/constraints/Environment.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/constraints/Environment.java
@@ -25,7 +25,6 @@ import com.google.devtools.build.lib.analysis.RunfilesProvider;
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.collect.nestedset.Order;
import com.google.devtools.build.lib.packages.EnvironmentGroup;
-import com.google.devtools.build.lib.packages.Package;
import com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory;
import com.google.devtools.build.lib.syntax.Label;
@@ -42,18 +41,12 @@ public class Environment implements RuleConfiguredTargetFactory {
//
// This will likely expand when we add support for environments fulfilling other environments.
Label label = ruleContext.getLabel();
- Package pkg = ruleContext.getRule().getPackage();
- EnvironmentGroup group = null;
- for (EnvironmentGroup pkgGroup : pkg.getTargets(EnvironmentGroup.class)) {
- if (pkgGroup.getEnvironments().contains(label)) {
- group = pkgGroup;
- break;
- }
- }
-
- if (group == null) {
- ruleContext.ruleError("no matching environment group from the same package");
+ EnvironmentGroup group;
+ try {
+ group = ConstraintSemantics.getEnvironmentGroup(ruleContext.getRule());
+ } catch (ConstraintSemantics.EnvironmentLookupException e) {
+ ruleContext.ruleError(e.getMessage());
return null;
}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/constraints/EnvironmentCollection.java b/src/main/java/com/google/devtools/build/lib/analysis/constraints/EnvironmentCollection.java
index 1ce5f1cb4a..38216e17af 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/constraints/EnvironmentCollection.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/constraints/EnvironmentCollection.java
@@ -91,14 +91,17 @@ public class EnvironmentCollection {
static final EnvironmentCollection EMPTY =
new EnvironmentCollection(ImmutableMultimap.<EnvironmentGroup, Label>of());
- static class Builder {
+ /**
+ * Builder for {@link EnvironmentCollection}.
+ */
+ public static class Builder {
private final ImmutableMultimap.Builder<EnvironmentGroup, Label> mapBuilder =
ImmutableMultimap.builder();
/**
* Inserts the given environment / owning group pair.
*/
- Builder put(EnvironmentGroup group, Label environment) {
+ public Builder put(EnvironmentGroup group, Label environment) {
mapBuilder.put(group, environment);
return this;
}
@@ -106,7 +109,7 @@ public class EnvironmentCollection {
/**
* Inserts the given set of environments, all belonging to the specified group.
*/
- Builder putAll(EnvironmentGroup group, Iterable<Label> environments) {
+ public Builder putAll(EnvironmentGroup group, Iterable<Label> environments) {
mapBuilder.putAll(group, environments);
return this;
}
@@ -114,12 +117,12 @@ public class EnvironmentCollection {
/**
* Inserts the contents of another {@link EnvironmentCollection} into this one.
*/
- Builder putAll(EnvironmentCollection other) {
+ public Builder putAll(EnvironmentCollection other) {
mapBuilder.putAll(other.map);
return this;
}
- EnvironmentCollection build() {
+ public EnvironmentCollection build() {
return new EnvironmentCollection(mapBuilder.build());
}
}
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 a27cc50433..63c17df710 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,9 +14,11 @@
package com.google.devtools.build.lib.buildtool;
import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.base.Stopwatch;
import com.google.common.base.Throwables;
+import com.google.common.base.Verify;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.common.eventbus.EventBus;
@@ -41,6 +43,9 @@ import com.google.devtools.build.lib.analysis.config.BuildConfigurationKey;
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.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;
@@ -53,6 +58,8 @@ import com.google.devtools.build.lib.events.Reporter;
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.PackageIdentifier;
import com.google.devtools.build.lib.packages.Rule;
import com.google.devtools.build.lib.packages.Target;
@@ -61,10 +68,12 @@ import com.google.devtools.build.lib.packages.Type;
import com.google.devtools.build.lib.pkgcache.LoadingFailedException;
import com.google.devtools.build.lib.pkgcache.LoadingPhaseRunner.Callback;
import com.google.devtools.build.lib.pkgcache.LoadingPhaseRunner.LoadingResult;
+import com.google.devtools.build.lib.pkgcache.PackageManager;
import com.google.devtools.build.lib.profiler.ProfilePhase;
import com.google.devtools.build.lib.profiler.Profiler;
import com.google.devtools.build.lib.runtime.BlazeRuntime;
import com.google.devtools.build.lib.skyframe.SkyframeExecutor;
+import com.google.devtools.build.lib.syntax.Label;
import com.google.devtools.build.lib.util.AbruptExitException;
import com.google.devtools.build.lib.util.ExitCode;
import com.google.devtools.build.lib.vfs.Path;
@@ -186,6 +195,8 @@ public class BuildTool {
result.setActualTargets(analysisResult.getTargetsToBuild());
result.setTestTargets(analysisResult.getTargetsToTest());
+ checkTargetEnvironmentRestrictions(analysisResult.getTargetsToBuild(),
+ runtime.getPackageManager());
reportTargets(analysisResult);
// Execution phase.
@@ -229,6 +240,55 @@ public class BuildTool {
}
}
+ /**
+ * 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 void checkTargetEnvironmentRestrictions(Iterable<ConfiguredTarget> topLevelTargets,
+ PackageManager packageManager) throws ViewCreationFailedException {
+ 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() || config.getTargetEnvironments().isEmpty()) {
+ continue;
+ }
+
+ // Parse and collect this configuration's environments.
+ EnvironmentCollection.Builder builder = new EnvironmentCollection.Builder();
+ for (Label envLabel : config.getTargetEnvironments()) {
+ 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.
+ SupportedEnvironmentsProvider provider =
+ Verify.verifyNotNull(topLevelTarget.getProvider(SupportedEnvironmentsProvider.class));
+ Collection<Label> missingEnvironments = ConstraintSemantics.getUnsupportedEnvironments(
+ provider.getEnvironments(), 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 ImmutableMap<PathFragment, Path> mergePackageRoots(
ImmutableMap<PackageIdentifier, Path> first,
ImmutableMap<PackageIdentifier, Path> second) {