diff options
Diffstat (limited to 'src/main')
8 files changed, 402 insertions, 5 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/BUILD b/src/main/java/com/google/devtools/build/lib/BUILD index 03aeea9da7..adfc1a2c3c 100644 --- a/src/main/java/com/google/devtools/build/lib/BUILD +++ b/src/main/java/com/google/devtools/build/lib/BUILD @@ -613,6 +613,7 @@ java_library( "//src/main/java/com/google/devtools/build/lib/rules/apple", "//src/main/java/com/google/devtools/build/lib/rules/apple/cpp", "//src/main/java/com/google/devtools/build/lib/rules/apple/swift", + "//src/main/java/com/google/devtools/build/lib/rules/config", "//src/main/java/com/google/devtools/build/lib/rules/cpp", "//src/main/java/com/google/devtools/build/lib/rules/cpp/proto:CcProtoLibrary", "//src/main/java/com/google/devtools/build/lib/rules/genquery", diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java index c9ee722f19..5644d4a358 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java @@ -101,6 +101,9 @@ import com.google.devtools.build.lib.rules.apple.XcodeVersionRule; import com.google.devtools.build.lib.rules.apple.cpp.AppleCcToolchainRule; import com.google.devtools.build.lib.rules.apple.swift.SwiftCommandLineOptions; import com.google.devtools.build.lib.rules.apple.swift.SwiftConfiguration; +import com.google.devtools.build.lib.rules.config.ConfigFeatureFlagConfiguration; +import com.google.devtools.build.lib.rules.config.ConfigFeatureFlagRule; +import com.google.devtools.build.lib.rules.config.ConfigSkylarkCommon; import com.google.devtools.build.lib.rules.cpp.CcIncLibraryRule; import com.google.devtools.build.lib.rules.cpp.CcToolchainRule; import com.google.devtools.build.lib.rules.cpp.CcToolchainSuiteRule; @@ -279,7 +282,8 @@ public class BazelRuleClassProvider { BAZEL_SETUP.init(builder); CORE_RULES.init(builder); CORE_WORKSPACE_RULES.init(builder); - BASIC_RULES.init(builder); + GENERIC_RULES.init(builder); + CONFIG_RULES.init(builder); PLATFORM_RULES.init(builder); PROTO_RULES.init(builder); SH_RULES.init(builder); @@ -357,15 +361,12 @@ public class BazelRuleClassProvider { } }; - public static final RuleSet BASIC_RULES = + public static final RuleSet GENERIC_RULES = new RuleSet() { @Override public void init(Builder builder) { builder.addRuleDefinition(new EnvironmentRule()); - builder.addRuleDefinition(new ConfigRuleClasses.ConfigBaseRule()); - builder.addRuleDefinition(new ConfigRuleClasses.ConfigSettingRule()); - builder.addRuleDefinition(new AliasRule()); builder.addRuleDefinition(new BazelFilegroupRule()); builder.addRuleDefinition(new TestSuiteRule()); @@ -385,6 +386,26 @@ public class BazelRuleClassProvider { } }; + public static final RuleSet CONFIG_RULES = + new RuleSet() { + @Override + public void init(Builder builder) { + builder.addRuleDefinition(new ConfigRuleClasses.ConfigBaseRule()); + builder.addRuleDefinition(new ConfigRuleClasses.ConfigSettingRule()); + builder.addConfig( + ConfigFeatureFlagConfiguration.Options.class, + new ConfigFeatureFlagConfiguration.Loader()); + + builder.addRuleDefinition(new ConfigFeatureFlagRule()); + builder.addSkylarkAccessibleTopLevels("config_common", new ConfigSkylarkCommon()); + } + + @Override + public ImmutableList<RuleSet> requires() { + return ImmutableList.of(CORE_RULES); + } + }; + public static final RuleSet CORE_WORKSPACE_RULES = new RuleSet() { @Override diff --git a/src/main/java/com/google/devtools/build/lib/rules/config/BUILD b/src/main/java/com/google/devtools/build/lib/rules/config/BUILD index 6e480d9628..459150ab5f 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/config/BUILD +++ b/src/main/java/com/google/devtools/build/lib/rules/config/BUILD @@ -12,9 +12,15 @@ java_library( ]), deps = [ "//src/main/java/com/google/devtools/build/lib:build-base", + "//src/main/java/com/google/devtools/build/lib:collect", + "//src/main/java/com/google/devtools/build/lib:concurrent", + "//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:skylarkinterface", "//src/main/java/com/google/devtools/build/lib/actions", "//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/rules/config/ConfigFeatureFlag.java b/src/main/java/com/google/devtools/build/lib/rules/config/ConfigFeatureFlag.java new file mode 100644 index 0000000000..5b9ddb780e --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/config/ConfigFeatureFlag.java @@ -0,0 +1,104 @@ +// 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.rules.config; + +import static com.google.devtools.build.lib.collect.nestedset.Order.STABLE_ORDER; +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.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMultiset; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Multiset; +import com.google.devtools.build.lib.actions.Artifact; +import com.google.devtools.build.lib.analysis.ConfiguredTarget; +import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder; +import com.google.devtools.build.lib.analysis.RuleContext; +import com.google.devtools.build.lib.analysis.RunfilesProvider; +import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; +import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException; +import com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory; +import com.google.devtools.build.lib.syntax.Printer; +import java.util.List; + +/** + * The implementation of the config_feature_flag rule for defining custom flags for Android rules. + */ +public class ConfigFeatureFlag implements RuleConfiguredTargetFactory { + + @Override + public ConfiguredTarget create(RuleContext ruleContext) + throws InterruptedException, RuleErrorException { + List<String> specifiedValues = ruleContext.attributes().get("allowed_values", STRING_LIST); + ImmutableSet<String> values = ImmutableSet.copyOf(specifiedValues); + Predicate<String> isValidValue = Predicates.in(values); + if (values.size() != specifiedValues.size()) { + ImmutableMultiset<String> groupedValues = ImmutableMultiset.copyOf(specifiedValues); + ImmutableList.Builder<String> duplicates = new ImmutableList.Builder<String>(); + for (Multiset.Entry<String> value : groupedValues.entrySet()) { + if (value.getCount() > 1) { + duplicates.add(value.getElement()); + } + } + ruleContext.attributeError( + "allowed_values", + "cannot contain duplicates, but contained multiple of " + + Printer.repr(duplicates.build(), '\'')); + } + + String defaultValue = ruleContext.attributes().get("default_value", STRING); + if (!isValidValue.apply(defaultValue)) { + ruleContext.attributeError( + "default_value", + "must be one of " + + Printer.repr(values.asList(), '\'') + + ", but was " + + Printer.repr(defaultValue, '\'')); + } + + if (ruleContext.hasErrors()) { + // Don't bother validating the value if the flag was already incorrectly specified without + // looking at the value. + return null; + } + + String value = + ruleContext + .getFragment(ConfigFeatureFlagConfiguration.class) + .getFeatureFlagValue(ruleContext.getOwner()) + .or(defaultValue); + + if (!isValidValue.apply(value)) { + // TODO(mstaib): When configurationError is available, use that instead. + ruleContext.ruleError( + "value must be one of " + + Printer.repr(values.asList(), '\'') + + ", but was " + + Printer.repr(value, '\'')); + return null; + } + + ConfigFeatureFlagProvider provider = ConfigFeatureFlagProvider.create(value, isValidValue); + + return new RuleConfiguredTargetBuilder(ruleContext) + .setFilesToBuild(NestedSetBuilder.<Artifact>emptySet(STABLE_ORDER)) + .addProvider(RunfilesProvider.class, RunfilesProvider.EMPTY) + .addProvider(ConfigFeatureFlagProvider.class, provider) + .addNativeDeclaredProvider(provider) + .build(); + } +} diff --git a/src/main/java/com/google/devtools/build/lib/rules/config/ConfigFeatureFlagProvider.java b/src/main/java/com/google/devtools/build/lib/rules/config/ConfigFeatureFlagProvider.java new file mode 100644 index 0000000000..0a25669615 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/config/ConfigFeatureFlagProvider.java @@ -0,0 +1,98 @@ +// 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.rules.config; + +import com.google.auto.value.AutoValue; +import com.google.common.base.Predicate; +import com.google.common.collect.ImmutableMap; +import com.google.devtools.build.lib.analysis.TransitiveInfoCollection; +import com.google.devtools.build.lib.analysis.TransitiveInfoProvider; +import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; +import com.google.devtools.build.lib.packages.ClassObjectConstructor; +import com.google.devtools.build.lib.packages.NativeClassObjectConstructor; +import com.google.devtools.build.lib.packages.SkylarkClassObject; +import com.google.devtools.build.lib.packages.SkylarkProviderIdentifier; +import com.google.devtools.build.lib.skylarkinterface.Param; +import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable; +import com.google.devtools.build.lib.skylarkinterface.SkylarkModule; +import com.google.devtools.build.lib.util.Preconditions; + +/** Provider for exporting value and valid value predicate of feature flags to consuming targets. */ +@SkylarkModule( + name = "FeatureFlagInfo", + doc = "A provider used to access information about config_feature_flag rules." +) +@AutoValue +@Immutable +public abstract class ConfigFeatureFlagProvider extends SkylarkClassObject + implements TransitiveInfoProvider { + + /** Name used in Skylark for accessing ConfigFeatureFlagProvider. */ + static final String SKYLARK_NAME = "FeatureFlagInfo"; + + /** Skylark constructor and identifier for ConfigFeatureFlagProvider. */ + static final ClassObjectConstructor SKYLARK_CONSTRUCTOR = + new NativeClassObjectConstructor(SKYLARK_NAME) {}; + + /** Identifier used to retrieve this provider from rules which export it. */ + private static final SkylarkProviderIdentifier SKYLARK_IDENTIFIER = + SkylarkProviderIdentifier.forKey(SKYLARK_CONSTRUCTOR.getKey()); + + ConfigFeatureFlagProvider() { + super(SKYLARK_CONSTRUCTOR, ImmutableMap.<String, Object>of()); + } + + /** Creates a new ConfigFeatureFlagProvider with the given value and valid value predicate. */ + public static ConfigFeatureFlagProvider create(String value, Predicate<String> isValidValue) { + return new AutoValue_ConfigFeatureFlagProvider(value, isValidValue); + } + + /** Retrieves and casts the provider from the given target. */ + public static ConfigFeatureFlagProvider fromTarget(TransitiveInfoCollection target) { + Object provider = target.get(SKYLARK_IDENTIFIER); + if (provider == null) { + return null; + } + Preconditions.checkState(provider instanceof ConfigFeatureFlagProvider); + return (ConfigFeatureFlagProvider) provider; + } + + /** Gets the current value of the flag in the flag's current configuration. */ + @SkylarkCallable( + name = "value", + structField = true, + doc = "The value of the flag in the configuration used by the flag rule." + ) + public abstract String getValue(); + + /** Returns whether this value is valid for this flag. */ + @SkylarkCallable( + name = "is_valid_value", + doc = "The value of the flag in the configuration used by the flag rule.", + parameters = { + @Param( + name = "value", + type = String.class, + doc = "String, the value to check for validity for this flag." + ), + } + ) + public boolean isValidValue(String value) { + return getIsValidValue().apply(value); + } + + /** Gets the predicate which determines valid values for this flag. */ + abstract Predicate<String> getIsValidValue(); +} diff --git a/src/main/java/com/google/devtools/build/lib/rules/config/ConfigFeatureFlagRule.java b/src/main/java/com/google/devtools/build/lib/rules/config/ConfigFeatureFlagRule.java new file mode 100644 index 0000000000..3600b496aa --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/config/ConfigFeatureFlagRule.java @@ -0,0 +1,55 @@ +// 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.rules.config; + +import static com.google.devtools.build.lib.packages.Attribute.attr; +import static com.google.devtools.build.lib.syntax.Type.STRING; +import static com.google.devtools.build.lib.syntax.Type.STRING_LIST; + +import com.google.devtools.build.lib.analysis.BaseRuleClasses; +import com.google.devtools.build.lib.analysis.RuleDefinition; +import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment; +import com.google.devtools.build.lib.packages.RuleClass; + +/** Rule definition for Android's config_feature_flag rule. */ +public final class ConfigFeatureFlagRule implements RuleDefinition { + + @Override + public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment env) { + return builder + .setUndocumented(/* It's unusable as yet, as there are no ways to interact with it. */) + .requiresConfigurationFragments(ConfigFeatureFlagConfiguration.class) + .add( + attr("allowed_values", STRING_LIST) + .mandatory() + .nonEmpty() + .orderIndependent() + .nonconfigurable("policy decision; this is defining an element of configuration")) + .add( + attr("default_value", STRING) + .mandatory() + .nonconfigurable("policy decision; this is defining an element of configuration")) + .build(); + } + + @Override + public RuleDefinition.Metadata getMetadata() { + return RuleDefinition.Metadata.builder() + .name("config_feature_flag") + .ancestors(BaseRuleClasses.BaseRule.class) + .factoryClass(ConfigFeatureFlag.class) + .build(); + } +} diff --git a/src/main/java/com/google/devtools/build/lib/rules/config/ConfigFeatureFlagTransitionFactory.java b/src/main/java/com/google/devtools/build/lib/rules/config/ConfigFeatureFlagTransitionFactory.java new file mode 100644 index 0000000000..8f7ab87954 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/config/ConfigFeatureFlagTransitionFactory.java @@ -0,0 +1,75 @@ +// 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.rules.config; + +import static com.google.devtools.build.lib.packages.BuildType.LABEL_KEYED_STRING_DICT; + +import com.google.common.collect.ImmutableSortedMap; +import com.google.devtools.build.lib.analysis.config.BuildOptions; +import com.google.devtools.build.lib.analysis.config.PatchTransition; +import com.google.devtools.build.lib.cmdline.Label; +import com.google.devtools.build.lib.packages.NonconfigurableAttributeMapper; +import com.google.devtools.build.lib.packages.Rule; +import com.google.devtools.build.lib.packages.RuleTransitionFactory; +import java.util.Map; + +/** + * Transition factory which allows for setting the values of config_feature_flags below the rule + * it is attached to based on one of that rule's attributes. + * + * <p>Currently, this is only intended for use by android_binary and other Android top-level rules. + */ +public class ConfigFeatureFlagTransitionFactory implements RuleTransitionFactory { + + /** Transition which resets the set of flag-value pairs to the map it was constructed with. */ + private static final class ConfigFeatureFlagValuesTransition implements PatchTransition { + private final ImmutableSortedMap<Label, String> flagValues; + + public ConfigFeatureFlagValuesTransition(Map<Label, String> flagValues) { + this.flagValues = ImmutableSortedMap.copyOf(flagValues); + } + + @Override + public BuildOptions apply(BuildOptions options) { + BuildOptions result = options.clone(); + result.get(ConfigFeatureFlagConfiguration.Options.class).replaceFlagValues(flagValues); + return result; + } + + @Override + public boolean defaultsToSelf() { + throw new UnsupportedOperationException("supported in dynamic mode only"); + } + } + + private final String attributeName; + + /** + * Creates a transition factory which will generate a transition over a given rule which sets + * exactly the flag values in the attribute with the given {@code attributeName} of that rule, + * unsetting any flag values not listed there. + * + * <p>This attribute must be a nonconfigurable {@code LABEL_KEYED_STRING_DICT}. + */ + public ConfigFeatureFlagTransitionFactory(String attributeName) { + this.attributeName = attributeName; + } + + @Override + public PatchTransition buildTransitionFor(Rule rule) { + return new ConfigFeatureFlagValuesTransition( + NonconfigurableAttributeMapper.of(rule).get(attributeName, LABEL_KEYED_STRING_DICT)); + } +} diff --git a/src/main/java/com/google/devtools/build/lib/rules/config/ConfigSkylarkCommon.java b/src/main/java/com/google/devtools/build/lib/rules/config/ConfigSkylarkCommon.java new file mode 100644 index 0000000000..f464a45537 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/config/ConfigSkylarkCommon.java @@ -0,0 +1,37 @@ +// 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.rules.config; + +import com.google.devtools.build.lib.packages.ClassObjectConstructor; +import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable; +import com.google.devtools.build.lib.skylarkinterface.SkylarkModule; + +/** + * Skylark namespace used to interact with Blaze's configurability APIs. + */ +@SkylarkModule( + name = "config_common", + doc = "Functions for Skylark to interact with Blaze's configurability APIs." +) +public class ConfigSkylarkCommon { + @SkylarkCallable( + name = ConfigFeatureFlagProvider.SKYLARK_NAME, + doc = "The key used to retrieve the provider containing config_feature_flag's value.", + structField = true + ) + public ClassObjectConstructor getConfigFeatureFlagProviderConstructor() { + return ConfigFeatureFlagProvider.SKYLARK_CONSTRUCTOR; + } +} |