123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257 |
- //===- ClangScanDeps.cpp - Implementation of clang-scan-deps --------------===//
- //
- // 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
- //
- //===----------------------------------------------------------------------===//
- #include "clang/Frontend/CompilerInstance.h"
- #include "clang/Tooling/CommonOptionsParser.h"
- #include "clang/Tooling/DependencyScanning/DependencyScanningService.h"
- #include "clang/Tooling/DependencyScanning/DependencyScanningTool.h"
- #include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h"
- #include "clang/Tooling/JSONCompilationDatabase.h"
- #include "llvm/Support/InitLLVM.h"
- #include "llvm/Support/Options.h"
- #include "llvm/Support/Program.h"
- #include "llvm/Support/Signals.h"
- #include "llvm/Support/Threading.h"
- #include <mutex>
- #include <thread>
- using namespace clang;
- using namespace tooling::dependencies;
- namespace {
- class SharedStream {
- public:
- SharedStream(raw_ostream &OS) : OS(OS) {}
- void applyLocked(llvm::function_ref<void(raw_ostream &OS)> Fn) {
- std::unique_lock<std::mutex> LockGuard(Lock);
- Fn(OS);
- OS.flush();
- }
- private:
- std::mutex Lock;
- raw_ostream &OS;
- };
- llvm::cl::opt<bool> Help("h", llvm::cl::desc("Alias for -help"),
- llvm::cl::Hidden);
- llvm::cl::OptionCategory DependencyScannerCategory("Tool options");
- static llvm::cl::opt<ScanningMode> ScanMode(
- "mode",
- llvm::cl::desc("The preprocessing mode used to compute the dependencies"),
- llvm::cl::values(
- clEnumValN(ScanningMode::MinimizedSourcePreprocessing,
- "preprocess-minimized-sources",
- "The set of dependencies is computed by preprocessing the "
- "source files that were minimized to only include the "
- "contents that might affect the dependencies"),
- clEnumValN(ScanningMode::CanonicalPreprocessing, "preprocess",
- "The set of dependencies is computed by preprocessing the "
- "unmodified source files")),
- llvm::cl::init(ScanningMode::MinimizedSourcePreprocessing),
- llvm::cl::cat(DependencyScannerCategory));
- llvm::cl::opt<unsigned>
- NumThreads("j", llvm::cl::Optional,
- llvm::cl::desc("Number of worker threads to use (default: use "
- "all concurrent threads)"),
- llvm::cl::init(0), llvm::cl::cat(DependencyScannerCategory));
- llvm::cl::opt<std::string>
- CompilationDB("compilation-database",
- llvm::cl::desc("Compilation database"), llvm::cl::Required,
- llvm::cl::cat(DependencyScannerCategory));
- llvm::cl::opt<bool> ReuseFileManager(
- "reuse-filemanager",
- llvm::cl::desc("Reuse the file manager and its cache between invocations."),
- llvm::cl::init(true), llvm::cl::cat(DependencyScannerCategory));
- llvm::cl::opt<bool> SkipExcludedPPRanges(
- "skip-excluded-pp-ranges",
- llvm::cl::desc(
- "Use the preprocessor optimization that skips excluded conditionals by "
- "bumping the buffer pointer in the lexer instead of lexing the tokens "
- "until reaching the end directive."),
- llvm::cl::init(true), llvm::cl::cat(DependencyScannerCategory));
- llvm::cl::opt<bool> Verbose("v", llvm::cl::Optional,
- llvm::cl::desc("Use verbose output."),
- llvm::cl::init(false),
- llvm::cl::cat(DependencyScannerCategory));
- } // end anonymous namespace
- /// \returns object-file path derived from source-file path.
- static std::string getObjFilePath(StringRef SrcFile) {
- SmallString<128> ObjFileName(SrcFile);
- llvm::sys::path::replace_extension(ObjFileName, "o");
- return ObjFileName.str();
- }
- /// Takes the result of a dependency scan and prints error / dependency files
- /// based on the result.
- ///
- /// \returns True on error.
- static bool handleDependencyToolResult(const std::string &Input,
- llvm::Expected<std::string> &MaybeFile,
- SharedStream &OS, SharedStream &Errs) {
- if (!MaybeFile) {
- llvm::handleAllErrors(
- MaybeFile.takeError(), [&Input, &Errs](llvm::StringError &Err) {
- Errs.applyLocked([&](raw_ostream &OS) {
- OS << "Error while scanning dependencies for " << Input << ":\n";
- OS << Err.getMessage();
- });
- });
- return true;
- }
- OS.applyLocked([&](raw_ostream &OS) { OS << *MaybeFile; });
- return false;
- }
- int main(int argc, const char **argv) {
- llvm::InitLLVM X(argc, argv);
- llvm::cl::HideUnrelatedOptions(DependencyScannerCategory);
- if (!llvm::cl::ParseCommandLineOptions(argc, argv))
- return 1;
- std::string ErrorMessage;
- std::unique_ptr<tooling::JSONCompilationDatabase> Compilations =
- tooling::JSONCompilationDatabase::loadFromFile(
- CompilationDB, ErrorMessage,
- tooling::JSONCommandLineSyntax::AutoDetect);
- if (!Compilations) {
- llvm::errs() << "error: " << ErrorMessage << "\n";
- return 1;
- }
- llvm::cl::PrintOptionValues();
- // By default the tool runs on all inputs in the CDB.
- std::vector<std::pair<std::string, std::string>> Inputs;
- for (const auto &Command : Compilations->getAllCompileCommands())
- Inputs.emplace_back(Command.Filename, Command.Directory);
- // The command options are rewritten to run Clang in preprocessor only mode.
- auto AdjustingCompilations =
- std::make_unique<tooling::ArgumentsAdjustingCompilations>(
- std::move(Compilations));
- AdjustingCompilations->appendArgumentsAdjuster(
- [](const tooling::CommandLineArguments &Args, StringRef FileName) {
- std::string LastO = "";
- bool HasMT = false;
- bool HasMQ = false;
- bool HasMD = false;
- // We need to find the last -o value.
- if (!Args.empty()) {
- std::size_t Idx = Args.size() - 1;
- for (auto It = Args.rbegin(); It != Args.rend(); ++It) {
- if (It != Args.rbegin()) {
- if (Args[Idx] == "-o")
- LastO = Args[Idx + 1];
- if (Args[Idx] == "-MT")
- HasMT = true;
- if (Args[Idx] == "-MQ")
- HasMQ = true;
- if (Args[Idx] == "-MD")
- HasMD = true;
- }
- --Idx;
- }
- }
- // If there's no -MT/-MQ Driver would add -MT with the value of the last
- // -o option.
- tooling::CommandLineArguments AdjustedArgs = Args;
- AdjustedArgs.push_back("-o");
- AdjustedArgs.push_back("/dev/null");
- if (!HasMT && !HasMQ) {
- AdjustedArgs.push_back("-M");
- AdjustedArgs.push_back("-MT");
- // We're interested in source dependencies of an object file.
- if (!HasMD) {
- // FIXME: We are missing the directory unless the -o value is an
- // absolute path.
- AdjustedArgs.push_back(!LastO.empty() ? LastO
- : getObjFilePath(FileName));
- } else {
- AdjustedArgs.push_back(FileName);
- }
- }
- AdjustedArgs.push_back("-Xclang");
- AdjustedArgs.push_back("-Eonly");
- AdjustedArgs.push_back("-Xclang");
- AdjustedArgs.push_back("-sys-header-deps");
- AdjustedArgs.push_back("-Wno-error");
- return AdjustedArgs;
- });
- AdjustingCompilations->appendArgumentsAdjuster(
- tooling::getClangStripSerializeDiagnosticAdjuster());
- SharedStream Errs(llvm::errs());
- // Print out the dependency results to STDOUT by default.
- SharedStream DependencyOS(llvm::outs());
- DependencyScanningService Service(ScanMode, ReuseFileManager,
- SkipExcludedPPRanges);
- #if LLVM_ENABLE_THREADS
- unsigned NumWorkers =
- NumThreads == 0 ? llvm::hardware_concurrency() : NumThreads;
- #else
- unsigned NumWorkers = 1;
- #endif
- std::vector<std::unique_ptr<DependencyScanningTool>> WorkerTools;
- for (unsigned I = 0; I < NumWorkers; ++I)
- WorkerTools.push_back(std::make_unique<DependencyScanningTool>(
- Service, *AdjustingCompilations));
- std::vector<std::thread> WorkerThreads;
- std::atomic<bool> HadErrors(false);
- std::mutex Lock;
- size_t Index = 0;
- if (Verbose) {
- llvm::outs() << "Running clang-scan-deps on " << Inputs.size()
- << " files using " << NumWorkers << " workers\n";
- }
- for (unsigned I = 0; I < NumWorkers; ++I) {
- auto Worker = [I, &Lock, &Index, &Inputs, &HadErrors, &WorkerTools,
- &DependencyOS, &Errs]() {
- while (true) {
- std::string Input;
- StringRef CWD;
- // Take the next input.
- {
- std::unique_lock<std::mutex> LockGuard(Lock);
- if (Index >= Inputs.size())
- return;
- const auto &Compilation = Inputs[Index++];
- Input = Compilation.first;
- CWD = Compilation.second;
- }
- // Run the tool on it.
- auto MaybeFile = WorkerTools[I]->getDependencyFile(Input, CWD);
- if (handleDependencyToolResult(Input, MaybeFile, DependencyOS, Errs))
- HadErrors = true;
- }
- };
- #if LLVM_ENABLE_THREADS
- WorkerThreads.emplace_back(std::move(Worker));
- #else
- // Run the worker without spawning a thread when threads are disabled.
- Worker();
- #endif
- }
- for (auto &W : WorkerThreads)
- W.join();
- return HadErrors;
- }
|