diff options
author | gregce <gregce@google.com> | 2017-07-28 19:07:10 +0200 |
---|---|---|
committer | Dmitry Lomov <dslomov@google.com> | 2017-07-31 16:32:09 +0200 |
commit | ed08fcfcfe42b96e13570c5cc0060a337b8a55bb (patch) | |
tree | b1d76651b13f2268d11d0a7e3410274879facb41 /src/test/java/com/google/devtools/build/lib/analysis/util | |
parent | 644bb1101a368ef68401c251ca4ac3d634dc205b (diff) |
Provide a lambda interface for custom mock rule class behavior.
This extends the "easy use" idea of MockRule from just custom
attributes to full-on custom behavior.
For a proof of concept, also port Bazel's late-bound attribute
tests.
PiperOrigin-RevId: 163483121
Diffstat (limited to 'src/test/java/com/google/devtools/build/lib/analysis/util')
-rw-r--r-- | src/test/java/com/google/devtools/build/lib/analysis/util/MockRule.java | 91 | ||||
-rw-r--r-- | src/test/java/com/google/devtools/build/lib/analysis/util/MockRuleCustomBehavior.java | 54 |
2 files changed, 110 insertions, 35 deletions
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/util/MockRule.java b/src/test/java/com/google/devtools/build/lib/analysis/util/MockRule.java index bba074bec4..9c97e7d52d 100644 --- a/src/test/java/com/google/devtools/build/lib/analysis/util/MockRule.java +++ b/src/test/java/com/google/devtools/build/lib/analysis/util/MockRule.java @@ -21,6 +21,7 @@ import static com.google.devtools.build.lib.syntax.Type.BOOLEAN; 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.base.Preconditions; import com.google.devtools.build.lib.analysis.BaseRuleClasses; import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider; import com.google.devtools.build.lib.analysis.RuleDefinition; @@ -31,75 +32,94 @@ import com.google.devtools.build.lib.packages.RuleClass; import com.google.devtools.build.lib.util.FileTypeSet; import java.util.Arrays; -import java.util.List; /** * Provides a simple API for creating custom rule classes for tests. * - * <p>Usage: + * <p>Usage (for a custom rule type that just needs to exist): * * <pre> * MockRule fooRule = () -> MockRule.define("foo_rule"); - * MockRule ruleWithCustomAttr = () -> MockRule.define("attr_rule", attr("myattr", Type.STRING)); * </pre> * - * <p>If you need special behavior beyond custom attributes: + * <p>Usage (for custom attributes): * * <pre> - * class MyCustomRuleClass implements MockRule { - * @Override - * public MockRule.State define() { - * return MockRule.define("my_custom_rule"); - * } + * MockRule fooRule = () -> MockRule.define("foo_rule", attr("myattr", Type.STRING)); + * </pre> + * + * <p>Usage (for arbitrary customization): * - * @Override - * public void customize(RuleClass.Builder builder, RuleDefinitionEnvironment environment) { - * builder.depsCfg(HostTransition.INSTANCE); - * } - * } + * <pre> + * MockRule fooRule = () -> MockRule.define( + * "foo_rule", + * (builder, env) -> + * builder + * .removeAttribute("tags") + * .requiresConfigurationFragments(FooConfiguration.class); + * ); * </pre> * + * * <p>We use lambdas for custom rule classes because {@link ConfiguredRuleClassProvider} indexes * rule class definitions by their Java class names. So each definition has to have its own * unique Java class. + * + * <p>Both of the following forms are valid: + * + * <pre>MockRule fooRule = () -> MockRule.define("foo_rule");</pre> + * <pre>RuleDefinition fooRule = (MockRule) () -> MockRule.define("foo_rule");</pre> + * + * <p>Use discretion in choosing your preferred form. The first is more compact. But the second + * makes it clearer that <code>fooRule</code> is a proper rule class definition. */ public interface MockRule extends RuleDefinition { /** - * Container for the desired name and custom attributes for this rule class. + * Container for the desired name and custom settings for this rule class. */ class State { private final String name; - private final List<Attribute.Builder<?>> attributes; + private final MockRuleCustomBehavior customBehavior; - State(String ruleClassName, Attribute.Builder<?>... attributes) { - this.name = ruleClassName; - this.attributes = Arrays.asList(attributes); + State(String ruleClassName, MockRuleCustomBehavior customBehavior) { + this.name = Preconditions.checkNotNull(ruleClassName); + this.customBehavior = Preconditions.checkNotNull(customBehavior); } } /** - * Returns a new {@link State} for this rule class. This is a convenience method for lambda - * definitions: + * Returns a new {@link State} for this rule class with custom attributes. This is a convenience + * method for lambda definitions: * * <pre> * MockRule myRule = () -> MockRule.define("my_rule", attr("myattr", Type.STRING)); * </pre> */ static State define(String ruleClassName, Attribute.Builder<?>... attributes) { - return new State(ruleClassName, attributes); + return new State( + ruleClassName, + new MockRuleCustomBehavior.CustomAttributes(Arrays.asList(attributes))); } /** - * Returns the basic state that defines this rule class. This is the only interface method - * implementers must override. + * Returns a new {@link State} for this rule class with arbitrary custom behavior. This is a + * convenience method for lambda definitions: + * + * <pre> + * MockRule myRule = () -> MockRule.define( + * "my_rule", + * (builder, env) -> builder.requiresConfigurationFragments(FooConfiguration.class)); + * </pre> */ - State define(); + static State define(String ruleClassName, MockRuleCustomBehavior customBehavior) { + return new State(ruleClassName, customBehavior); + } /** - * Allows for custom builder configuration beyond setting attributes. + * Returns the basic state that defines this rule class. This is the only interface method + * implementers must override. */ - default void customize(RuleClass.Builder builder, RuleDefinitionEnvironment environment) { - } + State define(); /** * Default <code>"deps"</code> attribute for rule classes that don't need special behavior. @@ -110,10 +130,12 @@ public interface MockRule extends RuleDefinition { * Builds out this rule with default attributes Blaze expects of all rules plus the custom * attributes defined by this implementation's {@link State}. * - * <p>Do not override this method. For extra custom behavior, override {@link #customize}. + * <p>Do not override this method. For extra custom behavior, use + * {@link #define(String, MockRuleCustomBehavior)} */ @Override default RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment environment) { + State state = define(); builder .add(attr("testonly", BOOLEAN).nonconfigurable("test").value(false)) .add(attr("deprecation", STRING).nonconfigurable("test").value((String) null)) @@ -121,13 +143,12 @@ public interface MockRule extends RuleDefinition { .add(attr("visibility", NODEP_LABEL_LIST).orderIndependent().cfg(HOST) .nonconfigurable("test")) .add(attr(RuleClass.COMPATIBLE_ENVIRONMENT_ATTR, LABEL_LIST) - .allowedFileTypes(FileTypeSet.NO_FILE)) + .allowedFileTypes(FileTypeSet.NO_FILE) + .dontCheckConstraints()) .add(attr(RuleClass.RESTRICTED_ENVIRONMENT_ATTR, LABEL_LIST) - .allowedFileTypes(FileTypeSet.NO_FILE)); - for (Attribute.Builder<?> customAttribute : define().attributes) { - builder.add(customAttribute); - } - customize(builder, environment); + .allowedFileTypes(FileTypeSet.NO_FILE) + .dontCheckConstraints()); + state.customBehavior.customize(builder, environment); return builder.build(); } diff --git a/src/test/java/com/google/devtools/build/lib/analysis/util/MockRuleCustomBehavior.java b/src/test/java/com/google/devtools/build/lib/analysis/util/MockRuleCustomBehavior.java new file mode 100644 index 0000000000..2d811dcdd9 --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/analysis/util/MockRuleCustomBehavior.java @@ -0,0 +1,54 @@ +// 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.util; + +import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment; +import com.google.devtools.build.lib.packages.Attribute; +import com.google.devtools.build.lib.packages.RuleClass; + +/** + * Interface for supporting arbitary custom behavior in mock rule classes. + * + * <p>See {@link MockRule} for details and usage instructions. + */ +public interface MockRuleCustomBehavior { + + /** + * Adds custom behavior to a mock rule class. + * + * <p>It's not necessary to call {@link RuleClass.Builder#build} here. + */ + void customize(RuleClass.Builder builder, RuleDefinitionEnvironment env); + + /* Predefined no-op behavior. */ + MockRuleCustomBehavior NOOP = (builder, env) -> {}; + + /** + * Predefined behavior that populates a list of attributes. + */ + class CustomAttributes implements MockRuleCustomBehavior { + private final Iterable<Attribute.Builder<?>> attributes; + + CustomAttributes(Iterable<Attribute.Builder<?>> attributes) { + this.attributes = attributes; + } + + @Override + public void customize(RuleClass.Builder builder, RuleDefinitionEnvironment env) { + for (Attribute.Builder<?> attribute : attributes) { + builder.add(attribute); + } + } + } +} |