| // 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.common.util.concurrent.ListenableFuture; |
| 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; |
| final List<PathFragment> frameworkIncludePaths; |
| |
| IncludeScannerParams( |
| List<PathFragment> quoteIncludePaths, |
| List<PathFragment> includePaths, |
| List<PathFragment> frameworkIncludePaths) { |
| this.quoteIncludePaths = quoteIncludePaths; |
| this.includePaths = includePaths; |
| this.frameworkIncludePaths = frameworkIncludePaths; |
| } |
| |
| @Override |
| public int hashCode() { |
| return Objects.hash(quoteIncludePaths, includePaths, frameworkIncludePaths); |
| } |
| |
| @Override |
| public boolean equals(Object other) { |
| if (this == other) { |
| return true; |
| } |
| if (!(other instanceof IncludeScannerParams)) { |
| return false; |
| } |
| IncludeScannerParams that = (IncludeScannerParams) other; |
| return this.quoteIncludePaths.equals(that.quoteIncludePaths) |
| && this.includePaths.equals(that.includePaths) |
| && this.frameworkIncludePaths.equals(that.frameworkIncludePaths); |
| } |
| } |
| |
| 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, ListenableFuture<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; |
| private final boolean useAsyncIncludeScanner; |
| |
| /** 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, |
| key.frameworkIncludePaths, |
| directories.getOutputPath(execRoot.getBaseName()), |
| execRoot, |
| artifactFactory, |
| spawnIncludeScannerSupplier, |
| useAsyncIncludeScanner); |
| } |
| }); |
| |
| public IncludeScannerSupplierImpl( |
| BlazeDirectories directories, |
| ExecutorService includePool, |
| ArtifactFactory artifactFactory, |
| Supplier<SpawnIncludeScanner> spawnIncludeScannerSupplier, |
| Path execRoot, |
| boolean useAsyncIncludeScanner) { |
| this.directories = directories; |
| this.includePool = includePool; |
| this.artifactFactory = artifactFactory; |
| this.spawnIncludeScannerSupplier = spawnIncludeScannerSupplier; |
| this.execRoot = execRoot; |
| this.pathCache = new PathExistenceCache(execRoot, artifactFactory); |
| this.useAsyncIncludeScanner = useAsyncIncludeScanner; |
| } |
| |
| @Override |
| public IncludeScanner scannerFor( |
| List<PathFragment> quoteIncludePaths, |
| List<PathFragment> includePaths, |
| List<PathFragment> frameworkPaths) { |
| Preconditions.checkNotNull(includeParser); |
| return scanners.getUnchecked( |
| new IncludeScannerParams(quoteIncludePaths, includePaths, frameworkPaths)); |
| } |
| |
| 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(); |
| } |
| } |
| } |