Damien Martin-Guillerez | f88f4d8 | 2015-09-25 13:56:55 +0000 | [diff] [blame] | 1 | // Copyright 2014 The Bazel Authors. All rights reserved. |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
| 15 | package com.google.devtools.build.lib.analysis; |
| 16 | |
gregce | 731900f | 2018-08-01 13:12:39 -0700 | [diff] [blame] | 17 | import com.google.common.annotations.VisibleForTesting; |
juliexxia | 8ee423d | 2017-10-18 16:36:57 -0400 | [diff] [blame] | 18 | import com.google.common.collect.ArrayListMultimap; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 19 | import com.google.common.collect.ImmutableList; |
| 20 | import com.google.common.collect.Iterables; |
juliexxia | 8ee423d | 2017-10-18 16:36:57 -0400 | [diff] [blame] | 21 | import com.google.common.collect.Multimap; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 22 | import com.google.devtools.build.lib.analysis.config.BuildConfiguration; |
juliexxia | 8ee423d | 2017-10-18 16:36:57 -0400 | [diff] [blame] | 23 | import com.google.devtools.build.lib.analysis.config.BuildConfigurationCollection; |
| 24 | import com.google.devtools.build.lib.analysis.config.ConfigurationResolver; |
juliexxia | b75e4530 | 2019-05-24 07:48:39 -0700 | [diff] [blame] | 25 | import com.google.devtools.build.lib.analysis.config.ConfigurationResolver.TopLevelTargetsAndConfigsResult; |
schmitt | 3e77024 | 2019-04-22 14:12:24 -0700 | [diff] [blame] | 26 | import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException; |
juliexxia | 8ee423d | 2017-10-18 16:36:57 -0400 | [diff] [blame] | 27 | import com.google.devtools.build.lib.analysis.config.TransitionResolver; |
lberki | a1f2ddb | 2019-03-04 23:43:25 -0800 | [diff] [blame] | 28 | import com.google.devtools.build.lib.analysis.config.transitions.ConfigurationTransition; |
| 29 | import com.google.devtools.build.lib.analysis.config.transitions.NoTransition; |
Lukacs Berki | 6e91eb9 | 2015-09-21 09:12:37 +0000 | [diff] [blame] | 30 | import com.google.devtools.build.lib.cmdline.Label; |
juliexxia | 8ee423d | 2017-10-18 16:36:57 -0400 | [diff] [blame] | 31 | import com.google.devtools.build.lib.events.ExtendedEventHandler; |
Lukacs Berki | ffa73ad | 2015-09-18 11:40:12 +0000 | [diff] [blame] | 32 | import com.google.devtools.build.lib.packages.BuildType; |
cparsons | 28cc833 | 2018-05-22 14:00:22 -0700 | [diff] [blame] | 33 | import com.google.devtools.build.lib.packages.BuiltinProvider; |
cparsons | 4ebf6c0 | 2018-08-17 14:49:36 -0700 | [diff] [blame] | 34 | import com.google.devtools.build.lib.packages.InfoInterface; |
dslomov | de965ac | 2017-07-31 21:07:51 +0200 | [diff] [blame] | 35 | import com.google.devtools.build.lib.packages.NativeProvider; |
juliexxia | 8ee423d | 2017-10-18 16:36:57 -0400 | [diff] [blame] | 36 | import com.google.devtools.build.lib.packages.Target; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 37 | import com.google.devtools.build.lib.packages.TriState; |
juliexxia | 8ee423d | 2017-10-18 16:36:57 -0400 | [diff] [blame] | 38 | import com.google.devtools.build.lib.skyframe.SkyframeExecutor; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 39 | import com.google.devtools.build.lib.vfs.PathFragment; |
juliexxia | 8ee423d | 2017-10-18 16:36:57 -0400 | [diff] [blame] | 40 | import java.util.Collection; |
| 41 | import java.util.LinkedHashSet; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 42 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 43 | /** |
| 44 | * Utility functions for use during analysis. |
| 45 | */ |
| 46 | public final class AnalysisUtils { |
| 47 | |
| 48 | private AnalysisUtils() { |
| 49 | throw new IllegalStateException(); // utility class |
| 50 | } |
| 51 | |
| 52 | /** |
| 53 | * Returns whether link stamping is enabled for a rule. |
| 54 | * |
jcater | 7f4f120 | 2019-12-02 13:18:15 -0800 | [diff] [blame] | 55 | * <p>This returns false for unstampable rule classes and for rules used to build tools. Otherwise |
| 56 | * it returns the value of the stamp attribute, or of the stamp option if the attribute value is |
| 57 | * -1. |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 58 | */ |
Googler | 38ad0bf | 2016-07-01 05:00:14 +0000 | [diff] [blame] | 59 | public static boolean isStampingEnabled(RuleContext ruleContext, BuildConfiguration config) { |
jcater | 7f4f120 | 2019-12-02 13:18:15 -0800 | [diff] [blame] | 60 | if (config.isToolConfiguration() |
Lukacs Berki | ffa73ad | 2015-09-18 11:40:12 +0000 | [diff] [blame] | 61 | || !ruleContext.attributes().has("stamp", BuildType.TRISTATE)) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 62 | return false; |
| 63 | } |
Lukacs Berki | ffa73ad | 2015-09-18 11:40:12 +0000 | [diff] [blame] | 64 | TriState stamp = ruleContext.attributes().get("stamp", BuildType.TRISTATE); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 65 | return stamp == TriState.YES || (stamp == TriState.AUTO && config.stampBinaries()); |
| 66 | } |
| 67 | |
Googler | 38ad0bf | 2016-07-01 05:00:14 +0000 | [diff] [blame] | 68 | public static boolean isStampingEnabled(RuleContext ruleContext) { |
| 69 | return isStampingEnabled(ruleContext, ruleContext.getConfiguration()); |
| 70 | } |
| 71 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 72 | // TODO(bazel-team): These need Iterable<? extends TransitiveInfoCollection> because they need to |
| 73 | // be called with Iterable<ConfiguredTarget>. Once the configured target lockdown is complete, we |
| 74 | // can eliminate the "extends" clauses. |
| 75 | /** |
| 76 | * Returns the list of providers of the specified type from a set of transitive info |
| 77 | * collections. |
| 78 | */ |
| 79 | public static <C extends TransitiveInfoProvider> Iterable<C> getProviders( |
| 80 | Iterable<? extends TransitiveInfoCollection> prerequisites, Class<C> provider) { |
Carmi Grushko | 175f911 | 2016-05-30 22:21:39 +0000 | [diff] [blame] | 81 | ImmutableList.Builder<C> result = ImmutableList.builder(); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 82 | for (TransitiveInfoCollection prerequisite : prerequisites) { |
| 83 | C prerequisiteProvider = prerequisite.getProvider(provider); |
| 84 | if (prerequisiteProvider != null) { |
| 85 | result.add(prerequisiteProvider); |
| 86 | } |
| 87 | } |
Carmi Grushko | 175f911 | 2016-05-30 22:21:39 +0000 | [diff] [blame] | 88 | return result.build(); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 89 | } |
| 90 | |
| 91 | /** |
Sergio Campama | fd93143 | 2016-12-09 21:47:35 +0000 | [diff] [blame] | 92 | * Returns the list of declared providers (native and Skylark) of the specified Skylark key from a |
| 93 | * set of transitive info collections. |
| 94 | */ |
cparsons | 4ebf6c0 | 2018-08-17 14:49:36 -0700 | [diff] [blame] | 95 | public static <T extends InfoInterface> Iterable<T> getProviders( |
Sergio Campama | fd93143 | 2016-12-09 21:47:35 +0000 | [diff] [blame] | 96 | Iterable<? extends TransitiveInfoCollection> prerequisites, |
dslomov | de965ac | 2017-07-31 21:07:51 +0200 | [diff] [blame] | 97 | final NativeProvider<T> skylarkKey) { |
dslomov | f969734 | 2017-05-02 16:26:39 +0200 | [diff] [blame] | 98 | ImmutableList.Builder<T> result = ImmutableList.builder(); |
Sergio Campama | fd93143 | 2016-12-09 21:47:35 +0000 | [diff] [blame] | 99 | for (TransitiveInfoCollection prerequisite : prerequisites) { |
dslomov | 77baa4c | 2017-07-10 17:15:27 +0200 | [diff] [blame] | 100 | T prerequisiteProvider = prerequisite.get(skylarkKey); |
Sergio Campama | fd93143 | 2016-12-09 21:47:35 +0000 | [diff] [blame] | 101 | if (prerequisiteProvider != null) { |
dslomov | 77baa4c | 2017-07-10 17:15:27 +0200 | [diff] [blame] | 102 | result.add(prerequisiteProvider); |
Sergio Campama | fd93143 | 2016-12-09 21:47:35 +0000 | [diff] [blame] | 103 | } |
| 104 | } |
| 105 | return result.build(); |
| 106 | } |
| 107 | |
| 108 | /** |
cparsons | 28cc833 | 2018-05-22 14:00:22 -0700 | [diff] [blame] | 109 | * Returns the list of declared providers (native and Skylark) of the specified Skylark key from a |
| 110 | * set of transitive info collections. |
| 111 | */ |
cparsons | 4ebf6c0 | 2018-08-17 14:49:36 -0700 | [diff] [blame] | 112 | public static <T extends InfoInterface> Iterable<T> getProviders( |
cparsons | 28cc833 | 2018-05-22 14:00:22 -0700 | [diff] [blame] | 113 | Iterable<? extends TransitiveInfoCollection> prerequisites, |
| 114 | final BuiltinProvider<T> skylarkKey) { |
| 115 | ImmutableList.Builder<T> result = ImmutableList.builder(); |
| 116 | for (TransitiveInfoCollection prerequisite : prerequisites) { |
| 117 | T prerequisiteProvider = prerequisite.get(skylarkKey); |
| 118 | if (prerequisiteProvider != null) { |
| 119 | result.add(prerequisiteProvider); |
| 120 | } |
| 121 | } |
| 122 | return result.build(); |
| 123 | } |
| 124 | |
| 125 | /** |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 126 | * Returns the iterable of collections that have the specified provider. |
| 127 | */ |
| 128 | public static <S extends TransitiveInfoCollection, C extends TransitiveInfoProvider> Iterable<S> |
| 129 | filterByProvider(Iterable<S> prerequisites, final Class<C> provider) { |
dslomov | 73527c3 | 2017-07-27 17:35:46 +0200 | [diff] [blame] | 130 | return Iterables.filter(prerequisites, target -> target.getProvider(provider) != null); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 131 | } |
| 132 | |
dslomov | de965ac | 2017-07-31 21:07:51 +0200 | [diff] [blame] | 133 | /** Returns the iterable of collections that have the specified provider. */ |
cparsons | 4ebf6c0 | 2018-08-17 14:49:36 -0700 | [diff] [blame] | 134 | public static <S extends TransitiveInfoCollection, C extends InfoInterface> Iterable<S> |
| 135 | filterByProvider(Iterable<S> prerequisites, final NativeProvider<C> provider) { |
dslomov | 73527c3 | 2017-07-27 17:35:46 +0200 | [diff] [blame] | 136 | return Iterables.filter(prerequisites, target -> target.get(provider) != null); |
| 137 | } |
| 138 | |
Googler | 7ac7723 | 2019-06-04 14:26:47 -0700 | [diff] [blame] | 139 | /** Returns the iterable of collections that have the specified provider. */ |
| 140 | public static <S extends TransitiveInfoCollection, C extends InfoInterface> |
| 141 | Iterable<S> filterByProvider(Iterable<S> prerequisites, final BuiltinProvider<C> provider) { |
| 142 | return Iterables.filter(prerequisites, target -> target.get(provider) != null); |
| 143 | } |
dslomov | 73527c3 | 2017-07-27 17:35:46 +0200 | [diff] [blame] | 144 | |
| 145 | /** |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 146 | * Returns the path of the associated manifest file for the path of a Fileset. Works for both |
| 147 | * exec paths and root relative paths. |
| 148 | */ |
| 149 | public static PathFragment getManifestPathFromFilesetPath(PathFragment filesetDir) { |
| 150 | PathFragment manifestDir = filesetDir.replaceName("_" + filesetDir.getBaseName()); |
| 151 | PathFragment outputManifestFrag = manifestDir.getRelative("MANIFEST"); |
| 152 | return outputManifestFrag; |
| 153 | } |
| 154 | |
| 155 | /** |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 156 | * Returns a path fragment qualified by the rule name and unique fragment to |
| 157 | * disambiguate artifacts produced from the source file appearing in |
| 158 | * multiple rules. |
| 159 | * |
| 160 | * <p>For example "//pkg:target" -> "pkg/<fragment>/target. |
| 161 | */ |
| 162 | public static PathFragment getUniqueDirectory(Label label, PathFragment fragment) { |
Dmitry Lomov | e36a66c | 2017-02-17 14:48:48 +0000 | [diff] [blame] | 163 | return label.getPackageIdentifier().getSourceRoot().getRelative(fragment) |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 164 | .getRelative(label.getName()); |
| 165 | } |
| 166 | |
| 167 | /** |
| 168 | * Checks that the given provider class either refers to an interface or to a value class. |
| 169 | */ |
| 170 | public static <T extends TransitiveInfoProvider> void checkProvider(Class<T> clazz) { |
Googler | cecca15 | 2016-06-20 22:51:10 +0000 | [diff] [blame] | 171 | // Write this check in terms of getName() rather than getSimpleName(); the latter is expensive. |
| 172 | if (!clazz.isInterface() && clazz.getName().contains(".AutoValue_")) { |
| 173 | // We must have a superclass due to the generic bound above. |
| 174 | throw new IllegalArgumentException( |
| 175 | clazz + " is generated by @AutoValue; use " + clazz.getSuperclass() + " instead"); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 176 | } |
| 177 | } |
juliexxia | 8ee423d | 2017-10-18 16:36:57 -0400 | [diff] [blame] | 178 | |
| 179 | /** |
| 180 | * Given a set of *top-level* targets and a configuration collection, evaluate top level |
| 181 | * transitions, resolve configurations and return the appropriate <Target, Configuration> pair for |
| 182 | * each target. |
| 183 | * |
| 184 | * <p>Preserves the original input ordering. |
| 185 | */ |
ulfjack | 36fbbde | 2018-08-02 05:16:00 -0700 | [diff] [blame] | 186 | // Keep this in sync with PrepareAnalysisPhaseFunction. |
juliexxia | b75e4530 | 2019-05-24 07:48:39 -0700 | [diff] [blame] | 187 | public static TopLevelTargetsAndConfigsResult getTargetsWithConfigs( |
juliexxia | 8ee423d | 2017-10-18 16:36:57 -0400 | [diff] [blame] | 188 | BuildConfigurationCollection configurations, |
| 189 | Collection<Target> targets, |
| 190 | ExtendedEventHandler eventHandler, |
| 191 | ConfiguredRuleClassProvider ruleClassProvider, |
schmitt | 3e77024 | 2019-04-22 14:12:24 -0700 | [diff] [blame] | 192 | SkyframeExecutor skyframeExecutor) |
| 193 | throws InvalidConfigurationException { |
juliexxia | 8ee423d | 2017-10-18 16:36:57 -0400 | [diff] [blame] | 194 | // We use a hash set here to remove duplicate nodes; this can happen for input files and package |
| 195 | // groups. |
| 196 | LinkedHashSet<TargetAndConfiguration> nodes = new LinkedHashSet<>(targets.size()); |
| 197 | for (BuildConfiguration config : configurations.getTargetConfigurations()) { |
| 198 | for (Target target : targets) { |
lberki | 9525a60 | 2019-03-06 09:33:01 -0800 | [diff] [blame] | 199 | nodes.add(new TargetAndConfiguration(target, config)); |
juliexxia | 8ee423d | 2017-10-18 16:36:57 -0400 | [diff] [blame] | 200 | } |
| 201 | } |
| 202 | |
| 203 | // We'll get the configs from SkyframeExecutor#getConfigurations, which gets configurations |
| 204 | // for deps including transitions. So to satisfy its API we resolve transitions and repackage |
| 205 | // each target as a Dependency (with a NONE transition if necessary). |
| 206 | Multimap<BuildConfiguration, Dependency> asDeps = |
| 207 | AnalysisUtils.targetsToDeps(nodes, ruleClassProvider); |
| 208 | |
juliexxia | b75e4530 | 2019-05-24 07:48:39 -0700 | [diff] [blame] | 209 | return ConfigurationResolver.getConfigurationsFromExecutor( |
| 210 | nodes, asDeps, eventHandler, skyframeExecutor); |
juliexxia | 8ee423d | 2017-10-18 16:36:57 -0400 | [diff] [blame] | 211 | } |
| 212 | |
gregce | 731900f | 2018-08-01 13:12:39 -0700 | [diff] [blame] | 213 | @VisibleForTesting |
juliexxia | 8ee423d | 2017-10-18 16:36:57 -0400 | [diff] [blame] | 214 | public static Multimap<BuildConfiguration, Dependency> targetsToDeps( |
ulfjack | 36fbbde | 2018-08-02 05:16:00 -0700 | [diff] [blame] | 215 | Collection<TargetAndConfiguration> nodes, ConfiguredRuleClassProvider ruleClassProvider) { |
juliexxia | 8ee423d | 2017-10-18 16:36:57 -0400 | [diff] [blame] | 216 | Multimap<BuildConfiguration, Dependency> asDeps = |
| 217 | ArrayListMultimap.<BuildConfiguration, Dependency>create(); |
| 218 | for (TargetAndConfiguration targetAndConfig : nodes) { |
lberki | a1f2ddb | 2019-03-04 23:43:25 -0800 | [diff] [blame] | 219 | ConfigurationTransition transition = |
| 220 | TransitionResolver.evaluateTransition( |
| 221 | targetAndConfig.getConfiguration(), |
| 222 | NoTransition.INSTANCE, |
| 223 | targetAndConfig.getTarget(), |
| 224 | ruleClassProvider.getTrimmingTransitionFactory()); |
juliexxia | 8ee423d | 2017-10-18 16:36:57 -0400 | [diff] [blame] | 225 | if (targetAndConfig.getConfiguration() != null) { |
| 226 | asDeps.put( |
| 227 | targetAndConfig.getConfiguration(), |
| 228 | Dependency.withTransitionAndAspects( |
| 229 | targetAndConfig.getLabel(), |
lberki | a1f2ddb | 2019-03-04 23:43:25 -0800 | [diff] [blame] | 230 | transition, |
juliexxia | 8ee423d | 2017-10-18 16:36:57 -0400 | [diff] [blame] | 231 | // TODO(bazel-team): support top-level aspects |
| 232 | AspectCollection.EMPTY)); |
| 233 | } |
| 234 | } |
| 235 | return asDeps; |
| 236 | } |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 237 | } |