CompilationDatabase.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. //===- CompilationDatabase.cpp --------------------------------------------===//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. //
  9. // This file contains implementations of the CompilationDatabase base class
  10. // and the FixedCompilationDatabase.
  11. //
  12. // FIXME: Various functions that take a string &ErrorMessage should be upgraded
  13. // to Expected.
  14. //
  15. //===----------------------------------------------------------------------===//
  16. #include "clang/Tooling/CompilationDatabase.h"
  17. #include "clang/Basic/Diagnostic.h"
  18. #include "clang/Basic/DiagnosticIDs.h"
  19. #include "clang/Basic/DiagnosticOptions.h"
  20. #include "clang/Basic/LLVM.h"
  21. #include "clang/Driver/Action.h"
  22. #include "clang/Driver/Compilation.h"
  23. #include "clang/Driver/Driver.h"
  24. #include "clang/Driver/DriverDiagnostic.h"
  25. #include "clang/Driver/Job.h"
  26. #include "clang/Frontend/TextDiagnosticPrinter.h"
  27. #include "clang/Tooling/CompilationDatabasePluginRegistry.h"
  28. #include "clang/Tooling/Tooling.h"
  29. #include "llvm/ADT/ArrayRef.h"
  30. #include "llvm/ADT/IntrusiveRefCntPtr.h"
  31. #include "llvm/ADT/STLExtras.h"
  32. #include "llvm/ADT/SmallString.h"
  33. #include "llvm/ADT/SmallVector.h"
  34. #include "llvm/ADT/StringRef.h"
  35. #include "llvm/Option/Arg.h"
  36. #include "llvm/Support/Casting.h"
  37. #include "llvm/Support/Compiler.h"
  38. #include "llvm/Support/ErrorOr.h"
  39. #include "llvm/Support/Host.h"
  40. #include "llvm/Support/LineIterator.h"
  41. #include "llvm/Support/MemoryBuffer.h"
  42. #include "llvm/Support/Path.h"
  43. #include "llvm/Support/raw_ostream.h"
  44. #include <algorithm>
  45. #include <cassert>
  46. #include <cstring>
  47. #include <iterator>
  48. #include <memory>
  49. #include <sstream>
  50. #include <string>
  51. #include <system_error>
  52. #include <utility>
  53. #include <vector>
  54. using namespace clang;
  55. using namespace tooling;
  56. LLVM_INSTANTIATE_REGISTRY(CompilationDatabasePluginRegistry)
  57. CompilationDatabase::~CompilationDatabase() = default;
  58. std::unique_ptr<CompilationDatabase>
  59. CompilationDatabase::loadFromDirectory(StringRef BuildDirectory,
  60. std::string &ErrorMessage) {
  61. llvm::raw_string_ostream ErrorStream(ErrorMessage);
  62. for (CompilationDatabasePluginRegistry::iterator
  63. It = CompilationDatabasePluginRegistry::begin(),
  64. Ie = CompilationDatabasePluginRegistry::end();
  65. It != Ie; ++It) {
  66. std::string DatabaseErrorMessage;
  67. std::unique_ptr<CompilationDatabasePlugin> Plugin(It->instantiate());
  68. if (std::unique_ptr<CompilationDatabase> DB =
  69. Plugin->loadFromDirectory(BuildDirectory, DatabaseErrorMessage))
  70. return DB;
  71. ErrorStream << It->getName() << ": " << DatabaseErrorMessage << "\n";
  72. }
  73. return nullptr;
  74. }
  75. static std::unique_ptr<CompilationDatabase>
  76. findCompilationDatabaseFromDirectory(StringRef Directory,
  77. std::string &ErrorMessage) {
  78. std::stringstream ErrorStream;
  79. bool HasErrorMessage = false;
  80. while (!Directory.empty()) {
  81. std::string LoadErrorMessage;
  82. if (std::unique_ptr<CompilationDatabase> DB =
  83. CompilationDatabase::loadFromDirectory(Directory, LoadErrorMessage))
  84. return DB;
  85. if (!HasErrorMessage) {
  86. ErrorStream << "No compilation database found in " << Directory.str()
  87. << " or any parent directory\n" << LoadErrorMessage;
  88. HasErrorMessage = true;
  89. }
  90. Directory = llvm::sys::path::parent_path(Directory);
  91. }
  92. ErrorMessage = ErrorStream.str();
  93. return nullptr;
  94. }
  95. std::unique_ptr<CompilationDatabase>
  96. CompilationDatabase::autoDetectFromSource(StringRef SourceFile,
  97. std::string &ErrorMessage) {
  98. SmallString<1024> AbsolutePath(getAbsolutePath(SourceFile));
  99. StringRef Directory = llvm::sys::path::parent_path(AbsolutePath);
  100. std::unique_ptr<CompilationDatabase> DB =
  101. findCompilationDatabaseFromDirectory(Directory, ErrorMessage);
  102. if (!DB)
  103. ErrorMessage = ("Could not auto-detect compilation database for file \"" +
  104. SourceFile + "\"\n" + ErrorMessage).str();
  105. return DB;
  106. }
  107. std::unique_ptr<CompilationDatabase>
  108. CompilationDatabase::autoDetectFromDirectory(StringRef SourceDir,
  109. std::string &ErrorMessage) {
  110. SmallString<1024> AbsolutePath(getAbsolutePath(SourceDir));
  111. std::unique_ptr<CompilationDatabase> DB =
  112. findCompilationDatabaseFromDirectory(AbsolutePath, ErrorMessage);
  113. if (!DB)
  114. ErrorMessage = ("Could not auto-detect compilation database from directory \"" +
  115. SourceDir + "\"\n" + ErrorMessage).str();
  116. return DB;
  117. }
  118. std::vector<CompileCommand> CompilationDatabase::getAllCompileCommands() const {
  119. std::vector<CompileCommand> Result;
  120. for (const auto &File : getAllFiles()) {
  121. auto C = getCompileCommands(File);
  122. std::move(C.begin(), C.end(), std::back_inserter(Result));
  123. }
  124. return Result;
  125. }
  126. CompilationDatabasePlugin::~CompilationDatabasePlugin() = default;
  127. namespace {
  128. // Helper for recursively searching through a chain of actions and collecting
  129. // all inputs, direct and indirect, of compile jobs.
  130. struct CompileJobAnalyzer {
  131. SmallVector<std::string, 2> Inputs;
  132. void run(const driver::Action *A) {
  133. runImpl(A, false);
  134. }
  135. private:
  136. void runImpl(const driver::Action *A, bool Collect) {
  137. bool CollectChildren = Collect;
  138. switch (A->getKind()) {
  139. case driver::Action::CompileJobClass:
  140. CollectChildren = true;
  141. break;
  142. case driver::Action::InputClass:
  143. if (Collect) {
  144. const auto *IA = cast<driver::InputAction>(A);
  145. Inputs.push_back(IA->getInputArg().getSpelling());
  146. }
  147. break;
  148. default:
  149. // Don't care about others
  150. break;
  151. }
  152. for (const driver::Action *AI : A->inputs())
  153. runImpl(AI, CollectChildren);
  154. }
  155. };
  156. // Special DiagnosticConsumer that looks for warn_drv_input_file_unused
  157. // diagnostics from the driver and collects the option strings for those unused
  158. // options.
  159. class UnusedInputDiagConsumer : public DiagnosticConsumer {
  160. public:
  161. UnusedInputDiagConsumer(DiagnosticConsumer &Other) : Other(Other) {}
  162. void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
  163. const Diagnostic &Info) override {
  164. if (Info.getID() == diag::warn_drv_input_file_unused) {
  165. // Arg 1 for this diagnostic is the option that didn't get used.
  166. UnusedInputs.push_back(Info.getArgStdStr(0));
  167. } else if (DiagLevel >= DiagnosticsEngine::Error) {
  168. // If driver failed to create compilation object, show the diagnostics
  169. // to user.
  170. Other.HandleDiagnostic(DiagLevel, Info);
  171. }
  172. }
  173. DiagnosticConsumer &Other;
  174. SmallVector<std::string, 2> UnusedInputs;
  175. };
  176. // Unary functor for asking "Given a StringRef S1, does there exist a string
  177. // S2 in Arr where S1 == S2?"
  178. struct MatchesAny {
  179. MatchesAny(ArrayRef<std::string> Arr) : Arr(Arr) {}
  180. bool operator() (StringRef S) {
  181. for (const std::string *I = Arr.begin(), *E = Arr.end(); I != E; ++I)
  182. if (*I == S)
  183. return true;
  184. return false;
  185. }
  186. private:
  187. ArrayRef<std::string> Arr;
  188. };
  189. // Filter of tools unused flags such as -no-integrated-as and -Wa,*.
  190. // They are not used for syntax checking, and could confuse targets
  191. // which don't support these options.
  192. struct FilterUnusedFlags {
  193. bool operator() (StringRef S) {
  194. return (S == "-no-integrated-as") || S.startswith("-Wa,");
  195. }
  196. };
  197. std::string GetClangToolCommand() {
  198. static int Dummy;
  199. std::string ClangExecutable =
  200. llvm::sys::fs::getMainExecutable("clang", (void *)&Dummy);
  201. SmallString<128> ClangToolPath;
  202. ClangToolPath = llvm::sys::path::parent_path(ClangExecutable);
  203. llvm::sys::path::append(ClangToolPath, "clang-tool");
  204. return ClangToolPath.str();
  205. }
  206. } // namespace
  207. /// Strips any positional args and possible argv[0] from a command-line
  208. /// provided by the user to construct a FixedCompilationDatabase.
  209. ///
  210. /// FixedCompilationDatabase requires a command line to be in this format as it
  211. /// constructs the command line for each file by appending the name of the file
  212. /// to be compiled. FixedCompilationDatabase also adds its own argv[0] to the
  213. /// start of the command line although its value is not important as it's just
  214. /// ignored by the Driver invoked by the ClangTool using the
  215. /// FixedCompilationDatabase.
  216. ///
  217. /// FIXME: This functionality should probably be made available by
  218. /// clang::driver::Driver although what the interface should look like is not
  219. /// clear.
  220. ///
  221. /// \param[in] Args Args as provided by the user.
  222. /// \return Resulting stripped command line.
  223. /// \li true if successful.
  224. /// \li false if \c Args cannot be used for compilation jobs (e.g.
  225. /// contains an option like -E or -version).
  226. static bool stripPositionalArgs(std::vector<const char *> Args,
  227. std::vector<std::string> &Result,
  228. std::string &ErrorMsg) {
  229. IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
  230. llvm::raw_string_ostream Output(ErrorMsg);
  231. TextDiagnosticPrinter DiagnosticPrinter(Output, &*DiagOpts);
  232. UnusedInputDiagConsumer DiagClient(DiagnosticPrinter);
  233. DiagnosticsEngine Diagnostics(
  234. IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()),
  235. &*DiagOpts, &DiagClient, false);
  236. // The clang executable path isn't required since the jobs the driver builds
  237. // will not be executed.
  238. std::unique_ptr<driver::Driver> NewDriver(new driver::Driver(
  239. /* ClangExecutable= */ "", llvm::sys::getDefaultTargetTriple(),
  240. Diagnostics));
  241. NewDriver->setCheckInputsExist(false);
  242. // This becomes the new argv[0]. The value is used to detect libc++ include
  243. // dirs on Mac, it isn't used for other platforms.
  244. std::string Argv0 = GetClangToolCommand();
  245. Args.insert(Args.begin(), Argv0.c_str());
  246. // By adding -c, we force the driver to treat compilation as the last phase.
  247. // It will then issue warnings via Diagnostics about un-used options that
  248. // would have been used for linking. If the user provided a compiler name as
  249. // the original argv[0], this will be treated as a linker input thanks to
  250. // insertng a new argv[0] above. All un-used options get collected by
  251. // UnusedInputdiagConsumer and get stripped out later.
  252. Args.push_back("-c");
  253. // Put a dummy C++ file on to ensure there's at least one compile job for the
  254. // driver to construct. If the user specified some other argument that
  255. // prevents compilation, e.g. -E or something like -version, we may still end
  256. // up with no jobs but then this is the user's fault.
  257. Args.push_back("placeholder.cpp");
  258. Args.erase(std::remove_if(Args.begin(), Args.end(), FilterUnusedFlags()),
  259. Args.end());
  260. const std::unique_ptr<driver::Compilation> Compilation(
  261. NewDriver->BuildCompilation(Args));
  262. if (!Compilation)
  263. return false;
  264. const driver::JobList &Jobs = Compilation->getJobs();
  265. CompileJobAnalyzer CompileAnalyzer;
  266. for (const auto &Cmd : Jobs) {
  267. // Collect only for Assemble, Backend, and Compile jobs. If we do all jobs
  268. // we get duplicates since Link jobs point to Assemble jobs as inputs.
  269. // -flto* flags make the BackendJobClass, which still needs analyzer.
  270. if (Cmd.getSource().getKind() == driver::Action::AssembleJobClass ||
  271. Cmd.getSource().getKind() == driver::Action::BackendJobClass ||
  272. Cmd.getSource().getKind() == driver::Action::CompileJobClass) {
  273. CompileAnalyzer.run(&Cmd.getSource());
  274. }
  275. }
  276. if (CompileAnalyzer.Inputs.empty()) {
  277. ErrorMsg = "warning: no compile jobs found\n";
  278. return false;
  279. }
  280. // Remove all compilation input files from the command line. This is
  281. // necessary so that getCompileCommands() can construct a command line for
  282. // each file.
  283. std::vector<const char *>::iterator End = std::remove_if(
  284. Args.begin(), Args.end(), MatchesAny(CompileAnalyzer.Inputs));
  285. // Remove all inputs deemed unused for compilation.
  286. End = std::remove_if(Args.begin(), End, MatchesAny(DiagClient.UnusedInputs));
  287. // Remove the -c add above as well. It will be at the end right now.
  288. assert(strcmp(*(End - 1), "-c") == 0);
  289. --End;
  290. Result = std::vector<std::string>(Args.begin() + 1, End);
  291. return true;
  292. }
  293. std::unique_ptr<FixedCompilationDatabase>
  294. FixedCompilationDatabase::loadFromCommandLine(int &Argc,
  295. const char *const *Argv,
  296. std::string &ErrorMsg,
  297. Twine Directory) {
  298. ErrorMsg.clear();
  299. if (Argc == 0)
  300. return nullptr;
  301. const char *const *DoubleDash = std::find(Argv, Argv + Argc, StringRef("--"));
  302. if (DoubleDash == Argv + Argc)
  303. return nullptr;
  304. std::vector<const char *> CommandLine(DoubleDash + 1, Argv + Argc);
  305. Argc = DoubleDash - Argv;
  306. std::vector<std::string> StrippedArgs;
  307. if (!stripPositionalArgs(CommandLine, StrippedArgs, ErrorMsg))
  308. return nullptr;
  309. return std::make_unique<FixedCompilationDatabase>(Directory, StrippedArgs);
  310. }
  311. std::unique_ptr<FixedCompilationDatabase>
  312. FixedCompilationDatabase::loadFromFile(StringRef Path, std::string &ErrorMsg) {
  313. ErrorMsg.clear();
  314. llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> File =
  315. llvm::MemoryBuffer::getFile(Path);
  316. if (std::error_code Result = File.getError()) {
  317. ErrorMsg = "Error while opening fixed database: " + Result.message();
  318. return nullptr;
  319. }
  320. std::vector<std::string> Args{llvm::line_iterator(**File),
  321. llvm::line_iterator()};
  322. return std::make_unique<FixedCompilationDatabase>(
  323. llvm::sys::path::parent_path(Path), std::move(Args));
  324. }
  325. FixedCompilationDatabase::
  326. FixedCompilationDatabase(Twine Directory, ArrayRef<std::string> CommandLine) {
  327. std::vector<std::string> ToolCommandLine(1, GetClangToolCommand());
  328. ToolCommandLine.insert(ToolCommandLine.end(),
  329. CommandLine.begin(), CommandLine.end());
  330. CompileCommands.emplace_back(Directory, StringRef(),
  331. std::move(ToolCommandLine),
  332. StringRef());
  333. }
  334. std::vector<CompileCommand>
  335. FixedCompilationDatabase::getCompileCommands(StringRef FilePath) const {
  336. std::vector<CompileCommand> Result(CompileCommands);
  337. Result[0].CommandLine.push_back(FilePath);
  338. Result[0].Filename = FilePath;
  339. return Result;
  340. }
  341. namespace {
  342. class FixedCompilationDatabasePlugin : public CompilationDatabasePlugin {
  343. std::unique_ptr<CompilationDatabase>
  344. loadFromDirectory(StringRef Directory, std::string &ErrorMessage) override {
  345. SmallString<1024> DatabasePath(Directory);
  346. llvm::sys::path::append(DatabasePath, "compile_flags.txt");
  347. return FixedCompilationDatabase::loadFromFile(DatabasePath, ErrorMessage);
  348. }
  349. };
  350. } // namespace
  351. static CompilationDatabasePluginRegistry::Add<FixedCompilationDatabasePlugin>
  352. X("fixed-compilation-database", "Reads plain-text flags file");
  353. namespace clang {
  354. namespace tooling {
  355. // This anchor is used to force the linker to link in the generated object file
  356. // and thus register the JSONCompilationDatabasePlugin.
  357. extern volatile int JSONAnchorSource;
  358. static int LLVM_ATTRIBUTE_UNUSED JSONAnchorDest = JSONAnchorSource;
  359. } // namespace tooling
  360. } // namespace clang