blob: 40864c57265e946a64a4df7161ffba70e29b83ef [file] [log] [blame]
ulfjack366e9482018-10-02 05:44:00 -07001// Copyright 2018 The Bazel Authors. 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.includescanning;
15
16import com.google.devtools.build.lib.actions.Artifact;
17import com.google.devtools.build.lib.includescanning.IncludeParser.Inclusion;
18import com.google.devtools.build.lib.includescanning.IncludeParser.Inclusion.Kind;
19import com.google.devtools.build.lib.vfs.PathFragment;
20import java.util.List;
21
22/**
23 * Helper class that deals with the processing of a given file's inclusions. Extracted for use in
24 * multiple include scanning implementations.
25 */
26class IncludeScannerHelper {
27 private final List<PathFragment> includePaths;
28 private final List<PathFragment> quoteIncludePaths;
29 private final Artifact source;
30
31 private boolean hasQuoteContextPathPos = false;
32 private int quoteContextPathPos = -1;
33 private boolean hasAngleContextPathPos = false;
34 private int angleContextPathPos = -1;
35
36 IncludeScannerHelper(List<PathFragment> includePaths, List<PathFragment> quoteIncludePaths,
37 Artifact source) {
38 this.includePaths = includePaths;
39 this.quoteIncludePaths = quoteIncludePaths;
40 this.source = source;
41 }
42
43 /**
44 * Finds {@code source} in the given {@includePaths} as if it were #included as {@include}.
45 *
46 * @return the index in {@includePaths} a #include_next directive should start searching from,
47 * or -1 if the source file was not found.
48 */
49 private static int findContextPathPos(PathFragment include, Artifact source,
50 List<PathFragment> includePaths) {
51 for (int i = 0; i < includePaths.size(); ++i) {
52 PathFragment execPath = includePaths.get(i);
53 if (execPath.getRelative(include).equals(source.getExecPath())) {
54 return i + 1;
55 }
56 }
57 return -1;
58 }
59
60 /**
61 * Most inclusions become inclusions with context as-is -- the context of their including file is
62 * their context. However, in the case of a top-level #include_next inclusion, a more involved
63 * procedure is needed to find the proper context.
64 */
65 InclusionWithContext createInclusionWithContext(Inclusion inclusion, int contextPathPos,
66 Kind contextKind) {
67 if (inclusion.kind.isNext() && contextPathPos == -1) {
68 // Special handling for the case when a #include_next directive is found without context,
69 // e. g., in the main source file. This is the case if either:
70 // 1. We do header preprocessing / parsing to check strict layering, or when building
71 // header modules; in this case we emulate clang's behavior; gcc's implementation
72 // diverges from clang here, but gcc does not support these use cases.
73 // 2. A .c / .cc file contains #include_next. Here the behavior of clang/gcc is different
74 // from what we implement, but it will lead to an error anyway.
75
76 // First, try to find the source of the inclusion via a header search.
77 if (inclusion.kind == Kind.NEXT_QUOTE) {
78 if (!hasQuoteContextPathPos) {
79 quoteContextPathPos =
80 findContextPathPos(inclusion.pathFragment, source, quoteIncludePaths);
81 hasQuoteContextPathPos = true;
82 }
83 contextPathPos = quoteContextPathPos;
84 } else {
85 if (!hasAngleContextPathPos) {
86 angleContextPathPos =
87 findContextPathPos(inclusion.pathFragment, source, includePaths);
88 hasAngleContextPathPos = true;
89 }
90 contextPathPos = angleContextPathPos;
91 }
92 Kind kind = (inclusion.kind == Kind.NEXT_QUOTE) ? Kind.QUOTE : Kind.ANGLE;
93 if (contextPathPos != -1) {
94 // If the source was found via the header search, assume that is a valid context to
95 // start the search from.
96 // The context kind was previously unknown, as this inclusion came from a top-level
97 // source; assume the context kind is the same as the kind of the include_next
98 // directive.
99 contextKind = kind;
100 } else {
101 // If the source was not found, #include_next behaves exactly like #include.
102 inclusion = new Inclusion(inclusion.pathFragment, kind);
103 }
104 }
105 return new InclusionWithContext(inclusion, contextPathPos, contextKind);
106 }
107}