blob: 4957120ff0b39746ff2b05e39f588508ab9fd7cf [file] [log] [blame]
// Copyright 2018 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.includescanning;
import com.google.common.base.Preconditions;
import com.google.common.base.Supplier;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.ArtifactFactory;
import com.google.devtools.build.lib.analysis.BlazeDirectories;
import com.google.devtools.build.lib.includescanning.IncludeParser.Inclusion;
import com.google.devtools.build.lib.rules.cpp.IncludeScanner;
import com.google.devtools.build.lib.rules.cpp.IncludeScanner.IncludeScannerSupplier;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
/** IncludeScannerSupplier implementation. */
public class IncludeScannerSupplierImpl implements IncludeScannerSupplier {
private static class IncludeScannerParams {
final List<PathFragment> quoteIncludePaths;
final List<PathFragment> includePaths;
IncludeScannerParams(List<PathFragment> quoteIncludePaths, List<PathFragment> includePaths) {
this.quoteIncludePaths = quoteIncludePaths;
this.includePaths = includePaths;
}
@Override
public int hashCode() {
return Objects.hash(quoteIncludePaths, includePaths);
}
@Override
public boolean equals(Object other) {
if (other == null) {
return false;
}
if (this == other) {
return true;
}
if (getClass() != other.getClass()) {
return false;
}
IncludeScannerParams that = (IncludeScannerParams) other;
return this.quoteIncludePaths.equals(that.quoteIncludePaths)
&& this.includePaths.equals(that.includePaths);
}
}
private final BlazeDirectories directories;
private final ExecutorService includePool;
private final ArtifactFactory artifactFactory;
private IncludeParser includeParser;
/**
* Cache of include scan results mapping source paths to sets of scanned inclusions. Shared by all
* scanner instances.
*/
private final ConcurrentMap<Artifact, Collection<Inclusion>> includeParseCache =
new ConcurrentHashMap<>();
/** Map of grepped include files from input (.cc or .h) to a header-grepped file. */
private final PathExistenceCache pathCache;
private final Supplier<SpawnIncludeScanner> spawnIncludeScannerSupplier;
private final Path execRoot;
/** Cache of include scanner instances mapped by include-path hashes. */
private final LoadingCache<IncludeScannerParams, IncludeScanner> scanners =
CacheBuilder.newBuilder()
.build(
new CacheLoader<IncludeScannerParams, IncludeScanner>() {
@Override
public IncludeScanner load(IncludeScannerParams key) {
return new LegacyIncludeScanner(
includeParser,
includePool,
includeParseCache,
pathCache,
key.quoteIncludePaths,
key.includePaths,
directories.getOutputPath(execRoot.getBaseName()),
execRoot,
artifactFactory,
spawnIncludeScannerSupplier);
}
});
public IncludeScannerSupplierImpl(
BlazeDirectories directories,
ExecutorService includePool,
ArtifactFactory artifactFactory,
Supplier<SpawnIncludeScanner> spawnIncludeScannerSupplier,
Path execRoot) {
this.directories = directories;
this.includePool = includePool;
this.artifactFactory = artifactFactory;
this.spawnIncludeScannerSupplier = spawnIncludeScannerSupplier;
this.execRoot = execRoot;
this.pathCache = new PathExistenceCache(execRoot, artifactFactory);
}
@Override
public IncludeScanner scannerFor(
List<PathFragment> quoteIncludePaths, List<PathFragment> includePaths) {
Preconditions.checkNotNull(includeParser);
return scanners.getUnchecked(new IncludeScannerParams(quoteIncludePaths, includePaths));
}
public void init(IncludeParser includeParser) {
Preconditions.checkState(
this.includeParser == null,
"Must only be initialized once: %s %s",
this.includeParser,
includeParser);
Preconditions.checkState(includeParseCache.isEmpty(), includeParseCache);
Preconditions.checkState(scanners.asMap().isEmpty(), scanners);
this.includeParser = Preconditions.checkNotNull(includeParser);
if (this.includeParser.getHints() != null) {
// The Hints object lives across the lifetime of the Blaze server, but its cached hints may
// be stale.
this.includeParser.getHints().clearCachedLegacyHints();
}
}
}