aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/skyframe/GraphBackedRecursivePackageProvider.java
blob: 2d1b1de6c061f41847163839f93181ca783b70f7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
// Copyright 2015 Google Inc. 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.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.cmdline.PackageIdentifier;
import com.google.devtools.build.lib.cmdline.PackageIdentifier.RepositoryName;
import com.google.devtools.build.lib.cmdline.TargetPattern;
import com.google.devtools.build.lib.cmdline.TargetPattern.Type;
import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.events.EventHandler;
import com.google.devtools.build.lib.packages.BuildFileNotFoundException;
import com.google.devtools.build.lib.packages.NoSuchPackageException;
import com.google.devtools.build.lib.packages.NoSuchTargetException;
import com.google.devtools.build.lib.packages.Package;
import com.google.devtools.build.lib.packages.Target;
import com.google.devtools.build.lib.pkgcache.FilteringPolicies;
import com.google.devtools.build.lib.pkgcache.FilteringPolicy;
import com.google.devtools.build.lib.pkgcache.RecursivePackageProvider;
import com.google.devtools.build.lib.skyframe.TargetPatternValue.TargetPatternKey;
import com.google.devtools.build.lib.vfs.PathFragment;
import com.google.devtools.build.lib.vfs.RootedPath;
import com.google.devtools.build.skyframe.SkyKey;
import com.google.devtools.build.skyframe.WalkableGraph;

/**
 * A {@link RecursivePackageProvider} backed by a {@link WalkableGraph}, used by
 * {@code SkyQueryEnvironment} to look up the packages and targets matching the universe that's
 * been preloaded in {@code graph}.
 * */
public final class GraphBackedRecursivePackageProvider implements RecursivePackageProvider {

  private final WalkableGraph graph;
  private final ImmutableList<TargetPatternKey> universeTargetPatternKeys;

  public GraphBackedRecursivePackageProvider(WalkableGraph graph,
      ImmutableList<TargetPatternKey> universeTargetPatternKeys) {
    this.graph = Preconditions.checkNotNull(graph);
    this.universeTargetPatternKeys = Preconditions.checkNotNull(universeTargetPatternKeys);
  }

  @Override
  public Package getPackage(EventHandler eventHandler, PackageIdentifier packageName)
      throws NoSuchPackageException {
    SkyKey pkgKey = PackageValue.key(packageName);

    PackageValue pkgValue;
    if (graph.exists(pkgKey)) {
      pkgValue = (PackageValue) graph.getValue(pkgKey);
      if (pkgValue == null) {
        throw (NoSuchPackageException)
            Preconditions.checkNotNull(graph.getException(pkgKey), pkgKey);
      }
    } else {
      // If the package key does not exist in the graph, then it must not correspond to any package,
      // because the SkyQuery environment has already loaded the universe.
      throw new BuildFileNotFoundException(packageName, "BUILD file not found on package path");
    }
    return pkgValue.getPackage();
  }

  @Override
  public boolean isPackage(EventHandler eventHandler, PackageIdentifier packageName) {
    SkyKey packageLookupKey = PackageLookupValue.key(packageName);
    if (!graph.exists(packageLookupKey)) {
      // If the package lookup key does not exist in the graph, then it must not correspond to any
      // package, because the SkyQuery environment has already loaded the universe.
      return false;
    }
    PackageLookupValue packageLookupValue = (PackageLookupValue) graph.getValue(packageLookupKey);
    if (packageLookupValue == null) {
      Exception exception = Preconditions.checkNotNull(graph.getException(packageLookupKey),
          "During package lookup for '%s', got null for exception", packageName);
      if (exception instanceof NoSuchPackageException
          || exception instanceof InconsistentFilesystemException) {
        eventHandler.handle(Event.error(exception.getMessage()));
        return false;
      } else {
        throw new IllegalStateException("During package lookup for '" + packageName
            + "', got unexpected exception type", exception);
      }
    }
    return packageLookupValue.packageExists();
  }

  @Override
  public Iterable<PathFragment> getPackagesUnderDirectory(
      RepositoryName repository, RootedPath directory,
      ImmutableSet<PathFragment> excludedSubdirectories) {
    PathFragment.checkAllPathsAreUnder(excludedSubdirectories, directory.getRelativePath());

    // Find the filtering policy of a TargetsBelowDirectory pattern, if any, in the universe that
    // contains this directory.
    FilteringPolicy filteringPolicy = null;
    for (TargetPatternKey patternKey : universeTargetPatternKeys) {
      TargetPattern pattern = patternKey.getParsedPattern();
      boolean isTBD = pattern.getType().equals(Type.TARGETS_BELOW_DIRECTORY);
      if (isTBD && pattern.containsBelowDirectory(directory.getRelativePath().getPathString())) {
        filteringPolicy =
            pattern.getRulesOnly() ? FilteringPolicies.RULES_ONLY : FilteringPolicies.NO_FILTER;
        break;
      }
    }

    // If we found a TargetsBelowDirectory pattern in the universe that contains this directory,
    // then we can look for packages in and under it in the graph. If we didn't find one, then the
    // directory wasn't in the universe, so return an empty list.
    ImmutableList.Builder<PathFragment> builder = ImmutableList.builder();
    if (filteringPolicy != null) {
      collectPackagesUnder(repository, directory, excludedSubdirectories, builder, filteringPolicy);
    }
    return builder.build();
  }

  private void collectPackagesUnder(RepositoryName repository, RootedPath directory,
      ImmutableSet<PathFragment> excludedSubdirectories,
      ImmutableList.Builder<PathFragment> builder, FilteringPolicy policy) {
    SkyKey key =
        PrepareDepsOfTargetsUnderDirectoryValue.key(
            repository, directory, excludedSubdirectories, policy);
    // If the key does not exist in the graph, because the SkyQuery environment has
    // already loaded the universe, and we found a TargetsBelowDirectory pattern in the universe
    // that contained it, then we know the directory does not exist in the universe.
    if (!graph.exists(key)) {
      return;
    }

    // If the key exists in the graph, then it must have a value and must not have an exception,
    // because PrepareDepsOfTargetsUnderDirectoryFunction#compute never throws.
    PrepareDepsOfTargetsUnderDirectoryValue prepDepsValue =
        (PrepareDepsOfTargetsUnderDirectoryValue) Preconditions.checkNotNull(graph.getValue(key));
    if (prepDepsValue.isDirectoryPackage()) {
      builder.add(directory.getRelativePath());
    }
    ImmutableMap<RootedPath, Boolean> subdirectoryTransitivelyContainsPackages =
        prepDepsValue.getSubdirectoryTransitivelyContainsPackages();
    for (RootedPath subdirectory : subdirectoryTransitivelyContainsPackages.keySet()) {
      if (subdirectoryTransitivelyContainsPackages.get(subdirectory)) {
        PathFragment subdirectoryRelativePath = subdirectory.getRelativePath();
        ImmutableSet<PathFragment> excludedSubdirectoriesBeneathThisSubdirectory =
            PathFragment.filterPathsStartingWith(excludedSubdirectories, subdirectoryRelativePath);
        collectPackagesUnder(repository, subdirectory,
            excludedSubdirectoriesBeneathThisSubdirectory, builder, policy);
      }
    }
  }

  @Override
  public Target getTarget(EventHandler eventHandler, Label label)
      throws NoSuchPackageException, NoSuchTargetException {
    return getPackage(eventHandler, label.getPackageIdentifier()).getTarget(label.getName());
  }
}