| // Copyright 2015 The Bazel Authors. All rights reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| package com.google.devtools.build.lib.analysis.constraints; |
| |
| import com.google.common.collect.HashMultimap; |
| import com.google.common.collect.ImmutableSet; |
| import com.google.common.collect.Lists; |
| import com.google.common.collect.Multimap; |
| import com.google.devtools.build.lib.analysis.util.BuildViewTestCase; |
| import com.google.devtools.build.lib.cmdline.Label; |
| import com.google.errorprone.annotations.CanIgnoreReturnValue; |
| import java.util.Collection; |
| import java.util.List; |
| import java.util.Set; |
| |
| /** |
| * Common functionality for tests for the constraint enforcement system. |
| */ |
| public abstract class AbstractConstraintsTest extends BuildViewTestCase { |
| /** |
| * Creates an environment group on the scratch filesystem consisting of the specified |
| * environments and specified defaults, set via a builder-style interface. The package name |
| * is the same as the group name. |
| */ |
| protected class EnvironmentGroupMaker { |
| private final String name; |
| private Set<String> environments; |
| private Set<String> defaults; |
| private final Multimap<String, String> fulfillsMap = HashMultimap.create(); |
| |
| public EnvironmentGroupMaker(String name) { |
| this.name = name; |
| } |
| |
| @CanIgnoreReturnValue |
| public EnvironmentGroupMaker setEnvironments(String... environments) { |
| this.environments = ImmutableSet.copyOf(environments); |
| return this; |
| } |
| |
| @CanIgnoreReturnValue |
| public EnvironmentGroupMaker setDefaults(String... environments) { |
| this.defaults = ImmutableSet.copyOf(environments); |
| return this; |
| } |
| |
| /** Declares that env1 fulfills env2. */ |
| @CanIgnoreReturnValue |
| public EnvironmentGroupMaker setFulfills(String env1, String env2) { |
| fulfillsMap.put(env1, env2); |
| return this; |
| } |
| |
| protected final void make() throws Exception { |
| StringBuilder builder = new StringBuilder(); |
| for (String env : environments) { |
| builder.append("environment(name = '" + env + "',\n") |
| .append(getAttrDef("fulfills", fulfillsMap.get(env).toArray(new String[0]))) |
| .append(")\n"); |
| } |
| String envGroupName = name.contains("/") ? name.substring(name.lastIndexOf("/") + 1) : name; |
| builder.append("environment_group(\n") |
| .append(" name = '" + envGroupName + "',\n") |
| .append(getAttrDef("environments", environments.toArray(new String[0])) + ",\n") |
| .append(getAttrDef("defaults", defaults.toArray(new String[0])) + ",\n") |
| .append(")"); |
| scratch.file("" + name + "/BUILD", builder.toString()); |
| } |
| } |
| |
| /** |
| * Returns a rule definition of the given name, type and custom attribute settings. |
| */ |
| protected static String getRuleDef(String ruleType, String ruleName, String... customAttributes) { |
| String ruleDef = |
| ruleType + "(\n" |
| + " name = '" + ruleName + "',\n" |
| + " srcs = ['" + ruleName + ".sh'],\n"; |
| for (String customAttribute : customAttributes) { |
| ruleDef += " " + customAttribute + ",\n"; |
| } |
| ruleDef += ")\n"; |
| return ruleDef; |
| } |
| |
| /** |
| * Given the inputs, returns the string "attrName = [':label1', ':label2', etc.]" |
| */ |
| protected static String getAttrDef(String attrName, String... labels) { |
| String attrDef = " " + attrName + " = ["; |
| for (String label : labels) { |
| attrDef += "'" + label + "', "; |
| } |
| attrDef += "]"; |
| return attrDef; |
| } |
| |
| /** |
| * The core constraint semantics check that if rule A depends on rule B, B must support all of |
| * A's environments. To model this in the tests below, we construct two rules: a "depending" |
| * rule (i.e. A) that depends on a "dependency" rule (i.e. B). Each test can construct its |
| * own instance of these rules with its own environments specifications by calling this method |
| * and {@link #getDependencyRule} with appropriate environment settings passed in as custom |
| * attributes. |
| * |
| * <p>This method constructs and returns the depending rule (i.e. A). |
| */ |
| protected static String getDependingRule(String... customAttributes) { |
| List<String> attrsAsList = Lists.newArrayList(customAttributes); |
| attrsAsList.add(getAttrDef("deps", "dep")); |
| return getRuleDef("sh_library", "main", attrsAsList.toArray(new String[0])); |
| } |
| |
| /** |
| * Returns the rule that {@link #getDependingRule} depends on. This rule must support every |
| * environment supported by the other one for their constraint relationship to be considered |
| * valid. |
| */ |
| protected static String getDependencyRule(String... customAttributes) { |
| return getRuleDef("sh_library", "dep", customAttributes); |
| } |
| |
| /** |
| * Returns the attribute definition that constrains a rule to the given environments. Inputs |
| * are expected to be package-relative labels (e.g. {@code "foo_env"}). |
| */ |
| protected static String constrainedTo(String... environments) { |
| return getAttrDef("restricted_to", environments); |
| } |
| |
| /** |
| * Returns the attribute definition that designates a rule compatible with the given environments. |
| */ |
| protected static String compatibleWith(String... environments) { |
| return getAttrDef("compatible_with", environments); |
| } |
| |
| /** |
| * Returns the environments supported by a rule. |
| */ |
| protected Collection<Label> supportedEnvironments(String ruleName, String ruleDef) |
| throws Exception { |
| return new RuleContextConstraintSemantics() |
| .getSupportedEnvironments( |
| getRuleContext(scratchConfiguredTarget("hello", ruleName, ruleDef))) |
| .getEnvironments(); |
| } |
| } |