blob: 54166dabebb05bbdbab534ac358df1259af67ee1 [file] [log] [blame] [edit]
//===- DependencyScannerImpl.h - Implements dependency scanning *- C++ -*--===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLING_DEPENDENCYSCANNING_DEPENDENCYSCANNER_H
#define LLVM_CLANG_TOOLING_DEPENDENCYSCANNING_DEPENDENCYSCANNER_H
#include "clang/Driver/Compilation.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Serialization/ObjectFilePCHContainerReader.h"
#include "clang/Tooling/DependencyScanning/DependencyScanningFilesystem.h"
#include "clang/Tooling/DependencyScanning/ModuleDepCollector.h"
namespace clang {
class DiagnosticConsumer;
namespace tooling {
namespace dependencies {
class DependencyScanningService;
class DependencyScanningWorker;
class DependencyConsumer;
class DependencyActionController;
class DependencyScanningWorkerFilesystem;
class DependencyScanningAction {
public:
DependencyScanningAction(
DependencyScanningService &Service, StringRef WorkingDirectory,
DependencyConsumer &Consumer, DependencyActionController &Controller,
IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS,
std::optional<StringRef> ModuleName = std::nullopt)
: Service(Service), WorkingDirectory(WorkingDirectory),
Consumer(Consumer), Controller(Controller), DepFS(std::move(DepFS)) {}
bool runInvocation(std::unique_ptr<CompilerInvocation> Invocation,
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
std::shared_ptr<PCHContainerOperations> PCHContainerOps,
DiagnosticConsumer *DiagConsumer);
bool hasScanned() const { return Scanned; }
bool hasDiagConsumerFinished() const { return DiagConsumerFinished; }
/// Take the cc1 arguments corresponding to the most recent invocation used
/// with this action. Any modifications implied by the discovered dependencies
/// will have already been applied.
std::vector<std::string> takeLastCC1Arguments() {
std::vector<std::string> Result;
std::swap(Result, LastCC1Arguments); // Reset LastCC1Arguments to empty.
return Result;
}
private:
void setLastCC1Arguments(CompilerInvocation &&CI) {
if (MDC)
MDC->applyDiscoveredDependencies(CI);
LastCC1Arguments = CI.getCC1CommandLine();
}
DependencyScanningService &Service;
StringRef WorkingDirectory;
DependencyConsumer &Consumer;
DependencyActionController &Controller;
IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS;
std::optional<CompilerInstance> ScanInstanceStorage;
std::shared_ptr<ModuleDepCollector> MDC;
std::vector<std::string> LastCC1Arguments;
bool Scanned = false;
bool DiagConsumerFinished = false;
};
// Helper functions and data types.
std::unique_ptr<DiagnosticOptions>
createDiagOptions(ArrayRef<std::string> CommandLine);
struct DignosticsEngineWithDiagOpts {
// We need to bound the lifetime of the DiagOpts used to create the
// DiganosticsEngine with the DiagnosticsEngine itself.
std::unique_ptr<DiagnosticOptions> DiagOpts;
IntrusiveRefCntPtr<DiagnosticsEngine> DiagEngine;
DignosticsEngineWithDiagOpts(ArrayRef<std::string> CommandLine,
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
DiagnosticConsumer &DC);
};
struct TextDiagnosticsPrinterWithOutput {
// We need to bound the lifetime of the data that supports the DiagPrinter
// with it together so they have the same lifetime.
std::string DiagnosticOutput;
llvm::raw_string_ostream DiagnosticsOS;
std::unique_ptr<DiagnosticOptions> DiagOpts;
TextDiagnosticPrinter DiagPrinter;
TextDiagnosticsPrinterWithOutput(ArrayRef<std::string> CommandLine)
: DiagnosticsOS(DiagnosticOutput),
DiagOpts(createDiagOptions(CommandLine)),
DiagPrinter(DiagnosticsOS, *DiagOpts) {}
};
std::pair<std::unique_ptr<driver::Driver>, std::unique_ptr<driver::Compilation>>
buildCompilation(ArrayRef<std::string> ArgStrs, DiagnosticsEngine &Diags,
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
llvm::BumpPtrAllocator &Alloc);
std::unique_ptr<CompilerInvocation>
createCompilerInvocation(ArrayRef<std::string> CommandLine,
DiagnosticsEngine &Diags);
std::pair<IntrusiveRefCntPtr<llvm::vfs::FileSystem>, std::vector<std::string>>
initVFSForTUBuferScanning(IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS,
ArrayRef<std::string> CommandLine,
StringRef WorkingDirectory,
llvm::MemoryBufferRef TUBuffer);
std::pair<IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem>,
std::vector<std::string>>
initVFSForByNameScanning(IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS,
ArrayRef<std::string> CommandLine,
StringRef WorkingDirectory, StringRef ModuleName);
bool initializeScanCompilerInstance(
CompilerInstance &ScanInstance,
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
DiagnosticConsumer *DiagConsumer, DependencyScanningService &Service,
IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS);
SmallVector<StringRef>
getInitialStableDirs(const CompilerInstance &ScanInstance);
std::optional<PrebuiltModulesAttrsMap>
computePrebuiltModulesASTMap(CompilerInstance &ScanInstance,
SmallVector<StringRef> &StableDirs);
std::unique_ptr<DependencyOutputOptions>
takeAndUpdateDependencyOutputOptionsFrom(CompilerInstance &ScanInstance);
/// Create the dependency collector that will collect the produced
/// dependencies. May return the created ModuleDepCollector depending
/// on the scanning format.
std::shared_ptr<ModuleDepCollector> initializeScanInstanceDependencyCollector(
CompilerInstance &ScanInstance,
std::unique_ptr<DependencyOutputOptions> DepOutputOpts,
StringRef WorkingDirectory, DependencyConsumer &Consumer,
DependencyScanningService &Service, CompilerInvocation &Inv,
DependencyActionController &Controller,
PrebuiltModulesAttrsMap PrebuiltModulesASTMap,
llvm::SmallVector<StringRef> &StableDirs);
class CompilerInstanceWithContext {
// Context
DependencyScanningWorker &Worker;
llvm::StringRef CWD;
std::vector<std::string> CommandLine;
// Context - file systems
llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFS;
// Context - Diagnostics engine.
std::unique_ptr<TextDiagnosticsPrinterWithOutput> DiagPrinterWithOS;
// DiagConsumer may points to DiagPrinterWithOS->DiagPrinter, or a custom
// DiagnosticConsumer passed in from initialize.
DiagnosticConsumer *DiagConsumer = nullptr;
std::unique_ptr<DignosticsEngineWithDiagOpts> DiagEngineWithCmdAndOpts;
// Context - compiler invocation
// Compilation's command's arguments may be owned by Alloc when expanded from
// response files, so we need to keep Alloc alive in the context.
llvm::BumpPtrAllocator Alloc;
std::unique_ptr<clang::driver::Driver> Driver;
std::unique_ptr<clang::driver::Compilation> Compilation;
std::unique_ptr<CompilerInvocation> OriginalInvocation;
// Context - output options
std::unique_ptr<DependencyOutputOptions> OutputOpts;
// Context - stable directory handling
llvm::SmallVector<StringRef> StableDirs;
PrebuiltModulesAttrsMap PrebuiltModuleASTMap;
// Compiler Instance
std::unique_ptr<CompilerInstance> CIPtr;
// Source location offset.
int32_t SrcLocOffset = 0;
public:
CompilerInstanceWithContext(DependencyScanningWorker &Worker, StringRef CWD,
const std::vector<std::string> &CMD)
: Worker(Worker), CWD(CWD), CommandLine(CMD) {};
// The three methods below returns false when they fail, with the detail
// accumulated in DiagConsumer.
bool initialize(DiagnosticConsumer *DC);
bool computeDependencies(StringRef ModuleName, DependencyConsumer &Consumer,
DependencyActionController &Controller);
bool finalize();
// The method below turns the return status from the above methods
// into an llvm::Error using a default DiagnosticConsumer.
llvm::Error handleReturnStatus(bool Success);
};
} // namespace dependencies
} // namespace tooling
} // namespace clang
#endif