10b57cec5SDimitry Andric //===- CompilationDatabase.cpp --------------------------------------------===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric // This file contains implementations of the CompilationDatabase base class 100b57cec5SDimitry Andric // and the FixedCompilationDatabase. 110b57cec5SDimitry Andric // 120b57cec5SDimitry Andric // FIXME: Various functions that take a string &ErrorMessage should be upgraded 130b57cec5SDimitry Andric // to Expected. 140b57cec5SDimitry Andric // 150b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 160b57cec5SDimitry Andric 170b57cec5SDimitry Andric #include "clang/Tooling/CompilationDatabase.h" 180b57cec5SDimitry Andric #include "clang/Basic/Diagnostic.h" 190b57cec5SDimitry Andric #include "clang/Basic/DiagnosticIDs.h" 200b57cec5SDimitry Andric #include "clang/Basic/DiagnosticOptions.h" 210b57cec5SDimitry Andric #include "clang/Basic/LLVM.h" 220b57cec5SDimitry Andric #include "clang/Driver/Action.h" 230b57cec5SDimitry Andric #include "clang/Driver/Compilation.h" 240b57cec5SDimitry Andric #include "clang/Driver/Driver.h" 250b57cec5SDimitry Andric #include "clang/Driver/DriverDiagnostic.h" 260b57cec5SDimitry Andric #include "clang/Driver/Job.h" 270b57cec5SDimitry Andric #include "clang/Frontend/TextDiagnosticPrinter.h" 280b57cec5SDimitry Andric #include "clang/Tooling/CompilationDatabasePluginRegistry.h" 290b57cec5SDimitry Andric #include "clang/Tooling/Tooling.h" 300b57cec5SDimitry Andric #include "llvm/ADT/ArrayRef.h" 310b57cec5SDimitry Andric #include "llvm/ADT/IntrusiveRefCntPtr.h" 320b57cec5SDimitry Andric #include "llvm/ADT/STLExtras.h" 330b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h" 340b57cec5SDimitry Andric #include "llvm/ADT/SmallVector.h" 350b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h" 360b57cec5SDimitry Andric #include "llvm/Option/Arg.h" 370b57cec5SDimitry Andric #include "llvm/Support/Casting.h" 380b57cec5SDimitry Andric #include "llvm/Support/Compiler.h" 390b57cec5SDimitry Andric #include "llvm/Support/ErrorOr.h" 400b57cec5SDimitry Andric #include "llvm/Support/LineIterator.h" 410b57cec5SDimitry Andric #include "llvm/Support/MemoryBuffer.h" 420b57cec5SDimitry Andric #include "llvm/Support/Path.h" 430b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h" 4406c3fb27SDimitry Andric #include "llvm/TargetParser/Host.h" 450b57cec5SDimitry Andric #include <algorithm> 460b57cec5SDimitry Andric #include <cassert> 470b57cec5SDimitry Andric #include <cstring> 480b57cec5SDimitry Andric #include <iterator> 490b57cec5SDimitry Andric #include <memory> 500b57cec5SDimitry Andric #include <sstream> 510b57cec5SDimitry Andric #include <string> 520b57cec5SDimitry Andric #include <system_error> 530b57cec5SDimitry Andric #include <utility> 540b57cec5SDimitry Andric #include <vector> 550b57cec5SDimitry Andric 560b57cec5SDimitry Andric using namespace clang; 570b57cec5SDimitry Andric using namespace tooling; 580b57cec5SDimitry Andric 590b57cec5SDimitry Andric LLVM_INSTANTIATE_REGISTRY(CompilationDatabasePluginRegistry) 600b57cec5SDimitry Andric 610b57cec5SDimitry Andric CompilationDatabase::~CompilationDatabase() = default; 620b57cec5SDimitry Andric 630b57cec5SDimitry Andric std::unique_ptr<CompilationDatabase> 640b57cec5SDimitry Andric CompilationDatabase::loadFromDirectory(StringRef BuildDirectory, 650b57cec5SDimitry Andric std::string &ErrorMessage) { 660b57cec5SDimitry Andric llvm::raw_string_ostream ErrorStream(ErrorMessage); 675ffd83dbSDimitry Andric for (const CompilationDatabasePluginRegistry::entry &Database : 685ffd83dbSDimitry Andric CompilationDatabasePluginRegistry::entries()) { 690b57cec5SDimitry Andric std::string DatabaseErrorMessage; 705ffd83dbSDimitry Andric std::unique_ptr<CompilationDatabasePlugin> Plugin(Database.instantiate()); 710b57cec5SDimitry Andric if (std::unique_ptr<CompilationDatabase> DB = 720b57cec5SDimitry Andric Plugin->loadFromDirectory(BuildDirectory, DatabaseErrorMessage)) 730b57cec5SDimitry Andric return DB; 745ffd83dbSDimitry Andric ErrorStream << Database.getName() << ": " << DatabaseErrorMessage << "\n"; 750b57cec5SDimitry Andric } 760b57cec5SDimitry Andric return nullptr; 770b57cec5SDimitry Andric } 780b57cec5SDimitry Andric 790b57cec5SDimitry Andric static std::unique_ptr<CompilationDatabase> 800b57cec5SDimitry Andric findCompilationDatabaseFromDirectory(StringRef Directory, 810b57cec5SDimitry Andric std::string &ErrorMessage) { 820b57cec5SDimitry Andric std::stringstream ErrorStream; 830b57cec5SDimitry Andric bool HasErrorMessage = false; 840b57cec5SDimitry Andric while (!Directory.empty()) { 850b57cec5SDimitry Andric std::string LoadErrorMessage; 860b57cec5SDimitry Andric 870b57cec5SDimitry Andric if (std::unique_ptr<CompilationDatabase> DB = 880b57cec5SDimitry Andric CompilationDatabase::loadFromDirectory(Directory, LoadErrorMessage)) 890b57cec5SDimitry Andric return DB; 900b57cec5SDimitry Andric 910b57cec5SDimitry Andric if (!HasErrorMessage) { 920b57cec5SDimitry Andric ErrorStream << "No compilation database found in " << Directory.str() 930b57cec5SDimitry Andric << " or any parent directory\n" << LoadErrorMessage; 940b57cec5SDimitry Andric HasErrorMessage = true; 950b57cec5SDimitry Andric } 960b57cec5SDimitry Andric 970b57cec5SDimitry Andric Directory = llvm::sys::path::parent_path(Directory); 980b57cec5SDimitry Andric } 990b57cec5SDimitry Andric ErrorMessage = ErrorStream.str(); 1000b57cec5SDimitry Andric return nullptr; 1010b57cec5SDimitry Andric } 1020b57cec5SDimitry Andric 1030b57cec5SDimitry Andric std::unique_ptr<CompilationDatabase> 1040b57cec5SDimitry Andric CompilationDatabase::autoDetectFromSource(StringRef SourceFile, 1050b57cec5SDimitry Andric std::string &ErrorMessage) { 1060b57cec5SDimitry Andric SmallString<1024> AbsolutePath(getAbsolutePath(SourceFile)); 1070b57cec5SDimitry Andric StringRef Directory = llvm::sys::path::parent_path(AbsolutePath); 1080b57cec5SDimitry Andric 1090b57cec5SDimitry Andric std::unique_ptr<CompilationDatabase> DB = 1100b57cec5SDimitry Andric findCompilationDatabaseFromDirectory(Directory, ErrorMessage); 1110b57cec5SDimitry Andric 1120b57cec5SDimitry Andric if (!DB) 1130b57cec5SDimitry Andric ErrorMessage = ("Could not auto-detect compilation database for file \"" + 1140b57cec5SDimitry Andric SourceFile + "\"\n" + ErrorMessage).str(); 1150b57cec5SDimitry Andric return DB; 1160b57cec5SDimitry Andric } 1170b57cec5SDimitry Andric 1180b57cec5SDimitry Andric std::unique_ptr<CompilationDatabase> 1190b57cec5SDimitry Andric CompilationDatabase::autoDetectFromDirectory(StringRef SourceDir, 1200b57cec5SDimitry Andric std::string &ErrorMessage) { 1210b57cec5SDimitry Andric SmallString<1024> AbsolutePath(getAbsolutePath(SourceDir)); 1220b57cec5SDimitry Andric 1230b57cec5SDimitry Andric std::unique_ptr<CompilationDatabase> DB = 1240b57cec5SDimitry Andric findCompilationDatabaseFromDirectory(AbsolutePath, ErrorMessage); 1250b57cec5SDimitry Andric 1260b57cec5SDimitry Andric if (!DB) 1270b57cec5SDimitry Andric ErrorMessage = ("Could not auto-detect compilation database from directory \"" + 1280b57cec5SDimitry Andric SourceDir + "\"\n" + ErrorMessage).str(); 1290b57cec5SDimitry Andric return DB; 1300b57cec5SDimitry Andric } 1310b57cec5SDimitry Andric 1320b57cec5SDimitry Andric std::vector<CompileCommand> CompilationDatabase::getAllCompileCommands() const { 1330b57cec5SDimitry Andric std::vector<CompileCommand> Result; 1340b57cec5SDimitry Andric for (const auto &File : getAllFiles()) { 1350b57cec5SDimitry Andric auto C = getCompileCommands(File); 1360b57cec5SDimitry Andric std::move(C.begin(), C.end(), std::back_inserter(Result)); 1370b57cec5SDimitry Andric } 1380b57cec5SDimitry Andric return Result; 1390b57cec5SDimitry Andric } 1400b57cec5SDimitry Andric 1410b57cec5SDimitry Andric CompilationDatabasePlugin::~CompilationDatabasePlugin() = default; 1420b57cec5SDimitry Andric 1430b57cec5SDimitry Andric namespace { 1440b57cec5SDimitry Andric 1450b57cec5SDimitry Andric // Helper for recursively searching through a chain of actions and collecting 1460b57cec5SDimitry Andric // all inputs, direct and indirect, of compile jobs. 1470b57cec5SDimitry Andric struct CompileJobAnalyzer { 1480b57cec5SDimitry Andric SmallVector<std::string, 2> Inputs; 1490b57cec5SDimitry Andric 1500b57cec5SDimitry Andric void run(const driver::Action *A) { 1510b57cec5SDimitry Andric runImpl(A, false); 1520b57cec5SDimitry Andric } 1530b57cec5SDimitry Andric 1540b57cec5SDimitry Andric private: 1550b57cec5SDimitry Andric void runImpl(const driver::Action *A, bool Collect) { 1560b57cec5SDimitry Andric bool CollectChildren = Collect; 1570b57cec5SDimitry Andric switch (A->getKind()) { 1580b57cec5SDimitry Andric case driver::Action::CompileJobClass: 159*7a6dacacSDimitry Andric case driver::Action::PrecompileJobClass: 1600b57cec5SDimitry Andric CollectChildren = true; 1610b57cec5SDimitry Andric break; 1620b57cec5SDimitry Andric 1630b57cec5SDimitry Andric case driver::Action::InputClass: 1640b57cec5SDimitry Andric if (Collect) { 1650b57cec5SDimitry Andric const auto *IA = cast<driver::InputAction>(A); 1665ffd83dbSDimitry Andric Inputs.push_back(std::string(IA->getInputArg().getSpelling())); 1670b57cec5SDimitry Andric } 1680b57cec5SDimitry Andric break; 1690b57cec5SDimitry Andric 1700b57cec5SDimitry Andric default: 1710b57cec5SDimitry Andric // Don't care about others 1720b57cec5SDimitry Andric break; 1730b57cec5SDimitry Andric } 1740b57cec5SDimitry Andric 1750b57cec5SDimitry Andric for (const driver::Action *AI : A->inputs()) 1760b57cec5SDimitry Andric runImpl(AI, CollectChildren); 1770b57cec5SDimitry Andric } 1780b57cec5SDimitry Andric }; 1790b57cec5SDimitry Andric 1800b57cec5SDimitry Andric // Special DiagnosticConsumer that looks for warn_drv_input_file_unused 1810b57cec5SDimitry Andric // diagnostics from the driver and collects the option strings for those unused 1820b57cec5SDimitry Andric // options. 1830b57cec5SDimitry Andric class UnusedInputDiagConsumer : public DiagnosticConsumer { 1840b57cec5SDimitry Andric public: 1850b57cec5SDimitry Andric UnusedInputDiagConsumer(DiagnosticConsumer &Other) : Other(Other) {} 1860b57cec5SDimitry Andric 1870b57cec5SDimitry Andric void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, 1880b57cec5SDimitry Andric const Diagnostic &Info) override { 1890b57cec5SDimitry Andric if (Info.getID() == diag::warn_drv_input_file_unused) { 1900b57cec5SDimitry Andric // Arg 1 for this diagnostic is the option that didn't get used. 1910b57cec5SDimitry Andric UnusedInputs.push_back(Info.getArgStdStr(0)); 1920b57cec5SDimitry Andric } else if (DiagLevel >= DiagnosticsEngine::Error) { 1930b57cec5SDimitry Andric // If driver failed to create compilation object, show the diagnostics 1940b57cec5SDimitry Andric // to user. 1950b57cec5SDimitry Andric Other.HandleDiagnostic(DiagLevel, Info); 1960b57cec5SDimitry Andric } 1970b57cec5SDimitry Andric } 1980b57cec5SDimitry Andric 1990b57cec5SDimitry Andric DiagnosticConsumer &Other; 2000b57cec5SDimitry Andric SmallVector<std::string, 2> UnusedInputs; 2010b57cec5SDimitry Andric }; 2020b57cec5SDimitry Andric 2030b57cec5SDimitry Andric // Filter of tools unused flags such as -no-integrated-as and -Wa,*. 2040b57cec5SDimitry Andric // They are not used for syntax checking, and could confuse targets 2050b57cec5SDimitry Andric // which don't support these options. 2060b57cec5SDimitry Andric struct FilterUnusedFlags { 2070b57cec5SDimitry Andric bool operator() (StringRef S) { 2085f757f3fSDimitry Andric return (S == "-no-integrated-as") || S.starts_with("-Wa,"); 2090b57cec5SDimitry Andric } 2100b57cec5SDimitry Andric }; 2110b57cec5SDimitry Andric 2120b57cec5SDimitry Andric std::string GetClangToolCommand() { 2130b57cec5SDimitry Andric static int Dummy; 2140b57cec5SDimitry Andric std::string ClangExecutable = 2150b57cec5SDimitry Andric llvm::sys::fs::getMainExecutable("clang", (void *)&Dummy); 2160b57cec5SDimitry Andric SmallString<128> ClangToolPath; 2170b57cec5SDimitry Andric ClangToolPath = llvm::sys::path::parent_path(ClangExecutable); 2180b57cec5SDimitry Andric llvm::sys::path::append(ClangToolPath, "clang-tool"); 219*7a6dacacSDimitry Andric return std::string(ClangToolPath); 2200b57cec5SDimitry Andric } 2210b57cec5SDimitry Andric 2220b57cec5SDimitry Andric } // namespace 2230b57cec5SDimitry Andric 2240b57cec5SDimitry Andric /// Strips any positional args and possible argv[0] from a command-line 2250b57cec5SDimitry Andric /// provided by the user to construct a FixedCompilationDatabase. 2260b57cec5SDimitry Andric /// 2270b57cec5SDimitry Andric /// FixedCompilationDatabase requires a command line to be in this format as it 2280b57cec5SDimitry Andric /// constructs the command line for each file by appending the name of the file 2290b57cec5SDimitry Andric /// to be compiled. FixedCompilationDatabase also adds its own argv[0] to the 2300b57cec5SDimitry Andric /// start of the command line although its value is not important as it's just 2310b57cec5SDimitry Andric /// ignored by the Driver invoked by the ClangTool using the 2320b57cec5SDimitry Andric /// FixedCompilationDatabase. 2330b57cec5SDimitry Andric /// 2340b57cec5SDimitry Andric /// FIXME: This functionality should probably be made available by 2350b57cec5SDimitry Andric /// clang::driver::Driver although what the interface should look like is not 2360b57cec5SDimitry Andric /// clear. 2370b57cec5SDimitry Andric /// 2380b57cec5SDimitry Andric /// \param[in] Args Args as provided by the user. 2390b57cec5SDimitry Andric /// \return Resulting stripped command line. 2400b57cec5SDimitry Andric /// \li true if successful. 2410b57cec5SDimitry Andric /// \li false if \c Args cannot be used for compilation jobs (e.g. 2420b57cec5SDimitry Andric /// contains an option like -E or -version). 2430b57cec5SDimitry Andric static bool stripPositionalArgs(std::vector<const char *> Args, 2440b57cec5SDimitry Andric std::vector<std::string> &Result, 2450b57cec5SDimitry Andric std::string &ErrorMsg) { 2460b57cec5SDimitry Andric IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); 2470b57cec5SDimitry Andric llvm::raw_string_ostream Output(ErrorMsg); 2480b57cec5SDimitry Andric TextDiagnosticPrinter DiagnosticPrinter(Output, &*DiagOpts); 2490b57cec5SDimitry Andric UnusedInputDiagConsumer DiagClient(DiagnosticPrinter); 2500b57cec5SDimitry Andric DiagnosticsEngine Diagnostics( 2510b57cec5SDimitry Andric IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), 2520b57cec5SDimitry Andric &*DiagOpts, &DiagClient, false); 2530b57cec5SDimitry Andric 2540b57cec5SDimitry Andric // The clang executable path isn't required since the jobs the driver builds 2550b57cec5SDimitry Andric // will not be executed. 2560b57cec5SDimitry Andric std::unique_ptr<driver::Driver> NewDriver(new driver::Driver( 2570b57cec5SDimitry Andric /* ClangExecutable= */ "", llvm::sys::getDefaultTargetTriple(), 2580b57cec5SDimitry Andric Diagnostics)); 2590b57cec5SDimitry Andric NewDriver->setCheckInputsExist(false); 2600b57cec5SDimitry Andric 2610b57cec5SDimitry Andric // This becomes the new argv[0]. The value is used to detect libc++ include 2620b57cec5SDimitry Andric // dirs on Mac, it isn't used for other platforms. 2630b57cec5SDimitry Andric std::string Argv0 = GetClangToolCommand(); 2640b57cec5SDimitry Andric Args.insert(Args.begin(), Argv0.c_str()); 2650b57cec5SDimitry Andric 2660b57cec5SDimitry Andric // By adding -c, we force the driver to treat compilation as the last phase. 2670b57cec5SDimitry Andric // It will then issue warnings via Diagnostics about un-used options that 2680b57cec5SDimitry Andric // would have been used for linking. If the user provided a compiler name as 2690b57cec5SDimitry Andric // the original argv[0], this will be treated as a linker input thanks to 2700b57cec5SDimitry Andric // insertng a new argv[0] above. All un-used options get collected by 2710b57cec5SDimitry Andric // UnusedInputdiagConsumer and get stripped out later. 2720b57cec5SDimitry Andric Args.push_back("-c"); 2730b57cec5SDimitry Andric 2740b57cec5SDimitry Andric // Put a dummy C++ file on to ensure there's at least one compile job for the 2750b57cec5SDimitry Andric // driver to construct. If the user specified some other argument that 2760b57cec5SDimitry Andric // prevents compilation, e.g. -E or something like -version, we may still end 2770b57cec5SDimitry Andric // up with no jobs but then this is the user's fault. 2780b57cec5SDimitry Andric Args.push_back("placeholder.cpp"); 2790b57cec5SDimitry Andric 280e8d8bef9SDimitry Andric llvm::erase_if(Args, FilterUnusedFlags()); 2810b57cec5SDimitry Andric 2820b57cec5SDimitry Andric const std::unique_ptr<driver::Compilation> Compilation( 2830b57cec5SDimitry Andric NewDriver->BuildCompilation(Args)); 2840b57cec5SDimitry Andric if (!Compilation) 2850b57cec5SDimitry Andric return false; 2860b57cec5SDimitry Andric 2870b57cec5SDimitry Andric const driver::JobList &Jobs = Compilation->getJobs(); 2880b57cec5SDimitry Andric 2890b57cec5SDimitry Andric CompileJobAnalyzer CompileAnalyzer; 2900b57cec5SDimitry Andric 2910b57cec5SDimitry Andric for (const auto &Cmd : Jobs) { 2920b57cec5SDimitry Andric // Collect only for Assemble, Backend, and Compile jobs. If we do all jobs 2930b57cec5SDimitry Andric // we get duplicates since Link jobs point to Assemble jobs as inputs. 2940b57cec5SDimitry Andric // -flto* flags make the BackendJobClass, which still needs analyzer. 2950b57cec5SDimitry Andric if (Cmd.getSource().getKind() == driver::Action::AssembleJobClass || 2960b57cec5SDimitry Andric Cmd.getSource().getKind() == driver::Action::BackendJobClass || 297*7a6dacacSDimitry Andric Cmd.getSource().getKind() == driver::Action::CompileJobClass || 298*7a6dacacSDimitry Andric Cmd.getSource().getKind() == driver::Action::PrecompileJobClass) { 2990b57cec5SDimitry Andric CompileAnalyzer.run(&Cmd.getSource()); 3000b57cec5SDimitry Andric } 3010b57cec5SDimitry Andric } 3020b57cec5SDimitry Andric 3030b57cec5SDimitry Andric if (CompileAnalyzer.Inputs.empty()) { 3040b57cec5SDimitry Andric ErrorMsg = "warning: no compile jobs found\n"; 3050b57cec5SDimitry Andric return false; 3060b57cec5SDimitry Andric } 3070b57cec5SDimitry Andric 308e8d8bef9SDimitry Andric // Remove all compilation input files from the command line and inputs deemed 309e8d8bef9SDimitry Andric // unused for compilation. This is necessary so that getCompileCommands() can 310e8d8bef9SDimitry Andric // construct a command line for each file. 311e8d8bef9SDimitry Andric std::vector<const char *>::iterator End = 312e8d8bef9SDimitry Andric llvm::remove_if(Args, [&](StringRef S) { 313e8d8bef9SDimitry Andric return llvm::is_contained(CompileAnalyzer.Inputs, S) || 314e8d8bef9SDimitry Andric llvm::is_contained(DiagClient.UnusedInputs, S); 315e8d8bef9SDimitry Andric }); 3160b57cec5SDimitry Andric // Remove the -c add above as well. It will be at the end right now. 3170b57cec5SDimitry Andric assert(strcmp(*(End - 1), "-c") == 0); 3180b57cec5SDimitry Andric --End; 3190b57cec5SDimitry Andric 3200b57cec5SDimitry Andric Result = std::vector<std::string>(Args.begin() + 1, End); 3210b57cec5SDimitry Andric return true; 3220b57cec5SDimitry Andric } 3230b57cec5SDimitry Andric 3240b57cec5SDimitry Andric std::unique_ptr<FixedCompilationDatabase> 3250b57cec5SDimitry Andric FixedCompilationDatabase::loadFromCommandLine(int &Argc, 3260b57cec5SDimitry Andric const char *const *Argv, 3270b57cec5SDimitry Andric std::string &ErrorMsg, 328e8d8bef9SDimitry Andric const Twine &Directory) { 3290b57cec5SDimitry Andric ErrorMsg.clear(); 3300b57cec5SDimitry Andric if (Argc == 0) 3310b57cec5SDimitry Andric return nullptr; 3320b57cec5SDimitry Andric const char *const *DoubleDash = std::find(Argv, Argv + Argc, StringRef("--")); 3330b57cec5SDimitry Andric if (DoubleDash == Argv + Argc) 3340b57cec5SDimitry Andric return nullptr; 3350b57cec5SDimitry Andric std::vector<const char *> CommandLine(DoubleDash + 1, Argv + Argc); 3360b57cec5SDimitry Andric Argc = DoubleDash - Argv; 3370b57cec5SDimitry Andric 3380b57cec5SDimitry Andric std::vector<std::string> StrippedArgs; 3390b57cec5SDimitry Andric if (!stripPositionalArgs(CommandLine, StrippedArgs, ErrorMsg)) 3400b57cec5SDimitry Andric return nullptr; 341a7dea167SDimitry Andric return std::make_unique<FixedCompilationDatabase>(Directory, StrippedArgs); 3420b57cec5SDimitry Andric } 3430b57cec5SDimitry Andric 3440b57cec5SDimitry Andric std::unique_ptr<FixedCompilationDatabase> 3450b57cec5SDimitry Andric FixedCompilationDatabase::loadFromFile(StringRef Path, std::string &ErrorMsg) { 3460b57cec5SDimitry Andric ErrorMsg.clear(); 3470b57cec5SDimitry Andric llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> File = 3480b57cec5SDimitry Andric llvm::MemoryBuffer::getFile(Path); 3490b57cec5SDimitry Andric if (std::error_code Result = File.getError()) { 3500b57cec5SDimitry Andric ErrorMsg = "Error while opening fixed database: " + Result.message(); 3510b57cec5SDimitry Andric return nullptr; 3520b57cec5SDimitry Andric } 353e8d8bef9SDimitry Andric return loadFromBuffer(llvm::sys::path::parent_path(Path), 354e8d8bef9SDimitry Andric (*File)->getBuffer(), ErrorMsg); 355e8d8bef9SDimitry Andric } 356e8d8bef9SDimitry Andric 357e8d8bef9SDimitry Andric std::unique_ptr<FixedCompilationDatabase> 358e8d8bef9SDimitry Andric FixedCompilationDatabase::loadFromBuffer(StringRef Directory, StringRef Data, 359e8d8bef9SDimitry Andric std::string &ErrorMsg) { 360e8d8bef9SDimitry Andric ErrorMsg.clear(); 3615ffd83dbSDimitry Andric std::vector<std::string> Args; 362e8d8bef9SDimitry Andric StringRef Line; 363e8d8bef9SDimitry Andric while (!Data.empty()) { 364e8d8bef9SDimitry Andric std::tie(Line, Data) = Data.split('\n'); 3655ffd83dbSDimitry Andric // Stray whitespace is almost certainly unintended. 3665ffd83dbSDimitry Andric Line = Line.trim(); 3675ffd83dbSDimitry Andric if (!Line.empty()) 3685ffd83dbSDimitry Andric Args.push_back(Line.str()); 3695ffd83dbSDimitry Andric } 370e8d8bef9SDimitry Andric return std::make_unique<FixedCompilationDatabase>(Directory, std::move(Args)); 3710b57cec5SDimitry Andric } 3720b57cec5SDimitry Andric 373e8d8bef9SDimitry Andric FixedCompilationDatabase::FixedCompilationDatabase( 374e8d8bef9SDimitry Andric const Twine &Directory, ArrayRef<std::string> CommandLine) { 3750b57cec5SDimitry Andric std::vector<std::string> ToolCommandLine(1, GetClangToolCommand()); 3760b57cec5SDimitry Andric ToolCommandLine.insert(ToolCommandLine.end(), 3770b57cec5SDimitry Andric CommandLine.begin(), CommandLine.end()); 3780b57cec5SDimitry Andric CompileCommands.emplace_back(Directory, StringRef(), 3790b57cec5SDimitry Andric std::move(ToolCommandLine), 3800b57cec5SDimitry Andric StringRef()); 3810b57cec5SDimitry Andric } 3820b57cec5SDimitry Andric 3830b57cec5SDimitry Andric std::vector<CompileCommand> 3840b57cec5SDimitry Andric FixedCompilationDatabase::getCompileCommands(StringRef FilePath) const { 3850b57cec5SDimitry Andric std::vector<CompileCommand> Result(CompileCommands); 3865ffd83dbSDimitry Andric Result[0].CommandLine.push_back(std::string(FilePath)); 3875ffd83dbSDimitry Andric Result[0].Filename = std::string(FilePath); 3880b57cec5SDimitry Andric return Result; 3890b57cec5SDimitry Andric } 3900b57cec5SDimitry Andric 3910b57cec5SDimitry Andric namespace { 3920b57cec5SDimitry Andric 3930b57cec5SDimitry Andric class FixedCompilationDatabasePlugin : public CompilationDatabasePlugin { 3940b57cec5SDimitry Andric std::unique_ptr<CompilationDatabase> 3950b57cec5SDimitry Andric loadFromDirectory(StringRef Directory, std::string &ErrorMessage) override { 3960b57cec5SDimitry Andric SmallString<1024> DatabasePath(Directory); 3970b57cec5SDimitry Andric llvm::sys::path::append(DatabasePath, "compile_flags.txt"); 3980b57cec5SDimitry Andric return FixedCompilationDatabase::loadFromFile(DatabasePath, ErrorMessage); 3990b57cec5SDimitry Andric } 4000b57cec5SDimitry Andric }; 4010b57cec5SDimitry Andric 4020b57cec5SDimitry Andric } // namespace 4030b57cec5SDimitry Andric 4040b57cec5SDimitry Andric static CompilationDatabasePluginRegistry::Add<FixedCompilationDatabasePlugin> 4050b57cec5SDimitry Andric X("fixed-compilation-database", "Reads plain-text flags file"); 4060b57cec5SDimitry Andric 4070b57cec5SDimitry Andric namespace clang { 4080b57cec5SDimitry Andric namespace tooling { 4090b57cec5SDimitry Andric 4100b57cec5SDimitry Andric // This anchor is used to force the linker to link in the generated object file 4110b57cec5SDimitry Andric // and thus register the JSONCompilationDatabasePlugin. 4120b57cec5SDimitry Andric extern volatile int JSONAnchorSource; 4130b57cec5SDimitry Andric static int LLVM_ATTRIBUTE_UNUSED JSONAnchorDest = JSONAnchorSource; 4140b57cec5SDimitry Andric 4150b57cec5SDimitry Andric } // namespace tooling 4160b57cec5SDimitry Andric } // namespace clang 417