diff options
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.java | 376 |
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; + } +} |