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/Debug.h"
47 #include "llvm/Support/ErrorHandling.h"
48 #include "llvm/Support/FileSystem.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 "llvm/TargetParser/Host.h"
58 #include <system_error>
62 #define DEBUG_TYPE "clang-tooling"
64 using namespace clang
;
65 using namespace tooling
;
67 ToolAction::~ToolAction() = default;
69 FrontendActionFactory::~FrontendActionFactory() = default;
71 // FIXME: This file contains structural duplication with other parts of the
72 // code that sets up a compiler to run tools on it, and we should refactor
73 // it to be based on the same framework.
75 /// Builds a clang driver initialized for running clang tools.
76 static driver::Driver
*
77 newDriver(DiagnosticsEngine
*Diagnostics
, const char *BinaryName
,
78 IntrusiveRefCntPtr
<llvm::vfs::FileSystem
> VFS
) {
79 driver::Driver
*CompilerDriver
=
80 new driver::Driver(BinaryName
, llvm::sys::getDefaultTargetTriple(),
81 *Diagnostics
, "clang LLVM compiler", std::move(VFS
));
82 CompilerDriver
->setTitle("clang_based_tool");
83 return CompilerDriver
;
86 /// Decide whether extra compiler frontend commands can be ignored.
87 static bool ignoreExtraCC1Commands(const driver::Compilation
*Compilation
) {
88 const driver::JobList
&Jobs
= Compilation
->getJobs();
89 const driver::ActionList
&Actions
= Compilation
->getActions();
91 bool OffloadCompilation
= false;
93 // Jobs and Actions look very different depending on whether the Clang tool
94 // injected -fsyntax-only or not. Try to handle both cases here.
96 for (const auto &Job
: Jobs
)
97 if (StringRef(Job
.getExecutable()) == "clang-offload-bundler")
98 OffloadCompilation
= true;
100 if (Jobs
.size() > 1) {
101 for (auto *A
: Actions
){
102 // On MacOSX real actions may end up being wrapped in BindArchAction
103 if (isa
<driver::BindArchAction
>(A
))
104 A
= *A
->input_begin();
105 if (isa
<driver::OffloadAction
>(A
)) {
106 // Offload compilation has 2 top-level actions, one (at the front) is
107 // the original host compilation and the other is offload action
108 // composed of at least one device compilation. For such case, general
109 // tooling will consider host-compilation only. For tooling on device
110 // compilation, device compilation only option, such as
111 // `--cuda-device-only`, needs specifying.
112 assert(Actions
.size() > 1);
114 isa
<driver::CompileJobAction
>(Actions
.front()) ||
115 // On MacOSX real actions may end up being wrapped in
117 (isa
<driver::BindArchAction
>(Actions
.front()) &&
118 isa
<driver::CompileJobAction
>(*Actions
.front()->input_begin())));
119 OffloadCompilation
= true;
125 return OffloadCompilation
;
131 const llvm::opt::ArgStringList
*
132 getCC1Arguments(DiagnosticsEngine
*Diagnostics
,
133 driver::Compilation
*Compilation
) {
134 const driver::JobList
&Jobs
= Compilation
->getJobs();
136 auto IsCC1Command
= [](const driver::Command
&Cmd
) {
137 return StringRef(Cmd
.getCreator().getName()) == "clang";
140 auto IsSrcFile
= [](const driver::InputInfo
&II
) {
141 return isSrcFile(II
.getType());
144 llvm::SmallVector
<const driver::Command
*, 1> CC1Jobs
;
145 for (const driver::Command
&Job
: Jobs
)
146 if (IsCC1Command(Job
) && llvm::all_of(Job
.getInputInfos(), IsSrcFile
))
147 CC1Jobs
.push_back(&Job
);
149 if (CC1Jobs
.empty() ||
150 (CC1Jobs
.size() > 1 && !ignoreExtraCC1Commands(Compilation
))) {
151 SmallString
<256> error_msg
;
152 llvm::raw_svector_ostream
error_stream(error_msg
);
153 Jobs
.Print(error_stream
, "; ", true);
154 Diagnostics
->Report(diag::err_fe_expected_compiler_job
)
155 << error_stream
.str();
159 return &CC1Jobs
[0]->getArguments();
162 /// Returns a clang build invocation initialized from the CC1 flags.
163 CompilerInvocation
*newInvocation(DiagnosticsEngine
*Diagnostics
,
164 ArrayRef
<const char *> CC1Args
,
165 const char *const BinaryName
) {
166 assert(!CC1Args
.empty() && "Must at least contain the program name!");
167 CompilerInvocation
*Invocation
= new CompilerInvocation
;
168 CompilerInvocation::CreateFromArgs(*Invocation
, CC1Args
, *Diagnostics
,
170 Invocation
->getFrontendOpts().DisableFree
= false;
171 Invocation
->getCodeGenOpts().DisableFree
= false;
175 bool runToolOnCode(std::unique_ptr
<FrontendAction
> ToolAction
,
176 const Twine
&Code
, const Twine
&FileName
,
177 std::shared_ptr
<PCHContainerOperations
> PCHContainerOps
) {
178 return runToolOnCodeWithArgs(std::move(ToolAction
), Code
,
179 std::vector
<std::string
>(), FileName
,
180 "clang-tool", std::move(PCHContainerOps
));
183 } // namespace tooling
186 static std::vector
<std::string
>
187 getSyntaxOnlyToolArgs(const Twine
&ToolName
,
188 const std::vector
<std::string
> &ExtraArgs
,
189 StringRef FileName
) {
190 std::vector
<std::string
> Args
;
191 Args
.push_back(ToolName
.str());
192 Args
.push_back("-fsyntax-only");
193 Args
.insert(Args
.end(), ExtraArgs
.begin(), ExtraArgs
.end());
194 Args
.push_back(FileName
.str());
201 bool runToolOnCodeWithArgs(
202 std::unique_ptr
<FrontendAction
> ToolAction
, const Twine
&Code
,
203 llvm::IntrusiveRefCntPtr
<llvm::vfs::FileSystem
> VFS
,
204 const std::vector
<std::string
> &Args
, const Twine
&FileName
,
205 const Twine
&ToolName
,
206 std::shared_ptr
<PCHContainerOperations
> PCHContainerOps
) {
207 SmallString
<16> FileNameStorage
;
208 StringRef FileNameRef
= FileName
.toNullTerminatedStringRef(FileNameStorage
);
210 llvm::IntrusiveRefCntPtr
<FileManager
> Files(
211 new FileManager(FileSystemOptions(), VFS
));
212 ArgumentsAdjuster Adjuster
= getClangStripDependencyFileAdjuster();
213 ToolInvocation
Invocation(
214 getSyntaxOnlyToolArgs(ToolName
, Adjuster(Args
, FileNameRef
), FileNameRef
),
215 std::move(ToolAction
), Files
.get(), std::move(PCHContainerOps
));
216 return Invocation
.run();
219 bool runToolOnCodeWithArgs(
220 std::unique_ptr
<FrontendAction
> ToolAction
, const Twine
&Code
,
221 const std::vector
<std::string
> &Args
, const Twine
&FileName
,
222 const Twine
&ToolName
,
223 std::shared_ptr
<PCHContainerOperations
> PCHContainerOps
,
224 const FileContentMappings
&VirtualMappedFiles
) {
225 llvm::IntrusiveRefCntPtr
<llvm::vfs::OverlayFileSystem
> OverlayFileSystem(
226 new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
227 llvm::IntrusiveRefCntPtr
<llvm::vfs::InMemoryFileSystem
> InMemoryFileSystem(
228 new llvm::vfs::InMemoryFileSystem
);
229 OverlayFileSystem
->pushOverlay(InMemoryFileSystem
);
231 SmallString
<1024> CodeStorage
;
232 InMemoryFileSystem
->addFile(FileName
, 0,
233 llvm::MemoryBuffer::getMemBuffer(
234 Code
.toNullTerminatedStringRef(CodeStorage
)));
236 for (auto &FilenameWithContent
: VirtualMappedFiles
) {
237 InMemoryFileSystem
->addFile(
238 FilenameWithContent
.first
, 0,
239 llvm::MemoryBuffer::getMemBuffer(FilenameWithContent
.second
));
242 return runToolOnCodeWithArgs(std::move(ToolAction
), Code
, OverlayFileSystem
,
243 Args
, FileName
, ToolName
);
246 llvm::Expected
<std::string
> getAbsolutePath(llvm::vfs::FileSystem
&FS
,
248 StringRef
RelativePath(File
);
249 // FIXME: Should '.\\' be accepted on Win32?
250 if (RelativePath
.startswith("./")) {
251 RelativePath
= RelativePath
.substr(strlen("./"));
254 SmallString
<1024> AbsolutePath
= RelativePath
;
255 if (auto EC
= FS
.makeAbsolute(AbsolutePath
))
256 return llvm::errorCodeToError(EC
);
257 llvm::sys::path::native(AbsolutePath
);
258 return std::string(AbsolutePath
.str());
261 std::string
getAbsolutePath(StringRef File
) {
262 return llvm::cantFail(getAbsolutePath(*llvm::vfs::getRealFileSystem(), File
));
265 void addTargetAndModeForProgramName(std::vector
<std::string
> &CommandLine
,
266 StringRef InvokedAs
) {
267 if (CommandLine
.empty() || InvokedAs
.empty())
269 const auto &Table
= driver::getDriverOptTable();
271 const std::string TargetOPT
=
272 Table
.getOption(driver::options::OPT_target
).getPrefixedName();
274 const std::string TargetOPTLegacy
=
275 Table
.getOption(driver::options::OPT_target_legacy_spelling
)
278 const std::string DriverModeOPT
=
279 Table
.getOption(driver::options::OPT_driver_mode
).getPrefixedName();
281 driver::ToolChain::getTargetAndModeFromProgramName(InvokedAs
);
282 // No need to search for target args if we don't have a target/mode to insert.
283 bool ShouldAddTarget
= TargetMode
.TargetIsValid
;
284 bool ShouldAddMode
= TargetMode
.DriverMode
!= nullptr;
285 // Skip CommandLine[0].
286 for (auto Token
= ++CommandLine
.begin(); Token
!= CommandLine
.end();
288 StringRef
TokenRef(*Token
);
289 ShouldAddTarget
= ShouldAddTarget
&& !TokenRef
.startswith(TargetOPT
) &&
290 !TokenRef
.equals(TargetOPTLegacy
);
291 ShouldAddMode
= ShouldAddMode
&& !TokenRef
.startswith(DriverModeOPT
);
294 CommandLine
.insert(++CommandLine
.begin(), TargetMode
.DriverMode
);
296 if (ShouldAddTarget
) {
297 CommandLine
.insert(++CommandLine
.begin(),
298 TargetOPT
+ TargetMode
.TargetPrefix
);
302 } // namespace tooling
307 class SingleFrontendActionFactory
: public FrontendActionFactory
{
308 std::unique_ptr
<FrontendAction
> Action
;
311 SingleFrontendActionFactory(std::unique_ptr
<FrontendAction
> Action
)
312 : Action(std::move(Action
)) {}
314 std::unique_ptr
<FrontendAction
> create() override
{
315 return std::move(Action
);
321 ToolInvocation::ToolInvocation(
322 std::vector
<std::string
> CommandLine
, ToolAction
*Action
,
323 FileManager
*Files
, std::shared_ptr
<PCHContainerOperations
> PCHContainerOps
)
324 : CommandLine(std::move(CommandLine
)), Action(Action
), OwnsAction(false),
325 Files(Files
), PCHContainerOps(std::move(PCHContainerOps
)) {}
327 ToolInvocation::ToolInvocation(
328 std::vector
<std::string
> CommandLine
,
329 std::unique_ptr
<FrontendAction
> FAction
, FileManager
*Files
,
330 std::shared_ptr
<PCHContainerOperations
> PCHContainerOps
)
331 : CommandLine(std::move(CommandLine
)),
332 Action(new SingleFrontendActionFactory(std::move(FAction
))),
333 OwnsAction(true), Files(Files
),
334 PCHContainerOps(std::move(PCHContainerOps
)) {}
336 ToolInvocation::~ToolInvocation() {
341 bool ToolInvocation::run() {
342 llvm::opt::ArgStringList Argv
;
343 for (const std::string
&Str
: CommandLine
)
344 Argv
.push_back(Str
.c_str());
345 const char *const BinaryName
= Argv
[0];
347 // Parse diagnostic options from the driver command-line only if none were
349 IntrusiveRefCntPtr
<DiagnosticOptions
> ParsedDiagOpts
;
350 DiagnosticOptions
*DiagOpts
= this->DiagOpts
;
352 ParsedDiagOpts
= CreateAndPopulateDiagOpts(Argv
);
353 DiagOpts
= &*ParsedDiagOpts
;
356 TextDiagnosticPrinter
DiagnosticPrinter(llvm::errs(), DiagOpts
);
357 IntrusiveRefCntPtr
<DiagnosticsEngine
> Diagnostics
=
358 CompilerInstance::createDiagnostics(
359 &*DiagOpts
, DiagConsumer
? DiagConsumer
: &DiagnosticPrinter
, false);
360 // Although `Diagnostics` are used only for command-line parsing, the custom
361 // `DiagConsumer` might expect a `SourceManager` to be present.
362 SourceManager
SrcMgr(*Diagnostics
, *Files
);
363 Diagnostics
->setSourceManager(&SrcMgr
);
365 // We already have a cc1, just create an invocation.
366 if (CommandLine
.size() >= 2 && CommandLine
[1] == "-cc1") {
367 ArrayRef
<const char *> CC1Args
= ArrayRef(Argv
).drop_front();
368 std::unique_ptr
<CompilerInvocation
> Invocation(
369 newInvocation(&*Diagnostics
, CC1Args
, BinaryName
));
370 if (Diagnostics
->hasErrorOccurred())
372 return Action
->runInvocation(std::move(Invocation
), Files
,
373 std::move(PCHContainerOps
), DiagConsumer
);
376 const std::unique_ptr
<driver::Driver
> Driver(
377 newDriver(&*Diagnostics
, BinaryName
, &Files
->getVirtualFileSystem()));
378 // The "input file not found" diagnostics from the driver are useful.
379 // The driver is only aware of the VFS working directory, but some clients
380 // change this at the FileManager level instead.
381 // In this case the checks have false positives, so skip them.
382 if (!Files
->getFileSystemOpts().WorkingDir
.empty())
383 Driver
->setCheckInputsExist(false);
384 const std::unique_ptr
<driver::Compilation
> Compilation(
385 Driver
->BuildCompilation(llvm::ArrayRef(Argv
)));
388 const llvm::opt::ArgStringList
*const CC1Args
= getCC1Arguments(
389 &*Diagnostics
, Compilation
.get());
392 std::unique_ptr
<CompilerInvocation
> Invocation(
393 newInvocation(&*Diagnostics
, *CC1Args
, BinaryName
));
394 return runInvocation(BinaryName
, Compilation
.get(), std::move(Invocation
),
395 std::move(PCHContainerOps
));
398 bool ToolInvocation::runInvocation(
399 const char *BinaryName
, driver::Compilation
*Compilation
,
400 std::shared_ptr
<CompilerInvocation
> Invocation
,
401 std::shared_ptr
<PCHContainerOperations
> PCHContainerOps
) {
402 // Show the invocation, with -v.
403 if (Invocation
->getHeaderSearchOpts().Verbose
) {
404 llvm::errs() << "clang Invocation:\n";
405 Compilation
->getJobs().Print(llvm::errs(), "\n", true);
406 llvm::errs() << "\n";
409 return Action
->runInvocation(std::move(Invocation
), Files
,
410 std::move(PCHContainerOps
), DiagConsumer
);
413 bool FrontendActionFactory::runInvocation(
414 std::shared_ptr
<CompilerInvocation
> Invocation
, FileManager
*Files
,
415 std::shared_ptr
<PCHContainerOperations
> PCHContainerOps
,
416 DiagnosticConsumer
*DiagConsumer
) {
417 // Create a compiler instance to handle the actual work.
418 CompilerInstance
Compiler(std::move(PCHContainerOps
));
419 Compiler
.setInvocation(std::move(Invocation
));
420 Compiler
.setFileManager(Files
);
422 // The FrontendAction can have lifetime requirements for Compiler or its
423 // members, and we need to ensure it's deleted earlier than Compiler. So we
424 // pass it to an std::unique_ptr declared after the Compiler variable.
425 std::unique_ptr
<FrontendAction
> ScopedToolAction(create());
427 // Create the compiler's actual diagnostics engine.
428 Compiler
.createDiagnostics(DiagConsumer
, /*ShouldOwnClient=*/false);
429 if (!Compiler
.hasDiagnostics())
432 Compiler
.createSourceManager(*Files
);
434 const bool Success
= Compiler
.ExecuteAction(*ScopedToolAction
);
436 Files
->clearStatCache();
440 ClangTool::ClangTool(const CompilationDatabase
&Compilations
,
441 ArrayRef
<std::string
> SourcePaths
,
442 std::shared_ptr
<PCHContainerOperations
> PCHContainerOps
,
443 IntrusiveRefCntPtr
<llvm::vfs::FileSystem
> BaseFS
,
444 IntrusiveRefCntPtr
<FileManager
> Files
)
445 : Compilations(Compilations
), SourcePaths(SourcePaths
),
446 PCHContainerOps(std::move(PCHContainerOps
)),
447 OverlayFileSystem(new llvm::vfs::OverlayFileSystem(std::move(BaseFS
))),
448 InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem
),
450 : new FileManager(FileSystemOptions(), OverlayFileSystem
)) {
451 OverlayFileSystem
->pushOverlay(InMemoryFileSystem
);
452 appendArgumentsAdjuster(getClangStripOutputAdjuster());
453 appendArgumentsAdjuster(getClangSyntaxOnlyAdjuster());
454 appendArgumentsAdjuster(getClangStripDependencyFileAdjuster());
456 Files
->setVirtualFileSystem(OverlayFileSystem
);
459 ClangTool::~ClangTool() = default;
461 void ClangTool::mapVirtualFile(StringRef FilePath
, StringRef Content
) {
462 MappedFileContents
.push_back(std::make_pair(FilePath
, Content
));
465 void ClangTool::appendArgumentsAdjuster(ArgumentsAdjuster Adjuster
) {
466 ArgsAdjuster
= combineAdjusters(std::move(ArgsAdjuster
), std::move(Adjuster
));
469 void ClangTool::clearArgumentsAdjusters() {
470 ArgsAdjuster
= nullptr;
473 static void injectResourceDir(CommandLineArguments
&Args
, const char *Argv0
,
475 // Allow users to override the resource dir.
476 for (StringRef Arg
: Args
)
477 if (Arg
.startswith("-resource-dir"))
480 // If there's no override in place add our resource dir.
481 Args
= getInsertArgumentAdjuster(
482 ("-resource-dir=" + CompilerInvocation::GetResourcesPath(Argv0
, MainAddr
))
486 int ClangTool::run(ToolAction
*Action
) {
487 // Exists solely for the purpose of lookup of the resource path.
488 // This just needs to be some symbol in the binary.
489 static int StaticSymbol
;
491 // First insert all absolute paths into the in-memory VFS. These are global
492 // for all compile commands.
493 if (SeenWorkingDirectories
.insert("/").second
)
494 for (const auto &MappedFile
: MappedFileContents
)
495 if (llvm::sys::path::is_absolute(MappedFile
.first
))
496 InMemoryFileSystem
->addFile(
498 llvm::MemoryBuffer::getMemBuffer(MappedFile
.second
));
500 bool ProcessingFailed
= false;
501 bool FileSkipped
= false;
502 // Compute all absolute paths before we run any actions, as those will change
503 // the working directory.
504 std::vector
<std::string
> AbsolutePaths
;
505 AbsolutePaths
.reserve(SourcePaths
.size());
506 for (const auto &SourcePath
: SourcePaths
) {
507 auto AbsPath
= getAbsolutePath(*OverlayFileSystem
, SourcePath
);
509 llvm::errs() << "Skipping " << SourcePath
510 << ". Error while getting an absolute path: "
511 << llvm::toString(AbsPath
.takeError()) << "\n";
514 AbsolutePaths
.push_back(std::move(*AbsPath
));
517 // Remember the working directory in case we need to restore it.
518 std::string InitialWorkingDir
;
520 if (auto CWD
= OverlayFileSystem
->getCurrentWorkingDirectory()) {
521 InitialWorkingDir
= std::move(*CWD
);
523 llvm::errs() << "Could not get working directory: "
524 << CWD
.getError().message() << "\n";
528 for (llvm::StringRef File
: AbsolutePaths
) {
529 // Currently implementations of CompilationDatabase::getCompileCommands can
530 // change the state of the file system (e.g. prepare generated headers), so
531 // this method needs to run right before we invoke the tool, as the next
532 // file may require a different (incompatible) state of the file system.
534 // FIXME: Make the compilation database interface more explicit about the
535 // requirements to the order of invocation of its members.
536 std::vector
<CompileCommand
> CompileCommandsForFile
=
537 Compilations
.getCompileCommands(File
);
538 if (CompileCommandsForFile
.empty()) {
539 llvm::errs() << "Skipping " << File
<< ". Compile command not found.\n";
543 for (CompileCommand
&CompileCommand
: CompileCommandsForFile
) {
544 // FIXME: chdir is thread hostile; on the other hand, creating the same
545 // behavior as chdir is complex: chdir resolves the path once, thus
546 // guaranteeing that all subsequent relative path operations work
547 // on the same path the original chdir resulted in. This makes a
548 // difference for example on network filesystems, where symlinks might be
549 // switched during runtime of the tool. Fixing this depends on having a
550 // file system abstraction that allows openat() style interactions.
551 if (OverlayFileSystem
->setCurrentWorkingDirectory(
552 CompileCommand
.Directory
))
553 llvm::report_fatal_error("Cannot chdir into \"" +
554 Twine(CompileCommand
.Directory
) + "\"!");
556 // Now fill the in-memory VFS with the relative file mappings so it will
557 // have the correct relative paths. We never remove mappings but that
559 if (SeenWorkingDirectories
.insert(CompileCommand
.Directory
).second
)
560 for (const auto &MappedFile
: MappedFileContents
)
561 if (!llvm::sys::path::is_absolute(MappedFile
.first
))
562 InMemoryFileSystem
->addFile(
564 llvm::MemoryBuffer::getMemBuffer(MappedFile
.second
));
566 std::vector
<std::string
> CommandLine
= CompileCommand
.CommandLine
;
568 CommandLine
= ArgsAdjuster(CommandLine
, CompileCommand
.Filename
);
569 assert(!CommandLine
.empty());
571 // Add the resource dir based on the binary of this tool. argv[0] in the
572 // compilation database may refer to a different compiler and we want to
573 // pick up the very same standard library that compiler is using. The
574 // builtin headers in the resource dir need to match the exact clang
575 // version the tool is using.
576 // FIXME: On linux, GetMainExecutable is independent of the value of the
577 // first argument, thus allowing ClangTool and runToolOnCode to just
578 // pass in made-up names here. Make sure this works on other platforms.
579 injectResourceDir(CommandLine
, "clang_tool", &StaticSymbol
);
581 // FIXME: We need a callback mechanism for the tool writer to output a
582 // customized message for each file.
583 LLVM_DEBUG({ llvm::dbgs() << "Processing: " << File
<< ".\n"; });
584 ToolInvocation
Invocation(std::move(CommandLine
), Action
, Files
.get(),
586 Invocation
.setDiagnosticConsumer(DiagConsumer
);
588 if (!Invocation
.run()) {
589 // FIXME: Diagnostics should be used instead.
590 if (PrintErrorMessage
)
591 llvm::errs() << "Error while processing " << File
<< ".\n";
592 ProcessingFailed
= true;
597 if (!InitialWorkingDir
.empty()) {
599 OverlayFileSystem
->setCurrentWorkingDirectory(InitialWorkingDir
))
600 llvm::errs() << "Error when trying to restore working dir: "
601 << EC
.message() << "\n";
603 return ProcessingFailed
? 1 : (FileSkipped
? 2 : 0);
608 class ASTBuilderAction
: public ToolAction
{
609 std::vector
<std::unique_ptr
<ASTUnit
>> &ASTs
;
612 ASTBuilderAction(std::vector
<std::unique_ptr
<ASTUnit
>> &ASTs
) : ASTs(ASTs
) {}
614 bool runInvocation(std::shared_ptr
<CompilerInvocation
> Invocation
,
616 std::shared_ptr
<PCHContainerOperations
> PCHContainerOps
,
617 DiagnosticConsumer
*DiagConsumer
) override
{
618 std::unique_ptr
<ASTUnit
> AST
= ASTUnit::LoadFromCompilerInvocation(
619 Invocation
, std::move(PCHContainerOps
),
620 CompilerInstance::createDiagnostics(&Invocation
->getDiagnosticOpts(),
622 /*ShouldOwnClient=*/false),
627 ASTs
.push_back(std::move(AST
));
634 int ClangTool::buildASTs(std::vector
<std::unique_ptr
<ASTUnit
>> &ASTs
) {
635 ASTBuilderAction
Action(ASTs
);
639 void ClangTool::setRestoreWorkingDir(bool RestoreCWD
) {
640 this->RestoreCWD
= RestoreCWD
;
643 void ClangTool::setPrintErrorMessage(bool PrintErrorMessage
) {
644 this->PrintErrorMessage
= PrintErrorMessage
;
650 std::unique_ptr
<ASTUnit
>
651 buildASTFromCode(StringRef Code
, StringRef FileName
,
652 std::shared_ptr
<PCHContainerOperations
> PCHContainerOps
) {
653 return buildASTFromCodeWithArgs(Code
, std::vector
<std::string
>(), FileName
,
654 "clang-tool", std::move(PCHContainerOps
));
657 std::unique_ptr
<ASTUnit
> buildASTFromCodeWithArgs(
658 StringRef Code
, const std::vector
<std::string
> &Args
, StringRef FileName
,
659 StringRef ToolName
, std::shared_ptr
<PCHContainerOperations
> PCHContainerOps
,
660 ArgumentsAdjuster Adjuster
, const FileContentMappings
&VirtualMappedFiles
,
661 DiagnosticConsumer
*DiagConsumer
) {
662 std::vector
<std::unique_ptr
<ASTUnit
>> ASTs
;
663 ASTBuilderAction
Action(ASTs
);
664 llvm::IntrusiveRefCntPtr
<llvm::vfs::OverlayFileSystem
> OverlayFileSystem(
665 new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
666 llvm::IntrusiveRefCntPtr
<llvm::vfs::InMemoryFileSystem
> InMemoryFileSystem(
667 new llvm::vfs::InMemoryFileSystem
);
668 OverlayFileSystem
->pushOverlay(InMemoryFileSystem
);
669 llvm::IntrusiveRefCntPtr
<FileManager
> Files(
670 new FileManager(FileSystemOptions(), OverlayFileSystem
));
672 ToolInvocation
Invocation(
673 getSyntaxOnlyToolArgs(ToolName
, Adjuster(Args
, FileName
), FileName
),
674 &Action
, Files
.get(), std::move(PCHContainerOps
));
675 Invocation
.setDiagnosticConsumer(DiagConsumer
);
677 InMemoryFileSystem
->addFile(FileName
, 0,
678 llvm::MemoryBuffer::getMemBufferCopy(Code
));
679 for (auto &FilenameWithContent
: VirtualMappedFiles
) {
680 InMemoryFileSystem
->addFile(
681 FilenameWithContent
.first
, 0,
682 llvm::MemoryBuffer::getMemBuffer(FilenameWithContent
.second
));
685 if (!Invocation
.run())
688 assert(ASTs
.size() == 1);
689 return std::move(ASTs
[0]);
692 } // namespace tooling