| // 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.devtools.build.lib.analysis.config.ConfigurationEnvironment; |
| import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException; |
| import com.google.devtools.build.lib.cmdline.Label; |
| import com.google.devtools.build.lib.packages.AbstractAttributeMapper; |
| import com.google.devtools.build.lib.packages.BuildType; |
| import com.google.devtools.build.lib.packages.NoSuchThingException; |
| import com.google.devtools.build.lib.packages.Rule; |
| import com.google.devtools.build.lib.packages.Target; |
| import com.google.devtools.build.lib.syntax.Type; |
| import java.util.HashSet; |
| import java.util.Set; |
| import javax.annotation.Nullable; |
| |
| /** |
| * Tool for chasing redirects. This is intended to be used during configuration creation. |
| */ |
| public final class RedirectChaser { |
| |
| /** |
| * Custom attribute mapper that throws an exception if an attribute's value depends on the |
| * build configuration. |
| */ |
| private static class StaticValuedAttributeMapper extends AbstractAttributeMapper { |
| public StaticValuedAttributeMapper(Rule rule) { |
| super(rule.getPackage(), rule.getRuleClassObject(), rule.getLabel(), |
| rule.getAttributeContainer()); |
| } |
| |
| /** |
| * Returns the value of the given attribute. |
| * |
| * @throws InvalidConfigurationException if the value is configuration-dependent |
| */ |
| public <T> T getAndValidate(String attributeName, Type<T> type) |
| throws InvalidConfigurationException { |
| if (getSelectorList(attributeName, type) != null) { |
| throw new InvalidConfigurationException |
| ("The value of '" + attributeName + "' cannot be configuration-dependent"); |
| } |
| return super.get(attributeName, type); |
| } |
| } |
| |
| /** |
| * Follows the 'srcs' attribute of the given label recursively. Keeps repeating as long as the |
| * labels are either <code>alias</code> or <code>bind</code> rules. |
| * |
| * @param env for loading the packages |
| * @param label the label to start at |
| * @param name user-meaningful description of the content being resolved |
| * @return the label which cannot be further resolved |
| * @throws InvalidConfigurationException if something goes wrong |
| */ |
| @Nullable |
| public static Label followRedirects(ConfigurationEnvironment env, Label label, String name) |
| throws InvalidConfigurationException, InterruptedException { |
| Label oldLabel = null; |
| Set<Label> visitedLabels = new HashSet<>(); |
| visitedLabels.add(label); |
| try { |
| while (true) { |
| Target possibleRedirect = env.getTarget(label); |
| if (possibleRedirect == null) { |
| return null; |
| } |
| Label newLabel = getBindOrAliasRedirect(possibleRedirect); |
| if (newLabel == null) { |
| return label; |
| } |
| |
| newLabel = label.resolveRepositoryRelative(newLabel); |
| oldLabel = label; |
| label = newLabel; |
| if (!visitedLabels.add(label)) { |
| throw new InvalidConfigurationException("The " + name + " points to a rule which " |
| + "recursively references itself. The label " + label + " is part of the loop"); |
| } |
| } |
| } catch (NoSuchThingException e) { |
| String prefix = oldLabel == null |
| ? "" |
| : "in target '" + oldLabel + "': "; |
| throw new InvalidConfigurationException(prefix + e.getMessage(), e); |
| } |
| } |
| |
| private static Label getBindOrAliasRedirect(Target target) |
| throws InvalidConfigurationException { |
| if (!(target instanceof Rule)) { |
| return null; |
| } |
| |
| Rule rule = (Rule) target; |
| if (!rule.getRuleClass().equals("bind") && !rule.getRuleClass().equals("alias")) { |
| return null; |
| } |
| |
| return new StaticValuedAttributeMapper(rule).getAndValidate("actual", BuildType.LABEL); |
| } |
| } |