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 if (RelativePath
.startswith("./")) {
259 RelativePath
= RelativePath
.substr(strlen("./"));
262 SmallString
<1024> AbsolutePath
= RelativePath
;
263 if (auto EC
= FS
.makeAbsolute(AbsolutePath
))
264 return llvm::errorCodeToError(EC
);
265 llvm::sys::path::native(AbsolutePath
);
266 return std::string(AbsolutePath
.str());
269 std::string
getAbsolutePath(StringRef File
) {
270 return llvm::cantFail(getAbsolutePath(*llvm::vfs::getRealFileSystem(), File
));
273 void addTargetAndModeForProgramName(std::vector
<std::string
> &CommandLine
,
274 StringRef InvokedAs
) {
275 if (CommandLine
.empty() || InvokedAs
.empty())
277 const auto &Table
= driver::getDriverOptTable();
279 StringRef TargetOPT
=
280 Table
.getOption(driver::options::OPT_target
).getPrefixedName();
282 StringRef TargetOPTLegacy
=
283 Table
.getOption(driver::options::OPT_target_legacy_spelling
)
286 StringRef DriverModeOPT
=
287 Table
.getOption(driver::options::OPT_driver_mode
).getPrefixedName();
289 driver::ToolChain::getTargetAndModeFromProgramName(InvokedAs
);
290 // No need to search for target args if we don't have a target/mode to insert.
291 bool ShouldAddTarget
= TargetMode
.TargetIsValid
;
292 bool ShouldAddMode
= TargetMode
.DriverMode
!= nullptr;
293 // Skip CommandLine[0].
294 for (auto Token
= ++CommandLine
.begin(); Token
!= CommandLine
.end();
296 StringRef
TokenRef(*Token
);
297 ShouldAddTarget
= ShouldAddTarget
&& !TokenRef
.startswith(TargetOPT
) &&
298 !TokenRef
.equals(TargetOPTLegacy
);
299 ShouldAddMode
= ShouldAddMode
&& !TokenRef
.startswith(DriverModeOPT
);
302 CommandLine
.insert(++CommandLine
.begin(), TargetMode
.DriverMode
);
304 if (ShouldAddTarget
) {
305 CommandLine
.insert(++CommandLine
.begin(),
306 (TargetOPT
+ TargetMode
.TargetPrefix
).str());
310 void addExpandedResponseFiles(std::vector
<std::string
> &CommandLine
,
311 llvm::StringRef WorkingDir
,
312 llvm::cl::TokenizerCallback Tokenizer
,
313 llvm::vfs::FileSystem
&FS
) {
314 bool SeenRSPFile
= false;
315 llvm::SmallVector
<const char *, 20> Argv
;
316 Argv
.reserve(CommandLine
.size());
317 for (auto &Arg
: CommandLine
) {
318 Argv
.push_back(Arg
.c_str());
320 SeenRSPFile
|= Arg
.front() == '@';
324 llvm::BumpPtrAllocator Alloc
;
325 llvm::cl::ExpansionContext
ECtx(Alloc
, Tokenizer
);
327 ECtx
.setVFS(&FS
).setCurrentDir(WorkingDir
).expandResponseFiles(Argv
);
330 // Don't assign directly, Argv aliases CommandLine.
331 std::vector
<std::string
> ExpandedArgv(Argv
.begin(), Argv
.end());
332 CommandLine
= std::move(ExpandedArgv
);
335 } // namespace tooling
340 class SingleFrontendActionFactory
: public FrontendActionFactory
{
341 std::unique_ptr
<FrontendAction
> Action
;
344 SingleFrontendActionFactory(std::unique_ptr
<FrontendAction
> Action
)
345 : Action(std::move(Action
)) {}
347 std::unique_ptr
<FrontendAction
> create() override
{
348 return std::move(Action
);
354 ToolInvocation::ToolInvocation(
355 std::vector
<std::string
> CommandLine
, ToolAction
*Action
,
356 FileManager
*Files
, std::shared_ptr
<PCHContainerOperations
> PCHContainerOps
)
357 : CommandLine(std::move(CommandLine
)), Action(Action
), OwnsAction(false),
358 Files(Files
), PCHContainerOps(std::move(PCHContainerOps
)) {}
360 ToolInvocation::ToolInvocation(
361 std::vector
<std::string
> CommandLine
,
362 std::unique_ptr
<FrontendAction
> FAction
, FileManager
*Files
,
363 std::shared_ptr
<PCHContainerOperations
> PCHContainerOps
)
364 : CommandLine(std::move(CommandLine
)),
365 Action(new SingleFrontendActionFactory(std::move(FAction
))),
366 OwnsAction(true), Files(Files
),
367 PCHContainerOps(std::move(PCHContainerOps
)) {}
369 ToolInvocation::~ToolInvocation() {
374 bool ToolInvocation::run() {
375 llvm::opt::ArgStringList Argv
;
376 for (const std::string
&Str
: CommandLine
)
377 Argv
.push_back(Str
.c_str());
378 const char *const BinaryName
= Argv
[0];
380 // Parse diagnostic options from the driver command-line only if none were
382 IntrusiveRefCntPtr
<DiagnosticOptions
> ParsedDiagOpts
;
383 DiagnosticOptions
*DiagOpts
= this->DiagOpts
;
385 ParsedDiagOpts
= CreateAndPopulateDiagOpts(Argv
);
386 DiagOpts
= &*ParsedDiagOpts
;
389 TextDiagnosticPrinter
DiagnosticPrinter(llvm::errs(), DiagOpts
);
390 IntrusiveRefCntPtr
<DiagnosticsEngine
> Diagnostics
=
391 CompilerInstance::createDiagnostics(
392 &*DiagOpts
, DiagConsumer
? DiagConsumer
: &DiagnosticPrinter
, false);
393 // Although `Diagnostics` are used only for command-line parsing, the custom
394 // `DiagConsumer` might expect a `SourceManager` to be present.
395 SourceManager
SrcMgr(*Diagnostics
, *Files
);
396 Diagnostics
->setSourceManager(&SrcMgr
);
398 // We already have a cc1, just create an invocation.
399 if (CommandLine
.size() >= 2 && CommandLine
[1] == "-cc1") {
400 ArrayRef
<const char *> CC1Args
= ArrayRef(Argv
).drop_front();
401 std::unique_ptr
<CompilerInvocation
> Invocation(
402 newInvocation(&*Diagnostics
, CC1Args
, BinaryName
));
403 if (Diagnostics
->hasErrorOccurred())
405 return Action
->runInvocation(std::move(Invocation
), Files
,
406 std::move(PCHContainerOps
), DiagConsumer
);
409 const std::unique_ptr
<driver::Driver
> Driver(
410 newDriver(&*Diagnostics
, BinaryName
, &Files
->getVirtualFileSystem()));
411 // The "input file not found" diagnostics from the driver are useful.
412 // The driver is only aware of the VFS working directory, but some clients
413 // change this at the FileManager level instead.
414 // In this case the checks have false positives, so skip them.
415 if (!Files
->getFileSystemOpts().WorkingDir
.empty())
416 Driver
->setCheckInputsExist(false);
417 const std::unique_ptr
<driver::Compilation
> Compilation(
418 Driver
->BuildCompilation(llvm::ArrayRef(Argv
)));
421 const llvm::opt::ArgStringList
*const CC1Args
= getCC1Arguments(
422 &*Diagnostics
, Compilation
.get());
425 std::unique_ptr
<CompilerInvocation
> Invocation(
426 newInvocation(&*Diagnostics
, *CC1Args
, BinaryName
));
427 return runInvocation(BinaryName
, Compilation
.get(), std::move(Invocation
),
428 std::move(PCHContainerOps
));
431 bool ToolInvocation::runInvocation(
432 const char *BinaryName
, driver::Compilation
*Compilation
,
433 std::shared_ptr
<CompilerInvocation
> Invocation
,
434 std::shared_ptr
<PCHContainerOperations
> PCHContainerOps
) {
435 // Show the invocation, with -v.
436 if (Invocation
->getHeaderSearchOpts().Verbose
) {
437 llvm::errs() << "clang Invocation:\n";
438 Compilation
->getJobs().Print(llvm::errs(), "\n", true);
439 llvm::errs() << "\n";
442 return Action
->runInvocation(std::move(Invocation
), Files
,
443 std::move(PCHContainerOps
), DiagConsumer
);
446 bool FrontendActionFactory::runInvocation(
447 std::shared_ptr
<CompilerInvocation
> Invocation
, FileManager
*Files
,
448 std::shared_ptr
<PCHContainerOperations
> PCHContainerOps
,
449 DiagnosticConsumer
*DiagConsumer
) {
450 // Create a compiler instance to handle the actual work.
451 CompilerInstance
Compiler(std::move(PCHContainerOps
));
452 Compiler
.setInvocation(std::move(Invocation
));
453 Compiler
.setFileManager(Files
);
455 // The FrontendAction can have lifetime requirements for Compiler or its
456 // members, and we need to ensure it's deleted earlier than Compiler. So we
457 // pass it to an std::unique_ptr declared after the Compiler variable.
458 std::unique_ptr
<FrontendAction
> ScopedToolAction(create());
460 // Create the compiler's actual diagnostics engine.
461 Compiler
.createDiagnostics(DiagConsumer
, /*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
.startswith("-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 for (llvm::StringRef File
: AbsolutePaths
) {
560 // Currently implementations of CompilationDatabase::getCompileCommands can
561 // change the state of the file system (e.g. prepare generated headers), so
562 // this method needs to run right before we invoke the tool, as the next
563 // file may require a different (incompatible) state of the file system.
565 // FIXME: Make the compilation database interface more explicit about the
566 // requirements to the order of invocation of its members.
567 std::vector
<CompileCommand
> CompileCommandsForFile
=
568 Compilations
.getCompileCommands(File
);
569 if (CompileCommandsForFile
.empty()) {
570 llvm::errs() << "Skipping " << File
<< ". Compile command not found.\n";
574 for (CompileCommand
&CompileCommand
: CompileCommandsForFile
) {
575 // FIXME: chdir is thread hostile; on the other hand, creating the same
576 // behavior as chdir is complex: chdir resolves the path once, thus
577 // guaranteeing that all subsequent relative path operations work
578 // on the same path the original chdir resulted in. This makes a
579 // difference for example on network filesystems, where symlinks might be
580 // switched during runtime of the tool. Fixing this depends on having a
581 // file system abstraction that allows openat() style interactions.
582 if (OverlayFileSystem
->setCurrentWorkingDirectory(
583 CompileCommand
.Directory
))
584 llvm::report_fatal_error("Cannot chdir into \"" +
585 Twine(CompileCommand
.Directory
) + "\"!");
587 // Now fill the in-memory VFS with the relative file mappings so it will
588 // have the correct relative paths. We never remove mappings but that
590 if (SeenWorkingDirectories
.insert(CompileCommand
.Directory
).second
)
591 for (const auto &MappedFile
: MappedFileContents
)
592 if (!llvm::sys::path::is_absolute(MappedFile
.first
))
593 InMemoryFileSystem
->addFile(
595 llvm::MemoryBuffer::getMemBuffer(MappedFile
.second
));
597 std::vector
<std::string
> CommandLine
= CompileCommand
.CommandLine
;
599 CommandLine
= ArgsAdjuster(CommandLine
, CompileCommand
.Filename
);
600 assert(!CommandLine
.empty());
602 // Add the resource dir based on the binary of this tool. argv[0] in the
603 // compilation database may refer to a different compiler and we want to
604 // pick up the very same standard library that compiler is using. The
605 // builtin headers in the resource dir need to match the exact clang
606 // version the tool is using.
607 // FIXME: On linux, GetMainExecutable is independent of the value of the
608 // first argument, thus allowing ClangTool and runToolOnCode to just
609 // pass in made-up names here. Make sure this works on other platforms.
610 injectResourceDir(CommandLine
, "clang_tool", &StaticSymbol
);
612 // FIXME: We need a callback mechanism for the tool writer to output a
613 // customized message for each file.
614 LLVM_DEBUG({ llvm::dbgs() << "Processing: " << File
<< ".\n"; });
615 ToolInvocation
Invocation(std::move(CommandLine
), Action
, Files
.get(),
617 Invocation
.setDiagnosticConsumer(DiagConsumer
);
619 if (!Invocation
.run()) {
620 // FIXME: Diagnostics should be used instead.
621 if (PrintErrorMessage
)
622 llvm::errs() << "Error while processing " << File
<< ".\n";
623 ProcessingFailed
= true;
628 if (!InitialWorkingDir
.empty()) {
630 OverlayFileSystem
->setCurrentWorkingDirectory(InitialWorkingDir
))
631 llvm::errs() << "Error when trying to restore working dir: "
632 << EC
.message() << "\n";
634 return ProcessingFailed
? 1 : (FileSkipped
? 2 : 0);
639 class ASTBuilderAction
: public ToolAction
{
640 std::vector
<std::unique_ptr
<ASTUnit
>> &ASTs
;
643 ASTBuilderAction(std::vector
<std::unique_ptr
<ASTUnit
>> &ASTs
) : ASTs(ASTs
) {}
645 bool runInvocation(std::shared_ptr
<CompilerInvocation
> Invocation
,
647 std::shared_ptr
<PCHContainerOperations
> PCHContainerOps
,
648 DiagnosticConsumer
*DiagConsumer
) override
{
649 std::unique_ptr
<ASTUnit
> AST
= ASTUnit::LoadFromCompilerInvocation(
650 Invocation
, std::move(PCHContainerOps
),
651 CompilerInstance::createDiagnostics(&Invocation
->getDiagnosticOpts(),
653 /*ShouldOwnClient=*/false),
658 ASTs
.push_back(std::move(AST
));
665 int ClangTool::buildASTs(std::vector
<std::unique_ptr
<ASTUnit
>> &ASTs
) {
666 ASTBuilderAction
Action(ASTs
);
670 void ClangTool::setPrintErrorMessage(bool PrintErrorMessage
) {
671 this->PrintErrorMessage
= PrintErrorMessage
;
677 std::unique_ptr
<ASTUnit
>
678 buildASTFromCode(StringRef Code
, StringRef FileName
,
679 std::shared_ptr
<PCHContainerOperations
> PCHContainerOps
) {
680 return buildASTFromCodeWithArgs(Code
, std::vector
<std::string
>(), FileName
,
681 "clang-tool", std::move(PCHContainerOps
));
684 std::unique_ptr
<ASTUnit
> buildASTFromCodeWithArgs(
685 StringRef Code
, const std::vector
<std::string
> &Args
, StringRef FileName
,
686 StringRef ToolName
, std::shared_ptr
<PCHContainerOperations
> PCHContainerOps
,
687 ArgumentsAdjuster Adjuster
, const FileContentMappings
&VirtualMappedFiles
,
688 DiagnosticConsumer
*DiagConsumer
) {
689 std::vector
<std::unique_ptr
<ASTUnit
>> ASTs
;
690 ASTBuilderAction
Action(ASTs
);
691 llvm::IntrusiveRefCntPtr
<llvm::vfs::OverlayFileSystem
> OverlayFileSystem(
692 new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
693 llvm::IntrusiveRefCntPtr
<llvm::vfs::InMemoryFileSystem
> InMemoryFileSystem(
694 new llvm::vfs::InMemoryFileSystem
);
695 OverlayFileSystem
->pushOverlay(InMemoryFileSystem
);
696 llvm::IntrusiveRefCntPtr
<FileManager
> Files(
697 new FileManager(FileSystemOptions(), OverlayFileSystem
));
699 ToolInvocation
Invocation(
700 getSyntaxOnlyToolArgs(ToolName
, Adjuster(Args
, FileName
), FileName
),
701 &Action
, Files
.get(), std::move(PCHContainerOps
));
702 Invocation
.setDiagnosticConsumer(DiagConsumer
);
704 InMemoryFileSystem
->addFile(FileName
, 0,
705 llvm::MemoryBuffer::getMemBufferCopy(Code
));
706 for (auto &FilenameWithContent
: VirtualMappedFiles
) {
707 InMemoryFileSystem
->addFile(
708 FilenameWithContent
.first
, 0,
709 llvm::MemoryBuffer::getMemBuffer(FilenameWithContent
.second
));
712 if (!Invocation
.run())
715 assert(ASTs
.size() == 1);
716 return std::move(ASTs
[0]);
719 } // namespace tooling