// 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 com.google.common.base.Verify; import com.google.common.collect.ArrayListMultimap; 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.ListMultimap; import com.google.common.collect.Sets; import com.google.devtools.build.lib.analysis.config.BuildConfiguration; import com.google.devtools.build.lib.analysis.config.ConfigMatchingProvider; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.packages.Aspect; import com.google.devtools.build.lib.packages.AspectClass; import com.google.devtools.build.lib.packages.Attribute; import com.google.devtools.build.lib.packages.Attribute.LateBoundDefault; import com.google.devtools.build.lib.packages.AttributeMap; import com.google.devtools.build.lib.packages.BuildType; import com.google.devtools.build.lib.packages.EnvironmentGroup; import com.google.devtools.build.lib.packages.InputFile; import com.google.devtools.build.lib.packages.NativeAspectClass; import com.google.devtools.build.lib.packages.NoSuchThingException; import com.google.devtools.build.lib.packages.OutputFile; import com.google.devtools.build.lib.packages.PackageGroup; import com.google.devtools.build.lib.packages.Rule; import com.google.devtools.build.lib.packages.RuleClass; import com.google.devtools.build.lib.packages.Target; import com.google.devtools.build.lib.syntax.EvalException; import com.google.devtools.build.lib.syntax.EvalUtils; import com.google.devtools.build.lib.util.Preconditions; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.annotation.Nullable; /** * Resolver for dependencies between configured targets. * *

Includes logic to derive the right configurations depending on transition type. */ public abstract class DependencyResolver { protected DependencyResolver() { } /** * Returns ids for dependent nodes of a given node, sorted by attribute. Note that some * dependencies do not have a corresponding attribute here, and we use the null attribute to * represent those edges. Visibility attributes are only visited if {@code visitVisibility} is * {@code true}. * *

If {@code aspect} is null, returns the dependent nodes of the configured * target node representing the given target and configuration, otherwise that of the aspect * node accompanying the aforementioned configured target node for the specified aspect. * *

The values are not simply labels because this also implements the first step of applying * configuration transitions, namely, split transitions. This needs to be done before the labels * are resolved because late bound attributes depend on the configuration. A good example for this * is @{code :cc_toolchain}. * *

The long-term goal is that most configuration transitions be applied here. However, in order * to do that, we first have to eliminate transitions that depend on the rule class of the * dependency. * * @param node the target/configuration being evaluated * @param hostConfig the configuration this target would use if it was evaluated as a host * tool. This is needed to support {@link LateBoundDefault#useHostConfiguration()}. * @param aspect the aspect applied to this target (if any) * @param configConditions resolver for config_setting labels * * @return a mapping of each attribute in this rule or aspect to its dependent nodes */ public final ListMultimap dependentNodeMap( TargetAndConfiguration node, BuildConfiguration hostConfig, @Nullable Aspect aspect, ImmutableMap configConditions) throws EvalException, InterruptedException { NestedSetBuilder

If {@code aspect} is null, returns the dependent nodes of the configured * target node representing the given target and configuration, otherwise that of the aspect * node accompanying the aforementioned configured target node for the specified aspect. * *

The values are not simply labels because this also implements the first step of applying * configuration transitions, namely, split transitions. This needs to be done before the labels * are resolved because late bound attributes depend on the configuration. A good example for this * is @{code :cc_toolchain}. * *

The long-term goal is that most configuration transitions be applied here. However, in order * to do that, we first have to eliminate transitions that depend on the rule class of the * dependency. * * @param node the target/configuration being evaluated * @param hostConfig the configuration this target would use if it was evaluated as a host * tool. This is needed to support {@link LateBoundDefault#useHostConfiguration()}. * @param aspect the aspect applied to this target (if any) * @param configConditions resolver for config_setting labels * @param rootCauses collector for dep labels that can't be (loading phase) loaded * * @return a mapping of each attribute in this rule or aspect to its dependent nodes */ public final ListMultimap dependentNodeMap( TargetAndConfiguration node, BuildConfiguration hostConfig, @Nullable Aspect aspect, ImmutableMap configConditions, NestedSetBuilder

Late-bound attributes need special handling because they require configuration * transitions to determine their values. * *

In other words, the normal process of dependency resolution is: *

    *
  1. Find every label value in the rule's attributes
  2. *
  3. Apply configuration transitions over each value to get its dep configuration *
  4. Return each value with its dep configuration
  5. *
* * This doesn't work for late-bound attributes because you can't get their values without * knowing the configuration first. And that configuration may not be the owning rule's * configuration. Specifically, {@link LateBoundDefault#useHostConfiguration()} switches to the * host config and late-bound split attributes branch into multiple split configs. * *

This method implements that logic and makes sure the normal configuration * transition logic mixes with it cleanly. * * @param depResolver the resolver for this rule's deps * @param ruleConfig the rule's configuration * @param hostConfig the equivalent host configuration */ private void resolveLateBoundAttributes( RuleResolver depResolver, BuildConfiguration ruleConfig, BuildConfiguration hostConfig) throws EvalException, InterruptedException { ConfiguredAttributeMapper attributeMap = depResolver.attributeMap; for (Attribute attribute : depResolver.attributes) { if (!attribute.isLateBound() || !attribute.getCondition().apply(attributeMap)) { continue; } @SuppressWarnings("unchecked") LateBoundDefault lateBoundDefault = (LateBoundDefault) attribute.getLateBoundDefault(); if (attribute.hasSplitConfigurationTransition()) { // Late-bound attribute with a split transition: // Since we want to get the same results as BuildConfiguration.evaluateTransition (but // skip it since we've already applied the split), we want to make sure this logic // doesn't do anything differently. evaluateTransition has additional logic // for host configs and attributes with configurators. So we check here that neither of // of those apply, in the name of keeping the fork as simple as possible. Verify.verify(attribute.getConfigurator() == null); Verify.verify(!lateBoundDefault.useHostConfiguration()); for (BuildConfiguration splitConfig : ruleConfig.getSplitConfigurations( attribute.getSplitTransition(depResolver.rule))) { // TODO(gregce): support dynamic split transitions for (Label dep : resolveLateBoundAttribute( depResolver.rule, attribute, splitConfig, attributeMap)) { // Skip the normal config transition pipeline and directly feed the split config. This // is because the split already had to be applied to determine the attribute's value. // This makes the split logic in the normal pipeline redundant and potentially // incorrect. depResolver.resolveDep(attribute, dep, splitConfig); } } } else { // Late-bound attribute without a split transition: for (Label dep : resolveLateBoundAttribute(depResolver.rule, attribute, lateBoundDefault.useHostConfiguration() ? hostConfig : ruleConfig, attributeMap)) { // Process this dep like a normal attribute. depResolver.resolveDep(attribute, dep); } } } } /** * Returns the label dependencies for the given late-bound attribute in this rule. * * @param rule the rule being evaluated * @param attribute the attribute to evaluate * @param config the configuration to evaluate the attribute in * @param attributeMap mapper to attribute values */ private Iterable

The main difference between the two is that the latter applies configuration transitions, * i.e. it specifies not just which deps a rule has but also the configurations those deps * should take. */ private class RuleResolver { private final Rule rule; private final BuildConfiguration ruleConfig; private final Aspect aspect; private final ConfiguredAttributeMapper attributeMap; private final NestedSetBuilder

Use this method with care: it skips Bazel's standard config transition semantics * ({@link BuildConfiguration#evaluateTransition}). That means attributes passed through here * won't obey standard rules on which configurations apply to their deps. This should only * be done for special circumstances that really justify the difference. When in doubt, use * {@link #resolveDep(Attribute, Label)}. */ void resolveDep(Attribute attribute, Label depLabel, BuildConfiguration config) { Target toTarget = getTarget(rule, depLabel, rootCauses); if (toTarget == null) { return; // Skip this round: this is either a loading error or unevaluated Skyframe dep. } BuildConfiguration.TransitionApplier transitionApplier = config.getTransitionApplier(); if (BuildConfiguration.usesNullConfiguration(toTarget)) { transitionApplier.applyTransition(Attribute.ConfigurationTransition.NULL); } Dependency dep = Iterables.getOnlyElement(transitionApplier.getDependencies(depLabel, requiredAspects(aspect, attribute, toTarget, rule))); outgoingEdges.put(attribute, dep); } } private void visitTargetVisibility(TargetAndConfiguration node, NestedSetBuilder

Returns null if the target is not ready to be returned at this moment. If getTarget returns * null once or more during a {@link #dependentNodeMap} call, the results of that call will be * incomplete. For use within Skyframe, where several iterations may be needed to discover * all dependencies. */ @Nullable protected abstract Target getTarget(Target from, Label label, NestedSetBuilder