// 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.skyframe; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; import com.google.common.base.Supplier; import com.google.common.base.Verify; import com.google.common.base.VerifyException; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.LinkedListMultimap; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.collect.Sets; import com.google.devtools.build.lib.actions.Actions; import com.google.devtools.build.lib.actions.Actions.GeneratingActions; import com.google.devtools.build.lib.actions.MutableActionGraph.ActionConflictException; import com.google.devtools.build.lib.analysis.AspectCollection; import com.google.devtools.build.lib.analysis.AspectCollection.AspectDeps; import com.google.devtools.build.lib.analysis.CachingAnalysisEnvironment; import com.google.devtools.build.lib.analysis.ConfiguredAspect; import com.google.devtools.build.lib.analysis.ConfiguredTarget; import com.google.devtools.build.lib.analysis.ConfiguredTargetFactory; import com.google.devtools.build.lib.analysis.Dependency; import com.google.devtools.build.lib.analysis.DependencyResolver.InconsistentAspectOrderException; import com.google.devtools.build.lib.analysis.LabelAndConfiguration; import com.google.devtools.build.lib.analysis.MergedConfiguredTarget; import com.google.devtools.build.lib.analysis.MergedConfiguredTarget.DuplicateException; import com.google.devtools.build.lib.analysis.TargetAndConfiguration; import com.google.devtools.build.lib.analysis.ToolchainContext; import com.google.devtools.build.lib.analysis.config.BuildConfiguration; import com.google.devtools.build.lib.analysis.config.BuildOptions; import com.google.devtools.build.lib.analysis.config.ConfigMatchingProvider; import com.google.devtools.build.lib.analysis.config.HostTransition; import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException; import com.google.devtools.build.lib.analysis.config.PatchTransition; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.collect.nestedset.NestedSet; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.collect.nestedset.Order; import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; import com.google.devtools.build.lib.events.Event; import com.google.devtools.build.lib.events.StoredEventHandler; import com.google.devtools.build.lib.packages.Aspect; import com.google.devtools.build.lib.packages.AspectDescriptor; import com.google.devtools.build.lib.packages.Attribute; import com.google.devtools.build.lib.packages.BuildType; import com.google.devtools.build.lib.packages.NoSuchTargetException; import com.google.devtools.build.lib.packages.NoSuchThingException; import com.google.devtools.build.lib.packages.Package; import com.google.devtools.build.lib.packages.RawAttributeMapper; import com.google.devtools.build.lib.packages.Rule; import com.google.devtools.build.lib.packages.RuleClassProvider; import com.google.devtools.build.lib.packages.Target; import com.google.devtools.build.lib.packages.TargetUtils; import com.google.devtools.build.lib.skyframe.AspectFunction.AspectCreationException; import com.google.devtools.build.lib.skyframe.AspectValue.AspectKey; import com.google.devtools.build.lib.skyframe.SkyframeExecutor.BuildViewProvider; import com.google.devtools.build.lib.skyframe.ToolchainUtil.ToolchainContextException; import com.google.devtools.build.lib.syntax.EvalException; import com.google.devtools.build.lib.util.OrderedSetMultimap; import com.google.devtools.build.lib.util.Preconditions; import com.google.devtools.build.skyframe.SkyFunction; import com.google.devtools.build.skyframe.SkyFunctionException; import com.google.devtools.build.skyframe.SkyKey; import com.google.devtools.build.skyframe.SkyValue; import com.google.devtools.build.skyframe.ValueOrException; import com.google.devtools.build.skyframe.ValueOrException2; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.Semaphore; import javax.annotation.Nullable; /** * SkyFunction for {@link ConfiguredTargetValue}s. * *

This class, together with {@link AspectFunction} drives the analysis phase. For more * information, see {@link com.google.devtools.build.lib.analysis.RuleConfiguredTargetFactory}. * * @see com.google.devtools.build.lib.analysis.RuleConfiguredTargetFactory */ public final class ConfiguredTargetFunction implements SkyFunction { // This construction is a bit funky, but guarantees that the Object reference here is globally // unique. static final ImmutableMap NO_CONFIG_CONDITIONS = ImmutableMap.of(); /** * Exception class that signals an error during the evaluation of a dependency. */ public static class DependencyEvaluationException extends Exception { public DependencyEvaluationException(InvalidConfigurationException cause) { super(cause); } public DependencyEvaluationException(ConfiguredValueCreationException cause) { super(cause); } public DependencyEvaluationException(InconsistentAspectOrderException cause) { super(cause); } @Override public synchronized Exception getCause() { return (Exception) super.getCause(); } } private final BuildViewProvider buildViewProvider; private final RuleClassProvider ruleClassProvider; private final Semaphore cpuBoundSemaphore; private final Supplier removeActionsAfterEvaluation; ConfiguredTargetFunction( BuildViewProvider buildViewProvider, RuleClassProvider ruleClassProvider, Semaphore cpuBoundSemaphore, Supplier removeActionsAfterEvaluation) { this.buildViewProvider = buildViewProvider; this.ruleClassProvider = ruleClassProvider; this.cpuBoundSemaphore = cpuBoundSemaphore; this.removeActionsAfterEvaluation = Preconditions.checkNotNull(removeActionsAfterEvaluation); } @Override public SkyValue compute(SkyKey key, Environment env) throws ConfiguredTargetFunctionException, InterruptedException { SkyframeBuildView view = buildViewProvider.getSkyframeBuildView(); NestedSetBuilder transitivePackages = NestedSetBuilder.stableOrder(); NestedSetBuilder

Returns null if Skyframe hasn't evaluated the required dependencies yet. In this case, the * caller should also return null to Skyframe. * * @param env the Skyframe environment * @param resolver the dependency resolver * @param ctgValue the label and the configuration of the node * @param aspects * @param configConditions the configuration conditions for evaluating the attributes of the node * @param toolchainContext context information for required toolchains * @param ruleClassProvider rule class provider for determining the right configuration fragments * to apply to deps * @param hostConfiguration the host configuration. There's a noticeable performance hit from * instantiating this on demand for every dependency that wants it, so it's best to compute * the host configuration as early as possible and pass this reference to all consumers */ @Nullable static OrderedSetMultimap computeDependencies( Environment env, SkyframeDependencyResolver resolver, TargetAndConfiguration ctgValue, Iterable aspects, ImmutableMap configConditions, @Nullable ToolchainContext toolchainContext, RuleClassProvider ruleClassProvider, BuildConfiguration hostConfiguration, NestedSetBuilder transitivePackages, NestedSetBuilder

More specifically: given a set of {@link Dependency} instances holding dynamic config * transition requests (e.g. {@link Dependency#hasStaticConfiguration()} == false}), returns * equivalent dependencies containing dynamically created configurations applying those * transitions. If {@link BuildConfiguration.Options#trimConfigurations()} is true, these * configurations only contain the fragments needed by the dep and its transitive closure. Else * the configurations unconditionally include all fragments. * *

This method is heavily performance-optimized. Because it, in aggregate, reads over every * edge in the configured target graph, small inefficiencies can have observable impact on * analysis time. Keep this in mind when making modifications and performance-test any changes you * make. * * @param env Skyframe evaluation environment * @param ctgValue the label and the configuration of the node * @param originalDeps the set of configuration transition requests for this target's attributes * @param hostConfiguration the host configuration * @param ruleClassProvider the rule class provider for determining the right configuration * fragments to apply to deps * * @return a mapping from each attribute to the {@link BuildConfiguration}s and {@link Label}s * to use for that attribute's deps. Returns null if not all Skyframe dependencies are * available yet. */ @Nullable static OrderedSetMultimap getDynamicConfigurations( Environment env, TargetAndConfiguration ctgValue, OrderedSetMultimap originalDeps, BuildConfiguration hostConfiguration, RuleClassProvider ruleClassProvider) throws DependencyEvaluationException, InterruptedException { // Maps each Skyframe-evaluated BuildConfiguration to the dependencies that need that // configuration. For cases where Skyframe isn't needed to get the configuration (e.g. when // we just re-used the original rule's configuration), we should skip this outright. Multimap> keysToEntries = LinkedListMultimap.create(); // Stores the result of applying a dynamic transition to the current configuration using a // particular subset of fragments. By caching this, we save from redundantly computing the // same transition for every dependency edge that requests that transition. This can have // real effect on analysis time for commonly triggered transitions. // // Split transitions may map to multiple values. All other transitions map to one. Map> transitionsMap = new LinkedHashMap<>(); // The fragments used by the current target's configuration. Set> ctgFragments = ctgValue.getConfiguration().fragmentClasses(); BuildOptions ctgOptions = ctgValue.getConfiguration().getOptions(); // Stores the dynamically configured versions of each dependency. This method must preserve the // original label ordering of each attribute. For example, if originalDeps.get("data") is // [":a", ":b"], the dynamic variant must also be [":a", ":b"] in the same order. Because we may // not actualize the results in order (some results need Skyframe-evaluated configurations while // others can be computed trivially), we dump them all into this map, then as a final step // iterate through the original list and pluck out values from here for the final value. // // For split transitions, originaldeps.get("data") = [":a", ":b"] can produce the output // [":a", ":a", ..., ":b", ":b", ...]. All instances of ":a" // still appear before all instances of ":b". But the [":a", ":a""] subset may // be in any (deterministic) order. In particular, this may not be the same order as // SplitTransition.split. If needed, this code can be modified to use that order, but that // involves more runtime in performance-critical code, so we won't make that change without a // clear need. // // This map is used heavily by all builds. Inserts and gets should be as fast as possible. Multimap dynamicDeps = LinkedHashMultimap.create(); // Performance optimization: This method iterates over originalDeps twice. By storing // AttributeAndLabel instances in this list, we avoid having to recreate them the second time // (particularly avoid recomputing their hash codes). Profiling shows this shaves 25% off this // method's execution time (at the time of this comment). ArrayList attributesAndLabels = new ArrayList<>(originalDeps.size()); for (Map.Entry depsEntry : originalDeps.entries()) { Dependency dep = depsEntry.getValue(); AttributeAndLabel attributeAndLabel = new AttributeAndLabel(depsEntry.getKey(), dep.getLabel()); attributesAndLabels.add(attributeAndLabel); // Certain targets (like output files) trivially re-use their input configuration. Likewise, // deps with null configurations (e.g. source files), can be trivially computed. So we skip // all logic in this method for these cases and just reinsert their original configurations // back at the end (note that null-configured targets will have a static // NullConfigurationDependency instead of dynamic // Dependency(label, transition=Attribute.Configuration.Transition.NULL)). // // A *lot* of targets have null deps, so this produces real savings. Profiling tests over a // simple cc_binary show this saves ~1% of total analysis phase time. if (dep.hasStaticConfiguration()) { continue; } // Figure out the required fragments for this dep and its transitive closure. Set> depFragments = getTransitiveFragments(env, dep.getLabel(), ctgValue.getConfiguration()); if (depFragments == null) { return null; } // TODO(gregce): remove the below call once we have confidence dynamic configurations always // provide needed fragments. This unnecessarily drags performance on the critical path (up // to 0.5% of total analysis time as profiled over a simple cc_binary). if (ctgValue.getConfiguration().trimConfigurations()) { checkForMissingFragments(env, ctgValue, attributeAndLabel.attribute.getName(), dep, depFragments); } boolean sameFragments = depFragments.equals(ctgFragments); Attribute.Transition transition = dep.getTransition(); if (sameFragments) { if (transition == Attribute.ConfigurationTransition.NONE) { // The dep uses the same exact configuration. putOnlyEntry( dynamicDeps, attributeAndLabel, Dependency.withConfigurationAndAspects( dep.getLabel(), ctgValue.getConfiguration(), dep.getAspects())); continue; } else if (transition == HostTransition.INSTANCE) { // The current rule's host configuration can also be used for the dep. We short-circuit // the standard transition logic for host transitions because these transitions are // uniquely frequent. It's possible, e.g., for every node in the configured target graph // to incur multiple host transitions. So we aggressively optimize to avoid hurting // analysis time. putOnlyEntry( dynamicDeps, attributeAndLabel, Dependency.withConfigurationAndAspects( dep.getLabel(), hostConfiguration, dep.getAspects())); continue; } } // Apply the transition or use the cached result if it was already applied. FragmentsAndTransition transitionKey = new FragmentsAndTransition(depFragments, transition); List toOptions = transitionsMap.get(transitionKey); if (toOptions == null) { toOptions = getDynamicTransitionOptions(ctgOptions, transition, depFragments, ruleClassProvider, !sameFragments); transitionsMap.put(transitionKey, toOptions); } // If the transition doesn't change the configuration, trivially re-use the original // configuration. if (sameFragments && toOptions.size() == 1 && Iterables.getOnlyElement(toOptions).equals(ctgOptions)) { putOnlyEntry( dynamicDeps, attributeAndLabel, Dependency.withConfigurationAndAspects( dep.getLabel(), ctgValue.getConfiguration(), dep.getAspects())); continue; } // If we get here, we have to get the configuration from Skyframe. for (BuildOptions options : toOptions) { keysToEntries.put(BuildConfigurationValue.key(depFragments, options), depsEntry); } } // Get all BuildConfigurations we need from Skyframe. While not every value might be available, // we don't call env.valuesMissing() here because that could be true from the earlier // resolver.dependentNodeMap call in computeDependencies, which also calls Skyframe. This method // doesn't need those missing values, but it still has to be called after // resolver.dependentNodeMap because it consumes that method's output. The reason the missing // values don't matter is because resolver.dependentNodeMap still returns "partial" results // and this method runs over whatever's available. // // While there would be no *correctness* harm in nulling out early, there's significant // *performance* harm. Profiling shows that putting "if (env.valuesMissing()) { return null; }" // here (or even after resolver.dependentNodeMap) produces a ~30% performance hit on the // analysis phase. That's because resolveConfiguredTargetDependencies and // resolveAspectDependencies don't get a chance to make their own Skyframe requests before // bailing out of this ConfiguredTargetFunction call. Ideally we could batch all requests // from all methods into a single Skyframe call, but there are enough subtle data flow // dependencies in ConfiguredTargetFucntion to make that impractical. Map> depConfigValues = env.getValuesOrThrow(keysToEntries.keySet(), InvalidConfigurationException.class); // Now fill in the remaining unresolved deps with the now-resolved configurations. try { for (Map.Entry> entry : depConfigValues.entrySet()) { SkyKey key = entry.getKey(); ValueOrException valueOrException = entry.getValue(); if (valueOrException.get() == null) { // Instead of env.missingValues(), check for missing values here. This guarantees we only // null out on missing values from *this specific Skyframe request*. return null; } BuildConfigurationValue trimmedConfig = (BuildConfigurationValue) valueOrException.get(); for (Map.Entry info : keysToEntries.get(key)) { Dependency originalDep = info.getValue(); AttributeAndLabel attr = new AttributeAndLabel(info.getKey(), originalDep.getLabel()); Dependency resolvedDep = Dependency.withConfigurationAndAspects(originalDep.getLabel(), trimmedConfig.getConfiguration(), originalDep.getAspects()); if (attr.attribute.hasSplitConfigurationTransition()) { dynamicDeps.put(attr, resolvedDep); } else { putOnlyEntry(dynamicDeps, attr, resolvedDep); } } } } catch (InvalidConfigurationException e) { throw new DependencyEvaluationException(e); } return sortDynamicallyConfiguredDeps(originalDeps, dynamicDeps, attributesAndLabels); } /** * Returns the configuration fragments required by a dep and its transitive closure. * Returns null if Skyframe dependencies aren't yet available. * * @param env Skyframe evaluation environment * @param dep label of the dep to check * @param parentConfig configuration of the rule depending on the dep */ @Nullable private static Set> getTransitiveFragments( Environment env, Label dep, BuildConfiguration parentConfig) throws InterruptedException { if (!parentConfig.trimConfigurations()) { return parentConfig.getAllFragments().keySet(); } SkyKey fragmentsKey = TransitiveTargetValue.key(dep); TransitiveTargetValue transitiveDepInfo = (TransitiveTargetValue) env.getValue(fragmentsKey); if (transitiveDepInfo == null) { // This should only be possible for tests. In actual runs, this was already called // as a routine part of the loading phase. // TODO(bazel-team): check this only occurs in a test context. return null; } return transitiveDepInfo.getTransitiveConfigFragments().toSet(); } /** * Applies a dynamic configuration transition over a set of build options. * * @return the build options for the transitioned configuration. If trimResults is true, * only options needed by the required fragments are included. Else the same options as the * original input are included (with different possible values, of course). */ static List getDynamicTransitionOptions(BuildOptions fromOptions, Attribute.Transition transition, Iterable> requiredFragments, RuleClassProvider ruleClassProvider, boolean trimResults) { List result; if (transition == Attribute.ConfigurationTransition.NONE) { result = ImmutableList.of(fromOptions); } else if (transition instanceof PatchTransition) { // TODO(bazel-team): safety-check that this never mutates fromOptions. result = ImmutableList.of(((PatchTransition) transition).apply(fromOptions)); } else if (transition instanceof Attribute.SplitTransition) { @SuppressWarnings("unchecked") // Attribute.java doesn't have the BuildOptions symbol. List toOptions = ((Attribute.SplitTransition) transition).split(fromOptions); if (toOptions.isEmpty()) { // When the split returns an empty list, it's signaling it doesn't apply to this instance. // Check that it's safe to skip the transition and return the original options. Verify.verify(transition.defaultsToSelf()); result = ImmutableList.of(fromOptions); } else { result = toOptions; } } else { throw new IllegalStateException(String.format( "unsupported dynamic transition type: %s", transition.getClass().getName())); } if (!trimResults) { return result; } else { ImmutableList.Builder trimmedOptions = ImmutableList.builder(); for (BuildOptions toOptions : result) { trimmedOptions.add(toOptions.trim( BuildConfiguration.getOptionsClasses(requiredFragments, ruleClassProvider))); } return trimmedOptions.build(); } } /** * Diagnostic helper method for dynamic configurations: checks the config fragments required by * a dep against the fragments in its actual configuration. If any are missing, triggers a * descriptive "missing fragments" error. */ private static void checkForMissingFragments(Environment env, TargetAndConfiguration ctgValue, String attribute, Dependency dep, Set> expectedDepFragments) throws DependencyEvaluationException { Set ctgFragmentNames = new HashSet<>(); for (BuildConfiguration.Fragment fragment : ctgValue.getConfiguration().getAllFragments().values()) { ctgFragmentNames.add(fragment.getClass().getSimpleName()); } Set depFragmentNames = new HashSet<>(); for (Class fragmentClass : expectedDepFragments) { depFragmentNames.add(fragmentClass.getSimpleName()); } Set missing = Sets.difference(depFragmentNames, ctgFragmentNames); if (!missing.isEmpty()) { String msg = String.format( "%s: dependency %s from attribute \"%s\" is missing required config fragments: %s", ctgValue.getLabel(), dep.getLabel(), attribute, Joiner.on(", ").join(missing)); env.getListener().handle(Event.error(msg)); throw new DependencyEvaluationException(new InvalidConfigurationException(msg)); } } /** * Determines the output ordering of each -> * [dep, dep, ...] collection produced by a split transition. */ @VisibleForTesting static final Comparator DYNAMIC_SPLIT_DEP_ORDERING = new Comparator() { @Override public int compare(Dependency d1, Dependency d2) { return d1.getConfiguration().getMnemonic().compareTo(d2.getConfiguration().getMnemonic()); } }; /** * Helper method for {@link #getDynamicConfigurations}: returns a copy of the output deps * using the same key and value ordering as the input deps. * * @param originalDeps the input deps with the ordering to preserve * @param dynamicDeps the unordered output deps * @param attributesAndLabels collection of pairs guaranteed to match * the ordering of originalDeps.entries(). This is a performance optimization: see * {@link #getDynamicConfigurations#attributesAndLabels} for details. */ private static OrderedSetMultimap sortDynamicallyConfiguredDeps( OrderedSetMultimap originalDeps, Multimap dynamicDeps, ArrayList attributesAndLabels) { Iterator iterator = attributesAndLabels.iterator(); OrderedSetMultimap result = OrderedSetMultimap.create(); for (Map.Entry depsEntry : originalDeps.entries()) { AttributeAndLabel attrAndLabel = iterator.next(); if (depsEntry.getValue().hasStaticConfiguration()) { result.put(attrAndLabel.attribute, depsEntry.getValue()); } else { Collection dynamicAttrDeps = dynamicDeps.get(attrAndLabel); Verify.verify(!dynamicAttrDeps.isEmpty()); if (dynamicAttrDeps.size() > 1) { List sortedSplitList = new ArrayList<>(dynamicAttrDeps); Collections.sort(sortedSplitList, DYNAMIC_SPLIT_DEP_ORDERING); dynamicAttrDeps = sortedSplitList; } result.putAll(depsEntry.getKey(), dynamicAttrDeps); } } return result; } /** * Merges the each direct dependency configured target with the aspects associated with it. * *

Note that the combination of a configured target and its associated aspects are not * represented by a Skyframe node. This is because there can possibly be many different * combinations of aspects for a particular configured target, so it would result in a * combinatiorial explosion of Skyframe nodes. */ private static OrderedSetMultimap mergeAspects( OrderedSetMultimap depValueNames, Map depConfiguredTargetMap, OrderedSetMultimap depAspectMap) throws DuplicateException { OrderedSetMultimap result = OrderedSetMultimap.create(); for (Map.Entry entry : depValueNames.entries()) { Dependency dep = entry.getValue(); SkyKey depKey = ConfiguredTargetValue.key(dep.getLabel(), dep.getConfiguration()); ConfiguredTarget depConfiguredTarget = depConfiguredTargetMap.get(depKey); result.put(entry.getKey(), MergedConfiguredTarget.of(depConfiguredTarget, depAspectMap.get(dep))); } return result; } /** * Given a list of {@link Dependency} objects, returns a multimap from the * {@link Dependency}s too the {@link ConfiguredAspect} instances that should be merged into it. * *

Returns null if the required aspects are not computed yet. */ @Nullable private static OrderedSetMultimap resolveAspectDependencies( Environment env, Map configuredTargetMap, Iterable deps, NestedSetBuilder transitivePackages) throws AspectCreationException, InterruptedException { OrderedSetMultimap result = OrderedSetMultimap.create(); Set allAspectKeys = new HashSet<>(); for (Dependency dep : deps) { allAspectKeys.addAll(getAspectKeys(dep).values()); } Map> depAspects = env.getValuesOrThrow(allAspectKeys, AspectCreationException.class, NoSuchThingException.class); for (Dependency dep : deps) { Map aspectToKeys = getAspectKeys(dep); for (AspectDeps depAspect : dep.getAspects().getVisibleAspects()) { SkyKey aspectKey = aspectToKeys.get(depAspect.getAspect()); AspectValue aspectValue; try { // TODO(ulfjack): Catch all thrown AspectCreationException and NoSuchThingException // instances and merge them into a single Exception to get full root cause data. aspectValue = (AspectValue) depAspects.get(aspectKey).get(); } catch (NoSuchThingException e) { throw new AspectCreationException( String.format( "Evaluation of aspect %s on %s failed: %s", depAspect.getAspect().getAspectClass().getName(), dep.getLabel(), e.toString())); } if (aspectValue == null) { // Dependent aspect has either not been computed yet or is in error. return null; } // Validate that aspect is applicable to "bare" configured target. ConfiguredTarget associatedTarget = configuredTargetMap .get(ConfiguredTargetValue.key(dep.getLabel(), dep.getConfiguration())); if (!aspectMatchesConfiguredTarget(associatedTarget, aspectValue.getAspect())) { continue; } result.put(dep, aspectValue.getConfiguredAspect()); transitivePackages.addTransitive(aspectValue.getTransitivePackages()); } } return result; } private static Map getAspectKeys(Dependency dep) { HashMap result = new HashMap<>(); AspectCollection aspects = dep.getAspects(); for (AspectDeps aspectDeps : aspects.getVisibleAspects()) { buildAspectKey(aspectDeps, result, dep); } return result; } private static AspectKey buildAspectKey(AspectDeps aspectDeps, HashMap result, Dependency dep) { if (result.containsKey(aspectDeps.getAspect())) { return (AspectKey) result.get(aspectDeps.getAspect()).argument(); } ImmutableList.Builder dependentAspects = ImmutableList.builder(); for (AspectDeps path : aspectDeps.getDependentAspects()) { dependentAspects.add(buildAspectKey(path, result, dep)); } AspectKey aspectKey = AspectValue.createAspectKey( dep.getLabel(), dep.getConfiguration(), dependentAspects.build(), aspectDeps.getAspect(), dep.getAspectConfiguration(aspectDeps.getAspect())); result.put(aspectKey.getAspectDescriptor(), aspectKey.getSkyKey()); return aspectKey; } static boolean aspectMatchesConfiguredTarget(final ConfiguredTarget dep, Aspect aspect) { if (!aspect.getDefinition().applyToFiles() && !(dep.getTarget() instanceof Rule)) { return false; } if (dep.getTarget().getAssociatedRule() == null) { // even aspects that 'apply to files' cannot apply to input files. return false; } return dep.satisfies(aspect.getDefinition().getRequiredProviders()); } /** * Returns the set of {@link ConfigMatchingProvider}s that key the configurable attributes used by * this rule. * *

>If the configured targets supplying those providers aren't yet resolved by the dependency * resolver, returns null. */ @Nullable static ImmutableMap getConfigConditions( Target target, Environment env, SkyframeDependencyResolver resolver, TargetAndConfiguration ctgValue, NestedSetBuilder transitivePackages, NestedSetBuilder

Returns null if not all instances are available yet. */ @Nullable private static Map resolveConfiguredTargetDependencies( Environment env, Collection deps, NestedSetBuilder transitivePackages, NestedSetBuilder