Tooling.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647
  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, std::move(VFS));
  75. CompilerDriver->setTitle("clang_based_tool");
  76. return CompilerDriver;
  77. }
  78. /// Retrieves the clang CC1 specific flags out of the compilation's jobs.
  79. ///
  80. /// Returns nullptr on error.
  81. static const llvm::opt::ArgStringList *getCC1Arguments(
  82. DiagnosticsEngine *Diagnostics, driver::Compilation *Compilation) {
  83. // We expect to get back exactly one Command job, if we didn't something
  84. // failed. Extract that job from the Compilation.
  85. const driver::JobList &Jobs = Compilation->getJobs();
  86. const driver::ActionList &Actions = Compilation->getActions();
  87. bool OffloadCompilation = false;
  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. if (Jobs.size() == 0 || !isa<driver::Command>(*Jobs.begin()) ||
  113. (Jobs.size() > 1 && !OffloadCompilation)) {
  114. SmallString<256> error_msg;
  115. llvm::raw_svector_ostream error_stream(error_msg);
  116. Jobs.Print(error_stream, "; ", true);
  117. Diagnostics->Report(diag::err_fe_expected_compiler_job)
  118. << error_stream.str();
  119. return nullptr;
  120. }
  121. // The one job we find should be to invoke clang again.
  122. const auto &Cmd = cast<driver::Command>(*Jobs.begin());
  123. if (StringRef(Cmd.getCreator().getName()) != "clang") {
  124. Diagnostics->Report(diag::err_fe_expected_clang_command);
  125. return nullptr;
  126. }
  127. return &Cmd.getArguments();
  128. }
  129. namespace clang {
  130. namespace tooling {
  131. /// Returns a clang build invocation initialized from the CC1 flags.
  132. CompilerInvocation *newInvocation(
  133. DiagnosticsEngine *Diagnostics, const llvm::opt::ArgStringList &CC1Args) {
  134. assert(!CC1Args.empty() && "Must at least contain the program name!");
  135. CompilerInvocation *Invocation = new CompilerInvocation;
  136. CompilerInvocation::CreateFromArgs(*Invocation, CC1Args, *Diagnostics);
  137. Invocation->getFrontendOpts().DisableFree = false;
  138. Invocation->getCodeGenOpts().DisableFree = false;
  139. return Invocation;
  140. }
  141. bool runToolOnCode(std::unique_ptr<FrontendAction> ToolAction,
  142. const Twine &Code, const Twine &FileName,
  143. std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
  144. return runToolOnCodeWithArgs(std::move(ToolAction), Code,
  145. std::vector<std::string>(), FileName,
  146. "clang-tool", std::move(PCHContainerOps));
  147. }
  148. } // namespace tooling
  149. } // namespace clang
  150. static std::vector<std::string>
  151. getSyntaxOnlyToolArgs(const Twine &ToolName,
  152. const std::vector<std::string> &ExtraArgs,
  153. StringRef FileName) {
  154. std::vector<std::string> Args;
  155. Args.push_back(ToolName.str());
  156. Args.push_back("-fsyntax-only");
  157. Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end());
  158. Args.push_back(FileName.str());
  159. return Args;
  160. }
  161. namespace clang {
  162. namespace tooling {
  163. bool runToolOnCodeWithArgs(
  164. std::unique_ptr<FrontendAction> ToolAction, const Twine &Code,
  165. llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
  166. const std::vector<std::string> &Args, const Twine &FileName,
  167. const Twine &ToolName,
  168. std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
  169. SmallString<16> FileNameStorage;
  170. StringRef FileNameRef = FileName.toNullTerminatedStringRef(FileNameStorage);
  171. llvm::IntrusiveRefCntPtr<FileManager> Files(
  172. new FileManager(FileSystemOptions(), VFS));
  173. ArgumentsAdjuster Adjuster = getClangStripDependencyFileAdjuster();
  174. ToolInvocation Invocation(
  175. getSyntaxOnlyToolArgs(ToolName, Adjuster(Args, FileNameRef), FileNameRef),
  176. std::move(ToolAction), Files.get(), std::move(PCHContainerOps));
  177. return Invocation.run();
  178. }
  179. bool runToolOnCodeWithArgs(
  180. std::unique_ptr<FrontendAction> ToolAction, const Twine &Code,
  181. const std::vector<std::string> &Args, const Twine &FileName,
  182. const Twine &ToolName,
  183. std::shared_ptr<PCHContainerOperations> PCHContainerOps,
  184. const FileContentMappings &VirtualMappedFiles) {
  185. llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
  186. new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
  187. llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
  188. new llvm::vfs::InMemoryFileSystem);
  189. OverlayFileSystem->pushOverlay(InMemoryFileSystem);
  190. SmallString<1024> CodeStorage;
  191. InMemoryFileSystem->addFile(FileName, 0,
  192. llvm::MemoryBuffer::getMemBuffer(
  193. Code.toNullTerminatedStringRef(CodeStorage)));
  194. for (auto &FilenameWithContent : VirtualMappedFiles) {
  195. InMemoryFileSystem->addFile(
  196. FilenameWithContent.first, 0,
  197. llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
  198. }
  199. return runToolOnCodeWithArgs(std::move(ToolAction), Code, OverlayFileSystem,
  200. Args, FileName, ToolName);
  201. }
  202. llvm::Expected<std::string> getAbsolutePath(llvm::vfs::FileSystem &FS,
  203. StringRef File) {
  204. StringRef RelativePath(File);
  205. // FIXME: Should '.\\' be accepted on Win32?
  206. if (RelativePath.startswith("./")) {
  207. RelativePath = RelativePath.substr(strlen("./"));
  208. }
  209. SmallString<1024> AbsolutePath = RelativePath;
  210. if (auto EC = FS.makeAbsolute(AbsolutePath))
  211. return llvm::errorCodeToError(EC);
  212. llvm::sys::path::native(AbsolutePath);
  213. return AbsolutePath.str();
  214. }
  215. std::string getAbsolutePath(StringRef File) {
  216. return llvm::cantFail(getAbsolutePath(*llvm::vfs::getRealFileSystem(), File));
  217. }
  218. void addTargetAndModeForProgramName(std::vector<std::string> &CommandLine,
  219. StringRef InvokedAs) {
  220. if (!CommandLine.empty() && !InvokedAs.empty()) {
  221. bool AlreadyHasTarget = false;
  222. bool AlreadyHasMode = false;
  223. // Skip CommandLine[0].
  224. for (auto Token = ++CommandLine.begin(); Token != CommandLine.end();
  225. ++Token) {
  226. StringRef TokenRef(*Token);
  227. AlreadyHasTarget |=
  228. (TokenRef == "-target" || TokenRef.startswith("-target="));
  229. AlreadyHasMode |= (TokenRef == "--driver-mode" ||
  230. TokenRef.startswith("--driver-mode="));
  231. }
  232. auto TargetMode =
  233. driver::ToolChain::getTargetAndModeFromProgramName(InvokedAs);
  234. if (!AlreadyHasMode && TargetMode.DriverMode) {
  235. CommandLine.insert(++CommandLine.begin(), TargetMode.DriverMode);
  236. }
  237. if (!AlreadyHasTarget && TargetMode.TargetIsValid) {
  238. CommandLine.insert(++CommandLine.begin(), {"-target",
  239. TargetMode.TargetPrefix});
  240. }
  241. }
  242. }
  243. } // namespace tooling
  244. } // namespace clang
  245. namespace {
  246. class SingleFrontendActionFactory : public FrontendActionFactory {
  247. std::unique_ptr<FrontendAction> Action;
  248. public:
  249. SingleFrontendActionFactory(std::unique_ptr<FrontendAction> Action)
  250. : Action(std::move(Action)) {}
  251. std::unique_ptr<FrontendAction> create() override {
  252. return std::move(Action);
  253. }
  254. };
  255. } // namespace
  256. ToolInvocation::ToolInvocation(
  257. std::vector<std::string> CommandLine, ToolAction *Action,
  258. FileManager *Files, std::shared_ptr<PCHContainerOperations> PCHContainerOps)
  259. : CommandLine(std::move(CommandLine)), Action(Action), OwnsAction(false),
  260. Files(Files), PCHContainerOps(std::move(PCHContainerOps)) {}
  261. ToolInvocation::ToolInvocation(
  262. std::vector<std::string> CommandLine,
  263. std::unique_ptr<FrontendAction> FAction, FileManager *Files,
  264. std::shared_ptr<PCHContainerOperations> PCHContainerOps)
  265. : CommandLine(std::move(CommandLine)),
  266. Action(new SingleFrontendActionFactory(std::move(FAction))),
  267. OwnsAction(true), Files(Files),
  268. PCHContainerOps(std::move(PCHContainerOps)) {}
  269. ToolInvocation::~ToolInvocation() {
  270. if (OwnsAction)
  271. delete Action;
  272. }
  273. void ToolInvocation::mapVirtualFile(StringRef FilePath, StringRef Content) {
  274. SmallString<1024> PathStorage;
  275. llvm::sys::path::native(FilePath, PathStorage);
  276. MappedFileContents[PathStorage] = Content;
  277. }
  278. bool ToolInvocation::run() {
  279. std::vector<const char*> Argv;
  280. for (const std::string &Str : CommandLine)
  281. Argv.push_back(Str.c_str());
  282. const char *const BinaryName = Argv[0];
  283. IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
  284. unsigned MissingArgIndex, MissingArgCount;
  285. llvm::opt::InputArgList ParsedArgs = driver::getDriverOptTable().ParseArgs(
  286. ArrayRef<const char *>(Argv).slice(1), MissingArgIndex, MissingArgCount);
  287. ParseDiagnosticArgs(*DiagOpts, ParsedArgs);
  288. TextDiagnosticPrinter DiagnosticPrinter(
  289. llvm::errs(), &*DiagOpts);
  290. DiagnosticsEngine Diagnostics(
  291. IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), &*DiagOpts,
  292. DiagConsumer ? DiagConsumer : &DiagnosticPrinter, false);
  293. const std::unique_ptr<driver::Driver> Driver(
  294. newDriver(&Diagnostics, BinaryName, &Files->getVirtualFileSystem()));
  295. // The "input file not found" diagnostics from the driver are useful.
  296. // The driver is only aware of the VFS working directory, but some clients
  297. // change this at the FileManager level instead.
  298. // In this case the checks have false positives, so skip them.
  299. if (!Files->getFileSystemOpts().WorkingDir.empty())
  300. Driver->setCheckInputsExist(false);
  301. const std::unique_ptr<driver::Compilation> Compilation(
  302. Driver->BuildCompilation(llvm::makeArrayRef(Argv)));
  303. if (!Compilation)
  304. return false;
  305. const llvm::opt::ArgStringList *const CC1Args = getCC1Arguments(
  306. &Diagnostics, Compilation.get());
  307. if (!CC1Args)
  308. return false;
  309. std::unique_ptr<CompilerInvocation> Invocation(
  310. newInvocation(&Diagnostics, *CC1Args));
  311. // FIXME: remove this when all users have migrated!
  312. for (const auto &It : MappedFileContents) {
  313. // Inject the code as the given file name into the preprocessor options.
  314. std::unique_ptr<llvm::MemoryBuffer> Input =
  315. llvm::MemoryBuffer::getMemBuffer(It.getValue());
  316. Invocation->getPreprocessorOpts().addRemappedFile(It.getKey(),
  317. Input.release());
  318. }
  319. return runInvocation(BinaryName, Compilation.get(), std::move(Invocation),
  320. std::move(PCHContainerOps));
  321. }
  322. bool ToolInvocation::runInvocation(
  323. const char *BinaryName, driver::Compilation *Compilation,
  324. std::shared_ptr<CompilerInvocation> Invocation,
  325. std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
  326. // Show the invocation, with -v.
  327. if (Invocation->getHeaderSearchOpts().Verbose) {
  328. llvm::errs() << "clang Invocation:\n";
  329. Compilation->getJobs().Print(llvm::errs(), "\n", true);
  330. llvm::errs() << "\n";
  331. }
  332. return Action->runInvocation(std::move(Invocation), Files,
  333. std::move(PCHContainerOps), DiagConsumer);
  334. }
  335. bool FrontendActionFactory::runInvocation(
  336. std::shared_ptr<CompilerInvocation> Invocation, FileManager *Files,
  337. std::shared_ptr<PCHContainerOperations> PCHContainerOps,
  338. DiagnosticConsumer *DiagConsumer) {
  339. // Create a compiler instance to handle the actual work.
  340. CompilerInstance Compiler(std::move(PCHContainerOps));
  341. Compiler.setInvocation(std::move(Invocation));
  342. Compiler.setFileManager(Files);
  343. // The FrontendAction can have lifetime requirements for Compiler or its
  344. // members, and we need to ensure it's deleted earlier than Compiler. So we
  345. // pass it to an std::unique_ptr declared after the Compiler variable.
  346. std::unique_ptr<FrontendAction> ScopedToolAction(create());
  347. // Create the compiler's actual diagnostics engine.
  348. Compiler.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false);
  349. if (!Compiler.hasDiagnostics())
  350. return false;
  351. Compiler.createSourceManager(*Files);
  352. const bool Success = Compiler.ExecuteAction(*ScopedToolAction);
  353. Files->clearStatCache();
  354. return Success;
  355. }
  356. ClangTool::ClangTool(const CompilationDatabase &Compilations,
  357. ArrayRef<std::string> SourcePaths,
  358. std::shared_ptr<PCHContainerOperations> PCHContainerOps,
  359. IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS,
  360. IntrusiveRefCntPtr<FileManager> Files)
  361. : Compilations(Compilations), SourcePaths(SourcePaths),
  362. PCHContainerOps(std::move(PCHContainerOps)),
  363. OverlayFileSystem(new llvm::vfs::OverlayFileSystem(std::move(BaseFS))),
  364. InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem),
  365. Files(Files ? Files
  366. : new FileManager(FileSystemOptions(), OverlayFileSystem)) {
  367. OverlayFileSystem->pushOverlay(InMemoryFileSystem);
  368. appendArgumentsAdjuster(getClangStripOutputAdjuster());
  369. appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster());
  370. appendArgumentsAdjuster(getClangStripDependencyFileAdjuster());
  371. if (Files)
  372. Files->setVirtualFileSystem(OverlayFileSystem);
  373. }
  374. ClangTool::~ClangTool() = default;
  375. void ClangTool::mapVirtualFile(StringRef FilePath, StringRef Content) {
  376. MappedFileContents.push_back(std::make_pair(FilePath, Content));
  377. }
  378. void ClangTool::appendArgumentsAdjuster(ArgumentsAdjuster Adjuster) {
  379. ArgsAdjuster = combineAdjusters(std::move(ArgsAdjuster), std::move(Adjuster));
  380. }
  381. void ClangTool::clearArgumentsAdjusters() {
  382. ArgsAdjuster = nullptr;
  383. }
  384. static void injectResourceDir(CommandLineArguments &Args, const char *Argv0,
  385. void *MainAddr) {
  386. // Allow users to override the resource dir.
  387. for (StringRef Arg : Args)
  388. if (Arg.startswith("-resource-dir"))
  389. return;
  390. // If there's no override in place add our resource dir.
  391. Args.push_back("-resource-dir=" +
  392. CompilerInvocation::GetResourcesPath(Argv0, MainAddr));
  393. }
  394. int ClangTool::run(ToolAction *Action) {
  395. // Exists solely for the purpose of lookup of the resource path.
  396. // This just needs to be some symbol in the binary.
  397. static int StaticSymbol;
  398. // First insert all absolute paths into the in-memory VFS. These are global
  399. // for all compile commands.
  400. if (SeenWorkingDirectories.insert("/").second)
  401. for (const auto &MappedFile : MappedFileContents)
  402. if (llvm::sys::path::is_absolute(MappedFile.first))
  403. InMemoryFileSystem->addFile(
  404. MappedFile.first, 0,
  405. llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
  406. bool ProcessingFailed = false;
  407. bool FileSkipped = false;
  408. // Compute all absolute paths before we run any actions, as those will change
  409. // the working directory.
  410. std::vector<std::string> AbsolutePaths;
  411. AbsolutePaths.reserve(SourcePaths.size());
  412. for (const auto &SourcePath : SourcePaths) {
  413. auto AbsPath = getAbsolutePath(*OverlayFileSystem, SourcePath);
  414. if (!AbsPath) {
  415. llvm::errs() << "Skipping " << SourcePath
  416. << ". Error while getting an absolute path: "
  417. << llvm::toString(AbsPath.takeError()) << "\n";
  418. continue;
  419. }
  420. AbsolutePaths.push_back(std::move(*AbsPath));
  421. }
  422. // Remember the working directory in case we need to restore it.
  423. std::string InitialWorkingDir;
  424. if (RestoreCWD) {
  425. if (auto CWD = OverlayFileSystem->getCurrentWorkingDirectory()) {
  426. InitialWorkingDir = std::move(*CWD);
  427. } else {
  428. llvm::errs() << "Could not get working directory: "
  429. << CWD.getError().message() << "\n";
  430. }
  431. }
  432. for (llvm::StringRef File : AbsolutePaths) {
  433. // Currently implementations of CompilationDatabase::getCompileCommands can
  434. // change the state of the file system (e.g. prepare generated headers), so
  435. // this method needs to run right before we invoke the tool, as the next
  436. // file may require a different (incompatible) state of the file system.
  437. //
  438. // FIXME: Make the compilation database interface more explicit about the
  439. // requirements to the order of invocation of its members.
  440. std::vector<CompileCommand> CompileCommandsForFile =
  441. Compilations.getCompileCommands(File);
  442. if (CompileCommandsForFile.empty()) {
  443. llvm::errs() << "Skipping " << File << ". Compile command not found.\n";
  444. FileSkipped = true;
  445. continue;
  446. }
  447. for (CompileCommand &CompileCommand : CompileCommandsForFile) {
  448. // FIXME: chdir is thread hostile; on the other hand, creating the same
  449. // behavior as chdir is complex: chdir resolves the path once, thus
  450. // guaranteeing that all subsequent relative path operations work
  451. // on the same path the original chdir resulted in. This makes a
  452. // difference for example on network filesystems, where symlinks might be
  453. // switched during runtime of the tool. Fixing this depends on having a
  454. // file system abstraction that allows openat() style interactions.
  455. if (OverlayFileSystem->setCurrentWorkingDirectory(
  456. CompileCommand.Directory))
  457. llvm::report_fatal_error("Cannot chdir into \"" +
  458. Twine(CompileCommand.Directory) + "\"!");
  459. // Now fill the in-memory VFS with the relative file mappings so it will
  460. // have the correct relative paths. We never remove mappings but that
  461. // should be fine.
  462. if (SeenWorkingDirectories.insert(CompileCommand.Directory).second)
  463. for (const auto &MappedFile : MappedFileContents)
  464. if (!llvm::sys::path::is_absolute(MappedFile.first))
  465. InMemoryFileSystem->addFile(
  466. MappedFile.first, 0,
  467. llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
  468. std::vector<std::string> CommandLine = CompileCommand.CommandLine;
  469. if (ArgsAdjuster)
  470. CommandLine = ArgsAdjuster(CommandLine, CompileCommand.Filename);
  471. assert(!CommandLine.empty());
  472. // Add the resource dir based on the binary of this tool. argv[0] in the
  473. // compilation database may refer to a different compiler and we want to
  474. // pick up the very same standard library that compiler is using. The
  475. // builtin headers in the resource dir need to match the exact clang
  476. // version the tool is using.
  477. // FIXME: On linux, GetMainExecutable is independent of the value of the
  478. // first argument, thus allowing ClangTool and runToolOnCode to just
  479. // pass in made-up names here. Make sure this works on other platforms.
  480. injectResourceDir(CommandLine, "clang_tool", &StaticSymbol);
  481. // FIXME: We need a callback mechanism for the tool writer to output a
  482. // customized message for each file.
  483. LLVM_DEBUG({ llvm::dbgs() << "Processing: " << File << ".\n"; });
  484. ToolInvocation Invocation(std::move(CommandLine), Action, Files.get(),
  485. PCHContainerOps);
  486. Invocation.setDiagnosticConsumer(DiagConsumer);
  487. if (!Invocation.run()) {
  488. // FIXME: Diagnostics should be used instead.
  489. if (PrintErrorMessage)
  490. llvm::errs() << "Error while processing " << File << ".\n";
  491. ProcessingFailed = true;
  492. }
  493. }
  494. }
  495. if (!InitialWorkingDir.empty()) {
  496. if (auto EC =
  497. OverlayFileSystem->setCurrentWorkingDirectory(InitialWorkingDir))
  498. llvm::errs() << "Error when trying to restore working dir: "
  499. << EC.message() << "\n";
  500. }
  501. return ProcessingFailed ? 1 : (FileSkipped ? 2 : 0);
  502. }
  503. namespace {
  504. class ASTBuilderAction : public ToolAction {
  505. std::vector<std::unique_ptr<ASTUnit>> &ASTs;
  506. public:
  507. ASTBuilderAction(std::vector<std::unique_ptr<ASTUnit>> &ASTs) : ASTs(ASTs) {}
  508. bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
  509. FileManager *Files,
  510. std::shared_ptr<PCHContainerOperations> PCHContainerOps,
  511. DiagnosticConsumer *DiagConsumer) override {
  512. std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation(
  513. Invocation, std::move(PCHContainerOps),
  514. CompilerInstance::createDiagnostics(&Invocation->getDiagnosticOpts(),
  515. DiagConsumer,
  516. /*ShouldOwnClient=*/false),
  517. Files);
  518. if (!AST)
  519. return false;
  520. ASTs.push_back(std::move(AST));
  521. return true;
  522. }
  523. };
  524. } // namespace
  525. int ClangTool::buildASTs(std::vector<std::unique_ptr<ASTUnit>> &ASTs) {
  526. ASTBuilderAction Action(ASTs);
  527. return run(&Action);
  528. }
  529. void ClangTool::setRestoreWorkingDir(bool RestoreCWD) {
  530. this->RestoreCWD = RestoreCWD;
  531. }
  532. void ClangTool::setPrintErrorMessage(bool PrintErrorMessage) {
  533. this->PrintErrorMessage = PrintErrorMessage;
  534. }
  535. namespace clang {
  536. namespace tooling {
  537. std::unique_ptr<ASTUnit>
  538. buildASTFromCode(StringRef Code, StringRef FileName,
  539. std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
  540. return buildASTFromCodeWithArgs(Code, std::vector<std::string>(), FileName,
  541. "clang-tool", std::move(PCHContainerOps));
  542. }
  543. std::unique_ptr<ASTUnit> buildASTFromCodeWithArgs(
  544. StringRef Code, const std::vector<std::string> &Args, StringRef FileName,
  545. StringRef ToolName, std::shared_ptr<PCHContainerOperations> PCHContainerOps,
  546. ArgumentsAdjuster Adjuster) {
  547. std::vector<std::unique_ptr<ASTUnit>> ASTs;
  548. ASTBuilderAction Action(ASTs);
  549. llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem(
  550. new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
  551. llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
  552. new llvm::vfs::InMemoryFileSystem);
  553. OverlayFileSystem->pushOverlay(InMemoryFileSystem);
  554. llvm::IntrusiveRefCntPtr<FileManager> Files(
  555. new FileManager(FileSystemOptions(), OverlayFileSystem));
  556. ToolInvocation Invocation(
  557. getSyntaxOnlyToolArgs(ToolName, Adjuster(Args, FileName), FileName),
  558. &Action, Files.get(), std::move(PCHContainerOps));
  559. InMemoryFileSystem->addFile(FileName, 0,
  560. llvm::MemoryBuffer::getMemBufferCopy(Code));
  561. if (!Invocation.run())
  562. return nullptr;
  563. assert(ASTs.size() == 1);
  564. return std::move(ASTs[0]);
  565. }
  566. } // namespace tooling
  567. } // namespace clang