diff options
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib')
8 files changed, 202 insertions, 50 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/featurecontrol/FeaturePolicyConfiguration.java b/src/main/java/com/google/devtools/build/lib/analysis/featurecontrol/FeaturePolicyConfiguration.java index 96d3431836..37adc1a0b1 100644 --- a/src/main/java/com/google/devtools/build/lib/analysis/featurecontrol/FeaturePolicyConfiguration.java +++ b/src/main/java/com/google/devtools/build/lib/analysis/featurecontrol/FeaturePolicyConfiguration.java @@ -33,52 +33,58 @@ import com.google.devtools.build.lib.util.Preconditions; * should be limited to specified packages. Because this is part of the configuration, it can only * be accessed during the analysis phase; decisions which are made during the loading phase can't * use this information. Some examples of use cases for this fragment: + * * <ul> - * <li>A new rule class which could have a major impact on Blaze's memory usage is added. To - * limit this impact during the experimental phase, a feature policy is added which makes it - * an error for rules of that class to be created - or used by other rules - in any package - * other than those defined by the policy. The policy is populated with projects who are - * doing guided experimentation with the feature, and gradually expands as the feature rolls - * out. Then the feature's policy can be removed, making it generally available. - * <li>An attribute is being deprecated. To prevent rollback, a feature policy is added which - * makes it an error for rules in packages other than those defined by the policy to specify - * a value for that attribute. The policy is populated with existing users, and as those - * users are migrated, they are removed from the policy until it is completely empty. Then - * the attribute can be removed entirely. + * <li>A new rule class which could have a major impact on Blaze's memory usage is added. To limit + * this impact during the experimental phase, a feature policy is added which makes it an + * error for rules of that class to be created - or used by other rules - in any package other + * than those defined by the policy. The policy is populated with projects who are doing + * guided experimentation with the feature, and gradually expands as the feature rolls out. + * Then the feature's policy can be removed, making it generally available. + * <li>An attribute is being deprecated. To prevent rollback, a feature policy is added which + * makes it an error for rules in packages other than those defined by the policy to specify a + * value for that attribute. The policy is populated with existing users, and as those users + * are migrated, they are removed from the policy until it is completely empty. Then the + * attribute can be removed entirely. * </ul> * * <p>To use this package: + * * <ol> - * <li>Define a feature ID in the {@link FeaturePolicyLoader}'s constructor (in the - * RuleClassProvider). This is the string that will be used when checking for the feature in - * rule code, as well as the string used in the flag value for {@link FeaturePolicyOptions}. - * <li>In the RuleClass(es) which will change based on the feature's state, declare - * {@link FeaturePolicyConfiguration} as a required configuration fragment. - * <li>In the ConfiguredTargetFactory of those rules, get the FeaturePolicyConfiguration and - * check {@link #isFeatureEnabledForRule(String,Label)} with the feature ID created in - * step 1 and the label of the current rule. In the event that an error needs to be - * displayed, use {@link #getPolicyForFeature(String)} to show the user where the policy is. - * <li>Create a package_group containing the list of packages which should have access to this - * feature. It can be empty (no packages can access the feature) or contain //... (all - * packages can access the feature) to begin with. - * <li>After the a release containing the feature ID has been pushed, update the global RC file - * with a --feature_control_policy=(your_feature)=(your_package_group) flag. You can now - * alter access to your feature by changing the package_group. + * <li>Define a feature ID in the {@link FeaturePolicyLoader}'s constructor (in the + * RuleClassProvider). This is the string that will be used when checking for the feature in + * rule code, as well as the string used in the flag value for {@link FeaturePolicyOptions}. + * <li>In the RuleClass(es) which will change based on the feature's state, declare {@link + * FeaturePolicyConfiguration} as a required configuration fragment. + * <li>In the ConfiguredTargetFactory of those rules, get the FeaturePolicyConfiguration and check + * {@link #isFeatureEnabledForRule(String,Label)} with the feature ID created in step 1 and + * the label of the current rule. In the event that an error needs to be displayed, use {@link + * #getPolicyForFeature(String)} to show the user where the policy is. + * <li>Create a package_group containing the list of packages which should have access to this + * feature. It can be empty (no packages can access the feature) or contain //... (all + * packages can access the feature) to begin with. + * <li>After the a release containing the feature ID has been pushed, update the global RC file + * with a --feature_control_policy=(your_feature)=(your_package_group) flag. You can now alter + * access to your feature by changing the package_group. * </ol> * * <p>To stop using this package: + * * <ol> - * <li>Your policy should be at an end state - containing all packages (a rollout which has - * become generally available) or no packages (a deprecated feature which has been totally - * cleaned up). - * <li>Make the behavior the policy controlled permanent - remove a deprecated feature, or - * remove the check on a feature which is being rolled out. - * <li>After this new version is released, remove the flag from the global rc file, and remove - * the feature ID from the constructor for {@link FeaturePolicyLoader}. + * <li>Your policy should be at an end state - containing all packages (a rollout which has become + * generally available) or no packages (a deprecated feature which has been totally cleaned + * up). + * <li>Make the behavior the policy controlled permanent - remove a deprecated feature, or remove + * the check on a feature which is being rolled out. + * <li>After this new version is released, remove the flag from the global rc file, and remove the + * feature ID from the constructor for {@link FeaturePolicyLoader}. * </ol> * * @see FeaturePolicyLoader + * @deprecated This is deprecated because the dependency on the package group used to hold the + * whitelist is not accessible through blaze query. Use {@link Whitelist}. */ +@Deprecated public final class FeaturePolicyConfiguration extends BuildConfiguration.Fragment { private final ImmutableSetMultimap<String, PackageSpecification> features; diff --git a/src/main/java/com/google/devtools/build/lib/analysis/featurecontrol/FeaturePolicyLoader.java b/src/main/java/com/google/devtools/build/lib/analysis/featurecontrol/FeaturePolicyLoader.java index 9e2c856835..7704c8c6cd 100644 --- a/src/main/java/com/google/devtools/build/lib/analysis/featurecontrol/FeaturePolicyLoader.java +++ b/src/main/java/com/google/devtools/build/lib/analysis/featurecontrol/FeaturePolicyLoader.java @@ -37,7 +37,13 @@ import java.util.Queue; import java.util.Set; import javax.annotation.Nullable; -/** A loader for the FeaturePolicyConfiguration fragment. */ +/** + * A loader for the FeaturePolicyConfiguration fragment. + * + * @deprecated This is deprecated because the dependency on the package group used to hold the + * whitelist is not accessible through blaze query. Use {@link Whitelist}. + */ +@Deprecated public final class FeaturePolicyLoader implements ConfigurationFragmentFactory { private final ImmutableSet<String> permittedFeatures; diff --git a/src/main/java/com/google/devtools/build/lib/analysis/featurecontrol/FeaturePolicyOptions.java b/src/main/java/com/google/devtools/build/lib/analysis/featurecontrol/FeaturePolicyOptions.java index b0036d5f23..737c1c878c 100644 --- a/src/main/java/com/google/devtools/build/lib/analysis/featurecontrol/FeaturePolicyOptions.java +++ b/src/main/java/com/google/devtools/build/lib/analysis/featurecontrol/FeaturePolicyOptions.java @@ -21,7 +21,13 @@ import com.google.devtools.common.options.OptionDocumentationCategory; import com.google.devtools.common.options.OptionEffectTag; import java.util.List; -/** The options fragment which defines {@link FeaturePolicyConfiguration}. */ +/** + * The options fragment which defines {@link FeaturePolicyConfiguration}. + * + * @deprecated This is deprecated because the dependency on the package group used to hold the + * whitelist is not accessible through blaze query. Use {@link Whitelist}. + */ +@Deprecated public final class FeaturePolicyOptions extends FragmentOptions { /** The mapping from features to their associated package groups. */ @Option( diff --git a/src/main/java/com/google/devtools/build/lib/analysis/featurecontrol/PolicyEntry.java b/src/main/java/com/google/devtools/build/lib/analysis/featurecontrol/PolicyEntry.java index e0982a25df..b871637d17 100644 --- a/src/main/java/com/google/devtools/build/lib/analysis/featurecontrol/PolicyEntry.java +++ b/src/main/java/com/google/devtools/build/lib/analysis/featurecontrol/PolicyEntry.java @@ -17,7 +17,13 @@ package com.google.devtools.build.lib.analysis.featurecontrol; import com.google.auto.value.AutoValue; import com.google.devtools.build.lib.cmdline.Label; -/** Policy value object encoding the package group which can access a given feature. */ +/** + * Policy value object encoding the package group which can access a given feature. + * + * @deprecated This is deprecated because the dependency on the package group used to hold the + * whitelist is not accessible through blaze query. Use {@link Whitelist}. + */ +@Deprecated @AutoValue public abstract class PolicyEntry { /** Creates a new PolicyEntry for the given feature and package_group label. */ diff --git a/src/main/java/com/google/devtools/build/lib/analysis/featurecontrol/PolicyEntryConverter.java b/src/main/java/com/google/devtools/build/lib/analysis/featurecontrol/PolicyEntryConverter.java index a8f997b76b..ec245313a9 100644 --- a/src/main/java/com/google/devtools/build/lib/analysis/featurecontrol/PolicyEntryConverter.java +++ b/src/main/java/com/google/devtools/build/lib/analysis/featurecontrol/PolicyEntryConverter.java @@ -19,7 +19,13 @@ import com.google.devtools.build.lib.cmdline.LabelSyntaxException; import com.google.devtools.common.options.Converter; import com.google.devtools.common.options.OptionsParsingException; -/** A converter which creates a PolicyEntry from a flag value. */ +/** + * A converter which creates a PolicyEntry from a flag value. + * + * @deprecated This is deprecated because the dependency on the package group used to hold the + * whitelist is not accessible through blaze query. Use {@link Whitelist}. + */ +@Deprecated public final class PolicyEntryConverter implements Converter<PolicyEntry> { @Override public PolicyEntry convert(String input) throws OptionsParsingException { diff --git a/src/main/java/com/google/devtools/build/lib/analysis/whitelisting/BUILD b/src/main/java/com/google/devtools/build/lib/analysis/whitelisting/BUILD new file mode 100644 index 0000000000..b31e3c224b --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/analysis/whitelisting/BUILD @@ -0,0 +1,26 @@ +# Description: +# Whitelisting mechanism for rolling out and deprecating pieces of Bazel functionality. + +package( + default_visibility = ["//src:__subpackages__"], +) + +filegroup( + name = "srcs", + srcs = glob(["**"]), +) + +java_library( + name = "whitelisting", + srcs = glob(["*.java"]), + deps = [ + "//src/main/java/com/google/devtools/build/lib:build-base", + "//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/cmdline", + "//src/main/java/com/google/devtools/common/options", + "//third_party:auto_value", + "//third_party:guava", + "//third_party:jsr305", + ], +) diff --git a/src/main/java/com/google/devtools/build/lib/analysis/whitelisting/Whitelist.java b/src/main/java/com/google/devtools/build/lib/analysis/whitelisting/Whitelist.java new file mode 100644 index 0000000000..0da6e4d362 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/analysis/whitelisting/Whitelist.java @@ -0,0 +1,80 @@ +// 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.whitelisting; + +import static com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition.HOST; +import static com.google.devtools.build.lib.packages.Attribute.attr; +import static com.google.devtools.build.lib.packages.BuildType.LABEL; + +import com.google.common.collect.ImmutableList; +import com.google.devtools.build.lib.analysis.PackageSpecificationProvider; +import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode; +import com.google.devtools.build.lib.analysis.RuleContext; +import com.google.devtools.build.lib.analysis.TransitiveInfoCollection; +import com.google.devtools.build.lib.cmdline.Label; +import com.google.devtools.build.lib.packages.Attribute; +import com.google.devtools.build.lib.util.Preconditions; + +/** + * Class used for implementing whitelists using package groups. + * + * <p>To use add an attribute {@link getAttributeFromWhitelistName(String,Label) to the rule class + * which needs the whitelisting mechanism and use {@link isAvailable(RuleContext,String)} to check + * during analysis if a rule is present + */ +public final class Whitelist { + + private Whitelist() {} + + /** + * Returns an Attribute.Builder that can be used to add an implicit attribute to a rule containing + * a package group whitelist. + * + * @param whitelistName The name of the whitelist. This has to comply with attribute naming + * standards and will be used as a suffix for the attribute name. + * @param packageGroupWhitelist Label for the package group with the whitelist. + */ + public static Attribute.Builder<Label> getAttributeFromWhitelistName( + String whitelistName, Label packageGroupWhitelist) { + String attributeName = getAttributeNameFromWhitelistName(whitelistName); + return attr(attributeName, LABEL) + .value(packageGroupWhitelist) + .cfg(HOST) + .mandatoryNativeProviders(ImmutableList.of(PackageSpecificationProvider.class)); + } + + /** + * Returns whether the rule in the given RuleContext is in a whitelist. + * + * @param ruleContext The context in which this check is being executed. + * @param whitelistName The name of the whitelist being used. + */ + public static boolean isAvailable(RuleContext ruleContext, String whitelistName) { + String attributeName = getAttributeNameFromWhitelistName(whitelistName); + Preconditions.checkArgument(ruleContext.isAttrDefined(attributeName, LABEL)); + TransitiveInfoCollection packageGroup = ruleContext.getPrerequisite(attributeName, Mode.HOST); + Label label = ruleContext.getLabel(); + return packageGroup + .getProvider(PackageSpecificationProvider.class) + .getPackageSpecifications() + .toList() + .stream() + .anyMatch(p -> p.containsPackage(label.getPackageIdentifier())); + } + + private static String getAttributeNameFromWhitelistName(String whitelistName) { + return String.format("$whitelist_%s", whitelistName); + } +} diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelPrerequisiteValidator.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelPrerequisiteValidator.java index 81c097f19e..bb2456c08e 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelPrerequisiteValidator.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelPrerequisiteValidator.java @@ -14,12 +14,16 @@ package com.google.devtools.build.lib.bazel.rules; +import com.google.common.collect.ImmutableList; import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider; import com.google.devtools.build.lib.analysis.ConfiguredTarget; +import com.google.devtools.build.lib.analysis.PackageSpecificationProvider; import com.google.devtools.build.lib.analysis.RuleContext; +import com.google.devtools.build.lib.analysis.TransitiveInfoProvider; import com.google.devtools.build.lib.packages.Attribute; import com.google.devtools.build.lib.packages.NonconfigurableAttributeMapper; import com.google.devtools.build.lib.packages.PackageGroup; +import com.google.devtools.build.lib.packages.RawAttributeMapper; import com.google.devtools.build.lib.packages.Rule; import com.google.devtools.build.lib.packages.Target; import com.google.devtools.build.lib.rules.AliasProvider; @@ -72,19 +76,31 @@ public class BazelPrerequisiteValidator new VisibilityErrorEvent(context.getConfiguration(), rule.getLabel(), errorMessage)); } - if (prerequisiteTarget instanceof PackageGroup && !attrName.equals("visibility")) { - context.reportError( - rule.getAttributeLocation(attrName), - "in " - + attrName - + " attribute of " - + rule.getRuleClass() - + " rule " - + rule.getLabel() - + ": package group " - + AliasProvider.printLabelWithAliasChain(prerequisite) - + " is misplaced here " - + "(they are only allowed in the visibility attribute)"); + if (prerequisiteTarget instanceof PackageGroup) { + ImmutableList<ImmutableList<Class<? extends TransitiveInfoProvider>>> + mandatoryNativeProviders = + RawAttributeMapper.of(rule) + .getAttributeDefinition(attrName) + .getMandatoryNativeProvidersList(); + boolean containsPackageSpecificationProvider = + mandatoryNativeProviders + .stream() + .anyMatch(list -> list.contains(PackageSpecificationProvider.class)); + // TODO(plf): Add the PackageSpecificationProvider to the 'visibility' attribute. + if (!attrName.equals("visibility") && !containsPackageSpecificationProvider) { + context.reportError( + rule.getAttributeLocation(attrName), + "in " + + attrName + + " attribute of " + + rule.getRuleClass() + + " rule " + + rule.getLabel() + + ": package group " + + AliasProvider.printLabelWithAliasChain(prerequisite) + + " is misplaced here " + + "(they are only allowed in the visibility attribute)"); + } } } |