Tooling.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685
  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. const llvm::opt::ArgStringList &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. std::vector<const char*> 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. const std::unique_ptr<driver::Driver> Driver(
  320. newDriver(&*Diagnostics, BinaryName, &Files->getVirtualFileSystem()));
  321. // The "input file not found" diagnostics from the driver are useful.
  322. // The driver is only aware of the VFS working directory, but some clients
  323. // change this at the FileManager level instead.
  324. // In this case the checks have false positives, so skip them.
  325. if (!Files->getFileSystemOpts().WorkingDir.empty())
  326. Driver->setCheckInputsExist(false);
  327. const std::unique_ptr<driver::Compilation> Compilation(
  328. Driver->BuildCompilation(llvm::makeArrayRef(Argv)));
  329. if (!Compilation)
  330. return false;
  331. const llvm::opt::ArgStringList *const CC1Args = getCC1Arguments(
  332. &*Diagnostics, Compilation.get());
  333. if (!CC1Args)
  334. return false;
  335. std::unique_ptr<CompilerInvocation> Invocation(
  336. newInvocation(&*Diagnostics, *CC1Args, BinaryName));
  337. return runInvocation(BinaryName, Compilation.get(), std::move(Invocation),
  338. std::move(PCHContainerOps));
  339. }
  340. bool ToolInvocation::runInvocation(
  341. const char *BinaryName, driver::Compilation *Compilation,
  342. std::shared_ptr<CompilerInvocation> Invocation,
  343. std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
  344. // Show the invocation, with -v.
  345. if (Invocation->getHeaderSearchOpts().Verbose) {
  346. llvm::errs() << "clang Invocation:\n";
  347. Compilation->getJobs().Print(llvm::errs(), "\n", true);
  348. llvm::errs() << "\n";
  349. }
  350. return Action->runInvocation(std::move(Invocation), Files,
  351. std::move(PCHContainerOps), DiagConsumer);
  352. }
  353. bool FrontendActionFactory::runInvocation(
  354. std::shared_ptr<CompilerInvocation> Invocation, FileManager *Files,
  355. std::shared_ptr<PCHContainerOperations> PCHContainerOps,
  356. DiagnosticConsumer *DiagConsumer) {
  357. // Create a compiler instance to handle the actual work.
  358. CompilerInstance Compiler(std::move(PCHContainerOps));
  359. Compiler.setInvocation(std::move(Invocation));
  360. Compiler.setFileManager(Files);
  361. // The FrontendAction can have lifetime requirements for Compiler or its
  362. // members, and we need to ensure it's deleted earlier than Compiler. So we
  363. // pass it to an std::unique_ptr declared after the Compiler variable.
  364. std::unique_ptr<FrontendAction> ScopedToolAction(create());
  365. // Create the compiler's actual diagnostics engine.
  366. Compiler.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false);
  367. if (!Compiler.hasDiagnostics())
  368. return false;
  369. Compiler.createSourceManager(*Files);
  370. const bool Success = Compiler.ExecuteAction(*ScopedToolAction);
  371. Files->clearStatCache();
  372. return Success;
  373. }
  374. ClangTool::ClangTool(const CompilationDatabase &Compilations,
  375. ArrayRef<std::string> SourcePaths,
  376. std::shared_ptr<PCHContainerOperations> PCHContainerOps,
  377. IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS,
  378. IntrusiveRefCntPtr<FileManager> Files)
  379. : Compilations(Compilations), SourcePaths(SourcePaths),
  380. PCHContainerOps(std::move(PCHContainerOps)),
  381. OverlayFileSystem(new llvm::vfs::OverlayFileSystem(std::move(BaseFS))),
  382. InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem),
  383. Files(Files ? Files
  384. : new FileManager(FileSystemOptions(), OverlayFileSystem)) {
  385. OverlayFileSystem->pushOverlay(InMemoryFileSystem);
  386. appendArgumentsAdjuster(getClangStripOutputAdjuster());
  387. appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster());
  388. appendArgumentsAdjuster(getClangStripDependencyFileAdjuster());
  389. if (Files)
  390. Files->setVirtualFileSystem(OverlayFileSystem);
  391. }
  392. ClangTool::~ClangTool() = default;
  393. void ClangTool::mapVirtualFile(StringRef FilePath, StringRef Content) {
  394. MappedFileContents.push_back(std::make_pair(FilePath, Content));
  395. }
  396. void ClangTool::appendArgumentsAdjuster(ArgumentsAdjuster Adjuster) {
  397. ArgsAdjuster = combineAdjusters(std::move(ArgsAdjuster), std::move(Adjuster));
  398. }
  399. void ClangTool::clearArgumentsAdjusters() {
  400. ArgsAdjuster = nullptr;
  401. }
  402. static void injectResourceDir(CommandLineArguments &Args, const char *Argv0,
  403. void *MainAddr) {
  404. // Allow users to override the resource dir.
  405. for (StringRef Arg : Args)
  406. if (Arg.startswith("-resource-dir"))
  407. return;
  408. // If there's no override in place add our resource dir.
  409. Args = getInsertArgumentAdjuster(
  410. ("-resource-dir=" + CompilerInvocation::GetResourcesPath(Argv0, MainAddr))
  411. .c_str())(Args, "");
  412. }
  413. int ClangTool::run(ToolAction *Action) {
  414. // Exists solely for the purpose of lookup of the resource path.
  415. // This just needs to be some symbol in the binary.
  416. static int StaticSymbol;
  417. // First insert all absolute paths into the in-memory VFS. These are global
  418. // for all compile commands.
  419. if (SeenWorkingDirectories.insert("/").second)
  420. for (const auto &MappedFile : MappedFileContents)
  421. if (llvm::sys::path::is_absolute(MappedFile.first))
  422. InMemoryFileSystem->addFile(
  423. MappedFile.first, 0,
  424. llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
  425. bool ProcessingFailed = false;
  426. bool FileSkipped = false;
  427. // Compute all absolute paths before we run any actions, as those will change
  428. // the working directory.
  429. std::vector<std::string> AbsolutePaths;
  430. AbsolutePaths.reserve(SourcePaths.size());
  431. for (const auto &SourcePath : SourcePaths) {
  432. auto AbsPath = getAbsolutePath(*OverlayFileSystem, SourcePath);
  433. if (!AbsPath) {
  434. llvm::errs() << "Skipping " << SourcePath
  435. << ". Error while getting an absolute path: "
  436. << llvm::toString(AbsPath.takeError()) << "\n";
  437. continue;
  438. }
  439. AbsolutePaths.push_back(std::move(*AbsPath));
  440. }
  441. // Remember the working directory in case we need to restore it.
  442. std::string InitialWorkingDir;
  443. if (RestoreCWD) {
  444. if (auto CWD = OverlayFileSystem->getCurrentWorkingDirectory()) {
  445. InitialWorkingDir = std::move(*CWD);
  446. } else {
  447. llvm::errs() << "Could not get working directory: "
  448. << CWD.getError().message() << "\n";
  449. }
  450. }
  451. for (llvm::StringRef File : AbsolutePaths) {
  452. // Currently implementations of CompilationDatabase::getCompileCommands can
  453. // change the state of the file system (e.g. prepare generated headers), so
  454. // this method needs to run right before we invoke the tool, as the next
  455. // file may require a different (incompatible) state of the file system.
  456. //
  457. // FIXME: Make the compilation database interface more explicit about the
  458. // requirements to the order of invocation of its members.
  459. std::vector<CompileCommand> CompileCommandsForFile =
  460. Compilations.getCompileCommands(File);
  461. if (CompileCommandsForFile.empty()) {
  462. llvm::errs() << "Skipping " << File << ". Compile command not found.\n";
  463. FileSkipped = true;
  464. continue;
  465. }
  466. for (CompileCommand &CompileCommand : CompileCommandsForFile) {
  467. // FIXME: chdir is thread hostile; on the other hand, creating the same
  468. // behavior as chdir is complex: chdir resolves the path once, thus
  469. // guaranteeing that all subsequent relative path operations work
  470. // on the same path the original chdir resulted in. This makes a
  471. // difference for example on network filesystems, where symlinks might be
  472. // switched during runtime of the tool. Fixing this depends on having a
  473. // file system abstraction that allows openat() style interactions.
  474. if (OverlayFileSystem->setCurrentWorkingDirectory(
  475. CompileCommand.Directory))
  476. llvm::report_fatal_error("Cannot chdir into \"" +
  477. Twine(CompileCommand.Directory) + "\"!");
  478. // Now fill the in-memory VFS with the relative file mappings so it will
  479. // have the correct relative paths. We never remove mappings but that
  480. // should be fine.
  481. if (SeenWorkingDirectories.insert(CompileCommand.Directory).second)
  482. for (const auto &MappedFile : MappedFileContents)
  483. if (!llvm::sys::path::is_absolute(MappedFile.first))
  484. InMemoryFileSystem->addFile(
  485. MappedFile.first, 0,
  486. llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
  487. std::vector<std::string> CommandLine = CompileCommand.CommandLine;
  488. if (ArgsAdjuster)
  489. CommandLine = ArgsAdjuster(CommandLine, CompileCommand.Filename);
  490. assert(!CommandLine.empty());
  491. // Add the resource dir based on the binary of this tool. argv[0] in the
  492. // compilation database may refer to a different compiler and we want to
  493. // pick up the very same standard library that compiler is using. The
  494. // builtin headers in the resource dir need to match the exact clang
  495. // version the tool is using.
  496. // FIXME: On linux, GetMainExecutable is independent of the value of the
  497. // first argument, thus allowing ClangTool and runToolOnCode to just
  498. // pass in made-up names here. Make sure this works on other platforms.
  499. injectResourceDir(CommandLine, "clang_tool", &StaticSymbol);
  500. // FIXME: We need a callback mechanism for the tool writer to output a
  501. // customized message for each file.
  502. LLVM_DEBUG({ llvm::dbgs() << "Processing: " << File << ".\n"; });
  503. ToolInvocation Invocation(std::move(CommandLine), Action, Files.get(),
  504. PCHContainerOps);
  505. Invocation.setDiagnosticConsumer(DiagConsumer);
  506. if (!Invocation.run()) {
  507. // FIXME: Diagnostics should be used instead.
  508. if (PrintErrorMessage)
  509. llvm::errs() << "Error while processing " << File << ".\n";
  510. ProcessingFailed = true;
  511. }
  512. }
  513. }
  514. if (!InitialWorkingDir.empty()) {
  515. if (auto EC =
  516. OverlayFileSystem->setCurrentWorkingDirectory(InitialWorkingDir))
  517. llvm::errs() << "Error when trying to restore working dir: "
  518. << EC.message() << "\n";
  519. }
  520. return ProcessingFailed ? 1 : (FileSkipped ? 2 : 0);
  521. }
  522. namespace {
  523. class ASTBuilderAction : public ToolAction {
  524. std::vector<std::unique_ptr<ASTUnit>> &ASTs;
  525. public:
  526. ASTBuilderAction(std::vector<std::unique_ptr<ASTUnit>> &ASTs) : ASTs(ASTs) {}
  527. bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
  528. FileManager *Files,
  529. std::shared_ptr<PCHContainerOperations> PCHContainerOps,
  530. DiagnosticConsumer *DiagConsumer) override {
  531. std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation(
  532. Invocation, std::move(PCHContainerOps),
  533. CompilerInstance::createDiagnostics(&Invocation->getDiagnosticOpts(),
  534. DiagConsumer,
  535. /*ShouldOwnClient=*/false),
  536. Files);
  537. if (!AST)
  538. return false;
  539. ASTs.push_back(std::move(AST));
  540. return true;
  541. }
  542. };
  543. } // namespace
  544. int ClangTool::buildASTs(std::vector<std::unique_ptr<ASTUnit>> &ASTs) {
  545. ASTBuilderAction Action(ASTs);
  546. return run(&Action);
  547. }
  548. void ClangTool::setRestoreWorkingDir(bool RestoreCWD) {
  549. this->RestoreCWD = RestoreCWD;
  550. }
  551. void ClangTool::setPrintErrorMessage(bool PrintErrorMessage) {
  552. this->PrintErrorMessage = PrintErrorMessage;
  553. }
  554. namespace clang {
  555. namespace tooling {
  556. std::unique_ptr<ASTUnit>
  557. buildASTFromCode(StringRef Code, StringRef FileName,
  558. std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
  559. return buildASTFromCodeWithArgs(Code, std::vector<std::string>(), FileName,
  560. "clang-tool", std::move(PCHContainerOps));
  561. }
  562. std::unique_ptr<ASTUnit> buildASTFromCodeWithArgs(
  563. StringRef Code, const std::vector<std::string> &Args, StringRef FileName,
  564. StringRef ToolName, std::shared_ptr<PCHContainerOperations> PCHContainerOps,
  565. ArgumentsAdjuster Adjuster, const FileContentMappings &VirtualMappedFiles,
  566. DiagnosticConsumer *DiagConsumer) {
  567. std::vector<std::unique_ptr<ASTUnit>> ASTs;
  568. ASTBuilderAction Action(ASTs);
  569. llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
  570. new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
  571. llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
  572. new llvm::vfs::InMemoryFileSystem);
  573. OverlayFileSystem->pushOverlay(InMemoryFileSystem);
  574. llvm::IntrusiveRefCntPtr<FileManager> Files(
  575. new FileManager(FileSystemOptions(), OverlayFileSystem));
  576. ToolInvocation Invocation(
  577. getSyntaxOnlyToolArgs(ToolName, Adjuster(Args, FileName), FileName),
  578. &Action, Files.get(), std::move(PCHContainerOps));
  579. Invocation.setDiagnosticConsumer(DiagConsumer);
  580. InMemoryFileSystem->addFile(FileName, 0,
  581. llvm::MemoryBuffer::getMemBufferCopy(Code));
  582. for (auto &FilenameWithContent : VirtualMappedFiles) {
  583. InMemoryFileSystem->addFile(
  584. FilenameWithContent.first, 0,
  585. llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
  586. }
  587. if (!Invocation.run())
  588. return nullptr;
  589. assert(ASTs.size() == 1);
  590. return std::move(ASTs[0]);
  591. }
  592. } // namespace tooling
  593. } // namespace clang