148675466SDimitry Andric //===- CompilationDatabase.cpp --------------------------------------------===//
2dbe13110SDimitry Andric //
322989816SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
422989816SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
522989816SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6dbe13110SDimitry Andric //
7dbe13110SDimitry Andric //===----------------------------------------------------------------------===//
8dbe13110SDimitry Andric //
913cc256eSDimitry Andric // This file contains implementations of the CompilationDatabase base class
1013cc256eSDimitry Andric // and the FixedCompilationDatabase.
11dbe13110SDimitry Andric //
12461a67faSDimitry Andric // FIXME: Various functions that take a string &ErrorMessage should be upgraded
13461a67faSDimitry Andric // to Expected.
14461a67faSDimitry Andric //
15dbe13110SDimitry Andric //===----------------------------------------------------------------------===//
16dbe13110SDimitry Andric
17dbe13110SDimitry Andric #include "clang/Tooling/CompilationDatabase.h"
18bfef3995SDimitry Andric #include "clang/Basic/Diagnostic.h"
1948675466SDimitry Andric #include "clang/Basic/DiagnosticIDs.h"
209f4dbff6SDimitry Andric #include "clang/Basic/DiagnosticOptions.h"
2148675466SDimitry Andric #include "clang/Basic/LLVM.h"
22bfef3995SDimitry Andric #include "clang/Driver/Action.h"
239f4dbff6SDimitry Andric #include "clang/Driver/Compilation.h"
24bfef3995SDimitry Andric #include "clang/Driver/Driver.h"
25bfef3995SDimitry Andric #include "clang/Driver/DriverDiagnostic.h"
26bfef3995SDimitry Andric #include "clang/Driver/Job.h"
27bfef3995SDimitry Andric #include "clang/Frontend/TextDiagnosticPrinter.h"
289f4dbff6SDimitry Andric #include "clang/Tooling/CompilationDatabasePluginRegistry.h"
299f4dbff6SDimitry Andric #include "clang/Tooling/Tooling.h"
3048675466SDimitry Andric #include "llvm/ADT/ArrayRef.h"
3148675466SDimitry Andric #include "llvm/ADT/IntrusiveRefCntPtr.h"
3248675466SDimitry Andric #include "llvm/ADT/STLExtras.h"
339f4dbff6SDimitry Andric #include "llvm/ADT/SmallString.h"
3448675466SDimitry Andric #include "llvm/ADT/SmallVector.h"
3548675466SDimitry Andric #include "llvm/ADT/StringRef.h"
36bfef3995SDimitry Andric #include "llvm/Option/Arg.h"
3748675466SDimitry Andric #include "llvm/Support/Casting.h"
3848675466SDimitry Andric #include "llvm/Support/Compiler.h"
3948675466SDimitry Andric #include "llvm/Support/ErrorOr.h"
40461a67faSDimitry Andric #include "llvm/Support/LineIterator.h"
4148675466SDimitry Andric #include "llvm/Support/MemoryBuffer.h"
429f4dbff6SDimitry Andric #include "llvm/Support/Path.h"
43b5aee35cSDimitry Andric #include "llvm/Support/raw_ostream.h"
447fa27ce4SDimitry Andric #include "llvm/TargetParser/Host.h"
4548675466SDimitry Andric #include <algorithm>
4648675466SDimitry Andric #include <cassert>
4748675466SDimitry Andric #include <cstring>
4848675466SDimitry Andric #include <iterator>
4948675466SDimitry Andric #include <memory>
509f4dbff6SDimitry Andric #include <sstream>
5148675466SDimitry Andric #include <string>
529f4dbff6SDimitry Andric #include <system_error>
5348675466SDimitry Andric #include <utility>
5448675466SDimitry Andric #include <vector>
5548675466SDimitry Andric
565e20cdd8SDimitry Andric using namespace clang;
575e20cdd8SDimitry Andric using namespace tooling;
58dbe13110SDimitry Andric
59bab175ecSDimitry Andric LLVM_INSTANTIATE_REGISTRY(CompilationDatabasePluginRegistry)
60bab175ecSDimitry Andric
6148675466SDimitry Andric CompilationDatabase::~CompilationDatabase() = default;
62dbe13110SDimitry Andric
6306d4ba38SDimitry Andric std::unique_ptr<CompilationDatabase>
loadFromDirectory(StringRef BuildDirectory,std::string & ErrorMessage)64dbe13110SDimitry Andric CompilationDatabase::loadFromDirectory(StringRef BuildDirectory,
65dbe13110SDimitry Andric std::string &ErrorMessage) {
66bab175ecSDimitry Andric llvm::raw_string_ostream ErrorStream(ErrorMessage);
67cfca06d7SDimitry Andric for (const CompilationDatabasePluginRegistry::entry &Database :
68cfca06d7SDimitry Andric CompilationDatabasePluginRegistry::entries()) {
6913cc256eSDimitry Andric std::string DatabaseErrorMessage;
70cfca06d7SDimitry Andric std::unique_ptr<CompilationDatabasePlugin> Plugin(Database.instantiate());
7106d4ba38SDimitry Andric if (std::unique_ptr<CompilationDatabase> DB =
7213cc256eSDimitry Andric Plugin->loadFromDirectory(BuildDirectory, DatabaseErrorMessage))
7313cc256eSDimitry Andric return DB;
74cfca06d7SDimitry Andric ErrorStream << Database.getName() << ": " << DatabaseErrorMessage << "\n";
75dbe13110SDimitry Andric }
769f4dbff6SDimitry Andric return nullptr;
77dbe13110SDimitry Andric }
78dbe13110SDimitry Andric
7906d4ba38SDimitry Andric static std::unique_ptr<CompilationDatabase>
findCompilationDatabaseFromDirectory(StringRef Directory,std::string & ErrorMessage)8013cc256eSDimitry Andric findCompilationDatabaseFromDirectory(StringRef Directory,
8113cc256eSDimitry Andric std::string &ErrorMessage) {
8213cc256eSDimitry Andric std::stringstream ErrorStream;
8313cc256eSDimitry Andric bool HasErrorMessage = false;
8456d91b49SDimitry Andric while (!Directory.empty()) {
8556d91b49SDimitry Andric std::string LoadErrorMessage;
8656d91b49SDimitry Andric
8706d4ba38SDimitry Andric if (std::unique_ptr<CompilationDatabase> DB =
8856d91b49SDimitry Andric CompilationDatabase::loadFromDirectory(Directory, LoadErrorMessage))
8956d91b49SDimitry Andric return DB;
9056d91b49SDimitry Andric
9113cc256eSDimitry Andric if (!HasErrorMessage) {
9213cc256eSDimitry Andric ErrorStream << "No compilation database found in " << Directory.str()
9313cc256eSDimitry Andric << " or any parent directory\n" << LoadErrorMessage;
9413cc256eSDimitry Andric HasErrorMessage = true;
9513cc256eSDimitry Andric }
9613cc256eSDimitry Andric
9756d91b49SDimitry Andric Directory = llvm::sys::path::parent_path(Directory);
9856d91b49SDimitry Andric }
9913cc256eSDimitry Andric ErrorMessage = ErrorStream.str();
1009f4dbff6SDimitry Andric return nullptr;
10156d91b49SDimitry Andric }
10256d91b49SDimitry Andric
10306d4ba38SDimitry Andric std::unique_ptr<CompilationDatabase>
autoDetectFromSource(StringRef SourceFile,std::string & ErrorMessage)10456d91b49SDimitry Andric CompilationDatabase::autoDetectFromSource(StringRef SourceFile,
10556d91b49SDimitry Andric std::string &ErrorMessage) {
106809500fcSDimitry Andric SmallString<1024> AbsolutePath(getAbsolutePath(SourceFile));
10756d91b49SDimitry Andric StringRef Directory = llvm::sys::path::parent_path(AbsolutePath);
10856d91b49SDimitry Andric
10906d4ba38SDimitry Andric std::unique_ptr<CompilationDatabase> DB =
11006d4ba38SDimitry Andric findCompilationDatabaseFromDirectory(Directory, ErrorMessage);
11156d91b49SDimitry Andric
11256d91b49SDimitry Andric if (!DB)
11356d91b49SDimitry Andric ErrorMessage = ("Could not auto-detect compilation database for file \"" +
11413cc256eSDimitry Andric SourceFile + "\"\n" + ErrorMessage).str();
11556d91b49SDimitry Andric return DB;
11656d91b49SDimitry Andric }
11756d91b49SDimitry Andric
11806d4ba38SDimitry Andric std::unique_ptr<CompilationDatabase>
autoDetectFromDirectory(StringRef SourceDir,std::string & ErrorMessage)11956d91b49SDimitry Andric CompilationDatabase::autoDetectFromDirectory(StringRef SourceDir,
12056d91b49SDimitry Andric std::string &ErrorMessage) {
121809500fcSDimitry Andric SmallString<1024> AbsolutePath(getAbsolutePath(SourceDir));
12256d91b49SDimitry Andric
12306d4ba38SDimitry Andric std::unique_ptr<CompilationDatabase> DB =
12406d4ba38SDimitry Andric findCompilationDatabaseFromDirectory(AbsolutePath, ErrorMessage);
12556d91b49SDimitry Andric
12656d91b49SDimitry Andric if (!DB)
12756d91b49SDimitry Andric ErrorMessage = ("Could not auto-detect compilation database from directory \"" +
12813cc256eSDimitry Andric SourceDir + "\"\n" + ErrorMessage).str();
12956d91b49SDimitry Andric return DB;
13056d91b49SDimitry Andric }
13156d91b49SDimitry Andric
getAllCompileCommands() const132461a67faSDimitry Andric std::vector<CompileCommand> CompilationDatabase::getAllCompileCommands() const {
133461a67faSDimitry Andric std::vector<CompileCommand> Result;
134461a67faSDimitry Andric for (const auto &File : getAllFiles()) {
135461a67faSDimitry Andric auto C = getCompileCommands(File);
136461a67faSDimitry Andric std::move(C.begin(), C.end(), std::back_inserter(Result));
137461a67faSDimitry Andric }
138461a67faSDimitry Andric return Result;
139461a67faSDimitry Andric }
140461a67faSDimitry Andric
14148675466SDimitry Andric CompilationDatabasePlugin::~CompilationDatabasePlugin() = default;
14213cc256eSDimitry Andric
1435e20cdd8SDimitry Andric namespace {
14448675466SDimitry Andric
145bfef3995SDimitry Andric // Helper for recursively searching through a chain of actions and collecting
146bfef3995SDimitry Andric // all inputs, direct and indirect, of compile jobs.
147bfef3995SDimitry Andric struct CompileJobAnalyzer {
14848675466SDimitry Andric SmallVector<std::string, 2> Inputs;
14948675466SDimitry Andric
run__anon546b6d4e0111::CompileJobAnalyzer150bfef3995SDimitry Andric void run(const driver::Action *A) {
151bfef3995SDimitry Andric runImpl(A, false);
152bfef3995SDimitry Andric }
153bfef3995SDimitry Andric
154bfef3995SDimitry Andric private:
runImpl__anon546b6d4e0111::CompileJobAnalyzer155bfef3995SDimitry Andric void runImpl(const driver::Action *A, bool Collect) {
156bfef3995SDimitry Andric bool CollectChildren = Collect;
157bfef3995SDimitry Andric switch (A->getKind()) {
158bfef3995SDimitry Andric case driver::Action::CompileJobClass:
1594df029ccSDimitry Andric case driver::Action::PrecompileJobClass:
160bfef3995SDimitry Andric CollectChildren = true;
161bfef3995SDimitry Andric break;
162bfef3995SDimitry Andric
16348675466SDimitry Andric case driver::Action::InputClass:
164bfef3995SDimitry Andric if (Collect) {
16548675466SDimitry Andric const auto *IA = cast<driver::InputAction>(A);
166cfca06d7SDimitry Andric Inputs.push_back(std::string(IA->getInputArg().getSpelling()));
167bfef3995SDimitry Andric }
16848675466SDimitry Andric break;
169bfef3995SDimitry Andric
170bfef3995SDimitry Andric default:
171bfef3995SDimitry Andric // Don't care about others
17248675466SDimitry Andric break;
173bfef3995SDimitry Andric }
174bfef3995SDimitry Andric
1752b6b257fSDimitry Andric for (const driver::Action *AI : A->inputs())
1762b6b257fSDimitry Andric runImpl(AI, CollectChildren);
177bfef3995SDimitry Andric }
178bfef3995SDimitry Andric };
179bfef3995SDimitry Andric
180bfef3995SDimitry Andric // Special DiagnosticConsumer that looks for warn_drv_input_file_unused
181bfef3995SDimitry Andric // diagnostics from the driver and collects the option strings for those unused
182bfef3995SDimitry Andric // options.
183bfef3995SDimitry Andric class UnusedInputDiagConsumer : public DiagnosticConsumer {
184bfef3995SDimitry Andric public:
UnusedInputDiagConsumer(DiagnosticConsumer & Other)185b5aee35cSDimitry Andric UnusedInputDiagConsumer(DiagnosticConsumer &Other) : Other(Other) {}
186bfef3995SDimitry Andric
HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,const Diagnostic & Info)1875e20cdd8SDimitry Andric void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
1889f4dbff6SDimitry Andric const Diagnostic &Info) override {
18948675466SDimitry Andric if (Info.getID() == diag::warn_drv_input_file_unused) {
190bfef3995SDimitry Andric // Arg 1 for this diagnostic is the option that didn't get used.
191bfef3995SDimitry Andric UnusedInputs.push_back(Info.getArgStdStr(0));
192b5aee35cSDimitry Andric } else if (DiagLevel >= DiagnosticsEngine::Error) {
193b5aee35cSDimitry Andric // If driver failed to create compilation object, show the diagnostics
194b5aee35cSDimitry Andric // to user.
195b5aee35cSDimitry Andric Other.HandleDiagnostic(DiagLevel, Info);
196bfef3995SDimitry Andric }
197bfef3995SDimitry Andric }
198bfef3995SDimitry Andric
199b5aee35cSDimitry Andric DiagnosticConsumer &Other;
200bfef3995SDimitry Andric SmallVector<std::string, 2> UnusedInputs;
201bfef3995SDimitry Andric };
202bfef3995SDimitry Andric
203676fbe81SDimitry Andric // Filter of tools unused flags such as -no-integrated-as and -Wa,*.
204676fbe81SDimitry Andric // They are not used for syntax checking, and could confuse targets
205676fbe81SDimitry Andric // which don't support these options.
206676fbe81SDimitry Andric struct FilterUnusedFlags {
operator ()__anon546b6d4e0111::FilterUnusedFlags207676fbe81SDimitry Andric bool operator() (StringRef S) {
208312c0ed1SDimitry Andric return (S == "-no-integrated-as") || S.starts_with("-Wa,");
209676fbe81SDimitry Andric }
210676fbe81SDimitry Andric };
211676fbe81SDimitry Andric
GetClangToolCommand()212676fbe81SDimitry Andric std::string GetClangToolCommand() {
213676fbe81SDimitry Andric static int Dummy;
214676fbe81SDimitry Andric std::string ClangExecutable =
215676fbe81SDimitry Andric llvm::sys::fs::getMainExecutable("clang", (void *)&Dummy);
216676fbe81SDimitry Andric SmallString<128> ClangToolPath;
217676fbe81SDimitry Andric ClangToolPath = llvm::sys::path::parent_path(ClangExecutable);
218676fbe81SDimitry Andric llvm::sys::path::append(ClangToolPath, "clang-tool");
2194df029ccSDimitry Andric return std::string(ClangToolPath);
220676fbe81SDimitry Andric }
221676fbe81SDimitry Andric
2225e20cdd8SDimitry Andric } // namespace
223bfef3995SDimitry Andric
22448675466SDimitry Andric /// Strips any positional args and possible argv[0] from a command-line
225bfef3995SDimitry Andric /// provided by the user to construct a FixedCompilationDatabase.
226bfef3995SDimitry Andric ///
227bfef3995SDimitry Andric /// FixedCompilationDatabase requires a command line to be in this format as it
228bfef3995SDimitry Andric /// constructs the command line for each file by appending the name of the file
229bfef3995SDimitry Andric /// to be compiled. FixedCompilationDatabase also adds its own argv[0] to the
230bfef3995SDimitry Andric /// start of the command line although its value is not important as it's just
231bfef3995SDimitry Andric /// ignored by the Driver invoked by the ClangTool using the
232bfef3995SDimitry Andric /// FixedCompilationDatabase.
233bfef3995SDimitry Andric ///
234bfef3995SDimitry Andric /// FIXME: This functionality should probably be made available by
235bfef3995SDimitry Andric /// clang::driver::Driver although what the interface should look like is not
236bfef3995SDimitry Andric /// clear.
237bfef3995SDimitry Andric ///
238bfef3995SDimitry Andric /// \param[in] Args Args as provided by the user.
239bfef3995SDimitry Andric /// \return Resulting stripped command line.
240bfef3995SDimitry Andric /// \li true if successful.
241bfef3995SDimitry Andric /// \li false if \c Args cannot be used for compilation jobs (e.g.
242bfef3995SDimitry Andric /// contains an option like -E or -version).
stripPositionalArgs(std::vector<const char * > Args,std::vector<std::string> & Result,std::string & ErrorMsg)2439f4dbff6SDimitry Andric static bool stripPositionalArgs(std::vector<const char *> Args,
244b5aee35cSDimitry Andric std::vector<std::string> &Result,
245b5aee35cSDimitry Andric std::string &ErrorMsg) {
246bfef3995SDimitry Andric IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
247b5aee35cSDimitry Andric llvm::raw_string_ostream Output(ErrorMsg);
248b5aee35cSDimitry Andric TextDiagnosticPrinter DiagnosticPrinter(Output, &*DiagOpts);
249b5aee35cSDimitry Andric UnusedInputDiagConsumer DiagClient(DiagnosticPrinter);
250bfef3995SDimitry Andric DiagnosticsEngine Diagnostics(
25148675466SDimitry Andric IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()),
252bfef3995SDimitry Andric &*DiagOpts, &DiagClient, false);
253bfef3995SDimitry Andric
2549f4dbff6SDimitry Andric // The clang executable path isn't required since the jobs the driver builds
2559f4dbff6SDimitry Andric // will not be executed.
2569f4dbff6SDimitry Andric std::unique_ptr<driver::Driver> NewDriver(new driver::Driver(
257bfef3995SDimitry Andric /* ClangExecutable= */ "", llvm::sys::getDefaultTargetTriple(),
2589f4dbff6SDimitry Andric Diagnostics));
259bfef3995SDimitry Andric NewDriver->setCheckInputsExist(false);
260bfef3995SDimitry Andric
261676fbe81SDimitry Andric // This becomes the new argv[0]. The value is used to detect libc++ include
262676fbe81SDimitry Andric // dirs on Mac, it isn't used for other platforms.
263676fbe81SDimitry Andric std::string Argv0 = GetClangToolCommand();
264676fbe81SDimitry Andric Args.insert(Args.begin(), Argv0.c_str());
265bfef3995SDimitry Andric
266bfef3995SDimitry Andric // By adding -c, we force the driver to treat compilation as the last phase.
267bfef3995SDimitry Andric // It will then issue warnings via Diagnostics about un-used options that
268bfef3995SDimitry Andric // would have been used for linking. If the user provided a compiler name as
269bfef3995SDimitry Andric // the original argv[0], this will be treated as a linker input thanks to
270bfef3995SDimitry Andric // insertng a new argv[0] above. All un-used options get collected by
271bfef3995SDimitry Andric // UnusedInputdiagConsumer and get stripped out later.
272bfef3995SDimitry Andric Args.push_back("-c");
273bfef3995SDimitry Andric
274bfef3995SDimitry Andric // Put a dummy C++ file on to ensure there's at least one compile job for the
275bfef3995SDimitry Andric // driver to construct. If the user specified some other argument that
276bfef3995SDimitry Andric // prevents compilation, e.g. -E or something like -version, we may still end
277bfef3995SDimitry Andric // up with no jobs but then this is the user's fault.
278bfef3995SDimitry Andric Args.push_back("placeholder.cpp");
279bfef3995SDimitry Andric
280b60736ecSDimitry Andric llvm::erase_if(Args, FilterUnusedFlags());
2819f4dbff6SDimitry Andric
2829f4dbff6SDimitry Andric const std::unique_ptr<driver::Compilation> Compilation(
283bfef3995SDimitry Andric NewDriver->BuildCompilation(Args));
284b5aee35cSDimitry Andric if (!Compilation)
285b5aee35cSDimitry Andric return false;
286bfef3995SDimitry Andric
287bfef3995SDimitry Andric const driver::JobList &Jobs = Compilation->getJobs();
288bfef3995SDimitry Andric
289bfef3995SDimitry Andric CompileJobAnalyzer CompileAnalyzer;
290bfef3995SDimitry Andric
291c192b3dcSDimitry Andric for (const auto &Cmd : Jobs) {
292676fbe81SDimitry Andric // Collect only for Assemble, Backend, and Compile jobs. If we do all jobs
293676fbe81SDimitry Andric // we get duplicates since Link jobs point to Assemble jobs as inputs.
294676fbe81SDimitry Andric // -flto* flags make the BackendJobClass, which still needs analyzer.
295cf1b4019SDimitry Andric if (Cmd.getSource().getKind() == driver::Action::AssembleJobClass ||
296676fbe81SDimitry Andric Cmd.getSource().getKind() == driver::Action::BackendJobClass ||
2974df029ccSDimitry Andric Cmd.getSource().getKind() == driver::Action::CompileJobClass ||
2984df029ccSDimitry Andric Cmd.getSource().getKind() == driver::Action::PrecompileJobClass) {
29906d4ba38SDimitry Andric CompileAnalyzer.run(&Cmd.getSource());
300bfef3995SDimitry Andric }
301cf1b4019SDimitry Andric }
302bfef3995SDimitry Andric
303bfef3995SDimitry Andric if (CompileAnalyzer.Inputs.empty()) {
304b5aee35cSDimitry Andric ErrorMsg = "warning: no compile jobs found\n";
305bfef3995SDimitry Andric return false;
306bfef3995SDimitry Andric }
307bfef3995SDimitry Andric
308b60736ecSDimitry Andric // Remove all compilation input files from the command line and inputs deemed
309b60736ecSDimitry Andric // unused for compilation. This is necessary so that getCompileCommands() can
310b60736ecSDimitry Andric // construct a command line for each file.
311b60736ecSDimitry Andric std::vector<const char *>::iterator End =
312b60736ecSDimitry Andric llvm::remove_if(Args, [&](StringRef S) {
313b60736ecSDimitry Andric return llvm::is_contained(CompileAnalyzer.Inputs, S) ||
314b60736ecSDimitry Andric llvm::is_contained(DiagClient.UnusedInputs, S);
315b60736ecSDimitry Andric });
316bfef3995SDimitry Andric // Remove the -c add above as well. It will be at the end right now.
3179f4dbff6SDimitry Andric assert(strcmp(*(End - 1), "-c") == 0);
318bfef3995SDimitry Andric --End;
319bfef3995SDimitry Andric
320bfef3995SDimitry Andric Result = std::vector<std::string>(Args.begin() + 1, End);
321bfef3995SDimitry Andric return true;
322bfef3995SDimitry Andric }
323bfef3995SDimitry Andric
324b5aee35cSDimitry Andric std::unique_ptr<FixedCompilationDatabase>
loadFromCommandLine(int & Argc,const char * const * Argv,std::string & ErrorMsg,const Twine & Directory)325b5aee35cSDimitry Andric FixedCompilationDatabase::loadFromCommandLine(int &Argc,
326b5aee35cSDimitry Andric const char *const *Argv,
327b5aee35cSDimitry Andric std::string &ErrorMsg,
328b60736ecSDimitry Andric const Twine &Directory) {
329b5aee35cSDimitry Andric ErrorMsg.clear();
330b5aee35cSDimitry Andric if (Argc == 0)
331b5aee35cSDimitry Andric return nullptr;
3325e20cdd8SDimitry Andric const char *const *DoubleDash = std::find(Argv, Argv + Argc, StringRef("--"));
3336b9a6e39SDimitry Andric if (DoubleDash == Argv + Argc)
3349f4dbff6SDimitry Andric return nullptr;
335bfef3995SDimitry Andric std::vector<const char *> CommandLine(DoubleDash + 1, Argv + Argc);
3366b9a6e39SDimitry Andric Argc = DoubleDash - Argv;
337bfef3995SDimitry Andric
338bfef3995SDimitry Andric std::vector<std::string> StrippedArgs;
339b5aee35cSDimitry Andric if (!stripPositionalArgs(CommandLine, StrippedArgs, ErrorMsg))
3409f4dbff6SDimitry Andric return nullptr;
341519fc96cSDimitry Andric return std::make_unique<FixedCompilationDatabase>(Directory, StrippedArgs);
342461a67faSDimitry Andric }
343461a67faSDimitry Andric
344461a67faSDimitry Andric std::unique_ptr<FixedCompilationDatabase>
loadFromFile(StringRef Path,std::string & ErrorMsg)345461a67faSDimitry Andric FixedCompilationDatabase::loadFromFile(StringRef Path, std::string &ErrorMsg) {
346461a67faSDimitry Andric ErrorMsg.clear();
347461a67faSDimitry Andric llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> File =
348461a67faSDimitry Andric llvm::MemoryBuffer::getFile(Path);
349461a67faSDimitry Andric if (std::error_code Result = File.getError()) {
350461a67faSDimitry Andric ErrorMsg = "Error while opening fixed database: " + Result.message();
351461a67faSDimitry Andric return nullptr;
352461a67faSDimitry Andric }
353b60736ecSDimitry Andric return loadFromBuffer(llvm::sys::path::parent_path(Path),
354b60736ecSDimitry Andric (*File)->getBuffer(), ErrorMsg);
355b60736ecSDimitry Andric }
356b60736ecSDimitry Andric
357b60736ecSDimitry Andric std::unique_ptr<FixedCompilationDatabase>
loadFromBuffer(StringRef Directory,StringRef Data,std::string & ErrorMsg)358b60736ecSDimitry Andric FixedCompilationDatabase::loadFromBuffer(StringRef Directory, StringRef Data,
359b60736ecSDimitry Andric std::string &ErrorMsg) {
360b60736ecSDimitry Andric ErrorMsg.clear();
361cfca06d7SDimitry Andric std::vector<std::string> Args;
362b60736ecSDimitry Andric StringRef Line;
363b60736ecSDimitry Andric while (!Data.empty()) {
364b60736ecSDimitry Andric std::tie(Line, Data) = Data.split('\n');
365cfca06d7SDimitry Andric // Stray whitespace is almost certainly unintended.
366cfca06d7SDimitry Andric Line = Line.trim();
367cfca06d7SDimitry Andric if (!Line.empty())
368cfca06d7SDimitry Andric Args.push_back(Line.str());
369cfca06d7SDimitry Andric }
370b60736ecSDimitry Andric return std::make_unique<FixedCompilationDatabase>(Directory, std::move(Args));
3716b9a6e39SDimitry Andric }
3726b9a6e39SDimitry Andric
FixedCompilationDatabase(const Twine & Directory,ArrayRef<std::string> CommandLine)373b60736ecSDimitry Andric FixedCompilationDatabase::FixedCompilationDatabase(
374b60736ecSDimitry Andric const Twine &Directory, ArrayRef<std::string> CommandLine) {
375676fbe81SDimitry Andric std::vector<std::string> ToolCommandLine(1, GetClangToolCommand());
3766b9a6e39SDimitry Andric ToolCommandLine.insert(ToolCommandLine.end(),
3776b9a6e39SDimitry Andric CommandLine.begin(), CommandLine.end());
37845b53394SDimitry Andric CompileCommands.emplace_back(Directory, StringRef(),
379bab175ecSDimitry Andric std::move(ToolCommandLine),
380bab175ecSDimitry Andric StringRef());
3816b9a6e39SDimitry Andric }
3826b9a6e39SDimitry Andric
3836b9a6e39SDimitry Andric std::vector<CompileCommand>
getCompileCommands(StringRef FilePath) const3846b9a6e39SDimitry Andric FixedCompilationDatabase::getCompileCommands(StringRef FilePath) const {
3856b9a6e39SDimitry Andric std::vector<CompileCommand> Result(CompileCommands);
386cfca06d7SDimitry Andric Result[0].CommandLine.push_back(std::string(FilePath));
387cfca06d7SDimitry Andric Result[0].Filename = std::string(FilePath);
3886b9a6e39SDimitry Andric return Result;
3896b9a6e39SDimitry Andric }
3906b9a6e39SDimitry Andric
391461a67faSDimitry Andric namespace {
39256d91b49SDimitry Andric
393461a67faSDimitry Andric class FixedCompilationDatabasePlugin : public CompilationDatabasePlugin {
394461a67faSDimitry Andric std::unique_ptr<CompilationDatabase>
loadFromDirectory(StringRef Directory,std::string & ErrorMessage)395461a67faSDimitry Andric loadFromDirectory(StringRef Directory, std::string &ErrorMessage) override {
396461a67faSDimitry Andric SmallString<1024> DatabasePath(Directory);
397461a67faSDimitry Andric llvm::sys::path::append(DatabasePath, "compile_flags.txt");
398461a67faSDimitry Andric return FixedCompilationDatabase::loadFromFile(DatabasePath, ErrorMessage);
399809500fcSDimitry Andric }
400461a67faSDimitry Andric };
401461a67faSDimitry Andric
40248675466SDimitry Andric } // namespace
40348675466SDimitry Andric
404461a67faSDimitry Andric static CompilationDatabasePluginRegistry::Add<FixedCompilationDatabasePlugin>
405461a67faSDimitry Andric X("fixed-compilation-database", "Reads plain-text flags file");
406461a67faSDimitry Andric
4075e20cdd8SDimitry Andric namespace clang {
4085e20cdd8SDimitry Andric namespace tooling {
4095e20cdd8SDimitry Andric
41013cc256eSDimitry Andric // This anchor is used to force the linker to link in the generated object file
41113cc256eSDimitry Andric // and thus register the JSONCompilationDatabasePlugin.
41213cc256eSDimitry Andric extern volatile int JSONAnchorSource;
41345b53394SDimitry Andric static int LLVM_ATTRIBUTE_UNUSED JSONAnchorDest = JSONAnchorSource;
414dbe13110SDimitry Andric
41548675466SDimitry Andric } // namespace tooling
41648675466SDimitry Andric } // namespace clang
417