aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/rules/cpp/IncludeScanner.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/rules/cpp/IncludeScanner.java')
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/cpp/IncludeScanner.java177
1 files changed, 177 insertions, 0 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/IncludeScanner.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/IncludeScanner.java
new file mode 100644
index 0000000000..9c00efd64a
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/IncludeScanner.java
@@ -0,0 +1,177 @@
+// 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.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.google.devtools.build.lib.actions.ActionExecutionContext;
+import com.google.devtools.build.lib.actions.ActionExecutionException;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.actions.EnvironmentalExecException;
+import com.google.devtools.build.lib.actions.ExecException;
+import com.google.devtools.build.lib.actions.Executor;
+import com.google.devtools.build.lib.actions.UserExecException;
+import com.google.devtools.build.lib.profiler.Profiler;
+import com.google.devtools.build.lib.profiler.ProfilerTask;
+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.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Scans source files to determine the bounding set of transitively referenced include files.
+ *
+ * <p>Note that include scanning is performance-critical code.
+ */
+public interface IncludeScanner {
+ /**
+ * Processes a source file and a list of includes extracted from command line
+ * flags. Adds all found files to the provided set {@code includes}. This
+ * method takes into account the path- and file-level hints that are part of
+ * this include scanner.
+ */
+ public void process(Artifact source, Map<Artifact, Path> legalOutputPaths,
+ List<String> cmdlineIncludes, Set<Artifact> includes,
+ ActionExecutionContext actionExecutionContext)
+ throws IOException, ExecException, InterruptedException;
+
+ /** Supplies IncludeScanners upon request. */
+ interface IncludeScannerSupplier {
+ /** Returns the possibly shared scanner to be used for a given pair of include paths. */
+ IncludeScanner scannerFor(List<Path> quoteIncludePaths, List<Path> includePaths);
+ }
+
+ /**
+ * Helper class that exists just to provide a static method that prepares the arguments with which
+ * to call an IncludeScanner.
+ */
+ class IncludeScanningPreparer {
+ private IncludeScanningPreparer() {}
+
+ /**
+ * Returns the files transitively included by the source files of the given IncludeScannable.
+ *
+ * @param action IncludeScannable whose sources' transitive includes will be returned.
+ * @param includeScannerSupplier supplies IncludeScanners to actually do the transitive
+ * scanning (and caching results) for a given source file.
+ * @param actionExecutionContext the context for {@code action}.
+ * @param profilerTaskName what the {@link Profiler} should record this call for.
+ */
+ public static Collection<Artifact> scanForIncludedInputs(IncludeScannable action,
+ IncludeScannerSupplier includeScannerSupplier,
+ ActionExecutionContext actionExecutionContext,
+ String profilerTaskName)
+ throws ExecException, InterruptedException, ActionExecutionException {
+
+ Set<Artifact> includes = Sets.newConcurrentHashSet();
+
+ Executor executor = actionExecutionContext.getExecutor();
+ Path execRoot = executor.getExecRoot();
+
+ final List<Path> absoluteBuiltInIncludeDirs = new ArrayList<>();
+
+ Profiler profiler = Profiler.instance();
+ try {
+ profiler.startTask(ProfilerTask.SCANNER, profilerTaskName);
+
+ // We need to scan the action itself, but also the auxiliary scannables
+ // (for LIPO). There is no need to call getAuxiliaryScannables
+ // recursively.
+ for (IncludeScannable scannable :
+ Iterables.concat(ImmutableList.of(action), action.getAuxiliaryScannables())) {
+
+ Map<Artifact, Path> legalOutputPaths = scannable.getLegalGeneratedScannerFileMap();
+ List<PathFragment> includeDirs = new ArrayList<>(scannable.getIncludeDirs());
+ List<PathFragment> quoteIncludeDirs = scannable.getQuoteIncludeDirs();
+ List<String> cmdlineIncludes = scannable.getCmdlineIncludes();
+
+ for (PathFragment pathFragment : scannable.getSystemIncludeDirs()) {
+ includeDirs.add(pathFragment);
+ }
+
+ // Add the system include paths to the list of include paths.
+ for (PathFragment pathFragment : action.getBuiltInIncludeDirectories()) {
+ if (pathFragment.isAbsolute()) {
+ absoluteBuiltInIncludeDirs.add(execRoot.getRelative(pathFragment));
+ }
+ includeDirs.add(pathFragment);
+ }
+
+ IncludeScanner scanner = includeScannerSupplier.scannerFor(
+ relativeTo(execRoot, quoteIncludeDirs),
+ relativeTo(execRoot, includeDirs));
+
+ for (Artifact source : scannable.getIncludeScannerSources()) {
+ // Add all include scanning entry points to the inputs; this is necessary
+ // when we have more than one source to scan from, for example when building
+ // C++ modules.
+ // In that case we have one of two cases:
+ // 1. We compile a header module - there, the .cppmap file is the main source file
+ // (which we do not include-scan, as that would require an extra parser), and
+ // thus already in the input; all headers in the .cppmap file are our entry points
+ // for include scanning, but are not yet in the inputs - they get added here.
+ // 2. We compile an object file that uses a header module; currently using a header
+ // module requires all headers it can reference to be available for the compilation.
+ // The header module can reference headers that are not in the transitive include
+ // closure of the current translation unit. Therefore, {@code CppCompileAction}
+ // adds all headers specified transitively for compiled header modules as include
+ // scanning entry points, and we need to add the entry points to the inputs here.
+ includes.add(source);
+ scanner.process(source, legalOutputPaths, cmdlineIncludes, includes,
+ actionExecutionContext);
+ }
+ }
+ } catch (IOException e) {
+ throw new EnvironmentalExecException(e.getMessage());
+ } finally {
+ profiler.completeTask(ProfilerTask.SCANNER);
+ }
+
+ // Collect inputs and output
+ List<Artifact> inputs = new ArrayList<>();
+ IncludeProblems includeProblems = new IncludeProblems();
+ for (Artifact included : includes) {
+ if (FileSystemUtils.startsWithAny(included.getPath(), absoluteBuiltInIncludeDirs)) {
+ // Skip include files found in absolute include directories. This currently only applies
+ // to grte.
+ continue;
+ }
+ if (included.getRoot().getPath().getParentDirectory() == null) {
+ throw new UserExecException(
+ "illegal absolute path to include file: " + included.getPath());
+ }
+ inputs.add(included);
+ }
+ return inputs;
+ }
+
+ private static List<Path> relativeTo(
+ Path path, Collection<PathFragment> fragments) {
+ List<Path> result = Lists.newArrayListWithCapacity(fragments.size());
+ for (PathFragment fragment : fragments) {
+ result.add(path.getRelative(fragment));
+ }
+ return result;
+ }
+ }
+}