| // 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.analysis.test.ExecutionInfo.DEFAULT_TEST_RUNNER_EXEC_GROUP; |
| 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.packages.Type.BOOLEAN; |
| import static com.google.devtools.build.lib.packages.Type.INTEGER; |
| import static com.google.devtools.build.lib.packages.Type.STRING; |
| import static com.google.devtools.build.lib.packages.Types.STRING_DICT; |
| import static com.google.devtools.build.lib.packages.Types.STRING_LIST; |
| |
| import com.google.common.annotations.VisibleForTesting; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.devtools.build.lib.analysis.config.BuildConfigurationValue; |
| import com.google.devtools.build.lib.analysis.config.ExecutionTransitionFactory; |
| import com.google.devtools.build.lib.analysis.config.RunUnder; |
| import com.google.devtools.build.lib.analysis.constraints.ConstraintConstants; |
| import com.google.devtools.build.lib.analysis.platform.ConstraintValueInfo; |
| import com.google.devtools.build.lib.analysis.test.CoverageConfiguration; |
| import com.google.devtools.build.lib.analysis.test.TestConfiguration; |
| import com.google.devtools.build.lib.cmdline.Label; |
| import com.google.devtools.build.lib.packages.Attribute; |
| import com.google.devtools.build.lib.packages.Attribute.LabelLateBoundDefault; |
| import com.google.devtools.build.lib.packages.Attribute.LabelListLateBoundDefault; |
| import com.google.devtools.build.lib.packages.Attribute.LateBoundDefault.Resolver; |
| import com.google.devtools.build.lib.packages.AttributeMap; |
| import com.google.devtools.build.lib.packages.BuildType; |
| import com.google.devtools.build.lib.packages.ConfigurationFragmentPolicy.MissingFragmentPolicy; |
| import com.google.devtools.build.lib.packages.RuleClass; |
| import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType; |
| import com.google.devtools.build.lib.packages.TargetUtils; |
| import com.google.devtools.build.lib.packages.TestSize; |
| import com.google.devtools.build.lib.packages.Type; |
| import com.google.devtools.build.lib.packages.Type.ConversionException; |
| import com.google.devtools.build.lib.packages.Types; |
| import com.google.devtools.build.lib.skyframe.serialization.VisibleForSerialization; |
| import com.google.devtools.build.lib.skyframe.serialization.autocodec.SerializationConstant; |
| import com.google.devtools.build.lib.util.FileTypeSet; |
| import javax.annotation.Nullable; |
| import net.starlark.java.eval.StarlarkInt; |
| |
| /** |
| * Rule class definitions used by (almost) every rule. |
| */ |
| public class BaseRuleClasses { |
| |
| private BaseRuleClasses() {} |
| |
| @SerializationConstant @VisibleForSerialization |
| static final Attribute.ComputedDefault testonlyDefault = |
| new Attribute.ComputedDefault() { |
| @Override |
| public Object getDefault(AttributeMap rule) { |
| return rule.getPackageArgs().defaultTestOnly(); |
| } |
| }; |
| |
| @SerializationConstant @VisibleForSerialization |
| static final Attribute.ComputedDefault deprecationDefault = |
| new Attribute.ComputedDefault() { |
| @Override |
| public Object getDefault(AttributeMap rule) { |
| return rule.getPackageArgs().defaultDeprecation(); |
| } |
| }; |
| |
| @SerializationConstant @VisibleForSerialization |
| public static final Attribute.ComputedDefault TIMEOUT_DEFAULT = |
| 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"; |
| } |
| }; |
| |
| @SerializationConstant @VisibleForSerialization |
| public static final Attribute.ComputedDefault packageMetadataDefault = |
| new Attribute.ComputedDefault() { |
| @Override |
| public Object getDefault(AttributeMap rule) { |
| return rule.getPackageArgs().defaultPackageMetadata(); |
| } |
| }; |
| |
| // TODO(b/65746853): provide a way to do this without passing the entire configuration |
| /** |
| * Implementation for the :action_listener attribute. |
| * |
| * <p>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). |
| */ |
| @SerializationConstant @VisibleForSerialization @VisibleForTesting |
| static final LabelListLateBoundDefault<?> ACTION_LISTENER = |
| LabelListLateBoundDefault.fromTargetConfiguration( |
| BuildConfigurationValue.class, |
| (rule, attributes, configuration) -> configuration.getActionListeners()); |
| |
| public static final String DEFAULT_COVERAGE_SUPPORT_VALUE = "//tools/test:coverage_support"; |
| |
| @SerializationConstant @VisibleForSerialization |
| static final Resolver<TestConfiguration, Label> COVERAGE_SUPPORT_CONFIGURATION_RESOLVER = |
| (rule, attributes, configuration) -> configuration.getCoverageSupport(); |
| |
| public static LabelLateBoundDefault<TestConfiguration> coverageSupportAttribute( |
| Label defaultValue) { |
| return LabelLateBoundDefault.fromTargetConfiguration( |
| TestConfiguration.class, defaultValue, COVERAGE_SUPPORT_CONFIGURATION_RESOLVER); |
| } |
| |
| public static final String DEFAULT_COVERAGE_REPORT_GENERATOR_VALUE = |
| "//tools/test:coverage_report_generator"; |
| |
| @SerializationConstant @VisibleForSerialization |
| static final Resolver<CoverageConfiguration, Label> |
| COVERAGE_REPORT_GENERATOR_CONFIGURATION_RESOLVER = |
| (rule, attributes, configuration) -> configuration.reportGenerator(); |
| |
| public static LabelLateBoundDefault<CoverageConfiguration> coverageReportGeneratorAttribute( |
| Label defaultValue) { |
| return LabelLateBoundDefault.fromTargetConfiguration( |
| CoverageConfiguration.class, |
| defaultValue, |
| COVERAGE_REPORT_GENERATOR_CONFIGURATION_RESOLVER); |
| } |
| |
| public static LabelLateBoundDefault<CoverageConfiguration> getCoverageOutputGeneratorLabel() { |
| return LabelLateBoundDefault.fromTargetConfiguration( |
| CoverageConfiguration.class, null, COVERAGE_OUTPUT_GENERATOR_RESOLVER); |
| } |
| |
| @SerializationConstant @VisibleForSerialization |
| static final Resolver<CoverageConfiguration, Label> COVERAGE_OUTPUT_GENERATOR_RESOLVER = |
| (rule, attributes, configuration) -> configuration.outputGenerator(); |
| |
| // TODO(b/65746853): provide a way to do this without passing the entire configuration |
| /** Implementation for the :run_under attribute. */ |
| @SerializationConstant @VisibleForSerialization |
| public static final LabelLateBoundDefault<?> RUN_UNDER = |
| LabelLateBoundDefault.fromTargetConfiguration( |
| BuildConfigurationValue.class, |
| null, |
| (rule, attributes, configuration) -> { |
| RunUnder runUnder = configuration.getRunUnder(); |
| return runUnder != null ? runUnder.getLabel() : null; |
| }); |
| |
| /** |
| * A base rule for all test rules. |
| */ |
| public static final class TestBaseRule implements RuleDefinition { |
| @Override |
| public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment env) { |
| builder |
| .addExecGroup(DEFAULT_TEST_RUNNER_EXEC_GROUP) |
| .requiresConfigurationFragments(TestConfiguration.class) |
| // TestConfiguration only needed to create TestAction and TestProvider |
| // Only necessary at top-level and can be skipped if trimmed. |
| .setMissingFragmentPolicy(TestConfiguration.class, MissingFragmentPolicy.IGNORE) |
| .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(TIMEOUT_DEFAULT)) |
| .add( |
| attr("flaky", BOOLEAN) |
| .value(false) |
| .taggable() |
| .nonconfigurable("policy decision: should be consistent across configurations")) |
| .add(attr("shard_count", INTEGER).value(StarlarkInt.of(-1))) |
| .add( |
| attr("local", BOOLEAN) |
| .value(false) |
| .taggable() |
| .nonconfigurable("policy decision: should be consistent across configurations")) |
| .add(attr("args", STRING_LIST)) |
| .add(attr("env", STRING_DICT)) |
| .add(attr("env_inherit", STRING_LIST)) |
| // Input files for every test action |
| .add( |
| attr("$test_wrapper", LABEL) |
| .cfg(ExecutionTransitionFactory.createFactory()) |
| .singleArtifact() |
| .value(env.getToolsLabel("//tools/test:test_wrapper"))) |
| .add( |
| attr("$xml_writer", LABEL) |
| .cfg(ExecutionTransitionFactory.createFactory()) |
| .singleArtifact() |
| .value(env.getToolsLabel("//tools/test:xml_writer"))) |
| .add( |
| attr("$test_runtime", LABEL_LIST) |
| .cfg(ExecutionTransitionFactory.createFactory()) |
| .value(getTestRuntimeLabelList(env))) |
| .add( |
| attr("$test_setup_script", LABEL) |
| .cfg(ExecutionTransitionFactory.createFactory()) |
| .singleArtifact() |
| .value(env.getToolsLabel("//tools/test:test_setup"))) |
| .add( |
| attr("$xml_generator_script", LABEL) |
| .cfg(ExecutionTransitionFactory.createFactory()) |
| .singleArtifact() |
| .value(env.getToolsLabel("//tools/test:test_xml_generator"))) |
| .add( |
| attr("$collect_coverage_script", LABEL) |
| .cfg(ExecutionTransitionFactory.createFactory()) |
| .singleArtifact() |
| .value(env.getToolsLabel("//tools/test:collect_coverage"))) |
| // Input files for test actions collecting code coverage |
| .add( |
| attr(":coverage_support", LABEL) |
| .value( |
| coverageSupportAttribute(env.getToolsLabel(DEFAULT_COVERAGE_SUPPORT_VALUE)))) |
| // Used in the one-per-build coverage report generation action. |
| .add( |
| attr(":coverage_report_generator", LABEL) |
| .cfg(ExecutionTransitionFactory.createFactory()) |
| .value( |
| coverageReportGeneratorAttribute( |
| env.getToolsLabel(DEFAULT_COVERAGE_REPORT_GENERATOR_VALUE)))) |
| // The target itself and run_under both run on the same machine. |
| .add(attr(":run_under", LABEL).value(RUN_UNDER).skipPrereqValidatorCheck()); |
| |
| env.getNetworkAllowlistForTests() |
| .ifPresent( |
| label -> |
| builder.add( |
| Allowlist.getAttributeFromAllowlistName("external_network").value(label))); |
| |
| return builder.build(); |
| } |
| |
| @Override |
| public Metadata getMetadata() { |
| return RuleDefinition.Metadata.builder() |
| .name("$test_base_rule") |
| .type(RuleClassType.ABSTRACT) |
| .ancestors(MakeVariableExpandingRule.class) |
| .build(); |
| } |
| } |
| |
| private static final String TOOLS_TEST_RUNTIME_TARGET_PATTERN = "//tools/test:runtime"; |
| private static ImmutableList<Label> testRuntimeLabelList = null; |
| |
| // Always return the same ImmutableList<Label> for every $test_runtime attribute's default value. |
| public static synchronized ImmutableList<Label> getTestRuntimeLabelList( |
| RuleDefinitionEnvironment env) { |
| if (testRuntimeLabelList == null) { |
| testRuntimeLabelList = |
| ImmutableList.of( |
| Label.parseCanonicalUnchecked( |
| env.getToolsRepository() + TOOLS_TEST_RUNTIME_TARGET_PATTERN)); |
| } |
| return testRuntimeLabelList; |
| } |
| |
| /** |
| * The attribute used to list the configuration properties used by a target and its transitive |
| * dependencies. Currently only supports config_feature_flag. |
| * |
| * <p>A special value of "//command_line_option/fragments:test" instructs |
| * TestTrimmingTransitionFactory to skip trimming for this rule. |
| */ |
| public static final String TAGGED_TRIMMING_ATTR = "transitive_configs"; |
| |
| /** Share common attributes across both base and Starlark base rules. */ |
| // TODO(bazel-team): replace this with a common RuleDefinition ancestor of NativeBuildRule |
| // and StarlarkRuleClassFunctions.baseRule. This requires refactoring StarlarkRuleClassFunctions |
| // to instantiate its RuleClasses through RuleDefinition. |
| public static RuleClass.Builder commonCoreAndStarlarkAttributes(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(ExecutionTransitionFactory.createFactory()) |
| .nonconfigurable( |
| "special attribute integrated more deeply into Bazel's core logic")) |
| .add( |
| attr(TAGGED_TRIMMING_ATTR, NODEP_LABEL_LIST) |
| .orderIndependent() |
| .nonconfigurable("Used in determining configuration")) |
| .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(ExecutionTransitionFactory.createFactory()) |
| .value(ACTION_LISTENER)) |
| .add( |
| attr(RuleClass.COMPATIBLE_ENVIRONMENT_ATTR, LABEL_LIST) |
| .allowedRuleClasses(ConstraintConstants.ENVIRONMENT_RULE) |
| .cfg(ExecutionTransitionFactory.createFactory()) |
| .allowedFileTypes(FileTypeSet.NO_FILE) |
| .dontCheckConstraints() |
| .nonconfigurable( |
| "special logic for constraints and select: see ConstraintSemantics")) |
| .add( |
| attr(RuleClass.RESTRICTED_ENVIRONMENT_ATTR, LABEL_LIST) |
| .allowedRuleClasses(ConstraintConstants.ENVIRONMENT_RULE) |
| .cfg(ExecutionTransitionFactory.createFactory()) |
| .allowedFileTypes(FileTypeSet.NO_FILE) |
| .dontCheckConstraints() |
| .nonconfigurable( |
| "special logic for constraints and select: see ConstraintSemantics")) |
| .add( |
| attr(RuleClass.CONFIG_SETTING_DEPS_ATTRIBUTE, LABEL_LIST) |
| .nonconfigurable("stores configurability keys")) |
| .add( |
| attr(RuleClass.APPLICABLE_METADATA_ATTR, LABEL_LIST) |
| .value(packageMetadataDefault) |
| .cfg(ExecutionTransitionFactory.createFactory()) |
| .allowedFileTypes(FileTypeSet.NO_FILE) |
| .dontCheckConstraints() |
| .nonconfigurable("applicable_metadata is not configurable")) |
| .add( |
| attr("aspect_hints", LABEL_LIST) |
| .allowedFileTypes(FileTypeSet.NO_FILE) |
| .undocumented("experimental attribute")); |
| } |
| |
| public static RuleClass.Builder execPropertiesAttribute(RuleClass.Builder builder) |
| throws ConversionException { |
| return builder.add( |
| attr(RuleClass.EXEC_PROPERTIES_ATTR, STRING_DICT).defaultValue(ImmutableMap.of())); |
| } |
| |
| /** |
| * Ancestor of every native rule in BUILD files (not WORKSPACE files). |
| * |
| * <p>This includes: |
| * |
| * <ul> |
| * <li>rules that create actions ({@link NativeActionCreatingRule}) |
| * <li>rules that encapsulate toolchain and build environment context |
| * <li>rules that aggregate other rules (like file groups, test suites, or aliases) |
| * </ul> |
| */ |
| public static final class NativeBuildRule implements RuleDefinition { |
| @Override |
| public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment env) { |
| return commonCoreAndStarlarkAttributes(builder) |
| .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")) |
| // Any rule that provides its own meaning for the "target_compatible_with" attribute |
| // has to be excluded in `IncompatibleTargetChecker`. |
| .add( |
| attr(RuleClass.TARGET_COMPATIBLE_WITH_ATTR, LABEL_LIST) |
| .mandatoryProviders(ConstraintValueInfo.PROVIDER.id()) |
| // This should be configurable to allow for complex types of restrictions. |
| .tool( |
| "target_compatible_with exists for constraint checking, not to create an" |
| + " actual dependency") |
| .allowedFileTypes(FileTypeSet.NO_FILE)) |
| .build(); |
| } |
| |
| @Override |
| public Metadata getMetadata() { |
| return RuleDefinition.Metadata.builder() |
| .name("$native_build_rule") |
| .type(RuleClassType.ABSTRACT) |
| .build(); |
| } |
| } |
| |
| /** |
| * A rule that contains a {@code variables=} attribute to allow referencing Make variables. |
| */ |
| public static final class MakeVariableExpandingRule implements RuleDefinition { |
| @Override |
| public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment env) { |
| return builder |
| // Documented in |
| // com/google/devtools/build/docgen/templates/attributes/common/toolchains.html. |
| .add( |
| attr("toolchains", LABEL_LIST) |
| .allowedFileTypes(FileTypeSet.NO_FILE) |
| .mandatoryProviders(ImmutableList.of(TemplateVariableInfo.PROVIDER.id())) |
| .dontCheckConstraints()) |
| .build(); |
| } |
| |
| @Override |
| public Metadata getMetadata() { |
| return RuleDefinition.Metadata.builder() |
| .name("$make_variable_expanding_rule") |
| .type(RuleClassType.ABSTRACT) |
| .build(); |
| } |
| } |
| |
| /** |
| * Ancestor of every native BUILD rule that creates actions. |
| * |
| * <p>This is a subset of all BUILD rules. Filegroups and aliases, for example, simply encapsulate |
| * other rules. Toolchain rules provide metadata for actions of other rules. See {@link |
| * NativeBuildRule} for these. |
| */ |
| public static final class NativeActionCreatingRule implements RuleDefinition { |
| @Override |
| public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment env) { |
| return builder |
| .add(attr("deps", LABEL_LIST).legacyAllowAnyFileType()) |
| .add( |
| attr("data", LABEL_LIST) |
| .allowedFileTypes(FileTypeSet.ANY_FILE) |
| .dontCheckConstraints()) |
| .add(attr(RuleClass.EXEC_PROPERTIES_ATTR, Types.STRING_DICT).value(ImmutableMap.of())) |
| .add( |
| attr(RuleClass.EXEC_COMPATIBLE_WITH_ATTR, BuildType.LABEL_LIST) |
| .allowedFileTypes() |
| .nonconfigurable("Used in toolchain resolution") |
| .tool( |
| "exec_compatible_with exists for constraint checking, not to create an" |
| + " actual dependency") |
| .value(ImmutableList.of())) |
| .build(); |
| } |
| |
| @Override |
| public Metadata getMetadata() { |
| return RuleDefinition.Metadata.builder() |
| .name("$native_buildable_rule") |
| .type(RuleClassType.ABSTRACT) |
| .ancestors(BaseRuleClasses.NativeBuildRule.class) |
| .build(); |
| } |
| } |
| |
| /** A base rule for all binary rules. */ |
| public static final class BinaryBaseRule implements RuleDefinition { |
| @Override |
| public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment env) { |
| return builder |
| .add(attr("args", STRING_LIST)) |
| .add(attr("env", STRING_DICT)) |
| .add(attr("output_licenses", LICENSE)) |
| .add( |
| attr("$is_executable", BOOLEAN) |
| .value(true) |
| .nonconfigurable("Called from RunCommand.isExecutable, which takes a Target")) |
| .build(); |
| } |
| |
| @Override |
| public Metadata getMetadata() { |
| return RuleDefinition.Metadata.builder() |
| .name("$binary_base_rule") |
| .type(RuleClassType.ABSTRACT) |
| .ancestors(MakeVariableExpandingRule.class) |
| .build(); |
| } |
| } |
| |
| /** |
| * An empty rule that exists for the sole purpose to completely remove a native rule while it's |
| * still defined as a Starlark rule in builtins. |
| * |
| * <p>Use it like <code>builder.addRuleDefinition(new BaseRuleClasses.EmptyRule("name") {}); |
| * </code>. The <code>{}</code> create a new class for each rule. That's needed because {@link |
| * ConfiguredRuleClassProvider.Builder} assumes each rule class has a different Java class. |
| */ |
| public abstract static class EmptyRule implements RuleDefinition { |
| private final String name; |
| |
| public EmptyRule(String name) { |
| this.name = name; |
| } |
| |
| @Override |
| public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment env) { |
| return builder.removeAttribute("deps").removeAttribute("data").build(); |
| } |
| |
| @Override |
| public Metadata getMetadata() { |
| Metadata.Builder metadata = |
| Metadata.builder() |
| .name(name) |
| .type(TargetUtils.isTestRuleName(name) ? RuleClassType.TEST : RuleClassType.NORMAL) |
| .ancestors(BaseRuleClasses.NativeActionCreatingRule.class) |
| .factoryClass(EmptyRuleConfiguredTargetFactory.class); |
| if (TargetUtils.isTestRuleName(name)) { |
| metadata.ancestors( |
| BaseRuleClasses.TestBaseRule.class, BaseRuleClasses.NativeActionCreatingRule.class); |
| } |
| return metadata.build(); |
| } |
| } |
| |
| /** |
| * Factory used by rules' definitions that exist for the sole purpose of providing documentation. |
| * For most of these rules, the actual rule is implemented in Starlark but the documentation |
| * generation mechanism does not work yet for Starlark rules. TODO(bazel-team): Delete once |
| * documentation tools work for Starlark. |
| */ |
| public static class EmptyRuleConfiguredTargetFactory implements RuleConfiguredTargetFactory { |
| @Override |
| @Nullable |
| public ConfiguredTarget create(RuleContext ruleContext) { |
| ruleContext.ruleError("Rule is unimplemented."); |
| return null; |
| } |
| } |
| } |