blob: d81b6d293f4418ad65dc894dfde7680ed4a568bd [file] [log] [blame]
// Copyright 2014 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.collect.ImmutableList;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.devtools.build.lib.actions.ActionExecutionContext;
import com.google.devtools.build.lib.actions.ActionExecutionMetadata;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.ExecException;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.io.IOException;
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 source files and a list of includes extracted from command line flags. Adds all found
* files to the provided set {@code includes}.
*
* <p>The resulting set will include {@code mainSource} and {@code sources}. This has no real
* impact in the case that we are scanning a single source file, since it is already known to be
* an input. However, 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 possibilities:
*
* <ol>
* <li>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.
* <li>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, {@link 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.
* </ol>
*
* <p>{@code mainSource} is the source file relative to which the {@code cmdlineIncludes} are
* interpreted.
*/
ListenableFuture<?> processAsync(
Artifact mainSource,
Collection<Artifact> sources,
IncludeScanningHeaderData includeScanningHeaderData,
List<String> cmdlineIncludes,
Set<Artifact> includes,
ActionExecutionMetadata actionExecutionMetadata,
ActionExecutionContext actionExecutionContext,
Artifact grepIncludes)
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. The paths
* are specified as PathFragments relative to the execution root.
*/
IncludeScanner scannerFor(List<PathFragment> quoteIncludePaths,
List<PathFragment> includePaths);
}
/**
* Holds pre-aggregated information that the {@link IncludeScanner} needs from the compilation
* action.
*/
class IncludeScanningHeaderData {
/**
* Lookup table to find the {@link Artifact}s of generated files based on their {@link
* Artifact#getExecPath}.
*/
private final Map<PathFragment, Artifact> pathToLegalOutputArtifact;
/**
* The set of headers that are modular, i.e. are going to be read as a serialized AST rather
* than from the textual source file. Depending on the implementation, it is likely that further
* input discovery through such headers is unnecessary as the serialized AST is self-contained.
*/
private final Set<Artifact> modularHeaders;
/**
* The list of "-isystem" include paths that should be used by the IncludeScanner for this
* action. The compiler searches these paths ahead of the built-in system include paths, but
* after all other paths. "-isystem" paths are treated the same as normal system directories.
*/
private final List<PathFragment> systemIncludeDirs;
/**
* A list of "-include" inclusions specified explicitly on the command line of this action. The
* compiler will imagine that these files have been quote-included at the beginning of each
* source file.
*/
private final List<String> cmdlineIncludes;
public IncludeScanningHeaderData(
Map<PathFragment, Artifact> pathToLegalOutputArtifact,
Set<Artifact> modularHeaders,
List<PathFragment> systemIncludeDirs,
List<String> cmdlineIncludes) {
this.pathToLegalOutputArtifact = pathToLegalOutputArtifact;
this.modularHeaders = modularHeaders;
this.systemIncludeDirs = systemIncludeDirs;
this.cmdlineIncludes = cmdlineIncludes;
}
public Set<Artifact> getModularHeaders() {
return modularHeaders;
}
public Map<PathFragment, Artifact> getPathToLegalOutputArtifact() {
return pathToLegalOutputArtifact;
}
public List<PathFragment> getSystemIncludeDirs() {
return systemIncludeDirs;
}
public List<String> getCmdlineIncludes() {
return cmdlineIncludes;
}
public static class Builder {
private final Map<PathFragment, Artifact> pathToLegalOutputArtifact;
private final Set<Artifact> modularHeaders;
private List<PathFragment> systemIncludeDirs = ImmutableList.of();
private List<String> cmdlineIncludes = ImmutableList.of();
public Builder(
Map<PathFragment, Artifact> pathToLegalOutputArtifact, Set<Artifact> modularHeaders) {
this.pathToLegalOutputArtifact = pathToLegalOutputArtifact;
this.modularHeaders = modularHeaders;
}
public Builder setSystemIncludeDirs(List<PathFragment> systemIncludeDirs) {
this.systemIncludeDirs = systemIncludeDirs;
return this;
}
public Builder setCmdlineIncludes(List<String> cmdlineIncludes) {
this.cmdlineIncludes = cmdlineIncludes;
return this;
}
public IncludeScanningHeaderData build() {
return new IncludeScanningHeaderData(
pathToLegalOutputArtifact, modularHeaders, systemIncludeDirs, cmdlineIncludes);
}
}
}
}