| // Copyright 2014 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; | |
| import static com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition.DATA; | |
| 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.DISTRIBUTIONS; | |
| import static com.google.devtools.build.lib.packages.BuildType.LABEL; | |
| import static com.google.devtools.build.lib.packages.BuildType.LABEL_LIST; | |
| import static com.google.devtools.build.lib.packages.BuildType.LICENSE; | |
| import static com.google.devtools.build.lib.packages.BuildType.NODEP_LABEL_LIST; | |
| import static com.google.devtools.build.lib.syntax.Type.BOOLEAN; | |
| import static com.google.devtools.build.lib.syntax.Type.INTEGER; | |
| import static com.google.devtools.build.lib.syntax.Type.STRING; | |
| import static com.google.devtools.build.lib.syntax.Type.STRING_LIST; | |
| import com.google.common.annotations.VisibleForTesting; | |
| import com.google.common.collect.ImmutableList; | |
| import com.google.devtools.build.lib.analysis.config.BuildConfiguration; | |
| import com.google.devtools.build.lib.analysis.config.RunUnder; | |
| import com.google.devtools.build.lib.analysis.constraints.EnvironmentRule; | |
| import com.google.devtools.build.lib.cmdline.Label; | |
| import com.google.devtools.build.lib.packages.Attribute; | |
| import com.google.devtools.build.lib.packages.Attribute.LateBoundLabel; | |
| import com.google.devtools.build.lib.packages.Attribute.LateBoundLabelList; | |
| import com.google.devtools.build.lib.packages.AttributeMap; | |
| import com.google.devtools.build.lib.packages.Rule; | |
| import com.google.devtools.build.lib.packages.RuleClass; | |
| import com.google.devtools.build.lib.packages.RuleClass.Builder; | |
| import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType; | |
| import com.google.devtools.build.lib.packages.TestSize; | |
| import com.google.devtools.build.lib.syntax.Type; | |
| import com.google.devtools.build.lib.util.FileTypeSet; | |
| import java.util.List; | |
| /** | |
| * Rule class definitions used by (almost) every rule. | |
| */ | |
| public class BaseRuleClasses { | |
| private static final Attribute.ComputedDefault testonlyDefault = | |
| new Attribute.ComputedDefault() { | |
| @Override | |
| public Object getDefault(AttributeMap rule) { | |
| return rule.getPackageDefaultTestOnly(); | |
| } | |
| }; | |
| private static final Attribute.ComputedDefault deprecationDefault = | |
| new Attribute.ComputedDefault() { | |
| @Override | |
| public Object getDefault(AttributeMap rule) { | |
| return rule.getPackageDefaultDeprecation(); | |
| } | |
| }; | |
| /** | |
| * Implementation for the :action_listener attribute. | |
| */ | |
| @VisibleForTesting | |
| static final LateBoundLabelList<BuildConfiguration> ACTION_LISTENER = | |
| new LateBoundLabelList<BuildConfiguration>() { | |
| @Override | |
| public List<Label> resolve(Rule rule, AttributeMap attributes, | |
| BuildConfiguration configuration) { | |
| // action_listeners are special rules; they tell the build system to add extra_actions to | |
| // existing rules. As such they need an edge to every ConfiguredTarget with the limitation | |
| // that they only run on the target configuration and should not operate on action_listeners | |
| // and extra_actions themselves (to avoid cycles). | |
| return configuration.getActionListeners(); | |
| } | |
| }; | |
| /** | |
| * Implementation for the :run_under attribute. | |
| */ | |
| public static final LateBoundLabel<BuildConfiguration> RUN_UNDER = | |
| new LateBoundLabel<BuildConfiguration>() { | |
| @Override | |
| public Label resolve(Rule rule, AttributeMap attributes, | |
| BuildConfiguration configuration) { | |
| RunUnder runUnder = configuration.getRunUnder(); | |
| return runUnder == null ? null : runUnder.getLabel(); | |
| } | |
| }; | |
| /** | |
| * A base rule for all test rules. | |
| */ | |
| public static final class TestBaseRule implements RuleDefinition { | |
| @Override | |
| public RuleClass build(Builder builder, RuleDefinitionEnvironment env) { | |
| return builder | |
| .add(attr("size", STRING).value("medium").taggable() | |
| .nonconfigurable("policy decision: should be consistent across configurations")) | |
| .add(attr("timeout", STRING).taggable() | |
| .nonconfigurable("policy decision: should be consistent across configurations") | |
| .value(new Attribute.ComputedDefault() { | |
| @Override | |
| public Object getDefault(AttributeMap rule) { | |
| TestSize size = TestSize.getTestSize(rule.get("size", Type.STRING)); | |
| if (size != null) { | |
| String timeout = size.getDefaultTimeout().toString(); | |
| if (timeout != null) { | |
| return timeout; | |
| } | |
| } | |
| return "illegal"; | |
| } | |
| })) | |
| .add(attr("flaky", BOOLEAN).value(false).taggable() | |
| .nonconfigurable("policy decision: should be consistent across configurations")) | |
| .add(attr("shard_count", INTEGER).value(-1)) | |
| .add(attr("local", BOOLEAN).value(false).taggable() | |
| .nonconfigurable("policy decision: should be consistent across configurations")) | |
| .add(attr("args", STRING_LIST)) | |
| // Input files for every test action | |
| .add(attr("$test_runtime", LABEL_LIST).cfg(HOST).value(ImmutableList.of( | |
| env.getToolsLabel("//tools/test:runtime")))) | |
| // Input files for test actions collecting code coverage | |
| .add(attr("$coverage_support", LABEL) | |
| .cfg(HOST) | |
| .value(env.getLabel("//tools/defaults:coverage_support"))) | |
| // Used in the one-per-build coverage report generation action. | |
| .add(attr("$coverage_report_generator", LABEL) | |
| .cfg(HOST) | |
| .value(env.getLabel("//tools/defaults:coverage_report_generator")) | |
| .singleArtifact()) | |
| // The target itself and run_under both run on the same machine. We use the DATA config | |
| // here because the run_under acts like a data dependency (e.g. no LIPO optimization). | |
| .add(attr(":run_under", LABEL).cfg(DATA).value(RUN_UNDER) | |
| .skipPrereqValidatorCheck()) | |
| .build(); | |
| } | |
| @Override | |
| public Metadata getMetadata() { | |
| return RuleDefinition.Metadata.builder() | |
| .name("$test_base_rule") | |
| .type(RuleClassType.ABSTRACT) | |
| .build(); | |
| } | |
| } | |
| /** | |
| * Share common attributes across both base and Skylark base rules. | |
| */ | |
| public static RuleClass.Builder commonCoreAndSkylarkAttributes(RuleClass.Builder builder) { | |
| return builder | |
| // The visibility attribute is special: it is a nodep label, and loading the | |
| // necessary package groups is handled by {@link LabelVisitor#visitTargetVisibility}. | |
| // Package groups always have the null configuration so that they are not duplicated | |
| // needlessly. | |
| .add(attr("visibility", NODEP_LABEL_LIST).orderIndependent().cfg(HOST) | |
| .nonconfigurable("special attribute integrated more deeply into Bazel's core logic")) | |
| .add(attr("deprecation", STRING).value(deprecationDefault) | |
| .nonconfigurable("Used in core loading phase logic with no access to configs")) | |
| .add(attr("tags", STRING_LIST).orderIndependent().taggable() | |
| .nonconfigurable("low-level attribute, used in TargetUtils without configurations")) | |
| .add(attr("generator_name", STRING).undocumented("internal") | |
| .nonconfigurable("static structure of a rule")) | |
| .add(attr("generator_function", STRING).undocumented("internal") | |
| .nonconfigurable("static structure of a rule")) | |
| .add(attr("generator_location", STRING).undocumented("internal") | |
| .nonconfigurable("static structure of a rule")) | |
| .add(attr("testonly", BOOLEAN).value(testonlyDefault) | |
| .nonconfigurable("policy decision: rules testability should be consistent")) | |
| .add(attr("features", STRING_LIST).orderIndependent()) | |
| .add(attr(":action_listener", LABEL_LIST).cfg(HOST).value(ACTION_LISTENER)) | |
| .add(attr(RuleClass.COMPATIBLE_ENVIRONMENT_ATTR, LABEL_LIST) | |
| .allowedRuleClasses(EnvironmentRule.RULE_NAME) | |
| .cfg(Attribute.ConfigurationTransition.HOST) | |
| .allowedFileTypes(FileTypeSet.NO_FILE) | |
| .nonconfigurable("special logic for constraints and select: see ConstraintSemantics") | |
| ) | |
| .add(attr(RuleClass.RESTRICTED_ENVIRONMENT_ATTR, LABEL_LIST) | |
| .allowedRuleClasses(EnvironmentRule.RULE_NAME) | |
| .cfg(Attribute.ConfigurationTransition.HOST) | |
| .allowedFileTypes(FileTypeSet.NO_FILE) | |
| .nonconfigurable("special logic for constraints and select: see ConstraintSemantics") | |
| ); | |
| } | |
| /** | |
| * Common parts of rules. | |
| */ | |
| public static final class BaseRule implements RuleDefinition { | |
| @Override | |
| public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment env) { | |
| return commonCoreAndSkylarkAttributes(builder) | |
| // The name attribute is handled specially, so it does not appear here. | |
| // | |
| // Aggregates the labels of all {@link ConfigRuleClasses} rules this rule uses (e.g. | |
| // keys for configurable attributes). This is specially populated in | |
| // {@RuleClass#populateRuleAttributeValues}. | |
| // | |
| // This attribute is not needed for actual builds. Its main purpose is so query's | |
| // proto/XML output includes the labels of config dependencies, so, e.g., depserver | |
| // reverse dependency lookups remain accurate. These can't just be added to the | |
| // attribute definitions proto/XML queries already output because not all attributes | |
| // contain labels. | |
| // | |
| // Builds and Blaze-interactive queries don't need this because they find dependencies | |
| // through direct Rule label visitation, which already factors these in. | |
| .add(attr("$config_dependencies", LABEL_LIST) | |
| .nonconfigurable("not intended for actual builds")) | |
| .add(attr("licenses", LICENSE) | |
| .nonconfigurable("Used in core loading phase logic with no access to configs")) | |
| .add(attr("distribs", DISTRIBUTIONS) | |
| .nonconfigurable("Used in core loading phase logic with no access to configs")) | |
| .build(); | |
| } | |
| @Override | |
| public Metadata getMetadata() { | |
| return RuleDefinition.Metadata.builder() | |
| .name("$base_rule") | |
| .type(RuleClassType.ABSTRACT) | |
| .build(); | |
| } | |
| } | |
| /** | |
| * Common ancestor class for all rules. | |
| */ | |
| public static final class RuleBase implements RuleDefinition { | |
| @Override | |
| public RuleClass build(Builder builder, RuleDefinitionEnvironment env) { | |
| return builder | |
| .add(attr("deps", LABEL_LIST).legacyAllowAnyFileType()) | |
| .add(attr("data", LABEL_LIST).cfg(DATA).allowedFileTypes(FileTypeSet.ANY_FILE)) | |
| .build(); | |
| } | |
| @Override | |
| public Metadata getMetadata() { | |
| return RuleDefinition.Metadata.builder() | |
| .name("$rule") | |
| .type(RuleClassType.ABSTRACT) | |
| .ancestors(BaseRule.class) | |
| .build(); | |
| } | |
| } | |
| } |