blob: 2c0bd9e0f679a0ce0bf480ef384e7176971c9277 [file] [log] [blame]
// 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.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
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.ImmutableBiMap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.lib.analysis.buildinfo.BuildInfoFactory;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration.Fragment;
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.DynamicTransitionMapper;
import com.google.devtools.build.lib.analysis.config.FragmentOptions;
import com.google.devtools.build.lib.analysis.skylark.SkylarkModules;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
import com.google.devtools.build.lib.cmdline.PackageIdentifier;
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.NativeAspectClass;
import com.google.devtools.build.lib.packages.NonconfigurableAttributeMapper;
import com.google.devtools.build.lib.packages.OutputFile;
import com.google.devtools.build.lib.packages.Rule;
import com.google.devtools.build.lib.packages.RuleClass;
import com.google.devtools.build.lib.packages.RuleClassProvider;
import com.google.devtools.build.lib.packages.RuleErrorConsumer;
import com.google.devtools.build.lib.packages.Target;
import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.InvocationPolicy;
import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
import com.google.devtools.build.lib.syntax.Environment;
import com.google.devtools.build.lib.syntax.Environment.Extension;
import com.google.devtools.build.lib.syntax.Environment.Phase;
import com.google.devtools.build.lib.syntax.Mutability;
import com.google.devtools.build.lib.syntax.SkylarkSemantics;
import com.google.devtools.build.lib.syntax.SkylarkUtils;
import com.google.devtools.build.lib.syntax.Type;
import com.google.devtools.common.options.OptionsClassProvider;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import javax.annotation.Nullable;
/**
* 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 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);
}
/** Validator to check for and warn on the deprecation of dependencies. */
public static final class DeprecationValidator implements PrerequisiteValidator {
/** Checks if the given prerequisite is deprecated and prints a warning if so. */
@Override
public void validate(
RuleContext.Builder contextBuilder, ConfiguredTarget prerequisite, Attribute attribute) {
validateDirectPrerequisiteForDeprecation(
contextBuilder, contextBuilder.getRule(), prerequisite, contextBuilder.forAspect());
}
/**
* Returns whether two packages are considered the same for purposes of deprecation warnings.
* Dependencies within the same package do not print deprecation warnings; a package in the
* javatests directory may also depend on its corresponding java package without a warning.
*/
public static boolean isSameLogicalPackage(
PackageIdentifier thisPackage, PackageIdentifier prerequisitePackage) {
if (thisPackage.equals(prerequisitePackage)) {
// If the packages are equal, they are the same logical package (and just the same package).
return true;
}
if (!thisPackage.getRepository().equals(prerequisitePackage.getRepository())) {
// If the packages are in different repositories, they are not the same logical package.
return false;
}
// If the packages are in the same repository, it's allowed iff this package is the javatests
// companion to the prerequisite java package.
String thisPackagePath = thisPackage.getPackageFragment().getPathString();
String prerequisitePackagePath = prerequisitePackage.getPackageFragment().getPathString();
return thisPackagePath.startsWith("javatests/")
&& prerequisitePackagePath.startsWith("java/")
&& thisPackagePath.substring("javatests/".length()).equals(
prerequisitePackagePath.substring("java/".length()));
}
/** Returns whether a deprecation warning should be printed for the prerequisite described. */
private static boolean shouldEmitDeprecationWarningFor(
String thisDeprecation, PackageIdentifier thisPackage,
String prerequisiteDeprecation, PackageIdentifier prerequisitePackage,
boolean forAspect) {
// Don't report deprecation edges from javatests to java or within a package;
// otherwise tests of deprecated code generate nuisance warnings.
// Don't report deprecation if the current target is also deprecated,
// or if the current context is evaluating an aspect,
// as the base target would have already printed the deprecation warnings.
return (!forAspect
&& prerequisiteDeprecation != null
&& !isSameLogicalPackage(thisPackage, prerequisitePackage)
&& thisDeprecation == null);
}
/** Checks if the given prerequisite is deprecated and prints a warning if so. */
public static void validateDirectPrerequisiteForDeprecation(
RuleErrorConsumer errors, Rule rule, ConfiguredTarget prerequisite, boolean forAspect) {
Target prerequisiteTarget = prerequisite.getTarget();
Label prerequisiteLabel = prerequisiteTarget.getLabel();
PackageIdentifier thatPackage = prerequisiteLabel.getPackageIdentifier();
PackageIdentifier thisPackage = rule.getLabel().getPackageIdentifier();
if (prerequisiteTarget instanceof Rule) {
Rule prerequisiteRule = (Rule) prerequisiteTarget;
String thisDeprecation =
NonconfigurableAttributeMapper.of(rule).get("deprecation", Type.STRING);
String thatDeprecation =
NonconfigurableAttributeMapper.of(prerequisiteRule).get("deprecation", Type.STRING);
if (shouldEmitDeprecationWarningFor(
thisDeprecation, thisPackage, thatDeprecation, thatPackage, forAspect)) {
errors.ruleWarning("target '" + rule.getLabel() + "' depends on deprecated target '"
+ prerequisiteLabel + "': " + thatDeprecation);
}
}
if (prerequisiteTarget instanceof OutputFile) {
Rule generatingRule = ((OutputFile) prerequisiteTarget).getGeneratingRule();
String thisDeprecation =
NonconfigurableAttributeMapper.of(rule).get("deprecation", Type.STRING);
String thatDeprecation =
NonconfigurableAttributeMapper.of(generatingRule).get("deprecation", Type.STRING);
if (shouldEmitDeprecationWarningFor(
thisDeprecation, thisPackage, thatDeprecation, thatPackage, forAspect)) {
errors.ruleWarning("target '" + rule.getLabel() + "' depends on the output file "
+ prerequisiteLabel + " of a deprecated rule " + generatingRule.getLabel()
+ "': " + thatDeprecation);
}
}
}
}
/**
* A coherent set of options, fragments, aspects and rules; each of these may declare a dependency
* on other such sets.
*/
public static interface RuleSet {
/** Add stuff to the configured rule class provider builder. */
void init(ConfiguredRuleClassProvider.Builder builder);
/** List of required modules. */
ImmutableList<RuleSet> requires();
}
/** Builder for {@link ConfiguredRuleClassProvider}. */
public static class Builder implements RuleDefinitionEnvironment {
private final StringBuilder defaultWorkspaceFilePrefix = new StringBuilder();
private final StringBuilder defaultWorkspaceFileSuffix = new StringBuilder();
private Label preludeLabel;
private String runfilesPrefix;
private String toolsRepository;
private final List<ConfigurationFragmentFactory> configurationFragmentFactories =
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, RuleDefinition> ruleDefinitionMap = new HashMap<>();
private final Map<String, NativeAspectClass> nativeAspectClassMap =
new HashMap<>();
private final Map<Class<? extends RuleDefinition>, RuleClass> ruleMap = new HashMap<>();
private final Digraph<Class<? extends RuleDefinition>> dependencyGraph =
new Digraph<>();
private ImmutableMap.Builder<Attribute.Transition, Attribute.Transition> dynamicTransitionMaps
= ImmutableMap.builder();
private Class<? extends BuildConfiguration.Fragment> universalFragment;
private PrerequisiteValidator prerequisiteValidator;
private ImmutableMap.Builder<String, Object> skylarkAccessibleTopLevels =
ImmutableMap.builder();
private ImmutableList.Builder<Class<?>> skylarkModules =
ImmutableList.<Class<?>>builder().addAll(SkylarkModules.MODULES);
private ImmutableBiMap.Builder<String, Class<? extends TransitiveInfoProvider>>
registeredSkylarkProviders = ImmutableBiMap.builder();
private Map<String, String> platformRegexps = new TreeMap<>();
// TODO(pcloudy): Remove this field after Bazel rule definitions are not used internally.
private String nativeLauncherLabel;
public Builder setNativeLauncherLabel(String label) {
this.nativeLauncherLabel = label;
return this;
}
public Builder addWorkspaceFilePrefix(String contents) {
defaultWorkspaceFilePrefix.append(contents);
return this;
}
public Builder addWorkspaceFileSuffix(String contents) {
defaultWorkspaceFileSuffix.append(contents);
return this;
}
public Builder setPrelude(String preludeLabelString) {
try {
this.preludeLabel = Label.parseAbsolute(preludeLabelString);
} catch (LabelSyntaxException e) {
String errorMsg =
String.format("Prelude label '%s' is invalid: %s", preludeLabelString, e.getMessage());
throw new IllegalArgumentException(errorMsg);
}
return this;
}
public Builder setRunfilesPrefix(String runfilesPrefix) {
this.runfilesPrefix = runfilesPrefix;
return this;
}
public Builder setToolsRepository(String toolsRepository) {
this.toolsRepository = toolsRepository;
return this;
}
public Builder setPrerequisiteValidator(PrerequisiteValidator prerequisiteValidator) {
this.prerequisiteValidator = prerequisiteValidator;
return this;
}
public Builder addBuildInfoFactory(BuildInfoFactory factory) {
buildInfoFactories.add(factory);
return this;
}
public Builder addRuleDefinition(RuleDefinition ruleDefinition) {
Class<? extends RuleDefinition> ruleDefinitionClass = ruleDefinition.getClass();
ruleDefinitionMap.put(ruleDefinitionClass.getName(), ruleDefinition);
dependencyGraph.createNode(ruleDefinitionClass);
for (Class<? extends RuleDefinition> ancestor : ruleDefinition.getMetadata().ancestors()) {
dependencyGraph.addEdge(ancestor, ruleDefinitionClass);
}
return this;
}
public Builder addNativeAspectClass(NativeAspectClass aspectFactoryClass) {
nativeAspectClassMap.put(aspectFactoryClass.getName(), aspectFactoryClass);
return this;
}
public Builder addConfigurationOptions(Class<? extends FragmentOptions> configurationOptions) {
this.configurationOptions.add(configurationOptions);
return this;
}
/**
* Adds an options class and a corresponding factory. There's usually a 1:1:1 correspondence
* between option classes, factories, and fragments, such that the factory depends only on the
* options class and creates the fragment. This method provides a convenient way of adding both
* the options class and the factory in a single call.
*
* <p>Note that configuration fragments annotated with a Skylark name must have a unique
* name; no two different configuration fragments can share the same name.
*/
public Builder addConfig(
Class<? extends FragmentOptions> options, ConfigurationFragmentFactory factory) {
// Enforce that the factory requires the options.
Preconditions.checkState(factory.requiredOptions().contains(options));
this.configurationOptions.add(options);
this.configurationFragmentFactories.add(factory);
return this;
}
public Builder addConfigurationOptions(
Collection<Class<? extends FragmentOptions>> optionsClasses) {
this.configurationOptions.addAll(optionsClasses);
return this;
}
/**
* Adds a configuration fragment factory.
*
* <p>Note that configuration fragments annotated with a Skylark name must have a unique
* name; no two different configuration fragments can share the same name.
*/
public Builder addConfigurationFragment(ConfigurationFragmentFactory factory) {
configurationFragmentFactories.add(factory);
return this;
}
public Builder addDynamicTransitionMaps(Map<Attribute.Transition, Attribute.Transition> maps) {
dynamicTransitionMaps.putAll(maps);
return this;
}
public Builder setUniversalConfigurationFragment(
Class<? extends BuildConfiguration.Fragment> fragment) {
this.universalFragment = fragment;
return this;
}
public Builder addSkylarkAccessibleTopLevels(String name, Object object) {
this.skylarkAccessibleTopLevels.put(name, object);
return this;
}
public Builder addSkylarkModule(Class<?>... modules) {
this.skylarkModules.add(modules);
return this;
}
/**
* Do not use - this only exists for backwards compatibility! Platform regexps are part of a
* legacy mechanism - {@code vardef} - that is not exposed in Bazel.
*
* <p>{@code vardef} needs explicit support in the rule implementations, and cannot express
* conditional dependencies, only conditional attribute values. This mechanism will be
* supplanted by configuration dependent attributes, and its effect can usually also be achieved
* with select().
*
* <p>This is a map of platform names to regexps. When a name is used as the third argument to
* {@code vardef}, the corresponding regexp is used to match on the C++ abi, and the variable is
* only set to that value if the regexp matches. For example, the entry
* {@code "oldlinux": "i[34]86-libc[345]-linux"} might define a set of platforms representing
* certain older linux releases.
*/
public Builder addPlatformRegexps(Map<String, String> platformRegexps) {
this.platformRegexps.putAll(Preconditions.checkNotNull(platformRegexps));
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) {
RuleDefinition instance = checkNotNull(ruleDefinitionMap.get(definitionClass.getName()),
"addRuleDefinition(new %s()) should be called before build()", definitionClass.getName());
RuleDefinition.Metadata metadata = instance.getMetadata();
checkArgument(
ruleClassMap.get(metadata.name()) == null,
"The rule " + metadata.name() + " was committed already, use another name");
List<Class<? extends RuleDefinition>> ancestors = metadata.ancestors();
checkArgument(
metadata.type() == ABSTRACT ^ metadata.factoryClass()
!= RuleConfiguredTargetFactory.class);
checkArgument(
(metadata.type() != TEST)
|| ancestors.contains(BaseRuleClasses.TestBaseRule.class));
RuleClass[] ancestorClasses = new RuleClass[ancestors.size()];
for (int i = 0; i < ancestorClasses.length; i++) {
ancestorClasses[i] = ruleMap.get(ancestors.get(i));
if (ancestorClasses[i] == null) {
// Ancestors should have been initialized by now
throw new IllegalStateException("Ancestor " + ancestors.get(i) + " of "
+ metadata.name() + " is not initialized");
}
}
RuleConfiguredTargetFactory factory = null;
if (metadata.type() != ABSTRACT) {
factory = createFactory(metadata.factoryClass());
}
RuleClass.Builder builder = new RuleClass.Builder(
metadata.name(), metadata.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(), instance);
return ruleClass;
}
public ConfiguredRuleClassProvider build() {
for (Node<Class<? extends RuleDefinition>> ruleDefinition :
dependencyGraph.getTopologicalOrder()) {
commitRuleDefinition(ruleDefinition.getLabel());
}
return new ConfiguredRuleClassProvider(
preludeLabel,
runfilesPrefix,
toolsRepository,
ImmutableMap.copyOf(ruleClassMap),
ImmutableMap.copyOf(ruleDefinitionMap),
ImmutableMap.copyOf(nativeAspectClassMap),
defaultWorkspaceFilePrefix.toString(),
defaultWorkspaceFileSuffix.toString(),
ImmutableList.copyOf(buildInfoFactories),
ImmutableList.copyOf(configurationOptions),
ImmutableList.copyOf(configurationFragmentFactories),
new DynamicTransitionMapper(dynamicTransitionMaps.build()),
universalFragment,
prerequisiteValidator,
skylarkAccessibleTopLevels.build(),
skylarkModules.build());
}
@Override
public Label getLabel(String labelValue) {
return LABELS.getUnchecked(labelValue);
}
@Override
public Label getToolsLabel(String labelValue) {
return getLabel(toolsRepository + labelValue);
}
@Override
public Label getLauncherLabel() {
if (nativeLauncherLabel == null) {
return null;
}
return getToolsLabel(nativeLauncherLabel);
}
@Override
public String getToolsRepository() {
return toolsRepository;
}
@Nullable
public Map<String, String> getPlatformRegexps() {
return platformRegexps.isEmpty() ? null : ImmutableMap.copyOf(platformRegexps);
}
}
/**
* Used to make the label instances unique, so that we don't create a new
* instance for every rule.
*/
private static final LoadingCache<String, Label> LABELS = CacheBuilder.newBuilder().build(
new CacheLoader<String, Label>() {
@Override
public Label load(String from) {
try {
return Label.parseAbsolute(from);
} catch (LabelSyntaxException e) {
throw new IllegalArgumentException(from, e);
}
}
});
/**
* Default content that should be added at the beginning of the WORKSPACE file.
*/
private final String defaultWorkspaceFilePrefix;
/**
* Default content that should be added at the end of the WORKSPACE file.
*/
private final String defaultWorkspaceFileSuffix;
/**
* Label for the prelude file.
*/
private final Label preludeLabel;
/**
* The default runfiles prefix.
*/
private final String runfilesPrefix;
/**
* The path to the tools repository.
*/
private final String toolsRepository;
/**
* 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 objects.
*/
private final ImmutableMap<String, RuleDefinition> ruleDefinitionMap;
/**
* Maps aspect name to the aspect factory meta class.
*/
private final ImmutableMap<String, NativeAspectClass> nativeAspectClassMap;
/**
* 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> configurationFragmentFactories;
/**
* The dynamic configuration transition mapper.
*/
private final DynamicTransitionMapper dynamicTransitionMapper;
/**
* A configuration fragment that should be available to all rules even when they don't
* explicitly require it.
*/
private final Class<? extends BuildConfiguration.Fragment> universalFragment;
private final ImmutableList<BuildInfoFactory> buildInfoFactories;
private final PrerequisiteValidator prerequisiteValidator;
private final Environment.Frame globals;
private final ImmutableMap<String, Class<?>> configurationFragmentMap;
private ConfiguredRuleClassProvider(
Label preludeLabel,
String runfilesPrefix,
String toolsRepository,
ImmutableMap<String, RuleClass> ruleClassMap,
ImmutableMap<String, RuleDefinition> ruleDefinitionMap,
ImmutableMap<String, NativeAspectClass> nativeAspectClassMap,
String defaultWorkspaceFilePrefix,
String defaultWorkspaceFileSuffix,
ImmutableList<BuildInfoFactory> buildInfoFactories,
ImmutableList<Class<? extends FragmentOptions>> configurationOptions,
ImmutableList<ConfigurationFragmentFactory> configurationFragments,
DynamicTransitionMapper dynamicTransitionMapper,
Class<? extends BuildConfiguration.Fragment> universalFragment,
PrerequisiteValidator prerequisiteValidator,
ImmutableMap<String, Object> skylarkAccessibleJavaClasses,
ImmutableList<Class<?>> skylarkModules) {
this.preludeLabel = preludeLabel;
this.runfilesPrefix = runfilesPrefix;
this.toolsRepository = toolsRepository;
this.ruleClassMap = ruleClassMap;
this.ruleDefinitionMap = ruleDefinitionMap;
this.nativeAspectClassMap = nativeAspectClassMap;
this.defaultWorkspaceFilePrefix = defaultWorkspaceFilePrefix;
this.defaultWorkspaceFileSuffix = defaultWorkspaceFileSuffix;
this.buildInfoFactories = buildInfoFactories;
this.configurationOptions = configurationOptions;
this.configurationFragmentFactories = configurationFragments;
this.dynamicTransitionMapper = dynamicTransitionMapper;
this.universalFragment = universalFragment;
this.prerequisiteValidator = prerequisiteValidator;
this.globals = createGlobals(skylarkAccessibleJavaClasses, skylarkModules);
this.configurationFragmentMap = createFragmentMap(configurationFragmentFactories);
}
public PrerequisiteValidator getPrerequisiteValidator() {
return prerequisiteValidator;
}
@Override
public Label getPreludeLabel() {
return preludeLabel;
}
@Override
public String getRunfilesPrefix() {
return runfilesPrefix;
}
@Override
public String getToolsRepository() {
return toolsRepository;
}
@Override
public Map<String, RuleClass> getRuleClassMap() {
return ruleClassMap;
}
@Override
public Map<String, NativeAspectClass> getNativeAspectClassMap() {
return nativeAspectClassMap;
}
@Override
public NativeAspectClass getNativeAspectClass(String key) {
return nativeAspectClassMap.get(key);
}
/**
* 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 configurationFragmentFactories;
}
/**
* 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 RuleDefinition getRuleClassDefinition(String ruleClassName) {
return ruleDefinitionMap.get(ruleClassName);
}
/**
* Returns the dynamic configuration transition mapper.
*/
public DynamicTransitionMapper getDynamicTransitionMapper() {
return dynamicTransitionMapper;
}
/**
* Returns the configuration fragment that should be available to all rules even when they
* don't explicitly require it.
*/
public Class<? extends BuildConfiguration.Fragment> getUniversalFragment() {
return universalFragment;
}
/**
* Returns the defaults package for the default settings.
*/
public String getDefaultsPackageContent(InvocationPolicy invocationPolicy) {
return DefaultsPackage.getDefaultsPackageContent(configurationOptions, invocationPolicy);
}
/**
* 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);
}
private Environment.Frame createGlobals(
ImmutableMap<String, Object> skylarkAccessibleToplLevels,
ImmutableList<Class<?>> modules) {
try (Mutability mutability = Mutability.create("ConfiguredRuleClassProvider globals")) {
Environment env = createSkylarkRuleClassEnvironment(
mutability,
SkylarkModules.getGlobals(modules),
SkylarkSemantics.DEFAULT_SEMANTICS,
/*eventHandler=*/ null,
/*astFileContentHashCode=*/ null,
/*importMap=*/ null);
for (Map.Entry<String, Object> entry : skylarkAccessibleToplLevels.entrySet()) {
env.setup(entry.getKey(), entry.getValue());
}
return env.getGlobals();
}
}
private static ImmutableMap<String, Class<?>> createFragmentMap(
Iterable<ConfigurationFragmentFactory> configurationFragmentFactories) {
ImmutableMap.Builder<String, Class<?>> mapBuilder = ImmutableMap.builder();
for (ConfigurationFragmentFactory fragmentFactory : configurationFragmentFactories) {
Class<? extends Fragment> fragmentClass = fragmentFactory.creates();
String fragmentName = SkylarkModule.Resolver.resolveName(fragmentClass);
if (fragmentName != null) {
mapBuilder.put(fragmentName, fragmentClass);
}
}
return mapBuilder.build();
}
private Environment createSkylarkRuleClassEnvironment(
Mutability mutability,
Environment.Frame globals,
SkylarkSemantics skylarkSemantics,
EventHandler eventHandler,
String astFileContentHashCode,
Map<String, Extension> importMap) {
Environment env =
Environment.builder(mutability)
.setGlobals(globals)
.setSemantics(skylarkSemantics)
.setEventHandler(eventHandler)
.setFileContentHashCode(astFileContentHashCode)
.setImportedExtensions(importMap)
.setPhase(Phase.LOADING)
.build();
SkylarkUtils.setToolsRepository(env, toolsRepository);
SkylarkUtils.setFragmentMap(env, configurationFragmentMap);
return env;
}
@Override
public Environment createSkylarkRuleClassEnvironment(
Label extensionLabel,
Mutability mutability,
SkylarkSemantics skylarkSemantics,
EventHandler eventHandler,
String astFileContentHashCode,
Map<String, Extension> importMap) {
return createSkylarkRuleClassEnvironment(
mutability,
globals.withLabel(extensionLabel),
skylarkSemantics,
eventHandler,
astFileContentHashCode,
importMap);
}
@Override
public String getDefaultWorkspacePrefix() {
return defaultWorkspaceFilePrefix;
}
@Override
public String getDefaultWorkspaceSuffix() {
return defaultWorkspaceFileSuffix;
}
@Override
public Map<String, Class<?>> getConfigurationFragmentMap() {
return configurationFragmentMap;
}
/**
* Returns all registered {@link BuildConfiguration.Fragment} classes.
*/
public Set<Class<? extends BuildConfiguration.Fragment>> getAllFragments() {
ImmutableSet.Builder<Class<? extends BuildConfiguration.Fragment>> fragmentsBuilder =
ImmutableSet.builder();
for (ConfigurationFragmentFactory factory : getConfigurationFragments()) {
fragmentsBuilder.add(factory.creates());
}
fragmentsBuilder.add(getUniversalFragment());
return fragmentsBuilder.build();
}
}