blob: bf328437877dc8b0e62b2a387df6a997aa47c929 [file] [log] [blame]
Mark Schaller7b0bc0a2015-06-30 23:57:45 +00001// Copyright 2015 Google Inc. All rights reserved.
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.
14package com.google.devtools.build.lib.skyframe;
15
16import com.google.common.collect.ImmutableList;
17import com.google.common.collect.ImmutableSet;
18import com.google.devtools.build.lib.cmdline.ResolvedTargets;
19import com.google.devtools.build.lib.cmdline.TargetParsingException;
20import com.google.devtools.build.lib.cmdline.TargetPattern;
21import com.google.devtools.build.lib.cmdline.TargetPatternResolver;
22import com.google.devtools.build.lib.events.Event;
23import com.google.devtools.build.lib.packages.NoSuchPackageException;
24import com.google.devtools.build.lib.packages.NoSuchTargetException;
25import com.google.devtools.build.lib.packages.NoSuchThingException;
26import com.google.devtools.build.lib.packages.Package;
27import com.google.devtools.build.lib.packages.PackageIdentifier;
28import com.google.devtools.build.lib.packages.Target;
29import com.google.devtools.build.lib.pkgcache.FilteringPolicies;
30import com.google.devtools.build.lib.pkgcache.FilteringPolicy;
31import com.google.devtools.build.lib.pkgcache.PathPackageLocator;
32import com.google.devtools.build.lib.pkgcache.TargetPatternResolverUtil;
33import com.google.devtools.build.lib.skyframe.EnvironmentBackedRecursivePackageProvider.MissingDepException;
34import com.google.devtools.build.lib.syntax.Label;
35import com.google.devtools.build.lib.vfs.Path;
36import com.google.devtools.build.lib.vfs.PathFragment;
37import com.google.devtools.build.lib.vfs.RootedPath;
38import com.google.devtools.build.skyframe.SkyFunction;
39import com.google.devtools.build.skyframe.SkyFunctionException;
40import com.google.devtools.build.skyframe.SkyKey;
41import com.google.devtools.build.skyframe.SkyValue;
42
43import java.util.concurrent.atomic.AtomicReference;
44
45import javax.annotation.Nullable;
46
47/**
48 * PrepareDepsOfPatternFunction ensures the graph loads targets matching the pattern and its
49 * transitive dependencies.
50 */
51public class PrepareDepsOfPatternFunction implements SkyFunction {
52
53 private final AtomicReference<PathPackageLocator> pkgPath;
54
55 public PrepareDepsOfPatternFunction(AtomicReference<PathPackageLocator> pkgPath) {
56 this.pkgPath = pkgPath;
57 }
58
59 @Nullable
60 @Override
61 public SkyValue compute(SkyKey key, Environment env)
62 throws SkyFunctionException, InterruptedException {
63 TargetPatternValue.TargetPatternKey patternKey =
64 ((TargetPatternValue.TargetPatternKey) key.argument());
65 try {
66 TargetPattern parsedPattern = patternKey.getParsedPattern();
67 DepsOfPatternPreparer preparer =
68 new DepsOfPatternPreparer(env, patternKey.getPolicy(), pkgPath.get());
69 ImmutableSet<String> excludedSubdirectories = patternKey.getExcludedSubdirectories();
70 parsedPattern.eval(preparer, excludedSubdirectories);
71 } catch (TargetParsingException e) {
72 throw new PrepareDepsOfPatternFunctionException(e);
73 } catch (MissingDepException e) {
74 // The DepsOfPatternPreparer constructed above might throw MissingDepException to signal
75 // when it has a dependency on a missing Environment value.
76 return null;
77 }
78 return PrepareDepsOfPatternValue.INSTANCE;
79 }
80
81 @Nullable
82 @Override
83 public String extractTag(SkyKey skyKey) {
84 return null;
85 }
86
87 /**
88 * Used to declare all the exception types that can be wrapped in the exception thrown by {@link
89 * PrepareDepsOfPatternFunction#compute}.
90 */
91 private static final class PrepareDepsOfPatternFunctionException extends SkyFunctionException {
92
93 public PrepareDepsOfPatternFunctionException(TargetParsingException e) {
94 super(e, Transience.PERSISTENT);
95 }
96 }
97
98 /**
99 * A {@link TargetPatternResolver} backed by an {@link Environment} whose methods do not actually
100 * return resolved targets, but that ensures the graph loads the matching targets <b>and</b> their
101 * transitive dependencies. Its methods may throw {@link MissingDepException} if the package
102 * values this depends on haven't been calculated and added to its environment.
103 */
104 static class DepsOfPatternPreparer implements TargetPatternResolver<Void> {
105
106 private final EnvironmentBackedRecursivePackageProvider packageProvider;
107 private final Environment env;
108 private final FilteringPolicy policy;
109 private final PathPackageLocator pkgPath;
110
111 public DepsOfPatternPreparer(Environment env, FilteringPolicy policy,
112 PathPackageLocator pkgPath) {
113 this.env = env;
114 this.packageProvider = new EnvironmentBackedRecursivePackageProvider(env);
115 this.policy = policy;
116 this.pkgPath = pkgPath;
117 }
118
119 @Override
120 public void warn(String msg) {
121 env.getListener().handle(Event.warn(msg));
122 }
123
124 @Override
125 public Void getTargetOrNull(String targetName) throws InterruptedException {
126 // Note:
127 // This method is used in just one place, TargetPattern.TargetsInPackage#getWildcardConflict.
128 // Returning null tells #getWildcardConflict that there is not a target with a name like
129 // "all" or "all-targets", which means that TargetPattern.TargetsInPackage will end up
130 // calling DepsOfTargetPreparer#getTargetsInPackage.
131 // TODO (bazel-team): Consider replacing this with an isTarget method on the interface.
132 return null;
133 }
134
135 @Override
136 public ResolvedTargets<Void> getExplicitTarget(String targetName)
137 throws TargetParsingException, InterruptedException {
138 Label label = TargetPatternResolverUtil.label(targetName);
139 try {
140 Target target = packageProvider.getTarget(env.getListener(), label);
141 SkyKey key = TransitiveTargetValue.key(target.getLabel());
142 SkyValue token =
143 env.getValueOrThrow(key, NoSuchPackageException.class, NoSuchTargetException.class);
144 if (token == null) {
145 throw new MissingDepException();
146 }
147 return ResolvedTargets.empty();
148 } catch (NoSuchThingException e) {
149 throw new TargetParsingException(e.getMessage(), e);
150 }
151 }
152
153 @Override
154 public ResolvedTargets<Void> getTargetsInPackage(String originalPattern, String packageName,
155 boolean rulesOnly) throws TargetParsingException, InterruptedException {
156 FilteringPolicy actualPolicy = rulesOnly
157 ? FilteringPolicies.and(FilteringPolicies.RULES_ONLY, policy)
158 : policy;
159 return getTargetsInPackage(originalPattern, new PathFragment(packageName), actualPolicy);
160 }
161
162 private ResolvedTargets<Void> getTargetsInPackage(String originalPattern,
163 PathFragment packageNameFragment, FilteringPolicy policy)
164 throws TargetParsingException, InterruptedException {
165 TargetPatternResolverUtil.validatePatternPackage(originalPattern, packageNameFragment, this);
166 try {
167 PackageIdentifier packageId = PackageIdentifier.createInDefaultRepo(packageNameFragment);
168 Package pkg = packageProvider.getPackage(env.getListener(), packageId);
169 ResolvedTargets<Target> packageTargets =
170 TargetPatternResolverUtil.resolvePackageTargets(pkg, policy);
171 ImmutableList.Builder<SkyKey> builder = ImmutableList.builder();
172 for (Target target : packageTargets.getTargets()) {
173 builder.add(TransitiveTargetValue.key(target.getLabel()));
174 }
175 ImmutableList<SkyKey> skyKeys = builder.build();
176 env.getValuesOrThrow(skyKeys, NoSuchPackageException.class, NoSuchTargetException.class);
177 if (env.valuesMissing()) {
178 throw new MissingDepException();
179 }
180 return ResolvedTargets.empty();
181 } catch (NoSuchThingException e) {
182 String message = TargetPatternResolverUtil.getParsingErrorMessage(
183 "package contains errors", originalPattern);
184 throw new TargetParsingException(message, e);
185 }
186 }
187
188 @Override
189 public boolean isPackage(String packageName) {
190 return packageProvider.isPackage(env.getListener(), packageName);
191 }
192
193 @Override
194 public String getTargetKind(Void target) {
195 // Note:
196 // This method is used in just one place, TargetPattern.TargetsInPackage#getWildcardConflict.
197 // Because DepsOfPatternPreparer#getTargetOrNull always returns null, this method is never
198 // called.
199 throw new UnsupportedOperationException();
200 }
201
202 @Override
203 public ResolvedTargets<Void> findTargetsBeneathDirectory(String originalPattern,
204 String directory, boolean rulesOnly, ImmutableSet<String> excludedSubdirectories)
205 throws TargetParsingException, InterruptedException {
206 FilteringPolicy actualPolicy = rulesOnly
207 ? FilteringPolicies.and(FilteringPolicies.RULES_ONLY, policy)
208 : policy;
209 ImmutableSet<PathFragment> excludedPathFragments =
210 TargetPatternResolverUtil.getPathFragments(excludedSubdirectories);
211 PathFragment pathFragment = TargetPatternResolverUtil.getPathFragment(directory);
212 for (Path root : pkgPath.getPathEntries()) {
213 RootedPath rootedPath = RootedPath.toRootedPath(root, pathFragment);
214 SkyValue token = env.getValue(PrepareDepsOfTargetsUnderDirectoryValue.key(rootedPath,
215 excludedPathFragments, actualPolicy));
216 if (token == null) {
217 // A null token value means there is a missing dependency, because RecursivePkgFunction
218 // never throws.
219 throw new MissingDepException();
220 }
221 }
222 return ResolvedTargets.empty();
223 }
224 }
225}