| // 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.github.benmanes.caffeine.cache.Caffeine; | |
| import com.github.benmanes.caffeine.cache.LoadingCache; | |
| import com.google.common.base.Preconditions; | |
| 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.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; | |
| import java.util.function.Supplier; | |
| /** | |
| * Creates include scanner instances. | |
| * | |
| * <p>Each include scanner is specific to a given triplet (-I, -isystem, -iquote) of include paths. | |
| */ | |
| public class 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 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<>(); | |
| /** Cache of include scanner instances mapped by include-path hashes. */ | |
| private final LoadingCache<IncludeScannerParams, IncludeScanner> scanners; | |
| public IncludeScannerSupplier( | |
| BlazeDirectories directories, | |
| ExecutorService includePool, | |
| ArtifactFactory artifactFactory, | |
| Supplier<SpawnIncludeScanner> spawnIncludeScannerSupplier, | |
| Path execRoot) { | |
| // Map of grepped include files from input (.cc or .h) to a header-grepped file. | |
| PathExistenceCache pathCache = new PathExistenceCache(execRoot, artifactFactory); | |
| scanners = | |
| Caffeine.newBuilder() | |
| .build( | |
| key -> | |
| new LegacyIncludeScanner( | |
| includeParser, | |
| includePool, | |
| includeParseCache, | |
| pathCache, | |
| key.quoteIncludePaths, | |
| key.includePaths, | |
| key.frameworkIncludePaths, | |
| directories.getOutputPath(execRoot.getBaseName()), | |
| execRoot, | |
| artifactFactory, | |
| spawnIncludeScannerSupplier)); | |
| } | |
| /** | |
| * Returns the possibly shared scanner to be used for a given triplet of include paths. The paths | |
| * are specified as PathFragments relative to the execution root. | |
| */ | |
| public IncludeScanner scannerFor( | |
| List<PathFragment> quoteIncludePaths, | |
| List<PathFragment> includePaths, | |
| List<PathFragment> frameworkPaths) { | |
| Preconditions.checkNotNull(includeParser); | |
| return scanners.get(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); | |
| } | |
| } |