xref: /freebsd-src/contrib/llvm-project/clang/lib/Tooling/CompilationDatabase.cpp (revision 7a6dacaca14b62ca4b74406814becb87a3fefac0)
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