aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java')
-rw-r--r--src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java376
1 files changed, 376 insertions, 0 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java
new file mode 100644
index 0000000000..e84f9aa187
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java
@@ -0,0 +1,376 @@
+// Copyright 2014 Google Inc. 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.RuleClass.Builder.RuleClassType.ABSTRACT;
+import static com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType.TEST;
+
+import com.google.common.base.Preconditions;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.devtools.build.lib.analysis.buildinfo.BuildInfoFactory;
+import com.google.devtools.build.lib.analysis.config.BuildOptions;
+import com.google.devtools.build.lib.analysis.config.ConfigurationFragmentFactory;
+import com.google.devtools.build.lib.analysis.config.DefaultsPackage;
+import com.google.devtools.build.lib.analysis.config.FragmentOptions;
+import com.google.devtools.build.lib.events.EventHandler;
+import com.google.devtools.build.lib.graph.Digraph;
+import com.google.devtools.build.lib.graph.Node;
+import com.google.devtools.build.lib.packages.Attribute;
+import com.google.devtools.build.lib.packages.RuleClass;
+import com.google.devtools.build.lib.packages.RuleClassProvider;
+import com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory;
+import com.google.devtools.build.lib.rules.SkylarkModules;
+import com.google.devtools.build.lib.syntax.Label;
+import com.google.devtools.build.lib.syntax.SkylarkEnvironment;
+import com.google.devtools.build.lib.syntax.SkylarkModule;
+import com.google.devtools.build.lib.syntax.SkylarkType;
+import com.google.devtools.build.lib.syntax.ValidationEnvironment;
+import com.google.devtools.common.options.OptionsClassProvider;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Knows about every rule Blaze supports and the associated configuration options.
+ *
+ * <p>This class is initialized on server startup and the set of rules, build info factories
+ * and configuration options is guarantees not to change over the life time of the Blaze server.
+ */
+public class ConfiguredRuleClassProvider implements RuleClassProvider {
+ /**
+ * Custom dependency validation logic.
+ */
+ public static interface PrerequisiteValidator {
+ /**
+ * Checks whether the rule in {@code contextBuilder} is allowed to depend on
+ * {@code prerequisite} through the attribute {@code attribute}.
+ *
+ * <p>Can be used for enforcing any organization-specific policies about the layout of the
+ * workspace.
+ */
+ void validate(
+ RuleContext.Builder contextBuilder, ConfiguredTarget prerequisite, Attribute attribute);
+ }
+
+ /**
+ * Builder for {@link ConfiguredRuleClassProvider}.
+ */
+ public static class Builder implements RuleDefinitionEnvironment {
+ private final List<ConfigurationFragmentFactory> configurationFragments = new ArrayList<>();
+ private final List<BuildInfoFactory> buildInfoFactories = new ArrayList<>();
+ private final List<Class<? extends FragmentOptions>> configurationOptions = new ArrayList<>();
+
+ private final Map<String, RuleClass> ruleClassMap = new HashMap<>();
+ private final Map<String, Class<? extends RuleDefinition>> ruleDefinitionMap =
+ new HashMap<>();
+ private final Map<Class<? extends RuleDefinition>, RuleClass> ruleMap = new HashMap<>();
+ private final Digraph<Class<? extends RuleDefinition>> dependencyGraph =
+ new Digraph<>();
+ private ConfigurationCollectionFactory configurationCollectionFactory;
+ private PrerequisiteValidator prerequisiteValidator;
+ private ImmutableMap<String, SkylarkType> skylarkAccessibleJavaClasses = ImmutableMap.of();
+
+ public Builder setPrerequisiteValidator(PrerequisiteValidator prerequisiteValidator) {
+ this.prerequisiteValidator = prerequisiteValidator;
+ return this;
+ }
+
+ public Builder addBuildInfoFactory(BuildInfoFactory factory) {
+ buildInfoFactories.add(factory);
+ return this;
+ }
+
+ public Builder addRuleDefinition(Class<? extends RuleDefinition> ruleDefinition) {
+ dependencyGraph.createNode(ruleDefinition);
+ BlazeRule annotation = ruleDefinition.getAnnotation(BlazeRule.class);
+ for (Class<? extends RuleDefinition> ancestor : annotation.ancestors()) {
+ dependencyGraph.addEdge(ancestor, ruleDefinition);
+ }
+
+ return this;
+ }
+
+ public Builder addConfigurationOptions(Class<? extends FragmentOptions> configurationOptions) {
+ this.configurationOptions.add(configurationOptions);
+ return this;
+ }
+
+ public Builder addConfigurationFragment(ConfigurationFragmentFactory factory) {
+ configurationFragments.add(factory);
+ return this;
+ }
+
+ public Builder setConfigurationCollectionFactory(ConfigurationCollectionFactory factory) {
+ this.configurationCollectionFactory = factory;
+ return this;
+ }
+
+ public Builder setSkylarkAccessibleJavaClasses(ImmutableMap<String, SkylarkType> objects) {
+ this.skylarkAccessibleJavaClasses = objects;
+ return this;
+ }
+
+ private RuleConfiguredTargetFactory createFactory(
+ Class<? extends RuleConfiguredTargetFactory> factoryClass) {
+ try {
+ Constructor<? extends RuleConfiguredTargetFactory> ctor = factoryClass.getConstructor();
+ return ctor.newInstance();
+ } catch (NoSuchMethodException | IllegalAccessException | InstantiationException
+ | InvocationTargetException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ private RuleClass commitRuleDefinition(Class<? extends RuleDefinition> definitionClass) {
+ BlazeRule annotation = definitionClass.getAnnotation(BlazeRule.class);
+ Preconditions.checkArgument(ruleClassMap.get(annotation.name()) == null, annotation.name());
+
+ Preconditions.checkArgument(
+ annotation.type() == ABSTRACT ^
+ annotation.factoryClass() != RuleConfiguredTargetFactory.class);
+ Preconditions.checkArgument(
+ (annotation.type() != TEST) ||
+ Arrays.asList(annotation.ancestors()).contains(
+ BaseRuleClasses.TestBaseRule.class));
+
+ RuleDefinition instance;
+ try {
+ instance = definitionClass.newInstance();
+ } catch (IllegalAccessException | InstantiationException e) {
+ throw new IllegalStateException(e);
+ }
+ RuleClass[] ancestorClasses = new RuleClass[annotation.ancestors().length];
+ for (int i = 0; i < annotation.ancestors().length; i++) {
+ ancestorClasses[i] = ruleMap.get(annotation.ancestors()[i]);
+ if (ancestorClasses[i] == null) {
+ // Ancestors should have been initialized by now
+ throw new IllegalStateException("Ancestor " + annotation.ancestors()[i] + " of "
+ + annotation.name() + " is not initialized");
+ }
+ }
+
+ RuleConfiguredTargetFactory factory = null;
+ if (annotation.type() != ABSTRACT) {
+ factory = createFactory(annotation.factoryClass());
+ }
+
+ RuleClass.Builder builder = new RuleClass.Builder(
+ annotation.name(), annotation.type(), false, ancestorClasses);
+ builder.factory(factory);
+ RuleClass ruleClass = instance.build(builder, this);
+ ruleMap.put(definitionClass, ruleClass);
+ ruleClassMap.put(ruleClass.getName(), ruleClass);
+ ruleDefinitionMap.put(ruleClass.getName(), definitionClass);
+
+ return ruleClass;
+ }
+
+ public ConfiguredRuleClassProvider build() {
+ for (Node<Class<? extends RuleDefinition>> ruleDefinition :
+ dependencyGraph.getTopologicalOrder()) {
+ commitRuleDefinition(ruleDefinition.getLabel());
+ }
+
+ return new ConfiguredRuleClassProvider(
+ ImmutableMap.copyOf(ruleClassMap),
+ ImmutableMap.copyOf(ruleDefinitionMap),
+ ImmutableList.copyOf(buildInfoFactories),
+ ImmutableList.copyOf(configurationOptions),
+ ImmutableList.copyOf(configurationFragments),
+ configurationCollectionFactory,
+ prerequisiteValidator,
+ skylarkAccessibleJavaClasses);
+ }
+
+ @Override
+ public Label getLabel(String labelValue) {
+ return LABELS.getUnchecked(labelValue);
+ }
+ }
+
+ /**
+ * Used to make the label instances unique, so that we don't create a new
+ * instance for every rule.
+ */
+ private static LoadingCache<String, Label> LABELS = CacheBuilder.newBuilder().build(
+ new CacheLoader<String, Label>() {
+ @Override
+ public Label load(String from) {
+ try {
+ return Label.parseAbsolute(from);
+ } catch (Label.SyntaxException e) {
+ throw new IllegalArgumentException(from);
+ }
+ }
+ });
+
+ /**
+ * Maps rule class name to the metaclass instance for that rule.
+ */
+ private final ImmutableMap<String, RuleClass> ruleClassMap;
+
+ /**
+ * Maps rule class name to the rule definition metaclasses.
+ */
+ private final ImmutableMap<String, Class<? extends RuleDefinition>> ruleDefinitionMap;
+
+ /**
+ * The configuration options that affect the behavior of the rules.
+ */
+ private final ImmutableList<Class<? extends FragmentOptions>> configurationOptions;
+
+ /**
+ * The set of configuration fragment factories.
+ */
+ private final ImmutableList<ConfigurationFragmentFactory> configurationFragments;
+
+ /**
+ * The factory that creates the configuration collection.
+ */
+ private final ConfigurationCollectionFactory configurationCollectionFactory;
+
+ private final ImmutableList<BuildInfoFactory> buildInfoFactories;
+
+ private final PrerequisiteValidator prerequisiteValidator;
+
+ private final ImmutableMap<String, SkylarkType> skylarkAccessibleJavaClasses;
+
+ private final ValidationEnvironment skylarkValidationEnvironment;
+
+ public ConfiguredRuleClassProvider(
+ ImmutableMap<String, RuleClass> ruleClassMap,
+ ImmutableMap<String, Class<? extends RuleDefinition>> ruleDefinitionMap,
+ ImmutableList<BuildInfoFactory> buildInfoFactories,
+ ImmutableList<Class<? extends FragmentOptions>> configurationOptions,
+ ImmutableList<ConfigurationFragmentFactory> configurationFragments,
+ ConfigurationCollectionFactory configurationCollectionFactory,
+ PrerequisiteValidator prerequisiteValidator,
+ ImmutableMap<String, SkylarkType> skylarkAccessibleJavaClasses) {
+
+ this.ruleClassMap = ruleClassMap;
+ this.ruleDefinitionMap = ruleDefinitionMap;
+ this.buildInfoFactories = buildInfoFactories;
+ this.configurationOptions = configurationOptions;
+ this.configurationFragments = configurationFragments;
+ this.configurationCollectionFactory = configurationCollectionFactory;
+ this.prerequisiteValidator = prerequisiteValidator;
+ this.skylarkAccessibleJavaClasses = skylarkAccessibleJavaClasses;
+ this.skylarkValidationEnvironment = SkylarkModules.getValidationEnvironment(
+ ImmutableMap.<String, SkylarkType>builder()
+ .putAll(skylarkAccessibleJavaClasses)
+ .put("native", SkylarkType.of(NativeModule.class))
+ .build());
+ }
+
+ public PrerequisiteValidator getPrerequisiteValidator() {
+ return prerequisiteValidator;
+ }
+
+ @Override
+ public Map<String, RuleClass> getRuleClassMap() {
+ return ruleClassMap;
+ }
+
+ /**
+ * Returns a list of build info factories that are needed for the supported languages.
+ */
+ public ImmutableList<BuildInfoFactory> getBuildInfoFactories() {
+ return buildInfoFactories;
+ }
+
+ /**
+ * Returns the set of configuration fragments provided by this module.
+ */
+ public ImmutableList<ConfigurationFragmentFactory> getConfigurationFragments() {
+ return configurationFragments;
+ }
+
+ /**
+ * Returns the set of configuration options that are supported in this module.
+ */
+ public ImmutableList<Class<? extends FragmentOptions>> getConfigurationOptions() {
+ return configurationOptions;
+ }
+
+ /**
+ * Returns the definition of the rule class definition with the specified name.
+ */
+ public Class<? extends RuleDefinition> getRuleClassDefinition(String ruleClassName) {
+ return ruleDefinitionMap.get(ruleClassName);
+ }
+
+ /**
+ * Returns the configuration collection creator.
+ */
+ public ConfigurationCollectionFactory getConfigurationCollectionFactory() {
+ return configurationCollectionFactory;
+ }
+
+ /**
+ * Returns the defaults package for the default settings.
+ */
+ public String getDefaultsPackageContent() {
+ return DefaultsPackage.getDefaultsPackageContent(configurationOptions);
+ }
+
+ /**
+ * Returns the defaults package for the given options taken from an optionsProvider.
+ */
+ public String getDefaultsPackageContent(OptionsClassProvider optionsProvider) {
+ return DefaultsPackage.getDefaultsPackageContent(
+ BuildOptions.of(configurationOptions, optionsProvider));
+ }
+
+ /**
+ * Creates a BuildOptions class for the given options taken from an optionsProvider.
+ */
+ public BuildOptions createBuildOptions(OptionsClassProvider optionsProvider) {
+ return BuildOptions.of(configurationOptions, optionsProvider);
+ }
+
+ @SkylarkModule(name = "native", namespace = true, onlyLoadingPhase = true,
+ doc = "Module for native rules.")
+ private static final class NativeModule {}
+
+ public static final NativeModule nativeModule = new NativeModule();
+
+ @Override
+ public SkylarkEnvironment createSkylarkRuleClassEnvironment(
+ EventHandler eventHandler, String astFileContentHashCode) {
+ SkylarkEnvironment env = SkylarkModules.getNewEnvironment(eventHandler, astFileContentHashCode);
+ for (Map.Entry<String, SkylarkType> entry : skylarkAccessibleJavaClasses.entrySet()) {
+ env.update(entry.getKey(), entry.getValue().getType());
+ }
+ return env;
+ }
+
+ @Override
+ public ValidationEnvironment getSkylarkValidationEnvironment() {
+ return skylarkValidationEnvironment;
+ }
+
+ @Override
+ public Object getNativeModule() {
+ return nativeModule;
+ }
+}