aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/rules/cpp/HeaderDiscovery.java
blob: 512281ad9374151157baaa7c43530e6d581419d0 (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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
// Copyright 2016 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.rules.cpp;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.devtools.build.lib.actions.Action;
import com.google.devtools.build.lib.actions.ActionExecutionException;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.ArtifactResolver;
import com.google.devtools.build.lib.cmdline.RepositoryName;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadCompatible;
import com.google.devtools.build.lib.rules.cpp.CppCompileAction.DotdFile;
import com.google.devtools.build.lib.rules.cpp.CppCompileAction.SpecialInputsHandler;
import com.google.devtools.build.lib.util.DependencySet;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.util.List;
import java.util.Map;

/** Manages the process of obtaining inputs used in a compilation from .d files. */
public class HeaderDiscovery {

  private final Action action;
  private final Artifact sourceFile;
  private final DotdFile dotdFile;

  private final SpecialInputsHandler specialInputsHandler;
  private final boolean shouldValidateInclusions;

  private final DependencySet depSet;
  private final List<Path> permittedSystemIncludePrefixes;
  private final Map<PathFragment, Artifact> allowedDerivedInputsMap;

  /**
   * Creates a HeaderDiscover instance
   *
   * @param action the action instance requiring header discovery
   * @param sourceFile the source file for the compile
   * @param dotdFile the .d file used for header discovery
   * @param specialInputsHandler the SpecialInputsHandler for the build
   * @param shouldValidateInclusions true if include validation should be performed
   */
  public HeaderDiscovery(
      Action action,
      Artifact sourceFile,
      DotdFile dotdFile,
      SpecialInputsHandler specialInputsHandler,
      boolean shouldValidateInclusions,
      DependencySet depSet,
      List<Path> permittedSystemIncludePrefixes,
      Map<PathFragment, Artifact> allowedDerivedInputsMap) {
    this.action = Preconditions.checkNotNull(action);
    this.sourceFile = Preconditions.checkNotNull(sourceFile);
    this.dotdFile = Preconditions.checkNotNull(dotdFile);
    this.specialInputsHandler = specialInputsHandler;
    this.shouldValidateInclusions = shouldValidateInclusions;
    this.depSet = depSet;
    this.permittedSystemIncludePrefixes = permittedSystemIncludePrefixes;
    this.allowedDerivedInputsMap = allowedDerivedInputsMap;
  }

  /**
   * Returns a collection with additional input artifacts relevant to the action by reading the
   * dynamically-discovered dependency information from the .d file after the action has run.
   *
   * <p>Artifacts are considered inputs but not "mandatory" inputs.
   *
   * @throws ActionExecutionException iff the .d is missing (when required), malformed, or has
   *     unresolvable included artifacts.
   */
  @VisibleForTesting
  @ThreadCompatible
  public NestedSet<Artifact> discoverInputsFromDotdFiles(
      Path execRoot, ArtifactResolver artifactResolver) throws ActionExecutionException {
    NestedSetBuilder<Artifact> inputs = NestedSetBuilder.stableOrder();
    if (dotdFile == null) {
      return inputs.build();
    }
    List<Path> systemIncludePrefixes = permittedSystemIncludePrefixes;

    // Check inclusions.
    IncludeProblems problems = new IncludeProblems();
    for (Path execPath : depSet.getDependencies()) {
      // Module .pcm files are generated and thus aren't declared inputs.
      if (execPath.getBaseName().endsWith(".pcm")) {
        continue;
      }
      PathFragment execPathFragment = execPath.asFragment();
      if (execPathFragment.isAbsolute()) {
        // Absolute includes from system paths are ignored.
        if (FileSystemUtils.startsWithAny(execPath, systemIncludePrefixes)) {
          continue;
        }
        // Since gcc is given only relative paths on the command line,
        // non-system include paths here should never be absolute. If they
        // are, it's probably due to a non-hermetic #include, & we should stop
        // the build with an error.
        if (execPath.startsWith(execRoot)) {
          execPathFragment = execPath.relativeTo(execRoot); // funky but tolerable path
        } else {
          problems.add(execPathFragment.getPathString());
          continue;
        }
      }
      Artifact artifact = allowedDerivedInputsMap.get(execPathFragment);
      if (artifact == null) {
        artifact = artifactResolver.resolveSourceArtifact(execPathFragment, RepositoryName.MAIN);
      }
      if (artifact != null) {
        inputs.add(artifact);
        // In some cases, execution backends need extra files for each included file. Add them
        // to the set of actual inputs.
        if (specialInputsHandler != null) {
          inputs.addAll(specialInputsHandler.getInputsForIncludedFile(artifact, artifactResolver));
        }
      } else {
        // Abort if we see files that we can't resolve, likely caused by
        // undeclared includes or illegal include constructs.
        problems.add(execPathFragment.getPathString());
      }
    }
    if (shouldValidateInclusions) {
      problems.assertProblemFree(action, sourceFile);
    }
    return inputs.build();
  }

  /** A Builder for HeaderDiscovery */
  public static class Builder {
    private Action action;
    private Artifact sourceFile;
    private DotdFile dotdFile;
    private SpecialInputsHandler specialInputsHandler;
    private boolean shouldValidateInclusions = false;

    private DependencySet depSet;
    private List<Path> permittedSystemIncludePrefixes;
    private Map<PathFragment, Artifact> allowedDerivedInputsMap;

    /** Sets the action for which to discover inputs. */
    public Builder setAction(Action action) {
      this.action = action;
      return this;
    }

    /** Sets the source file for which to discover inputs. */
    public Builder setSourceFile(Artifact sourceFile) {
      this.sourceFile = sourceFile;
      return this;
    }

    /** Sets the dotd file to be used to discover inputs. */
    public Builder setDotdFile(DotdFile dotdFile) {
      this.dotdFile = dotdFile;
      return this;
    }

    /** Sets the SpecialInputsHandler for inputs to this build. */
    public Builder setSpecialInputsHandler(SpecialInputsHandler specialInputsHandler) {
      this.specialInputsHandler = specialInputsHandler;
      return this;
    }

    /** Sets that this compile should validate inclusions against the dotd file. */
    public Builder shouldValidateInclusions() {
      this.shouldValidateInclusions = true;
      return this;
    }

    /** Sets the DependencySet capturing used headers by this compile. */
    public Builder setDependencySet(DependencySet depSet) {
      this.depSet = depSet;
      return this;
    }

    /** Sets prefixes of allowed absolute inclusions */
    public Builder setPermittedSystemIncludePrefixes(List<Path> systemIncludePrefixes) {
      this.permittedSystemIncludePrefixes = systemIncludePrefixes;
      return this;
    }

    /** Sets permitted inputs to the build */
    public Builder setAllowedDerivedinputsMap(Map<PathFragment, Artifact> allowedDerivedInputsMap) {
      this.allowedDerivedInputsMap = allowedDerivedInputsMap;
      return this;
    }

    /** Creates a CppHeaderDiscovery instance. */
    public HeaderDiscovery build() {
      return new HeaderDiscovery(
          action,
          sourceFile,
          dotdFile,
          specialInputsHandler,
          shouldValidateInclusions,
          depSet,
          permittedSystemIncludePrefixes,
          allowedDerivedInputsMap);
    }
  }
}