16366efedSEugene Zelenko //===- CompilationDatabase.cpp --------------------------------------------===//
247c245a5SManuel Klimek //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
647c245a5SManuel Klimek //
747c245a5SManuel Klimek //===----------------------------------------------------------------------===//
847c245a5SManuel Klimek //
96ed1f85cSDaniel Jasper // This file contains implementations of the CompilationDatabase base class
106ed1f85cSDaniel Jasper // and the FixedCompilationDatabase.
1147c245a5SManuel Klimek //
1260d74e45SSam McCall // FIXME: Various functions that take a string &ErrorMessage should be upgraded
1360d74e45SSam McCall // to Expected.
1460d74e45SSam McCall //
1547c245a5SManuel Klimek //===----------------------------------------------------------------------===//
1647c245a5SManuel Klimek
1747c245a5SManuel Klimek #include "clang/Tooling/CompilationDatabase.h"
18eb56f4feSEdwin Vane #include "clang/Basic/Diagnostic.h"
196366efedSEugene Zelenko #include "clang/Basic/DiagnosticIDs.h"
20f3ca2698SBenjamin Kramer #include "clang/Basic/DiagnosticOptions.h"
216366efedSEugene Zelenko #include "clang/Basic/LLVM.h"
22eb56f4feSEdwin Vane #include "clang/Driver/Action.h"
235553d0d4SChandler Carruth #include "clang/Driver/Compilation.h"
24eb56f4feSEdwin Vane #include "clang/Driver/Driver.h"
25eb56f4feSEdwin Vane #include "clang/Driver/DriverDiagnostic.h"
26eb56f4feSEdwin Vane #include "clang/Driver/Job.h"
27eb56f4feSEdwin Vane #include "clang/Frontend/TextDiagnosticPrinter.h"
285553d0d4SChandler Carruth #include "clang/Tooling/CompilationDatabasePluginRegistry.h"
295553d0d4SChandler Carruth #include "clang/Tooling/Tooling.h"
306366efedSEugene Zelenko #include "llvm/ADT/ArrayRef.h"
316366efedSEugene Zelenko #include "llvm/ADT/IntrusiveRefCntPtr.h"
326366efedSEugene Zelenko #include "llvm/ADT/STLExtras.h"
335553d0d4SChandler Carruth #include "llvm/ADT/SmallString.h"
346366efedSEugene Zelenko #include "llvm/ADT/SmallVector.h"
356366efedSEugene Zelenko #include "llvm/ADT/StringRef.h"
36eb56f4feSEdwin Vane #include "llvm/Option/Arg.h"
376366efedSEugene Zelenko #include "llvm/Support/Casting.h"
386366efedSEugene Zelenko #include "llvm/Support/Compiler.h"
396366efedSEugene Zelenko #include "llvm/Support/ErrorOr.h"
405553d0d4SChandler Carruth #include "llvm/Support/Host.h"
4160d74e45SSam McCall #include "llvm/Support/LineIterator.h"
426366efedSEugene Zelenko #include "llvm/Support/MemoryBuffer.h"
435553d0d4SChandler Carruth #include "llvm/Support/Path.h"
44c46064c2SSerge Pavlov #include "llvm/Support/raw_ostream.h"
456366efedSEugene Zelenko #include <algorithm>
466366efedSEugene Zelenko #include <cassert>
476366efedSEugene Zelenko #include <cstring>
486366efedSEugene Zelenko #include <iterator>
496366efedSEugene Zelenko #include <memory>
505553d0d4SChandler Carruth #include <sstream>
516366efedSEugene Zelenko #include <string>
528a8e554aSRafael Espindola #include <system_error>
536366efedSEugene Zelenko #include <utility>
546366efedSEugene Zelenko #include <vector>
556366efedSEugene Zelenko
566a1457e6SBenjamin Kramer using namespace clang;
576a1457e6SBenjamin Kramer using namespace tooling;
5847c245a5SManuel Klimek
594d79ec7fSJohn Brawn LLVM_INSTANTIATE_REGISTRY(CompilationDatabasePluginRegistry)
604d79ec7fSJohn Brawn
616366efedSEugene Zelenko CompilationDatabase::~CompilationDatabase() = default;
6247c245a5SManuel Klimek
63cdba84c0SDavid Blaikie std::unique_ptr<CompilationDatabase>
loadFromDirectory(StringRef BuildDirectory,std::string & ErrorMessage)6447c245a5SManuel Klimek CompilationDatabase::loadFromDirectory(StringRef BuildDirectory,
6547c245a5SManuel Klimek std::string &ErrorMessage) {
662d487db0SMehdi Amini llvm::raw_string_ostream ErrorStream(ErrorMessage);
678b0df1c1SNathan James for (const CompilationDatabasePluginRegistry::entry &Database :
688b0df1c1SNathan James CompilationDatabasePluginRegistry::entries()) {
696ed1f85cSDaniel Jasper std::string DatabaseErrorMessage;
708b0df1c1SNathan James std::unique_ptr<CompilationDatabasePlugin> Plugin(Database.instantiate());
71cdba84c0SDavid Blaikie if (std::unique_ptr<CompilationDatabase> DB =
726ed1f85cSDaniel Jasper Plugin->loadFromDirectory(BuildDirectory, DatabaseErrorMessage))
736ed1f85cSDaniel Jasper return DB;
748b0df1c1SNathan James ErrorStream << Database.getName() << ": " << DatabaseErrorMessage << "\n";
7547c245a5SManuel Klimek }
76ccbc35edSCraig Topper return nullptr;
7747c245a5SManuel Klimek }
7847c245a5SManuel Klimek
79cdba84c0SDavid Blaikie static std::unique_ptr<CompilationDatabase>
findCompilationDatabaseFromDirectory(StringRef Directory,std::string & ErrorMessage)806ed1f85cSDaniel Jasper findCompilationDatabaseFromDirectory(StringRef Directory,
816ed1f85cSDaniel Jasper std::string &ErrorMessage) {
826ed1f85cSDaniel Jasper std::stringstream ErrorStream;
8374351ff4SDaniel Jasper bool HasErrorMessage = false;
84617f5269SArnaud A. de Grandmaison while (!Directory.empty()) {
85617f5269SArnaud A. de Grandmaison std::string LoadErrorMessage;
86617f5269SArnaud A. de Grandmaison
87cdba84c0SDavid Blaikie if (std::unique_ptr<CompilationDatabase> DB =
88617f5269SArnaud A. de Grandmaison CompilationDatabase::loadFromDirectory(Directory, LoadErrorMessage))
89617f5269SArnaud A. de Grandmaison return DB;
9074351ff4SDaniel Jasper
9174351ff4SDaniel Jasper if (!HasErrorMessage) {
926ed1f85cSDaniel Jasper ErrorStream << "No compilation database found in " << Directory.str()
9374351ff4SDaniel Jasper << " or any parent directory\n" << LoadErrorMessage;
9474351ff4SDaniel Jasper HasErrorMessage = true;
9574351ff4SDaniel Jasper }
96617f5269SArnaud A. de Grandmaison
97617f5269SArnaud A. de Grandmaison Directory = llvm::sys::path::parent_path(Directory);
98617f5269SArnaud A. de Grandmaison }
996ed1f85cSDaniel Jasper ErrorMessage = ErrorStream.str();
100ccbc35edSCraig Topper return nullptr;
101617f5269SArnaud A. de Grandmaison }
102617f5269SArnaud A. de Grandmaison
103cdba84c0SDavid Blaikie std::unique_ptr<CompilationDatabase>
autoDetectFromSource(StringRef SourceFile,std::string & ErrorMessage)10465fd0e1fSManuel Klimek CompilationDatabase::autoDetectFromSource(StringRef SourceFile,
10565fd0e1fSManuel Klimek std::string &ErrorMessage) {
106f857950dSDmitri Gribenko SmallString<1024> AbsolutePath(getAbsolutePath(SourceFile));
10765fd0e1fSManuel Klimek StringRef Directory = llvm::sys::path::parent_path(AbsolutePath);
108617f5269SArnaud A. de Grandmaison
109cdba84c0SDavid Blaikie std::unique_ptr<CompilationDatabase> DB =
110cdba84c0SDavid Blaikie findCompilationDatabaseFromDirectory(Directory, ErrorMessage);
111617f5269SArnaud A. de Grandmaison
112617f5269SArnaud A. de Grandmaison if (!DB)
11365fd0e1fSManuel Klimek ErrorMessage = ("Could not auto-detect compilation database for file \"" +
1146ed1f85cSDaniel Jasper SourceFile + "\"\n" + ErrorMessage).str();
115617f5269SArnaud A. de Grandmaison return DB;
116617f5269SArnaud A. de Grandmaison }
117617f5269SArnaud A. de Grandmaison
118cdba84c0SDavid Blaikie std::unique_ptr<CompilationDatabase>
autoDetectFromDirectory(StringRef SourceDir,std::string & ErrorMessage)119617f5269SArnaud A. de Grandmaison CompilationDatabase::autoDetectFromDirectory(StringRef SourceDir,
120617f5269SArnaud A. de Grandmaison std::string &ErrorMessage) {
121f857950dSDmitri Gribenko SmallString<1024> AbsolutePath(getAbsolutePath(SourceDir));
122617f5269SArnaud A. de Grandmaison
123cdba84c0SDavid Blaikie std::unique_ptr<CompilationDatabase> DB =
124cdba84c0SDavid Blaikie findCompilationDatabaseFromDirectory(AbsolutePath, ErrorMessage);
125617f5269SArnaud A. de Grandmaison
126617f5269SArnaud A. de Grandmaison if (!DB)
127617f5269SArnaud A. de Grandmaison ErrorMessage = ("Could not auto-detect compilation database from directory \"" +
1286ed1f85cSDaniel Jasper SourceDir + "\"\n" + ErrorMessage).str();
129617f5269SArnaud A. de Grandmaison return DB;
13065fd0e1fSManuel Klimek }
13165fd0e1fSManuel Klimek
getAllCompileCommands() const132b50a36c8SSam McCall std::vector<CompileCommand> CompilationDatabase::getAllCompileCommands() const {
133b50a36c8SSam McCall std::vector<CompileCommand> Result;
134b50a36c8SSam McCall for (const auto &File : getAllFiles()) {
135b50a36c8SSam McCall auto C = getCompileCommands(File);
136b50a36c8SSam McCall std::move(C.begin(), C.end(), std::back_inserter(Result));
137b50a36c8SSam McCall }
138b50a36c8SSam McCall return Result;
139b50a36c8SSam McCall }
140b50a36c8SSam McCall
1416366efedSEugene Zelenko CompilationDatabasePlugin::~CompilationDatabasePlugin() = default;
1426ed1f85cSDaniel Jasper
1436a1457e6SBenjamin Kramer namespace {
1446366efedSEugene Zelenko
145eb56f4feSEdwin Vane // Helper for recursively searching through a chain of actions and collecting
146eb56f4feSEdwin Vane // all inputs, direct and indirect, of compile jobs.
147eb56f4feSEdwin Vane struct CompileJobAnalyzer {
1486366efedSEugene Zelenko SmallVector<std::string, 2> Inputs;
1496366efedSEugene Zelenko
run__anond722a6690111::CompileJobAnalyzer150eb56f4feSEdwin Vane void run(const driver::Action *A) {
151eb56f4feSEdwin Vane runImpl(A, false);
152eb56f4feSEdwin Vane }
153eb56f4feSEdwin Vane
154eb56f4feSEdwin Vane private:
runImpl__anond722a6690111::CompileJobAnalyzer155eb56f4feSEdwin Vane void runImpl(const driver::Action *A, bool Collect) {
156eb56f4feSEdwin Vane bool CollectChildren = Collect;
157eb56f4feSEdwin Vane switch (A->getKind()) {
158eb56f4feSEdwin Vane case driver::Action::CompileJobClass:
159eb56f4feSEdwin Vane CollectChildren = true;
160eb56f4feSEdwin Vane break;
161eb56f4feSEdwin Vane
1626366efedSEugene Zelenko case driver::Action::InputClass:
163eb56f4feSEdwin Vane if (Collect) {
1646366efedSEugene Zelenko const auto *IA = cast<driver::InputAction>(A);
165adcd0268SBenjamin Kramer Inputs.push_back(std::string(IA->getInputArg().getSpelling()));
166eb56f4feSEdwin Vane }
1676366efedSEugene Zelenko break;
168eb56f4feSEdwin Vane
169eb56f4feSEdwin Vane default:
170eb56f4feSEdwin Vane // Don't care about others
1716366efedSEugene Zelenko break;
172eb56f4feSEdwin Vane }
173eb56f4feSEdwin Vane
1745a459f82SNico Weber for (const driver::Action *AI : A->inputs())
1755a459f82SNico Weber runImpl(AI, CollectChildren);
176eb56f4feSEdwin Vane }
177eb56f4feSEdwin Vane };
178eb56f4feSEdwin Vane
179eb56f4feSEdwin Vane // Special DiagnosticConsumer that looks for warn_drv_input_file_unused
180eb56f4feSEdwin Vane // diagnostics from the driver and collects the option strings for those unused
181eb56f4feSEdwin Vane // options.
182eb56f4feSEdwin Vane class UnusedInputDiagConsumer : public DiagnosticConsumer {
183eb56f4feSEdwin Vane public:
UnusedInputDiagConsumer(DiagnosticConsumer & Other)184c46064c2SSerge Pavlov UnusedInputDiagConsumer(DiagnosticConsumer &Other) : Other(Other) {}
185eb56f4feSEdwin Vane
HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,const Diagnostic & Info)18634eb2072SAlexander Kornienko void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
187a798a9dbSCraig Topper const Diagnostic &Info) override {
1886366efedSEugene Zelenko if (Info.getID() == diag::warn_drv_input_file_unused) {
189eb56f4feSEdwin Vane // Arg 1 for this diagnostic is the option that didn't get used.
190eb56f4feSEdwin Vane UnusedInputs.push_back(Info.getArgStdStr(0));
191c46064c2SSerge Pavlov } else if (DiagLevel >= DiagnosticsEngine::Error) {
192c46064c2SSerge Pavlov // If driver failed to create compilation object, show the diagnostics
193c46064c2SSerge Pavlov // to user.
194c46064c2SSerge Pavlov Other.HandleDiagnostic(DiagLevel, Info);
195eb56f4feSEdwin Vane }
196eb56f4feSEdwin Vane }
197eb56f4feSEdwin Vane
198c46064c2SSerge Pavlov DiagnosticConsumer &Other;
199eb56f4feSEdwin Vane SmallVector<std::string, 2> UnusedInputs;
200eb56f4feSEdwin Vane };
201eb56f4feSEdwin Vane
20283494d2fSChih-Hung Hsieh // Filter of tools unused flags such as -no-integrated-as and -Wa,*.
20383494d2fSChih-Hung Hsieh // They are not used for syntax checking, and could confuse targets
20483494d2fSChih-Hung Hsieh // which don't support these options.
20583494d2fSChih-Hung Hsieh struct FilterUnusedFlags {
operator ()__anond722a6690111::FilterUnusedFlags20683494d2fSChih-Hung Hsieh bool operator() (StringRef S) {
20783494d2fSChih-Hung Hsieh return (S == "-no-integrated-as") || S.startswith("-Wa,");
20883494d2fSChih-Hung Hsieh }
20983494d2fSChih-Hung Hsieh };
21083494d2fSChih-Hung Hsieh
GetClangToolCommand()211f2e25e70SSam McCall std::string GetClangToolCommand() {
212f2e25e70SSam McCall static int Dummy;
213f2e25e70SSam McCall std::string ClangExecutable =
214f2e25e70SSam McCall llvm::sys::fs::getMainExecutable("clang", (void *)&Dummy);
215f2e25e70SSam McCall SmallString<128> ClangToolPath;
216f2e25e70SSam McCall ClangToolPath = llvm::sys::path::parent_path(ClangExecutable);
217f2e25e70SSam McCall llvm::sys::path::append(ClangToolPath, "clang-tool");
218adcd0268SBenjamin Kramer return std::string(ClangToolPath.str());
219f2e25e70SSam McCall }
220f2e25e70SSam McCall
2216a1457e6SBenjamin Kramer } // namespace
222eb56f4feSEdwin Vane
2239fc8faf9SAdrian Prantl /// Strips any positional args and possible argv[0] from a command-line
224eb56f4feSEdwin Vane /// provided by the user to construct a FixedCompilationDatabase.
225eb56f4feSEdwin Vane ///
226eb56f4feSEdwin Vane /// FixedCompilationDatabase requires a command line to be in this format as it
227eb56f4feSEdwin Vane /// constructs the command line for each file by appending the name of the file
228eb56f4feSEdwin Vane /// to be compiled. FixedCompilationDatabase also adds its own argv[0] to the
229eb56f4feSEdwin Vane /// start of the command line although its value is not important as it's just
230eb56f4feSEdwin Vane /// ignored by the Driver invoked by the ClangTool using the
231eb56f4feSEdwin Vane /// FixedCompilationDatabase.
232eb56f4feSEdwin Vane ///
233eb56f4feSEdwin Vane /// FIXME: This functionality should probably be made available by
234eb56f4feSEdwin Vane /// clang::driver::Driver although what the interface should look like is not
235eb56f4feSEdwin Vane /// clear.
236eb56f4feSEdwin Vane ///
237eb56f4feSEdwin Vane /// \param[in] Args Args as provided by the user.
2385335cf33SNAKAMURA Takumi /// \return Resulting stripped command line.
2395335cf33SNAKAMURA Takumi /// \li true if successful.
240eb56f4feSEdwin Vane /// \li false if \c Args cannot be used for compilation jobs (e.g.
241eb56f4feSEdwin Vane /// contains an option like -E or -version).
stripPositionalArgs(std::vector<const char * > Args,std::vector<std::string> & Result,std::string & ErrorMsg)2423a4fe369SArtyom Skrobov static bool stripPositionalArgs(std::vector<const char *> Args,
243c46064c2SSerge Pavlov std::vector<std::string> &Result,
244c46064c2SSerge Pavlov std::string &ErrorMsg) {
245eb56f4feSEdwin Vane IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
246c46064c2SSerge Pavlov llvm::raw_string_ostream Output(ErrorMsg);
247c46064c2SSerge Pavlov TextDiagnosticPrinter DiagnosticPrinter(Output, &*DiagOpts);
248c46064c2SSerge Pavlov UnusedInputDiagConsumer DiagClient(DiagnosticPrinter);
249eb56f4feSEdwin Vane DiagnosticsEngine Diagnostics(
2506366efedSEugene Zelenko IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()),
251eb56f4feSEdwin Vane &*DiagOpts, &DiagClient, false);
252eb56f4feSEdwin Vane
2531761f118SAlp Toker // The clang executable path isn't required since the jobs the driver builds
2541761f118SAlp Toker // will not be executed.
255b8984329SAhmed Charles std::unique_ptr<driver::Driver> NewDriver(new driver::Driver(
256eb56f4feSEdwin Vane /* ClangExecutable= */ "", llvm::sys::getDefaultTargetTriple(),
2571761f118SAlp Toker Diagnostics));
258eb56f4feSEdwin Vane NewDriver->setCheckInputsExist(false);
259eb56f4feSEdwin Vane
260f2e25e70SSam McCall // This becomes the new argv[0]. The value is used to detect libc++ include
261f2e25e70SSam McCall // dirs on Mac, it isn't used for other platforms.
262f2e25e70SSam McCall std::string Argv0 = GetClangToolCommand();
263f2e25e70SSam McCall Args.insert(Args.begin(), Argv0.c_str());
264eb56f4feSEdwin Vane
265eb56f4feSEdwin Vane // By adding -c, we force the driver to treat compilation as the last phase.
266eb56f4feSEdwin Vane // It will then issue warnings via Diagnostics about un-used options that
267eb56f4feSEdwin Vane // would have been used for linking. If the user provided a compiler name as
268eb56f4feSEdwin Vane // the original argv[0], this will be treated as a linker input thanks to
269eb56f4feSEdwin Vane // insertng a new argv[0] above. All un-used options get collected by
270eb56f4feSEdwin Vane // UnusedInputdiagConsumer and get stripped out later.
271eb56f4feSEdwin Vane Args.push_back("-c");
272eb56f4feSEdwin Vane
273eb56f4feSEdwin Vane // Put a dummy C++ file on to ensure there's at least one compile job for the
274eb56f4feSEdwin Vane // driver to construct. If the user specified some other argument that
275eb56f4feSEdwin Vane // prevents compilation, e.g. -E or something like -version, we may still end
276eb56f4feSEdwin Vane // up with no jobs but then this is the user's fault.
277eb56f4feSEdwin Vane Args.push_back("placeholder.cpp");
278eb56f4feSEdwin Vane
2790a6e051aSNathan James llvm::erase_if(Args, FilterUnusedFlags());
2803a4fe369SArtyom Skrobov
281b8984329SAhmed Charles const std::unique_ptr<driver::Compilation> Compilation(
282eb56f4feSEdwin Vane NewDriver->BuildCompilation(Args));
283c46064c2SSerge Pavlov if (!Compilation)
284c46064c2SSerge Pavlov return false;
285eb56f4feSEdwin Vane
286eb56f4feSEdwin Vane const driver::JobList &Jobs = Compilation->getJobs();
287eb56f4feSEdwin Vane
288eb56f4feSEdwin Vane CompileJobAnalyzer CompileAnalyzer;
289eb56f4feSEdwin Vane
2900cd9248dSJustin Bogner for (const auto &Cmd : Jobs) {
29183494d2fSChih-Hung Hsieh // Collect only for Assemble, Backend, and Compile jobs. If we do all jobs
29283494d2fSChih-Hung Hsieh // we get duplicates since Link jobs point to Assemble jobs as inputs.
29383494d2fSChih-Hung Hsieh // -flto* flags make the BackendJobClass, which still needs analyzer.
294e2863ecaSAlex Lorenz if (Cmd.getSource().getKind() == driver::Action::AssembleJobClass ||
29583494d2fSChih-Hung Hsieh Cmd.getSource().getKind() == driver::Action::BackendJobClass ||
296e2863ecaSAlex Lorenz Cmd.getSource().getKind() == driver::Action::CompileJobClass) {
297c11bf802SDavid Blaikie CompileAnalyzer.run(&Cmd.getSource());
298eb56f4feSEdwin Vane }
299e2863ecaSAlex Lorenz }
300eb56f4feSEdwin Vane
301eb56f4feSEdwin Vane if (CompileAnalyzer.Inputs.empty()) {
302c46064c2SSerge Pavlov ErrorMsg = "warning: no compile jobs found\n";
303eb56f4feSEdwin Vane return false;
304eb56f4feSEdwin Vane }
305eb56f4feSEdwin Vane
3060a6e051aSNathan James // Remove all compilation input files from the command line and inputs deemed
3070a6e051aSNathan James // unused for compilation. This is necessary so that getCompileCommands() can
3080a6e051aSNathan James // construct a command line for each file.
3090a6e051aSNathan James std::vector<const char *>::iterator End =
3100a6e051aSNathan James llvm::remove_if(Args, [&](StringRef S) {
3110a6e051aSNathan James return llvm::is_contained(CompileAnalyzer.Inputs, S) ||
3120a6e051aSNathan James llvm::is_contained(DiagClient.UnusedInputs, S);
3130a6e051aSNathan James });
314eb56f4feSEdwin Vane // Remove the -c add above as well. It will be at the end right now.
315e99bb4b2SRichard Trieu assert(strcmp(*(End - 1), "-c") == 0);
316eb56f4feSEdwin Vane --End;
317eb56f4feSEdwin Vane
318eb56f4feSEdwin Vane Result = std::vector<std::string>(Args.begin() + 1, End);
319eb56f4feSEdwin Vane return true;
320eb56f4feSEdwin Vane }
321eb56f4feSEdwin Vane
322c46064c2SSerge Pavlov std::unique_ptr<FixedCompilationDatabase>
loadFromCommandLine(int & Argc,const char * const * Argv,std::string & ErrorMsg,const Twine & Directory)323c46064c2SSerge Pavlov FixedCompilationDatabase::loadFromCommandLine(int &Argc,
324c46064c2SSerge Pavlov const char *const *Argv,
325c46064c2SSerge Pavlov std::string &ErrorMsg,
326*236129fbSSimon Pilgrim const Twine &Directory) {
327c46064c2SSerge Pavlov ErrorMsg.clear();
328c46064c2SSerge Pavlov if (Argc == 0)
329c46064c2SSerge Pavlov return nullptr;
330ab8f7d58SDavid Blaikie const char *const *DoubleDash = std::find(Argv, Argv + Argc, StringRef("--"));
331ff26efceSManuel Klimek if (DoubleDash == Argv + Argc)
332ccbc35edSCraig Topper return nullptr;
333eb56f4feSEdwin Vane std::vector<const char *> CommandLine(DoubleDash + 1, Argv + Argc);
334ff26efceSManuel Klimek Argc = DoubleDash - Argv;
335eb56f4feSEdwin Vane
336eb56f4feSEdwin Vane std::vector<std::string> StrippedArgs;
337c46064c2SSerge Pavlov if (!stripPositionalArgs(CommandLine, StrippedArgs, ErrorMsg))
338ccbc35edSCraig Topper return nullptr;
3392b3d49b6SJonas Devlieghere return std::make_unique<FixedCompilationDatabase>(Directory, StrippedArgs);
34060d74e45SSam McCall }
34160d74e45SSam McCall
34260d74e45SSam McCall std::unique_ptr<FixedCompilationDatabase>
loadFromFile(StringRef Path,std::string & ErrorMsg)34360d74e45SSam McCall FixedCompilationDatabase::loadFromFile(StringRef Path, std::string &ErrorMsg) {
34460d74e45SSam McCall ErrorMsg.clear();
34560d74e45SSam McCall llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> File =
34660d74e45SSam McCall llvm::MemoryBuffer::getFile(Path);
34760d74e45SSam McCall if (std::error_code Result = File.getError()) {
34860d74e45SSam McCall ErrorMsg = "Error while opening fixed database: " + Result.message();
34960d74e45SSam McCall return nullptr;
35060d74e45SSam McCall }
351a1cb9cbfSSam McCall return loadFromBuffer(llvm::sys::path::parent_path(Path),
352a1cb9cbfSSam McCall (*File)->getBuffer(), ErrorMsg);
353a1cb9cbfSSam McCall }
354a1cb9cbfSSam McCall
355a1cb9cbfSSam McCall std::unique_ptr<FixedCompilationDatabase>
loadFromBuffer(StringRef Directory,StringRef Data,std::string & ErrorMsg)356a1cb9cbfSSam McCall FixedCompilationDatabase::loadFromBuffer(StringRef Directory, StringRef Data,
357a1cb9cbfSSam McCall std::string &ErrorMsg) {
358a1cb9cbfSSam McCall ErrorMsg.clear();
35948aa781aSSam McCall std::vector<std::string> Args;
360a1cb9cbfSSam McCall StringRef Line;
361a1cb9cbfSSam McCall while (!Data.empty()) {
362a1cb9cbfSSam McCall std::tie(Line, Data) = Data.split('\n');
36348aa781aSSam McCall // Stray whitespace is almost certainly unintended.
36448aa781aSSam McCall Line = Line.trim();
36548aa781aSSam McCall if (!Line.empty())
36648aa781aSSam McCall Args.push_back(Line.str());
36748aa781aSSam McCall }
368a1cb9cbfSSam McCall return std::make_unique<FixedCompilationDatabase>(Directory, std::move(Args));
369ff26efceSManuel Klimek }
370ff26efceSManuel Klimek
FixedCompilationDatabase(const Twine & Directory,ArrayRef<std::string> CommandLine)371*236129fbSSimon Pilgrim FixedCompilationDatabase::FixedCompilationDatabase(
372*236129fbSSimon Pilgrim const Twine &Directory, ArrayRef<std::string> CommandLine) {
373f2e25e70SSam McCall std::vector<std::string> ToolCommandLine(1, GetClangToolCommand());
374ff26efceSManuel Klimek ToolCommandLine.insert(ToolCommandLine.end(),
375ff26efceSManuel Klimek CommandLine.begin(), CommandLine.end());
37674bcd21eSArgyrios Kyrtzidis CompileCommands.emplace_back(Directory, StringRef(),
377399aea30SJoerg Sonnenberger std::move(ToolCommandLine),
378399aea30SJoerg Sonnenberger StringRef());
379ff26efceSManuel Klimek }
380ff26efceSManuel Klimek
381ff26efceSManuel Klimek std::vector<CompileCommand>
getCompileCommands(StringRef FilePath) const382ff26efceSManuel Klimek FixedCompilationDatabase::getCompileCommands(StringRef FilePath) const {
383ff26efceSManuel Klimek std::vector<CompileCommand> Result(CompileCommands);
384adcd0268SBenjamin Kramer Result[0].CommandLine.push_back(std::string(FilePath));
385adcd0268SBenjamin Kramer Result[0].Filename = std::string(FilePath);
386ff26efceSManuel Klimek return Result;
387ff26efceSManuel Klimek }
388ff26efceSManuel Klimek
38960d74e45SSam McCall namespace {
39060d74e45SSam McCall
39160d74e45SSam McCall class FixedCompilationDatabasePlugin : public CompilationDatabasePlugin {
39260d74e45SSam McCall std::unique_ptr<CompilationDatabase>
loadFromDirectory(StringRef Directory,std::string & ErrorMessage)39360d74e45SSam McCall loadFromDirectory(StringRef Directory, std::string &ErrorMessage) override {
39460d74e45SSam McCall SmallString<1024> DatabasePath(Directory);
39560d74e45SSam McCall llvm::sys::path::append(DatabasePath, "compile_flags.txt");
39660d74e45SSam McCall return FixedCompilationDatabase::loadFromFile(DatabasePath, ErrorMessage);
39760d74e45SSam McCall }
39860d74e45SSam McCall };
39960d74e45SSam McCall
4006366efedSEugene Zelenko } // namespace
4016366efedSEugene Zelenko
40260d74e45SSam McCall static CompilationDatabasePluginRegistry::Add<FixedCompilationDatabasePlugin>
40360d74e45SSam McCall X("fixed-compilation-database", "Reads plain-text flags file");
40460d74e45SSam McCall
4056a1457e6SBenjamin Kramer namespace clang {
4066a1457e6SBenjamin Kramer namespace tooling {
4076a1457e6SBenjamin Kramer
4086ed1f85cSDaniel Jasper // This anchor is used to force the linker to link in the generated object file
4096ed1f85cSDaniel Jasper // and thus register the JSONCompilationDatabasePlugin.
4106ed1f85cSDaniel Jasper extern volatile int JSONAnchorSource;
411b7e8c7c7SYaron Keren static int LLVM_ATTRIBUTE_UNUSED JSONAnchorDest = JSONAnchorSource;
41247c245a5SManuel Klimek
4136366efedSEugene Zelenko } // namespace tooling
4146366efedSEugene Zelenko } // namespace clang
415