Tooling.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696
  1. //===- Tooling.cpp - Running clang standalone tools -----------------------===//
  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 implements functions to run clang tools standalone instead
  10. // of running them as a plugin.
  11. //
  12. //===----------------------------------------------------------------------===//
  13. #include "clang/Tooling/Tooling.h"
  14. #include "clang/Basic/Diagnostic.h"
  15. #include "clang/Basic/DiagnosticIDs.h"
  16. #include "clang/Basic/DiagnosticOptions.h"
  17. #include "clang/Basic/FileManager.h"
  18. #include "clang/Basic/FileSystemOptions.h"
  19. #include "clang/Basic/LLVM.h"
  20. #include "clang/Driver/Compilation.h"
  21. #include "clang/Driver/Driver.h"
  22. #include "clang/Driver/Job.h"
  23. #include "clang/Driver/Options.h"
  24. #include "clang/Driver/Tool.h"
  25. #include "clang/Driver/ToolChain.h"
  26. #include "clang/Frontend/ASTUnit.h"
  27. #include "clang/Frontend/CompilerInstance.h"
  28. #include "clang/Frontend/CompilerInvocation.h"
  29. #include "clang/Frontend/FrontendDiagnostic.h"
  30. #include "clang/Frontend/FrontendOptions.h"
  31. #include "clang/Frontend/TextDiagnosticPrinter.h"
  32. #include "clang/Lex/HeaderSearchOptions.h"
  33. #include "clang/Lex/PreprocessorOptions.h"
  34. #include "clang/Tooling/ArgumentsAdjusters.h"
  35. #include "clang/Tooling/CompilationDatabase.h"
  36. #include "llvm/ADT/ArrayRef.h"
  37. #include "llvm/ADT/IntrusiveRefCntPtr.h"
  38. #include "llvm/ADT/SmallString.h"
  39. #include "llvm/ADT/StringRef.h"
  40. #include "llvm/ADT/Twine.h"
  41. #include "llvm/Option/ArgList.h"
  42. #include "llvm/Option/OptTable.h"
  43. #include "llvm/Option/Option.h"
  44. #include "llvm/Support/Casting.h"
  45. #include "llvm/Support/Debug.h"
  46. #include "llvm/Support/ErrorHandling.h"
  47. #include "llvm/Support/FileSystem.h"
  48. #include "llvm/Support/Host.h"
  49. #include "llvm/Support/MemoryBuffer.h"
  50. #include "llvm/Support/Path.h"
  51. #include "llvm/Support/VirtualFileSystem.h"
  52. #include "llvm/Support/raw_ostream.h"
  53. #include <cassert>
  54. #include <cstring>
  55. #include <memory>
  56. #include <string>
  57. #include <system_error>
  58. #include <utility>
  59. #include <vector>
  60. #define DEBUG_TYPE "clang-tooling"
  61. using namespace clang;
  62. using namespace tooling;
  63. ToolAction::~ToolAction() = default;
  64. FrontendActionFactory::~FrontendActionFactory() = default;
  65. // FIXME: This file contains structural duplication with other parts of the
  66. // code that sets up a compiler to run tools on it, and we should refactor
  67. // it to be based on the same framework.
  68. /// Builds a clang driver initialized for running clang tools.
  69. static driver::Driver *
  70. newDriver(DiagnosticsEngine *Diagnostics, const char *BinaryName,
  71. IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {
  72. driver::Driver *CompilerDriver =
  73. new driver::Driver(BinaryName, llvm::sys::getDefaultTargetTriple(),
  74. *Diagnostics, "clang LLVM compiler", std::move(VFS));
  75. CompilerDriver->setTitle("clang_based_tool");
  76. return CompilerDriver;
  77. }
  78. /// Decide whether extra compiler frontend commands can be ignored.
  79. static bool ignoreExtraCC1Commands(const driver::Compilation *Compilation) {
  80. const driver::JobList &Jobs = Compilation->getJobs();
  81. const driver::ActionList &Actions = Compilation->getActions();
  82. bool OffloadCompilation = false;
  83. // Jobs and Actions look very different depending on whether the Clang tool
  84. // injected -fsyntax-only or not. Try to handle both cases here.
  85. for (const auto &Job : Jobs)
  86. if (StringRef(Job.getExecutable()) == "clang-offload-bundler")
  87. OffloadCompilation = true;
  88. if (Jobs.size() > 1) {
  89. for (auto *A : Actions){
  90. // On MacOSX real actions may end up being wrapped in BindArchAction
  91. if (isa<driver::BindArchAction>(A))
  92. A = *A->input_begin();
  93. if (isa<driver::OffloadAction>(A)) {
  94. // Offload compilation has 2 top-level actions, one (at the front) is
  95. // the original host compilation and the other is offload action
  96. // composed of at least one device compilation. For such case, general
  97. // tooling will consider host-compilation only. For tooling on device
  98. // compilation, device compilation only option, such as
  99. // `--cuda-device-only`, needs specifying.
  100. assert(Actions.size() > 1);
  101. assert(
  102. isa<driver::CompileJobAction>(Actions.front()) ||
  103. // On MacOSX real actions may end up being wrapped in
  104. // BindArchAction.
  105. (isa<driver::BindArchAction>(Actions.front()) &&
  106. isa<driver::CompileJobAction>(*Actions.front()->input_begin())));
  107. OffloadCompilation = true;
  108. break;
  109. }
  110. }
  111. }
  112. return OffloadCompilation;
  113. }
  114. namespace clang {
  115. namespace tooling {
  116. const llvm::opt::ArgStringList *
  117. getCC1Arguments(DiagnosticsEngine *Diagnostics,
  118. driver::Compilation *Compilation) {
  119. const driver::JobList &Jobs = Compilation->getJobs();
  120. auto IsCC1Command = [](const driver::Command &Cmd) {
  121. return StringRef(Cmd.getCreator().getName()) == "clang";
  122. };
  123. auto IsSrcFile = [](const driver::InputInfo &II) {
  124. return isSrcFile(II.getType());
  125. };
  126. llvm::SmallVector<const driver::Command *, 1> CC1Jobs;
  127. for (const driver::Command &Job : Jobs)
  128. if (IsCC1Command(Job) && llvm::all_of(Job.getInputInfos(), IsSrcFile))
  129. CC1Jobs.push_back(&Job);
  130. if (CC1Jobs.empty() ||
  131. (CC1Jobs.size() > 1 && !ignoreExtraCC1Commands(Compilation))) {
  132. SmallString<256> error_msg;
  133. llvm::raw_svector_ostream error_stream(error_msg);
  134. Jobs.Print(error_stream, "; ", true);
  135. Diagnostics->Report(diag::err_fe_expected_compiler_job)
  136. << error_stream.str();
  137. return nullptr;
  138. }
  139. return &CC1Jobs[0]->getArguments();
  140. }
  141. /// Returns a clang build invocation initialized from the CC1 flags.
  142. CompilerInvocation *newInvocation(DiagnosticsEngine *Diagnostics,
  143. ArrayRef<const char *> CC1Args,
  144. const char *const BinaryName) {
  145. assert(!CC1Args.empty() && "Must at least contain the program name!");
  146. CompilerInvocation *Invocation = new CompilerInvocation;
  147. CompilerInvocation::CreateFromArgs(*Invocation, CC1Args, *Diagnostics,
  148. BinaryName);
  149. Invocation->getFrontendOpts().DisableFree = false;
  150. Invocation->getCodeGenOpts().DisableFree = false;
  151. return Invocation;
  152. }
  153. bool runToolOnCode(std::unique_ptr<FrontendAction> ToolAction,
  154. const Twine &Code, const Twine &FileName,
  155. std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
  156. return runToolOnCodeWithArgs(std::move(ToolAction), Code,
  157. std::vector<std::string>(), FileName,
  158. "clang-tool", std::move(PCHContainerOps));
  159. }
  160. } // namespace tooling
  161. } // namespace clang
  162. static std::vector<std::string>
  163. getSyntaxOnlyToolArgs(const Twine &ToolName,
  164. const std::vector<std::string> &ExtraArgs,
  165. StringRef FileName) {
  166. std::vector<std::string> Args;
  167. Args.push_back(ToolName.str());
  168. Args.push_back("-fsyntax-only");
  169. Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());
  170. Args.push_back(FileName.str());
  171. return Args;
  172. }
  173. namespace clang {
  174. namespace tooling {
  175. bool runToolOnCodeWithArgs(
  176. std::unique_ptr<FrontendAction> ToolAction, const Twine &Code,
  177. llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
  178. const std::vector<std::string> &Args, const Twine &FileName,
  179. const Twine &ToolName,
  180. std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
  181. SmallString<16> FileNameStorage;
  182. StringRef FileNameRef = FileName.toNullTerminatedStringRef(FileNameStorage);
  183. llvm::IntrusiveRefCntPtr<FileManager> Files(
  184. new FileManager(FileSystemOptions(), VFS));
  185. ArgumentsAdjuster Adjuster = getClangStripDependencyFileAdjuster();
  186. ToolInvocation Invocation(
  187. getSyntaxOnlyToolArgs(ToolName, Adjuster(Args, FileNameRef), FileNameRef),
  188. std::move(ToolAction), Files.get(), std::move(PCHContainerOps));
  189. return Invocation.run();
  190. }
  191. bool runToolOnCodeWithArgs(
  192. std::unique_ptr<FrontendAction> ToolAction, const Twine &Code,
  193. const std::vector<std::string> &Args, const Twine &FileName,
  194. const Twine &ToolName,
  195. std::shared_ptr<PCHContainerOperations> PCHContainerOps,
  196. const FileContentMappings &VirtualMappedFiles) {
  197. llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
  198. new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
  199. llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
  200. new llvm::vfs::InMemoryFileSystem);
  201. OverlayFileSystem->pushOverlay(InMemoryFileSystem);
  202. SmallString<1024> CodeStorage;
  203. InMemoryFileSystem->addFile(FileName, 0,
  204. llvm::MemoryBuffer::getMemBuffer(
  205. Code.toNullTerminatedStringRef(CodeStorage)));
  206. for (auto &FilenameWithContent : VirtualMappedFiles) {
  207. InMemoryFileSystem->addFile(
  208. FilenameWithContent.first, 0,
  209. llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
  210. }
  211. return runToolOnCodeWithArgs(std::move(ToolAction), Code, OverlayFileSystem,
  212. Args, FileName, ToolName);
  213. }
  214. llvm::Expected<std::string> getAbsolutePath(llvm::vfs::FileSystem &FS,
  215. StringRef File) {
  216. StringRef RelativePath(File);
  217. // FIXME: Should '.\\' be accepted on Win32?
  218. if (RelativePath.startswith("./")) {
  219. RelativePath = RelativePath.substr(strlen("./"));
  220. }
  221. SmallString<1024> AbsolutePath = RelativePath;
  222. if (auto EC = FS.makeAbsolute(AbsolutePath))
  223. return llvm::errorCodeToError(EC);
  224. llvm::sys::path::native(AbsolutePath);
  225. return std::string(AbsolutePath.str());
  226. }
  227. std::string getAbsolutePath(StringRef File) {
  228. return llvm::cantFail(getAbsolutePath(*llvm::vfs::getRealFileSystem(), File));
  229. }
  230. void addTargetAndModeForProgramName(std::vector<std::string> &CommandLine,
  231. StringRef InvokedAs) {
  232. if (CommandLine.empty() || InvokedAs.empty())
  233. return;
  234. const auto &Table = driver::getDriverOptTable();
  235. // --target=X
  236. const std::string TargetOPT =
  237. Table.getOption(driver::options::OPT_target).getPrefixedName();
  238. // -target X
  239. const std::string TargetOPTLegacy =
  240. Table.getOption(driver::options::OPT_target_legacy_spelling)
  241. .getPrefixedName();
  242. // --driver-mode=X
  243. const std::string DriverModeOPT =
  244. Table.getOption(driver::options::OPT_driver_mode).getPrefixedName();
  245. auto TargetMode =
  246. driver::ToolChain::getTargetAndModeFromProgramName(InvokedAs);
  247. // No need to search for target args if we don't have a target/mode to insert.
  248. bool ShouldAddTarget = TargetMode.TargetIsValid;
  249. bool ShouldAddMode = TargetMode.DriverMode != nullptr;
  250. // Skip CommandLine[0].
  251. auto CommandLine1 = CommandLine.begin();
  252. ++CommandLine1;
  253. for (auto Token = CommandLine1; Token != CommandLine.end();
  254. ++Token) {
  255. StringRef TokenRef(*Token);
  256. ShouldAddTarget = ShouldAddTarget && !TokenRef.startswith(TargetOPT) &&
  257. !TokenRef.equals(TargetOPTLegacy);
  258. ShouldAddMode = ShouldAddMode && !TokenRef.startswith(DriverModeOPT);
  259. }
  260. if (ShouldAddMode) {
  261. CommandLine.insert(CommandLine1, TargetMode.DriverMode);
  262. }
  263. if (ShouldAddTarget) {
  264. CommandLine.insert(CommandLine1,
  265. TargetOPT + TargetMode.TargetPrefix);
  266. }
  267. }
  268. } // namespace tooling
  269. } // namespace clang
  270. namespace {
  271. class SingleFrontendActionFactory : public FrontendActionFactory {
  272. std::unique_ptr<FrontendAction> Action;
  273. public:
  274. SingleFrontendActionFactory(std::unique_ptr<FrontendAction> Action)
  275. : Action(std::move(Action)) {}
  276. std::unique_ptr<FrontendAction> create() override {
  277. return std::move(Action);
  278. }
  279. };
  280. } // namespace
  281. ToolInvocation::ToolInvocation(
  282. std::vector<std::string> CommandLine, ToolAction *Action,
  283. FileManager *Files, std::shared_ptr<PCHContainerOperations> PCHContainerOps)
  284. : CommandLine(std::move(CommandLine)), Action(Action), OwnsAction(false),
  285. Files(Files), PCHContainerOps(std::move(PCHContainerOps)) {}
  286. ToolInvocation::ToolInvocation(
  287. std::vector<std::string> CommandLine,
  288. std::unique_ptr<FrontendAction> FAction, FileManager *Files,
  289. std::shared_ptr<PCHContainerOperations> PCHContainerOps)
  290. : CommandLine(std::move(CommandLine)),
  291. Action(new SingleFrontendActionFactory(std::move(FAction))),
  292. OwnsAction(true), Files(Files),
  293. PCHContainerOps(std::move(PCHContainerOps)) {}
  294. ToolInvocation::~ToolInvocation() {
  295. if (OwnsAction)
  296. delete Action;
  297. }
  298. bool ToolInvocation::run() {
  299. llvm::opt::ArgStringList Argv;
  300. for (const std::string &Str : CommandLine)
  301. Argv.push_back(Str.c_str());
  302. const char *const BinaryName = Argv[0];
  303. // Parse diagnostic options from the driver command-line only if none were
  304. // explicitly set.
  305. IntrusiveRefCntPtr<DiagnosticOptions> ParsedDiagOpts;
  306. DiagnosticOptions *DiagOpts = this->DiagOpts;
  307. if (!DiagOpts) {
  308. ParsedDiagOpts = CreateAndPopulateDiagOpts(Argv);
  309. DiagOpts = &*ParsedDiagOpts;
  310. }
  311. TextDiagnosticPrinter DiagnosticPrinter(llvm::errs(), DiagOpts);
  312. IntrusiveRefCntPtr<DiagnosticsEngine> Diagnostics =
  313. CompilerInstance::createDiagnostics(
  314. &*DiagOpts, DiagConsumer ? DiagConsumer : &DiagnosticPrinter, false);
  315. // Although `Diagnostics` are used only for command-line parsing, the custom
  316. // `DiagConsumer` might expect a `SourceManager` to be present.
  317. SourceManager SrcMgr(*Diagnostics, *Files);
  318. Diagnostics->setSourceManager(&SrcMgr);
  319. // We already have a cc1, just create an invocation.
  320. if (CommandLine.size() >= 2 && CommandLine[1] == "-cc1") {
  321. ArrayRef<const char *> CC1Args = ArrayRef(Argv).drop_front();
  322. std::unique_ptr<CompilerInvocation> Invocation(
  323. newInvocation(&*Diagnostics, CC1Args, BinaryName));
  324. if (Diagnostics->hasErrorOccurred())
  325. return false;
  326. return Action->runInvocation(std::move(Invocation), Files,
  327. std::move(PCHContainerOps), DiagConsumer);
  328. }
  329. const std::unique_ptr<driver::Driver> Driver(
  330. newDriver(&*Diagnostics, BinaryName, &Files->getVirtualFileSystem()));
  331. // The "input file not found" diagnostics from the driver are useful.
  332. // The driver is only aware of the VFS working directory, but some clients
  333. // change this at the FileManager level instead.
  334. // In this case the checks have false positives, so skip them.
  335. if (!Files->getFileSystemOpts().WorkingDir.empty())
  336. Driver->setCheckInputsExist(false);
  337. const std::unique_ptr<driver::Compilation> Compilation(
  338. Driver->BuildCompilation(llvm::ArrayRef(Argv)));
  339. if (!Compilation)
  340. return false;
  341. const llvm::opt::ArgStringList *const CC1Args = getCC1Arguments(
  342. &*Diagnostics, Compilation.get());
  343. if (!CC1Args)
  344. return false;
  345. std::unique_ptr<CompilerInvocation> Invocation(
  346. newInvocation(&*Diagnostics, *CC1Args, BinaryName));
  347. return runInvocation(BinaryName, Compilation.get(), std::move(Invocation),
  348. std::move(PCHContainerOps));
  349. }
  350. bool ToolInvocation::runInvocation(
  351. const char *BinaryName, driver::Compilation *Compilation,
  352. std::shared_ptr<CompilerInvocation> Invocation,
  353. std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
  354. // Show the invocation, with -v.
  355. if (Invocation->getHeaderSearchOpts().Verbose) {
  356. llvm::errs() << "clang Invocation:\n";
  357. Compilation->getJobs().Print(llvm::errs(), "\n", true);
  358. llvm::errs() << "\n";
  359. }
  360. return Action->runInvocation(std::move(Invocation), Files,
  361. std::move(PCHContainerOps), DiagConsumer);
  362. }
  363. bool FrontendActionFactory::runInvocation(
  364. std::shared_ptr<CompilerInvocation> Invocation, FileManager *Files,
  365. std::shared_ptr<PCHContainerOperations> PCHContainerOps,
  366. DiagnosticConsumer *DiagConsumer) {
  367. // Create a compiler instance to handle the actual work.
  368. CompilerInstance Compiler(std::move(PCHContainerOps));
  369. Compiler.setInvocation(std::move(Invocation));
  370. Compiler.setFileManager(Files);
  371. // The FrontendAction can have lifetime requirements for Compiler or its
  372. // members, and we need to ensure it's deleted earlier than Compiler. So we
  373. // pass it to an std::unique_ptr declared after the Compiler variable.
  374. std::unique_ptr<FrontendAction> ScopedToolAction(create());
  375. // Create the compiler's actual diagnostics engine.
  376. Compiler.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false);
  377. if (!Compiler.hasDiagnostics())
  378. return false;
  379. Compiler.createSourceManager(*Files);
  380. const bool Success = Compiler.ExecuteAction(*ScopedToolAction);
  381. Files->clearStatCache();
  382. return Success;
  383. }
  384. ClangTool::ClangTool(const CompilationDatabase &Compilations,
  385. ArrayRef<std::string> SourcePaths,
  386. std::shared_ptr<PCHContainerOperations> PCHContainerOps,
  387. IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS,
  388. IntrusiveRefCntPtr<FileManager> Files)
  389. : Compilations(Compilations), SourcePaths(SourcePaths),
  390. PCHContainerOps(std::move(PCHContainerOps)),
  391. OverlayFileSystem(new llvm::vfs::OverlayFileSystem(std::move(BaseFS))),
  392. InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem),
  393. Files(Files ? Files
  394. : new FileManager(FileSystemOptions(), OverlayFileSystem)) {
  395. OverlayFileSystem->pushOverlay(InMemoryFileSystem);
  396. appendArgumentsAdjuster(getClangStripOutputAdjuster());
  397. appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster());
  398. appendArgumentsAdjuster(getClangStripDependencyFileAdjuster());
  399. if (Files)
  400. Files->setVirtualFileSystem(OverlayFileSystem);
  401. }
  402. ClangTool::~ClangTool() = default;
  403. void ClangTool::mapVirtualFile(StringRef FilePath, StringRef Content) {
  404. MappedFileContents.push_back(std::make_pair(FilePath, Content));
  405. }
  406. void ClangTool::appendArgumentsAdjuster(ArgumentsAdjuster Adjuster) {
  407. ArgsAdjuster = combineAdjusters(std::move(ArgsAdjuster), std::move(Adjuster));
  408. }
  409. void ClangTool::clearArgumentsAdjusters() {
  410. ArgsAdjuster = nullptr;
  411. }
  412. static void injectResourceDir(CommandLineArguments &Args, const char *Argv0,
  413. void *MainAddr) {
  414. // Allow users to override the resource dir.
  415. for (StringRef Arg : Args)
  416. if (Arg.startswith("-resource-dir"))
  417. return;
  418. // If there's no override in place add our resource dir.
  419. Args = getInsertArgumentAdjuster(
  420. ("-resource-dir=" + CompilerInvocation::GetResourcesPath(Argv0, MainAddr))
  421. .c_str())(Args, "");
  422. }
  423. int ClangTool::run(ToolAction *Action) {
  424. // Exists solely for the purpose of lookup of the resource path.
  425. // This just needs to be some symbol in the binary.
  426. static int StaticSymbol;
  427. // First insert all absolute paths into the in-memory VFS. These are global
  428. // for all compile commands.
  429. if (SeenWorkingDirectories.insert("/").second)
  430. for (const auto &MappedFile : MappedFileContents)
  431. if (llvm::sys::path::is_absolute(MappedFile.first))
  432. InMemoryFileSystem->addFile(
  433. MappedFile.first, 0,
  434. llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
  435. bool ProcessingFailed = false;
  436. bool FileSkipped = false;
  437. // Compute all absolute paths before we run any actions, as those will change
  438. // the working directory.
  439. std::vector<std::string> AbsolutePaths;
  440. AbsolutePaths.reserve(SourcePaths.size());
  441. for (const auto &SourcePath : SourcePaths) {
  442. auto AbsPath = getAbsolutePath(*OverlayFileSystem, SourcePath);
  443. if (!AbsPath) {
  444. llvm::errs() << "Skipping " << SourcePath
  445. << ". Error while getting an absolute path: "
  446. << llvm::toString(AbsPath.takeError()) << "\n";
  447. continue;
  448. }
  449. AbsolutePaths.push_back(std::move(*AbsPath));
  450. }
  451. // Remember the working directory in case we need to restore it.
  452. std::string InitialWorkingDir;
  453. if (RestoreCWD) {
  454. if (auto CWD = OverlayFileSystem->getCurrentWorkingDirectory()) {
  455. InitialWorkingDir = std::move(*CWD);
  456. } else {
  457. llvm::errs() << "Could not get working directory: "
  458. << CWD.getError().message() << "\n";
  459. }
  460. }
  461. for (llvm::StringRef File : AbsolutePaths) {
  462. // Currently implementations of CompilationDatabase::getCompileCommands can
  463. // change the state of the file system (e.g. prepare generated headers), so
  464. // this method needs to run right before we invoke the tool, as the next
  465. // file may require a different (incompatible) state of the file system.
  466. //
  467. // FIXME: Make the compilation database interface more explicit about the
  468. // requirements to the order of invocation of its members.
  469. std::vector<CompileCommand> CompileCommandsForFile =
  470. Compilations.getCompileCommands(File);
  471. if (CompileCommandsForFile.empty()) {
  472. llvm::errs() << "Skipping " << File << ". Compile command not found.\n";
  473. FileSkipped = true;
  474. continue;
  475. }
  476. for (CompileCommand &CompileCommand : CompileCommandsForFile) {
  477. // FIXME: chdir is thread hostile; on the other hand, creating the same
  478. // behavior as chdir is complex: chdir resolves the path once, thus
  479. // guaranteeing that all subsequent relative path operations work
  480. // on the same path the original chdir resulted in. This makes a
  481. // difference for example on network filesystems, where symlinks might be
  482. // switched during runtime of the tool. Fixing this depends on having a
  483. // file system abstraction that allows openat() style interactions.
  484. if (OverlayFileSystem->setCurrentWorkingDirectory(
  485. CompileCommand.Directory))
  486. llvm::report_fatal_error("Cannot chdir into \"" +
  487. Twine(CompileCommand.Directory) + "\"!");
  488. // Now fill the in-memory VFS with the relative file mappings so it will
  489. // have the correct relative paths. We never remove mappings but that
  490. // should be fine.
  491. if (SeenWorkingDirectories.insert(CompileCommand.Directory).second)
  492. for (const auto &MappedFile : MappedFileContents)
  493. if (!llvm::sys::path::is_absolute(MappedFile.first))
  494. InMemoryFileSystem->addFile(
  495. MappedFile.first, 0,
  496. llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
  497. std::vector<std::string> CommandLine = CompileCommand.CommandLine;
  498. if (ArgsAdjuster)
  499. CommandLine = ArgsAdjuster(CommandLine, CompileCommand.Filename);
  500. assert(!CommandLine.empty());
  501. // Add the resource dir based on the binary of this tool. argv[0] in the
  502. // compilation database may refer to a different compiler and we want to
  503. // pick up the very same standard library that compiler is using. The
  504. // builtin headers in the resource dir need to match the exact clang
  505. // version the tool is using.
  506. // FIXME: On linux, GetMainExecutable is independent of the value of the
  507. // first argument, thus allowing ClangTool and runToolOnCode to just
  508. // pass in made-up names here. Make sure this works on other platforms.
  509. injectResourceDir(CommandLine, "clang_tool", &StaticSymbol);
  510. // FIXME: We need a callback mechanism for the tool writer to output a
  511. // customized message for each file.
  512. LLVM_DEBUG({ llvm::dbgs() << "Processing: " << File << ".\n"; });
  513. ToolInvocation Invocation(std::move(CommandLine), Action, Files.get(),
  514. PCHContainerOps);
  515. Invocation.setDiagnosticConsumer(DiagConsumer);
  516. if (!Invocation.run()) {
  517. // FIXME: Diagnostics should be used instead.
  518. if (PrintErrorMessage)
  519. llvm::errs() << "Error while processing " << File << ".\n";
  520. ProcessingFailed = true;
  521. }
  522. }
  523. }
  524. if (!InitialWorkingDir.empty()) {
  525. if (auto EC =
  526. OverlayFileSystem->setCurrentWorkingDirectory(InitialWorkingDir))
  527. llvm::errs() << "Error when trying to restore working dir: "
  528. << EC.message() << "\n";
  529. }
  530. return ProcessingFailed ? 1 : (FileSkipped ? 2 : 0);
  531. }
  532. namespace {
  533. class ASTBuilderAction : public ToolAction {
  534. std::vector<std::unique_ptr<ASTUnit>> &ASTs;
  535. public:
  536. ASTBuilderAction(std::vector<std::unique_ptr<ASTUnit>> &ASTs) : ASTs(ASTs) {}
  537. bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
  538. FileManager *Files,
  539. std::shared_ptr<PCHContainerOperations> PCHContainerOps,
  540. DiagnosticConsumer *DiagConsumer) override {
  541. std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation(
  542. Invocation, std::move(PCHContainerOps),
  543. CompilerInstance::createDiagnostics(&Invocation->getDiagnosticOpts(),
  544. DiagConsumer,
  545. /*ShouldOwnClient=*/false),
  546. Files);
  547. if (!AST)
  548. return false;
  549. ASTs.push_back(std::move(AST));
  550. return true;
  551. }
  552. };
  553. } // namespace
  554. int ClangTool::buildASTs(std::vector<std::unique_ptr<ASTUnit>> &ASTs) {
  555. ASTBuilderAction Action(ASTs);
  556. return run(&Action);
  557. }
  558. void ClangTool::setRestoreWorkingDir(bool RestoreCWD) {
  559. this->RestoreCWD = RestoreCWD;
  560. }
  561. void ClangTool::setPrintErrorMessage(bool PrintErrorMessage) {
  562. this->PrintErrorMessage = PrintErrorMessage;
  563. }
  564. namespace clang {
  565. namespace tooling {
  566. std::unique_ptr<ASTUnit>
  567. buildASTFromCode(StringRef Code, StringRef FileName,
  568. std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
  569. return buildASTFromCodeWithArgs(Code, std::vector<std::string>(), FileName,
  570. "clang-tool", std::move(PCHContainerOps));
  571. }
  572. std::unique_ptr<ASTUnit> buildASTFromCodeWithArgs(
  573. StringRef Code, const std::vector<std::string> &Args, StringRef FileName,
  574. StringRef ToolName, std::shared_ptr<PCHContainerOperations> PCHContainerOps,
  575. ArgumentsAdjuster Adjuster, const FileContentMappings &VirtualMappedFiles,
  576. DiagnosticConsumer *DiagConsumer) {
  577. std::vector<std::unique_ptr<ASTUnit>> ASTs;
  578. ASTBuilderAction Action(ASTs);
  579. llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
  580. new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
  581. llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
  582. new llvm::vfs::InMemoryFileSystem);
  583. OverlayFileSystem->pushOverlay(InMemoryFileSystem);
  584. llvm::IntrusiveRefCntPtr<FileManager> Files(
  585. new FileManager(FileSystemOptions(), OverlayFileSystem));
  586. ToolInvocation Invocation(
  587. getSyntaxOnlyToolArgs(ToolName, Adjuster(Args, FileName), FileName),
  588. &Action, Files.get(), std::move(PCHContainerOps));
  589. Invocation.setDiagnosticConsumer(DiagConsumer);
  590. InMemoryFileSystem->addFile(FileName, 0,
  591. llvm::MemoryBuffer::getMemBufferCopy(Code));
  592. for (auto &FilenameWithContent : VirtualMappedFiles) {
  593. InMemoryFileSystem->addFile(
  594. FilenameWithContent.first, 0,
  595. llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
  596. }
  597. if (!Invocation.run())
  598. return nullptr;
  599. assert(ASTs.size() == 1);
  600. return std::move(ASTs[0]);
  601. }
  602. } // namespace tooling
  603. } // namespace clang