aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/rules/cpp/DiscoveredSourceInputsHelper.java
blob: a446125e5ca54a028bb5017622b4f2e3f5ba447f (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
// Copyright 2014 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.rules.cpp;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.actions.Action;
import com.google.devtools.build.lib.actions.ActionExecutionException;
import com.google.devtools.build.lib.actions.ActionInput;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.ArtifactResolver;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.PathFragment;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * Helper for actions that do include scanning. Currently only deals with source files, so is only
 * appropriate for actions that do not discover generated files. Currently does not do .d file
 * parsing, so the set of artifacts returned may be an overapproximation to the ones actually used
 * during execution.
 */
public class DiscoveredSourceInputsHelper {

  private DiscoveredSourceInputsHelper() {
  }

  /**
   * Converts PathFragments into source Artifacts using an ArtifactResolver, ignoring any that are
   * already in mandatoryInputs. Silently drops any PathFragments that cannot be resolved into
   * Artifacts.
   */
  public static ImmutableList<Artifact> getDiscoveredInputsFromPaths(
      Iterable<Artifact> mandatoryInputs, ArtifactResolver artifactResolver,
      Collection<PathFragment> inputPaths) {
    Set<PathFragment> knownPathFragments = new HashSet<>();
    for (Artifact input : mandatoryInputs) {
      knownPathFragments.add(input.getExecPath());
    }
    ImmutableList.Builder<Artifact> foundInputs = ImmutableList.builder();
    for (PathFragment execPath : inputPaths) {
      if (!knownPathFragments.add(execPath)) {
        // Don't add any inputs that we already added, or original inputs, which we probably
        // couldn't convert into artifacts anyway.
        continue;
      }
      Artifact artifact = artifactResolver.resolveSourceArtifact(execPath);
      // It is unlikely that this artifact is null, but tolerate the situation just in case.
      // It is safe to ignore such paths because dependency checker would identify change in inputs
      // (ignored path was used before) and will force action execution.
      if (artifact != null) {
        foundInputs.add(artifact);
      }
    }
    return foundInputs.build();
  }

  /**
   * Converts ActionInputs discovered as inputs during execution into source Artifacts, ignoring any
   * that are already in mandatoryInputs or that live in builtInIncludeDirectories. If any
   * ActionInputs cannot be resolved, an ActionExecutionException will be thrown.
   *
   * <p>This method duplicates the functionality of CppCompileAction#populateActionInputs, though it
   * is simpler because it need not deal with derived artifacts and doesn't parse the .d file.
   */
  public static ImmutableList<Artifact> getDiscoveredInputsFromActionInputs(
      Iterable<Artifact> mandatoryInputs,
      ArtifactResolver artifactResolver,
      Iterable<? extends ActionInput> discoveredInputs,
      Iterable<PathFragment> builtInIncludeDirectories,
      Action action,
      Artifact primaryInput) throws ActionExecutionException {
    List<PathFragment> systemIncludePrefixes = new ArrayList<>();
    for (PathFragment includePath : builtInIncludeDirectories) {
      if (includePath.isAbsolute()) {
        systemIncludePrefixes.add(includePath);
      }
    }

    // Avoid duplicates by keeping track of the ones we've seen so far, even though duplicates are
    // unlikely, since they would have to be inputs to this (non-CppCompile) action and also
    // #included by a C++ source file.
    Set<Artifact> knownInputs = new HashSet<>();
    Iterables.addAll(knownInputs, mandatoryInputs);
    ImmutableList.Builder<Artifact> foundInputs = ImmutableList.builder();
    // Check inclusions.
    IncludeProblems problems = new IncludeProblems();
    for (ActionInput input : discoveredInputs) {
      if (input instanceof Artifact) {
        Artifact artifact = (Artifact) input;
        if (knownInputs.add(artifact)) {
          foundInputs.add(artifact);
        }
        continue;
      }
      PathFragment execPath = new PathFragment(input.getExecPathString());
      if (execPath.isAbsolute()) {
        // Absolute includes from system paths are ignored.
        if (FileSystemUtils.startsWithAny(execPath, systemIncludePrefixes)) {
          continue;
        }
        // Theoretically, the more sophisticated logic of CppCompileAction#populateActioInputs could
        // be used here, to allow absolute includes that started with the execRoot. However, since
        // we don't hit this codepath for local execution, that should be unnecessary. If and when
        // we examine the results of local execution for scanned includes, that case may need to be
        // dealt with.
        problems.add(execPath.getPathString());
      }
      Artifact artifact = artifactResolver.resolveSourceArtifact(execPath);
      if (artifact != null) {
        if (knownInputs.add(artifact)) {
          foundInputs.add(artifact);
        }
      } else {
        // Abort if we see files that we can't resolve, likely caused by
        // undeclared includes or illegal include constructs.
        problems.add(execPath.getPathString());
      }
    }
    problems.assertProblemFree(action, primaryInput);
    return foundInputs.build();
  }
}