blob: 7ea8ca96080097522380aaa5eb8cd0bdfec53611 [file] [log] [blame]
// 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() {}
}