| // Copyright 2020 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.config; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.collect.ImmutableSet; |
| import com.google.common.collect.ImmutableSortedSet; |
| import com.google.devtools.build.lib.analysis.BuildSettingProvider; |
| import com.google.devtools.build.lib.analysis.RequiredConfigFragmentsProvider; |
| import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder; |
| import com.google.devtools.build.lib.analysis.config.transitions.ConfigurationTransition; |
| import com.google.devtools.build.lib.analysis.config.transitions.TransitionFactory; |
| import com.google.devtools.build.lib.cmdline.Label; |
| import com.google.devtools.build.lib.packages.Aspect; |
| import com.google.devtools.build.lib.packages.Attribute; |
| import com.google.devtools.build.lib.packages.AttributeMap; |
| import com.google.devtools.build.lib.packages.AttributeTransitionData; |
| import com.google.devtools.build.lib.packages.ConfigurationFragmentPolicy; |
| import com.google.devtools.build.lib.packages.ConfiguredAttributeMapper; |
| import com.google.devtools.build.lib.packages.Rule; |
| import com.google.devtools.build.lib.skyframe.ConfiguredTargetAndData; |
| import com.google.devtools.build.lib.util.ClassName; |
| import java.util.Collection; |
| import java.util.Optional; |
| import java.util.TreeSet; |
| |
| /** |
| * Utility methods for determining what {@link Fragment}s are required to analyze targets. |
| * |
| * <p>For example if a target reads <code>--copt</code> as part of its analysis logic, it requires |
| * the {@link com.google.devtools.build.lib.rules.cpp.CppConfiguration} fragment. |
| * |
| * <p>Used by {@link |
| * com.google.devtools.build.lib.query2.cquery.CqueryOptions#showRequiredConfigFragments}. |
| */ |
| public class RequiredFragmentsUtil { |
| |
| /** |
| * Returns a set of user-friendly strings identifying all pieces of configuration a target |
| * requires. |
| * |
| * <p>The returned config state includes things that are known to be required at the time when the |
| * target's dependencies have already been analyzed but before it's been analyzed itself. See |
| * {@link RuleConfiguredTargetBuilder#maybeAddRequiredConfigFragmentsProvider} for the remaining |
| * pieces of config state. |
| * |
| * <p>The strings can be names of {@link Fragment}s, names of {@link FragmentOptions}, and labels |
| * of user-defined options such as Starlark flags and Android feature flags. |
| * |
| * <p>If {@code configuration} is {@link CoreOptions.IncludeConfigFragmentsEnum#DIRECT}, the |
| * result includes only the config state considered to be directly required by this target. If |
| * it's {@link CoreOptions.IncludeConfigFragmentsEnum#TRANSITIVE}, it also includes config state |
| * needed by transitive dependencies. If it's {@link CoreOptions.IncludeConfigFragmentsEnum#OFF}, |
| * this method just returns an empty set. |
| * |
| * <p>{@code select()}s and toolchain dependencies are considered when looking at what config |
| * state is required. |
| * |
| * @param target the target |
| * @param configuration the configuration for this target |
| * @param universallyRequiredFragments fragments that are always required even if not explicitly |
| * specified for this target |
| * @param configurationFragmentPolicy source of truth for the fragments required by this target |
| * class definition |
| * @param configConditions <code>config_settings</code> required by this target's <code>select |
| * </code>s. Used for a) figuring out which options <code>select</code>s read and b) figuring |
| * out which transitions are attached to the target. {@link TransitionFactory}, which |
| * determines the transitions, may read the target's attributes. |
| * @param prerequisites all prerequisites of this aspect |
| * @return An alphabetically ordered set of required fragments, options, and labels of |
| * user-defined options. |
| */ |
| public static ImmutableSortedSet<String> getRequiredFragments( |
| Rule target, |
| BuildConfiguration configuration, |
| Collection<Class<? extends Fragment>> universallyRequiredFragments, |
| ConfigurationFragmentPolicy configurationFragmentPolicy, |
| ImmutableMap<Label, ConfigMatchingProvider> configConditions, |
| Iterable<ConfiguredTargetAndData> prerequisites) { |
| return getRequiredFragments( |
| target.isBuildSetting() ? Optional.of(target.getLabel()) : Optional.empty(), |
| configuration, |
| universallyRequiredFragments, |
| configurationFragmentPolicy, |
| configConditions.values(), |
| getTransitions(target, configConditions), |
| prerequisites); |
| } |
| |
| /** |
| * Variation of {@link #getRequiredFragments} for aspects. |
| * |
| * @param aspect the aspect |
| * @param associatedTarget the target this aspect is attached to |
| * @param configuration the configuration for this aspect |
| * @param universallyRequiredFragments fragments that are always required even if not explicitly |
| * specified for this aspect |
| * @param configurationFragmentPolicy source of truth for the fragments required by this aspect |
| * class definition |
| * @param configConditions <code>config_settings</code> required by <code>select</code>s on the |
| * associated target. Used for figuring out which transitions are attached to the target. |
| * @param prerequisites all prerequisites of this aspect |
| * @return An alphabetically ordered set of required fragments, options, and labels of |
| * user-defined options. |
| */ |
| public static ImmutableSortedSet<String> getRequiredFragments( |
| Aspect aspect, |
| Rule associatedTarget, |
| BuildConfiguration configuration, |
| Collection<Class<? extends Fragment>> universallyRequiredFragments, |
| ConfigurationFragmentPolicy configurationFragmentPolicy, |
| ImmutableMap<Label, ConfigMatchingProvider> configConditions, |
| Iterable<ConfiguredTargetAndData> prerequisites) { |
| return getRequiredFragments( |
| /*buildSettingLabel=*/ Optional.empty(), |
| configuration, |
| universallyRequiredFragments, |
| configurationFragmentPolicy, |
| configConditions.values(), |
| getTransitions(aspect, ConfiguredAttributeMapper.of(associatedTarget, configConditions)), |
| prerequisites); |
| } |
| |
| /** Internal implementation that handles any source (target or aspect). */ |
| private static ImmutableSortedSet<String> getRequiredFragments( |
| Optional<Label> buildSettingLabel, |
| BuildConfiguration configuration, |
| Collection<Class<? extends Fragment>> universallyRequiredFragments, |
| ConfigurationFragmentPolicy configurationFragmentPolicy, |
| Collection<ConfigMatchingProvider> configConditions, |
| Collection<ConfigurationTransition> associatedTransitions, |
| Iterable<ConfiguredTargetAndData> prerequisites) { |
| CoreOptions coreOptions = configuration.getOptions().get(CoreOptions.class); |
| if (coreOptions.includeRequiredConfigFragmentsProvider |
| == CoreOptions.IncludeConfigFragmentsEnum.OFF) { |
| return ImmutableSortedSet.of(); |
| } |
| ImmutableSortedSet.Builder<String> requiredFragments = ImmutableSortedSet.naturalOrder(); |
| // Add directly required fragments: |
| |
| // Fragments explicitly required by the native target/aspect definition API: |
| configurationFragmentPolicy |
| .getRequiredConfigurationFragments() |
| .forEach(fragment -> requiredFragments.add(ClassName.getSimpleNameWithOuter(fragment))); |
| // Fragments explicitly required by the Starlark target/aspect definition API: |
| configurationFragmentPolicy |
| .getRequiredStarlarkFragments() |
| .forEach( |
| starlarkName -> { |
| requiredFragments.add( |
| ClassName.getSimpleNameWithOuter( |
| configuration.getStarlarkFragmentByName(starlarkName))); |
| }); |
| // Fragments universally required by everything: |
| universallyRequiredFragments.forEach( |
| fragment -> requiredFragments.add(ClassName.getSimpleNameWithOuter(fragment))); |
| // Fragments required by attached select()s. |
| configConditions.forEach( |
| configCondition -> requiredFragments.addAll(configCondition.getRequiredFragmentOptions())); |
| // We consider build settings (which are both targets and configuration) to require themselves: |
| if (buildSettingLabel.isPresent()) { |
| requiredFragments.add(buildSettingLabel.get().toString()); |
| } |
| // Fragments required by attached configuration transitions. |
| for (ConfigurationTransition transition : associatedTransitions) { |
| requiredFragments.addAll(transition.requiresOptionFragments(configuration.getOptions())); |
| } |
| |
| // Optionally add transitively required fragments (only if --show_config_fragments=transitive): |
| requiredFragments.addAll(getRequiredFragmentsFromDeps(configuration, prerequisites)); |
| return requiredFragments.build(); |
| } |
| |
| /** |
| * Returns the {@link ConfigurationTransition}s "attached" to a target. |
| * |
| * <p>"Attached" means the transition is attached to the target itself or one of its attributes. |
| * |
| * <p>These are the transitions required for a target to successfully analyze. Technically, |
| * transitions attached to the target are evaluated during its parent's analysis, which is where |
| * the configuration for the child is determined. We still consider these the child's requirements |
| * because the child's properties determine that dependency. |
| */ |
| private static ImmutableList<ConfigurationTransition> getTransitions( |
| Rule target, ImmutableMap<Label, ConfigMatchingProvider> configConditions) { |
| ImmutableList.Builder<ConfigurationTransition> transitions = ImmutableList.builder(); |
| if (target.getRuleClassObject().getTransitionFactory() != null) { |
| transitions.add(target.getRuleClassObject().getTransitionFactory().create(target)); |
| } |
| // We don't set the execution platform in this data because a) that doesn't affect which |
| // fragments are required and b) it's one less parameter we have to pass to |
| // RequiredFragmenstUtil's public interface. |
| AttributeTransitionData attributeTransitionData = |
| AttributeTransitionData.builder() |
| .attributes(ConfiguredAttributeMapper.of(target, configConditions)) |
| .build(); |
| for (Attribute attribute : target.getRuleClassObject().getAttributes()) { |
| if (attribute.getTransitionFactory() != null) { |
| transitions.add(attribute.getTransitionFactory().create(attributeTransitionData)); |
| } |
| } |
| return transitions.build(); |
| } |
| |
| /** |
| * Returns the {@link ConfigurationTransition}s "attached" to an aspect. |
| * |
| * <p>"Attached" means the transition is attached to one of the aspect's attributes. Transitions |
| * can'be attached directly to aspects themselves. |
| */ |
| private static ImmutableList<ConfigurationTransition> getTransitions( |
| Aspect aspect, AttributeMap attributeMap) { |
| ImmutableList.Builder<ConfigurationTransition> transitions = ImmutableList.builder(); |
| AttributeTransitionData attributeTransitionData = |
| AttributeTransitionData.builder().attributes(attributeMap).build(); |
| for (Attribute attribute : aspect.getDefinition().getAttributes().values()) { |
| if (attribute.getTransitionFactory() != null) { |
| transitions.add(attribute.getTransitionFactory().create(attributeTransitionData)); |
| } |
| } |
| return transitions.build(); |
| } |
| |
| /** |
| * Returns fragments required by deps. This includes: |
| * |
| * <ul> |
| * <li>Requirements transitively required by deps iff {@link |
| * CoreOptions#includeRequiredConfigFragmentsProvider} is {@link |
| * CoreOptions.IncludeConfigFragmentsEnum#TRANSITIVE}, |
| * <li>Dependencies on Starlark build settings iff {@link |
| * CoreOptions#includeRequiredConfigFragmentsProvider} is not {@link |
| * CoreOptions.IncludeConfigFragmentsEnum#OFF}. These are considered direct requirements on |
| * the rule. |
| * </ul> |
| */ |
| private static ImmutableSet<String> getRequiredFragmentsFromDeps( |
| BuildConfiguration configuration, Iterable<ConfiguredTargetAndData> prerequisites) { |
| |
| TreeSet<String> requiredFragments = new TreeSet<>(); |
| CoreOptions coreOptions = configuration.getOptions().get(CoreOptions.class); |
| if (coreOptions.includeRequiredConfigFragmentsProvider |
| == CoreOptions.IncludeConfigFragmentsEnum.OFF) { |
| return ImmutableSet.of(); |
| } |
| |
| for (ConfiguredTargetAndData prereq : prerequisites) { |
| // If the target depends on a Starlark build setting, conceptually that means it directly |
| // requires that as an option (even though it's technically a dependency). |
| BuildSettingProvider buildSettingProvider = |
| prereq.getConfiguredTarget().getProvider(BuildSettingProvider.class); |
| if (buildSettingProvider != null) { |
| requiredFragments.add(buildSettingProvider.getLabel().toString()); |
| } |
| if (coreOptions.includeRequiredConfigFragmentsProvider |
| == CoreOptions.IncludeConfigFragmentsEnum.TRANSITIVE) { |
| // Add fragments only required because transitive deps need them. |
| RequiredConfigFragmentsProvider depProvider = |
| prereq.getConfiguredTarget().getProvider(RequiredConfigFragmentsProvider.class); |
| if (depProvider != null) { |
| requiredFragments.addAll(depProvider.getRequiredConfigFragments()); |
| } |
| } |
| } |
| |
| return ImmutableSet.copyOf(requiredFragments); |
| } |
| |
| private RequiredFragmentsUtil() {} |
| } |