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>
loadFromDirectory(StringRef BuildDirectory,std::string & ErrorMessage)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>
findCompilationDatabaseFromDirectory(StringRef Directory,std::string & ErrorMessage)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>
autoDetectFromSource(StringRef SourceFile,std::string & ErrorMessage)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>
autoDetectFromDirectory(StringRef SourceDir,std::string & ErrorMessage)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
getAllCompileCommands() const1320b57cec5SDimitry 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
run__anon01d1340b0111::CompileJobAnalyzer1500b57cec5SDimitry Andric void run(const driver::Action *A) {
1510b57cec5SDimitry Andric runImpl(A, false);
1520b57cec5SDimitry Andric }
1530b57cec5SDimitry Andric
1540b57cec5SDimitry Andric private:
runImpl__anon01d1340b0111::CompileJobAnalyzer1550b57cec5SDimitry 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:
UnusedInputDiagConsumer(DiagnosticConsumer & Other)1850b57cec5SDimitry Andric UnusedInputDiagConsumer(DiagnosticConsumer &Other) : Other(Other) {}
1860b57cec5SDimitry Andric
HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,const Diagnostic & Info)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 {
operator ()__anon01d1340b0111::FilterUnusedFlags2070b57cec5SDimitry Andric bool operator() (StringRef S) {
2085f757f3fSDimitry Andric return (S == "-no-integrated-as") || S.starts_with("-Wa,");
2090b57cec5SDimitry Andric }
2100b57cec5SDimitry Andric };
2110b57cec5SDimitry Andric
GetClangToolCommand()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).
stripPositionalArgs(std::vector<const char * > Args,std::vector<std::string> & Result,std::string & ErrorMsg)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>
loadFromCommandLine(int & Argc,const char * const * Argv,std::string & ErrorMsg,const Twine & Directory)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>
loadFromFile(StringRef Path,std::string & ErrorMsg)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>
loadFromBuffer(StringRef Directory,StringRef Data,std::string & ErrorMsg)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
FixedCompilationDatabase(const Twine & Directory,ArrayRef<std::string> CommandLine)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>
getCompileCommands(StringRef FilePath) const3840b57cec5SDimitry 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>
loadFromDirectory(StringRef Directory,std::string & ErrorMessage)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