1 //===- Tooling.cpp - Running clang standalone tools -----------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // This file implements functions to run clang tools standalone instead
10 // of running them as a plugin.
12 //===----------------------------------------------------------------------===//
14 #include "clang/Tooling/Tooling.h"
15 #include "clang/Basic/Diagnostic.h"
16 #include "clang/Basic/DiagnosticIDs.h"
17 #include "clang/Basic/DiagnosticOptions.h"
18 #include "clang/Basic/FileManager.h"
19 #include "clang/Basic/FileSystemOptions.h"
20 #include "clang/Basic/LLVM.h"
21 #include "clang/Driver/Compilation.h"
22 #include "clang/Driver/Driver.h"
23 #include "clang/Driver/Job.h"
24 #include "clang/Driver/Options.h"
25 #include "clang/Driver/Tool.h"
26 #include "clang/Driver/ToolChain.h"
27 #include "clang/Frontend/ASTUnit.h"
28 #include "clang/Frontend/CompilerInstance.h"
29 #include "clang/Frontend/CompilerInvocation.h"
30 #include "clang/Frontend/FrontendDiagnostic.h"
31 #include "clang/Frontend/FrontendOptions.h"
32 #include "clang/Frontend/TextDiagnosticPrinter.h"
33 #include "clang/Lex/HeaderSearchOptions.h"
34 #include "clang/Lex/PreprocessorOptions.h"
35 #include "clang/Tooling/ArgumentsAdjusters.h"
36 #include "clang/Tooling/CompilationDatabase.h"
37 #include "llvm/ADT/ArrayRef.h"
38 #include "llvm/ADT/IntrusiveRefCntPtr.h"
39 #include "llvm/ADT/SmallString.h"
40 #include "llvm/ADT/StringRef.h"
41 #include "llvm/ADT/Twine.h"
42 #include "llvm/Option/ArgList.h"
43 #include "llvm/Option/OptTable.h"
44 #include "llvm/Option/Option.h"
45 #include "llvm/Support/Casting.h"
46 #include "llvm/Support/CommandLine.h"
47 #include "llvm/Support/Debug.h"
48 #include "llvm/Support/ErrorHandling.h"
49 #include "llvm/Support/FileSystem.h"
50 #include "llvm/Support/MemoryBuffer.h"
51 #include "llvm/Support/Path.h"
52 #include "llvm/Support/VirtualFileSystem.h"
53 #include "llvm/Support/raw_ostream.h"
54 #include "llvm/TargetParser/Host.h"
59 #include <system_error>
63 #define DEBUG_TYPE "clang-tooling"
65 using namespace clang
;
66 using namespace tooling
;
68 ToolAction::~ToolAction() = default;
70 FrontendActionFactory::~FrontendActionFactory() = default;
72 // FIXME: This file contains structural duplication with other parts of the
73 // code that sets up a compiler to run tools on it, and we should refactor
74 // it to be based on the same framework.
76 /// Builds a clang driver initialized for running clang tools.
77 static driver::Driver
*
78 newDriver(DiagnosticsEngine
*Diagnostics
, const char *BinaryName
,
79 IntrusiveRefCntPtr
<llvm::vfs::FileSystem
> VFS
) {
80 driver::Driver
*CompilerDriver
=
81 new driver::Driver(BinaryName
, llvm::sys::getDefaultTargetTriple(),
82 *Diagnostics
, "clang LLVM compiler", std::move(VFS
));
83 CompilerDriver
->setTitle("clang_based_tool");
84 return CompilerDriver
;
87 /// Decide whether extra compiler frontend commands can be ignored.
88 static bool ignoreExtraCC1Commands(const driver::Compilation
*Compilation
) {
89 const driver::JobList
&Jobs
= Compilation
->getJobs();
90 const driver::ActionList
&Actions
= Compilation
->getActions();
92 bool OffloadCompilation
= false;
94 // Jobs and Actions look very different depending on whether the Clang tool
95 // injected -fsyntax-only or not. Try to handle both cases here.
97 for (const auto &Job
: Jobs
)
98 if (StringRef(Job
.getExecutable()) == "clang-offload-bundler")
99 OffloadCompilation
= true;
101 if (Jobs
.size() > 1) {
102 for (auto *A
: Actions
){
103 // On MacOSX real actions may end up being wrapped in BindArchAction
104 if (isa
<driver::BindArchAction
>(A
))
105 A
= *A
->input_begin();
106 if (isa
<driver::OffloadAction
>(A
)) {
107 // Offload compilation has 2 top-level actions, one (at the front) is
108 // the original host compilation and the other is offload action
109 // composed of at least one device compilation. For such case, general
110 // tooling will consider host-compilation only. For tooling on device
111 // compilation, device compilation only option, such as
112 // `--cuda-device-only`, needs specifying.
113 assert(Actions
.size() > 1);
115 isa
<driver::CompileJobAction
>(Actions
.front()) ||
116 // On MacOSX real actions may end up being wrapped in
118 (isa
<driver::BindArchAction
>(Actions
.front()) &&
119 isa
<driver::CompileJobAction
>(*Actions
.front()->input_begin())));
120 OffloadCompilation
= true;
126 return OffloadCompilation
;
132 const llvm::opt::ArgStringList
*
133 getCC1Arguments(DiagnosticsEngine
*Diagnostics
,
134 driver::Compilation
*Compilation
) {
135 const driver::JobList
&Jobs
= Compilation
->getJobs();
137 auto IsCC1Command
= [](const driver::Command
&Cmd
) {
138 return StringRef(Cmd
.getCreator().getName()) == "clang";
141 auto IsSrcFile
= [](const driver::InputInfo
&II
) {
142 return isSrcFile(II
.getType());
145 llvm::SmallVector
<const driver::Command
*, 1> CC1Jobs
;
146 for (const driver::Command
&Job
: Jobs
)
147 if (IsCC1Command(Job
) && llvm::all_of(Job
.getInputInfos(), IsSrcFile
))
148 CC1Jobs
.push_back(&Job
);
150 // If there are no jobs for source files, try checking again for a single job
151 // with any file type. This accepts a preprocessed file as input.
153 for (const driver::Command
&Job
: Jobs
)
154 if (IsCC1Command(Job
))
155 CC1Jobs
.push_back(&Job
);
157 if (CC1Jobs
.empty() ||
158 (CC1Jobs
.size() > 1 && !ignoreExtraCC1Commands(Compilation
))) {
159 SmallString
<256> error_msg
;
160 llvm::raw_svector_ostream
error_stream(error_msg
);
161 Jobs
.Print(error_stream
, "; ", true);
162 Diagnostics
->Report(diag::err_fe_expected_compiler_job
)
163 << error_stream
.str();
167 return &CC1Jobs
[0]->getArguments();
170 /// Returns a clang build invocation initialized from the CC1 flags.
171 CompilerInvocation
*newInvocation(DiagnosticsEngine
*Diagnostics
,
172 ArrayRef
<const char *> CC1Args
,
173 const char *const BinaryName
) {
174 assert(!CC1Args
.empty() && "Must at least contain the program name!");
175 CompilerInvocation
*Invocation
= new CompilerInvocation
;
176 CompilerInvocation::CreateFromArgs(*Invocation
, CC1Args
, *Diagnostics
,
178 Invocation
->getFrontendOpts().DisableFree
= false;
179 Invocation
->getCodeGenOpts().DisableFree
= false;
183 bool runToolOnCode(std::unique_ptr
<FrontendAction
> ToolAction
,
184 const Twine
&Code
, const Twine
&FileName
,
185 std::shared_ptr
<PCHContainerOperations
> PCHContainerOps
) {
186 return runToolOnCodeWithArgs(std::move(ToolAction
), Code
,
187 std::vector
<std::string
>(), FileName
,
188 "clang-tool", std::move(PCHContainerOps
));
191 } // namespace tooling
194 static std::vector
<std::string
>
195 getSyntaxOnlyToolArgs(const Twine
&ToolName
,
196 const std::vector
<std::string
> &ExtraArgs
,
197 StringRef FileName
) {
198 std::vector
<std::string
> Args
;
199 Args
.push_back(ToolName
.str());
200 Args
.push_back("-fsyntax-only");
201 Args
.insert(Args
.end(), ExtraArgs
.begin(), ExtraArgs
.end());
202 Args
.push_back(FileName
.str());
209 bool runToolOnCodeWithArgs(
210 std::unique_ptr
<FrontendAction
> ToolAction
, const Twine
&Code
,
211 llvm::IntrusiveRefCntPtr
<llvm::vfs::FileSystem
> VFS
,
212 const std::vector
<std::string
> &Args
, const Twine
&FileName
,
213 const Twine
&ToolName
,
214 std::shared_ptr
<PCHContainerOperations
> PCHContainerOps
) {
215 SmallString
<16> FileNameStorage
;
216 StringRef FileNameRef
= FileName
.toNullTerminatedStringRef(FileNameStorage
);
218 llvm::IntrusiveRefCntPtr
<FileManager
> Files(
219 new FileManager(FileSystemOptions(), VFS
));
220 ArgumentsAdjuster Adjuster
= getClangStripDependencyFileAdjuster();
221 ToolInvocation
Invocation(
222 getSyntaxOnlyToolArgs(ToolName
, Adjuster(Args
, FileNameRef
), FileNameRef
),
223 std::move(ToolAction
), Files
.get(), std::move(PCHContainerOps
));
224 return Invocation
.run();
227 bool runToolOnCodeWithArgs(
228 std::unique_ptr
<FrontendAction
> ToolAction
, const Twine
&Code
,
229 const std::vector
<std::string
> &Args
, const Twine
&FileName
,
230 const Twine
&ToolName
,
231 std::shared_ptr
<PCHContainerOperations
> PCHContainerOps
,
232 const FileContentMappings
&VirtualMappedFiles
) {
233 llvm::IntrusiveRefCntPtr
<llvm::vfs::OverlayFileSystem
> OverlayFileSystem(
234 new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
235 llvm::IntrusiveRefCntPtr
<llvm::vfs::InMemoryFileSystem
> InMemoryFileSystem(
236 new llvm::vfs::InMemoryFileSystem
);
237 OverlayFileSystem
->pushOverlay(InMemoryFileSystem
);
239 SmallString
<1024> CodeStorage
;
240 InMemoryFileSystem
->addFile(FileName
, 0,
241 llvm::MemoryBuffer::getMemBuffer(
242 Code
.toNullTerminatedStringRef(CodeStorage
)));
244 for (auto &FilenameWithContent
: VirtualMappedFiles
) {
245 InMemoryFileSystem
->addFile(
246 FilenameWithContent
.first
, 0,
247 llvm::MemoryBuffer::getMemBuffer(FilenameWithContent
.second
));
250 return runToolOnCodeWithArgs(std::move(ToolAction
), Code
, OverlayFileSystem
,
251 Args
, FileName
, ToolName
);
254 llvm::Expected
<std::string
> getAbsolutePath(llvm::vfs::FileSystem
&FS
,
256 StringRef
RelativePath(File
);
257 // FIXME: Should '.\\' be accepted on Win32?
258 RelativePath
.consume_front("./");
260 SmallString
<1024> AbsolutePath
= RelativePath
;
261 if (auto EC
= FS
.makeAbsolute(AbsolutePath
))
262 return llvm::errorCodeToError(EC
);
263 llvm::sys::path::native(AbsolutePath
);
264 return std::string(AbsolutePath
);
267 std::string
getAbsolutePath(StringRef File
) {
268 return llvm::cantFail(getAbsolutePath(*llvm::vfs::getRealFileSystem(), File
));
271 void addTargetAndModeForProgramName(std::vector
<std::string
> &CommandLine
,
272 StringRef InvokedAs
) {
273 if (CommandLine
.empty() || InvokedAs
.empty())
275 const auto &Table
= driver::getDriverOptTable();
277 StringRef TargetOPT
=
278 Table
.getOption(driver::options::OPT_target
).getPrefixedName();
280 StringRef TargetOPTLegacy
=
281 Table
.getOption(driver::options::OPT_target_legacy_spelling
)
284 StringRef DriverModeOPT
=
285 Table
.getOption(driver::options::OPT_driver_mode
).getPrefixedName();
287 driver::ToolChain::getTargetAndModeFromProgramName(InvokedAs
);
288 // No need to search for target args if we don't have a target/mode to insert.
289 bool ShouldAddTarget
= TargetMode
.TargetIsValid
;
290 bool ShouldAddMode
= TargetMode
.DriverMode
!= nullptr;
291 // Skip CommandLine[0].
292 for (auto Token
= ++CommandLine
.begin(); Token
!= CommandLine
.end();
294 StringRef
TokenRef(*Token
);
295 ShouldAddTarget
= ShouldAddTarget
&& !TokenRef
.starts_with(TargetOPT
) &&
296 TokenRef
!= TargetOPTLegacy
;
297 ShouldAddMode
= ShouldAddMode
&& !TokenRef
.starts_with(DriverModeOPT
);
300 CommandLine
.insert(++CommandLine
.begin(), TargetMode
.DriverMode
);
302 if (ShouldAddTarget
) {
303 CommandLine
.insert(++CommandLine
.begin(),
304 (TargetOPT
+ TargetMode
.TargetPrefix
).str());
308 void addExpandedResponseFiles(std::vector
<std::string
> &CommandLine
,
309 llvm::StringRef WorkingDir
,
310 llvm::cl::TokenizerCallback Tokenizer
,
311 llvm::vfs::FileSystem
&FS
) {
312 bool SeenRSPFile
= false;
313 llvm::SmallVector
<const char *, 20> Argv
;
314 Argv
.reserve(CommandLine
.size());
315 for (auto &Arg
: CommandLine
) {
316 Argv
.push_back(Arg
.c_str());
318 SeenRSPFile
|= Arg
.front() == '@';
322 llvm::BumpPtrAllocator Alloc
;
323 llvm::cl::ExpansionContext
ECtx(Alloc
, Tokenizer
);
325 ECtx
.setVFS(&FS
).setCurrentDir(WorkingDir
).expandResponseFiles(Argv
);
328 // Don't assign directly, Argv aliases CommandLine.
329 std::vector
<std::string
> ExpandedArgv(Argv
.begin(), Argv
.end());
330 CommandLine
= std::move(ExpandedArgv
);
333 } // namespace tooling
338 class SingleFrontendActionFactory
: public FrontendActionFactory
{
339 std::unique_ptr
<FrontendAction
> Action
;
342 SingleFrontendActionFactory(std::unique_ptr
<FrontendAction
> Action
)
343 : Action(std::move(Action
)) {}
345 std::unique_ptr
<FrontendAction
> create() override
{
346 return std::move(Action
);
352 ToolInvocation::ToolInvocation(
353 std::vector
<std::string
> CommandLine
, ToolAction
*Action
,
354 FileManager
*Files
, std::shared_ptr
<PCHContainerOperations
> PCHContainerOps
)
355 : CommandLine(std::move(CommandLine
)), Action(Action
), OwnsAction(false),
356 Files(Files
), PCHContainerOps(std::move(PCHContainerOps
)) {}
358 ToolInvocation::ToolInvocation(
359 std::vector
<std::string
> CommandLine
,
360 std::unique_ptr
<FrontendAction
> FAction
, FileManager
*Files
,
361 std::shared_ptr
<PCHContainerOperations
> PCHContainerOps
)
362 : CommandLine(std::move(CommandLine
)),
363 Action(new SingleFrontendActionFactory(std::move(FAction
))),
364 OwnsAction(true), Files(Files
),
365 PCHContainerOps(std::move(PCHContainerOps
)) {}
367 ToolInvocation::~ToolInvocation() {
372 bool ToolInvocation::run() {
373 llvm::opt::ArgStringList Argv
;
374 for (const std::string
&Str
: CommandLine
)
375 Argv
.push_back(Str
.c_str());
376 const char *const BinaryName
= Argv
[0];
378 // Parse diagnostic options from the driver command-line only if none were
380 IntrusiveRefCntPtr
<DiagnosticOptions
> ParsedDiagOpts
;
381 DiagnosticOptions
*DiagOpts
= this->DiagOpts
;
383 ParsedDiagOpts
= CreateAndPopulateDiagOpts(Argv
);
384 DiagOpts
= &*ParsedDiagOpts
;
387 TextDiagnosticPrinter
DiagnosticPrinter(llvm::errs(), DiagOpts
);
388 IntrusiveRefCntPtr
<DiagnosticsEngine
> Diagnostics
=
389 CompilerInstance::createDiagnostics(
390 Files
->getVirtualFileSystem(), &*DiagOpts
,
391 DiagConsumer
? DiagConsumer
: &DiagnosticPrinter
, false);
392 // Although `Diagnostics` are used only for command-line parsing, the custom
393 // `DiagConsumer` might expect a `SourceManager` to be present.
394 SourceManager
SrcMgr(*Diagnostics
, *Files
);
395 Diagnostics
->setSourceManager(&SrcMgr
);
397 // We already have a cc1, just create an invocation.
398 if (CommandLine
.size() >= 2 && CommandLine
[1] == "-cc1") {
399 ArrayRef
<const char *> CC1Args
= ArrayRef(Argv
).drop_front();
400 std::unique_ptr
<CompilerInvocation
> Invocation(
401 newInvocation(&*Diagnostics
, CC1Args
, BinaryName
));
402 if (Diagnostics
->hasErrorOccurred())
404 return Action
->runInvocation(std::move(Invocation
), Files
,
405 std::move(PCHContainerOps
), DiagConsumer
);
408 const std::unique_ptr
<driver::Driver
> Driver(
409 newDriver(&*Diagnostics
, BinaryName
, &Files
->getVirtualFileSystem()));
410 // The "input file not found" diagnostics from the driver are useful.
411 // The driver is only aware of the VFS working directory, but some clients
412 // change this at the FileManager level instead.
413 // In this case the checks have false positives, so skip them.
414 if (!Files
->getFileSystemOpts().WorkingDir
.empty())
415 Driver
->setCheckInputsExist(false);
416 const std::unique_ptr
<driver::Compilation
> Compilation(
417 Driver
->BuildCompilation(llvm::ArrayRef(Argv
)));
420 const llvm::opt::ArgStringList
*const CC1Args
= getCC1Arguments(
421 &*Diagnostics
, Compilation
.get());
424 std::unique_ptr
<CompilerInvocation
> Invocation(
425 newInvocation(&*Diagnostics
, *CC1Args
, BinaryName
));
426 return runInvocation(BinaryName
, Compilation
.get(), std::move(Invocation
),
427 std::move(PCHContainerOps
));
430 bool ToolInvocation::runInvocation(
431 const char *BinaryName
, driver::Compilation
*Compilation
,
432 std::shared_ptr
<CompilerInvocation
> Invocation
,
433 std::shared_ptr
<PCHContainerOperations
> PCHContainerOps
) {
434 // Show the invocation, with -v.
435 if (Invocation
->getHeaderSearchOpts().Verbose
) {
436 llvm::errs() << "clang Invocation:\n";
437 Compilation
->getJobs().Print(llvm::errs(), "\n", true);
438 llvm::errs() << "\n";
441 return Action
->runInvocation(std::move(Invocation
), Files
,
442 std::move(PCHContainerOps
), DiagConsumer
);
445 bool FrontendActionFactory::runInvocation(
446 std::shared_ptr
<CompilerInvocation
> Invocation
, FileManager
*Files
,
447 std::shared_ptr
<PCHContainerOperations
> PCHContainerOps
,
448 DiagnosticConsumer
*DiagConsumer
) {
449 // Create a compiler instance to handle the actual work.
450 CompilerInstance
Compiler(std::move(PCHContainerOps
));
451 Compiler
.setInvocation(std::move(Invocation
));
452 Compiler
.setFileManager(Files
);
454 // The FrontendAction can have lifetime requirements for Compiler or its
455 // members, and we need to ensure it's deleted earlier than Compiler. So we
456 // pass it to an std::unique_ptr declared after the Compiler variable.
457 std::unique_ptr
<FrontendAction
> ScopedToolAction(create());
459 // Create the compiler's actual diagnostics engine.
460 Compiler
.createDiagnostics(Files
->getVirtualFileSystem(), DiagConsumer
,
461 /*ShouldOwnClient=*/false);
462 if (!Compiler
.hasDiagnostics())
465 Compiler
.createSourceManager(*Files
);
467 const bool Success
= Compiler
.ExecuteAction(*ScopedToolAction
);
469 Files
->clearStatCache();
473 ClangTool::ClangTool(const CompilationDatabase
&Compilations
,
474 ArrayRef
<std::string
> SourcePaths
,
475 std::shared_ptr
<PCHContainerOperations
> PCHContainerOps
,
476 IntrusiveRefCntPtr
<llvm::vfs::FileSystem
> BaseFS
,
477 IntrusiveRefCntPtr
<FileManager
> Files
)
478 : Compilations(Compilations
), SourcePaths(SourcePaths
),
479 PCHContainerOps(std::move(PCHContainerOps
)),
480 OverlayFileSystem(new llvm::vfs::OverlayFileSystem(std::move(BaseFS
))),
481 InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem
),
483 : new FileManager(FileSystemOptions(), OverlayFileSystem
)) {
484 OverlayFileSystem
->pushOverlay(InMemoryFileSystem
);
485 appendArgumentsAdjuster(getClangStripOutputAdjuster());
486 appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster());
487 appendArgumentsAdjuster(getClangStripDependencyFileAdjuster());
489 Files
->setVirtualFileSystem(OverlayFileSystem
);
492 ClangTool::~ClangTool() = default;
494 void ClangTool::mapVirtualFile(StringRef FilePath
, StringRef Content
) {
495 MappedFileContents
.push_back(std::make_pair(FilePath
, Content
));
498 void ClangTool::appendArgumentsAdjuster(ArgumentsAdjuster Adjuster
) {
499 ArgsAdjuster
= combineAdjusters(std::move(ArgsAdjuster
), std::move(Adjuster
));
502 void ClangTool::clearArgumentsAdjusters() {
503 ArgsAdjuster
= nullptr;
506 static void injectResourceDir(CommandLineArguments
&Args
, const char *Argv0
,
508 // Allow users to override the resource dir.
509 for (StringRef Arg
: Args
)
510 if (Arg
.starts_with("-resource-dir"))
513 // If there's no override in place add our resource dir.
514 Args
= getInsertArgumentAdjuster(
515 ("-resource-dir=" + CompilerInvocation::GetResourcesPath(Argv0
, MainAddr
))
519 int ClangTool::run(ToolAction
*Action
) {
520 // Exists solely for the purpose of lookup of the resource path.
521 // This just needs to be some symbol in the binary.
522 static int StaticSymbol
;
524 // First insert all absolute paths into the in-memory VFS. These are global
525 // for all compile commands.
526 if (SeenWorkingDirectories
.insert("/").second
)
527 for (const auto &MappedFile
: MappedFileContents
)
528 if (llvm::sys::path::is_absolute(MappedFile
.first
))
529 InMemoryFileSystem
->addFile(
531 llvm::MemoryBuffer::getMemBuffer(MappedFile
.second
));
533 bool ProcessingFailed
= false;
534 bool FileSkipped
= false;
535 // Compute all absolute paths before we run any actions, as those will change
536 // the working directory.
537 std::vector
<std::string
> AbsolutePaths
;
538 AbsolutePaths
.reserve(SourcePaths
.size());
539 for (const auto &SourcePath
: SourcePaths
) {
540 auto AbsPath
= getAbsolutePath(*OverlayFileSystem
, SourcePath
);
542 llvm::errs() << "Skipping " << SourcePath
543 << ". Error while getting an absolute path: "
544 << llvm::toString(AbsPath
.takeError()) << "\n";
547 AbsolutePaths
.push_back(std::move(*AbsPath
));
550 // Remember the working directory in case we need to restore it.
551 std::string InitialWorkingDir
;
552 if (auto CWD
= OverlayFileSystem
->getCurrentWorkingDirectory()) {
553 InitialWorkingDir
= std::move(*CWD
);
555 llvm::errs() << "Could not get working directory: "
556 << CWD
.getError().message() << "\n";
559 size_t NumOfTotalFiles
= AbsolutePaths
.size();
560 unsigned ProcessedFileCounter
= 0;
561 for (llvm::StringRef File
: AbsolutePaths
) {
562 // Currently implementations of CompilationDatabase::getCompileCommands can
563 // change the state of the file system (e.g. prepare generated headers), so
564 // this method needs to run right before we invoke the tool, as the next
565 // file may require a different (incompatible) state of the file system.
567 // FIXME: Make the compilation database interface more explicit about the
568 // requirements to the order of invocation of its members.
569 std::vector
<CompileCommand
> CompileCommandsForFile
=
570 Compilations
.getCompileCommands(File
);
571 if (CompileCommandsForFile
.empty()) {
572 llvm::errs() << "Skipping " << File
<< ". Compile command not found.\n";
576 for (CompileCommand
&CompileCommand
: CompileCommandsForFile
) {
577 // FIXME: chdir is thread hostile; on the other hand, creating the same
578 // behavior as chdir is complex: chdir resolves the path once, thus
579 // guaranteeing that all subsequent relative path operations work
580 // on the same path the original chdir resulted in. This makes a
581 // difference for example on network filesystems, where symlinks might be
582 // switched during runtime of the tool. Fixing this depends on having a
583 // file system abstraction that allows openat() style interactions.
584 if (OverlayFileSystem
->setCurrentWorkingDirectory(
585 CompileCommand
.Directory
))
586 llvm::report_fatal_error("Cannot chdir into \"" +
587 Twine(CompileCommand
.Directory
) + "\"!");
589 // Now fill the in-memory VFS with the relative file mappings so it will
590 // have the correct relative paths. We never remove mappings but that
592 if (SeenWorkingDirectories
.insert(CompileCommand
.Directory
).second
)
593 for (const auto &MappedFile
: MappedFileContents
)
594 if (!llvm::sys::path::is_absolute(MappedFile
.first
))
595 InMemoryFileSystem
->addFile(
597 llvm::MemoryBuffer::getMemBuffer(MappedFile
.second
));
599 std::vector
<std::string
> CommandLine
= CompileCommand
.CommandLine
;
601 CommandLine
= ArgsAdjuster(CommandLine
, CompileCommand
.Filename
);
602 assert(!CommandLine
.empty());
604 // Add the resource dir based on the binary of this tool. argv[0] in the
605 // compilation database may refer to a different compiler and we want to
606 // pick up the very same standard library that compiler is using. The
607 // builtin headers in the resource dir need to match the exact clang
608 // version the tool is using.
609 // FIXME: On linux, GetMainExecutable is independent of the value of the
610 // first argument, thus allowing ClangTool and runToolOnCode to just
611 // pass in made-up names here. Make sure this works on other platforms.
612 injectResourceDir(CommandLine
, "clang_tool", &StaticSymbol
);
614 // FIXME: We need a callback mechanism for the tool writer to output a
615 // customized message for each file.
616 if (NumOfTotalFiles
> 1)
617 llvm::errs() << "[" + std::to_string(++ProcessedFileCounter
) + "/" +
618 std::to_string(NumOfTotalFiles
) +
619 "] Processing file " + File
621 ToolInvocation
Invocation(std::move(CommandLine
), Action
, Files
.get(),
623 Invocation
.setDiagnosticConsumer(DiagConsumer
);
625 if (!Invocation
.run()) {
626 // FIXME: Diagnostics should be used instead.
627 if (PrintErrorMessage
)
628 llvm::errs() << "Error while processing " << File
<< ".\n";
629 ProcessingFailed
= true;
634 if (!InitialWorkingDir
.empty()) {
636 OverlayFileSystem
->setCurrentWorkingDirectory(InitialWorkingDir
))
637 llvm::errs() << "Error when trying to restore working dir: "
638 << EC
.message() << "\n";
640 return ProcessingFailed
? 1 : (FileSkipped
? 2 : 0);
645 class ASTBuilderAction
: public ToolAction
{
646 std::vector
<std::unique_ptr
<ASTUnit
>> &ASTs
;
649 ASTBuilderAction(std::vector
<std::unique_ptr
<ASTUnit
>> &ASTs
) : ASTs(ASTs
) {}
651 bool runInvocation(std::shared_ptr
<CompilerInvocation
> Invocation
,
653 std::shared_ptr
<PCHContainerOperations
> PCHContainerOps
,
654 DiagnosticConsumer
*DiagConsumer
) override
{
655 std::unique_ptr
<ASTUnit
> AST
= ASTUnit::LoadFromCompilerInvocation(
656 Invocation
, std::move(PCHContainerOps
),
657 CompilerInstance::createDiagnostics(Files
->getVirtualFileSystem(),
658 &Invocation
->getDiagnosticOpts(),
660 /*ShouldOwnClient=*/false),
665 ASTs
.push_back(std::move(AST
));
672 int ClangTool::buildASTs(std::vector
<std::unique_ptr
<ASTUnit
>> &ASTs
) {
673 ASTBuilderAction
Action(ASTs
);
677 void ClangTool::setPrintErrorMessage(bool PrintErrorMessage
) {
678 this->PrintErrorMessage
= PrintErrorMessage
;
684 std::unique_ptr
<ASTUnit
>
685 buildASTFromCode(StringRef Code
, StringRef FileName
,
686 std::shared_ptr
<PCHContainerOperations
> PCHContainerOps
) {
687 return buildASTFromCodeWithArgs(Code
, std::vector
<std::string
>(), FileName
,
688 "clang-tool", std::move(PCHContainerOps
));
691 std::unique_ptr
<ASTUnit
> buildASTFromCodeWithArgs(
692 StringRef Code
, const std::vector
<std::string
> &Args
, StringRef FileName
,
693 StringRef ToolName
, std::shared_ptr
<PCHContainerOperations
> PCHContainerOps
,
694 ArgumentsAdjuster Adjuster
, const FileContentMappings
&VirtualMappedFiles
,
695 DiagnosticConsumer
*DiagConsumer
) {
696 std::vector
<std::unique_ptr
<ASTUnit
>> ASTs
;
697 ASTBuilderAction
Action(ASTs
);
698 llvm::IntrusiveRefCntPtr
<llvm::vfs::OverlayFileSystem
> OverlayFileSystem(
699 new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
700 llvm::IntrusiveRefCntPtr
<llvm::vfs::InMemoryFileSystem
> InMemoryFileSystem(
701 new llvm::vfs::InMemoryFileSystem
);
702 OverlayFileSystem
->pushOverlay(InMemoryFileSystem
);
703 llvm::IntrusiveRefCntPtr
<FileManager
> Files(
704 new FileManager(FileSystemOptions(), OverlayFileSystem
));
706 ToolInvocation
Invocation(
707 getSyntaxOnlyToolArgs(ToolName
, Adjuster(Args
, FileName
), FileName
),
708 &Action
, Files
.get(), std::move(PCHContainerOps
));
709 Invocation
.setDiagnosticConsumer(DiagConsumer
);
711 InMemoryFileSystem
->addFile(FileName
, 0,
712 llvm::MemoryBuffer::getMemBufferCopy(Code
));
713 for (auto &FilenameWithContent
: VirtualMappedFiles
) {
714 InMemoryFileSystem
->addFile(
715 FilenameWithContent
.first
, 0,
716 llvm::MemoryBuffer::getMemBuffer(FilenameWithContent
.second
));
719 if (!Invocation
.run())
722 assert(ASTs
.size() == 1);
723 return std::move(ASTs
[0]);
726 } // namespace tooling