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/Host.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"
58 #include <system_error>
62 #define DEBUG_TYPE "clang-tooling"
64 using namespace clang;
65 using namespace tooling;
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;
91 bool OffloadCompilation =
false;
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){
103 if (isa<driver::BindArchAction>(A))
104 A = *A->input_begin();
105 if (isa<driver::OffloadAction>(A)) {
112 assert(Actions.size() > 1);
114 isa<driver::CompileJobAction>(Actions.front()) ||
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 *
137 return StringRef(
Cmd.getCreator().getName()) ==
"clang";
146 if (IsCC1Command(Job) && llvm::all_of(Job.getInputInfos(), IsSrcFile))
147 CC1Jobs.push_back(&Job);
149 if (CC1Jobs.empty() ||
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();
164 const llvm::opt::ArgStringList &CC1Args,
165 const char *
const BinaryName) {
166 assert(!CC1Args.empty() &&
"Must at least contain the program name!");
176 const Twine &Code,
const Twine &FileName,
177 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
179 std::vector<std::string>(), FileName,
180 "clang-tool", std::move(PCHContainerOps));
186 static std::vector<std::string>
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());
202 std::unique_ptr<FrontendAction>
ToolAction,
const Twine &Code,
204 const std::vector<std::string> &Args,
const Twine &FileName,
205 const Twine &ToolName,
206 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
208 StringRef FileNameRef = FileName.toNullTerminatedStringRef(FileNameStorage);
215 std::move(
ToolAction), Files.get(), std::move(PCHContainerOps));
216 return Invocation.
run();
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,
226 new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
228 new llvm::vfs::InMemoryFileSystem);
229 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
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));
243 Args, FileName, ToolName);
248 StringRef RelativePath(
File);
250 if (RelativePath.startswith(
"./")) {
251 RelativePath = RelativePath.substr(strlen(
"./"));
255 if (
auto EC = FS.makeAbsolute(AbsolutePath))
256 return llvm::errorCodeToError(EC);
257 llvm::sys::path::native(AbsolutePath);
266 StringRef InvokedAs) {
267 if (CommandLine.empty() || InvokedAs.empty())
272 Table.getOption(driver::options::OPT_target).getPrefixedName();
275 Table.getOption(driver::options::OPT_target_legacy_spelling)
279 Table.getOption(driver::options::OPT_driver_mode).getPrefixedName();
283 bool ShouldAddTarget = TargetMode.TargetIsValid;
284 bool ShouldAddMode = TargetMode.DriverMode !=
nullptr;
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);
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);
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)) {}
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)) {}
342 std::vector<const char*> Argv;
344 Argv.push_back(Str.c_str());
345 const char *
const BinaryName = Argv[0];
353 DiagOpts = &*ParsedDiagOpts;
359 &*DiagOpts, DiagConsumer ? DiagConsumer : &DiagnosticPrinter,
false);
363 Diagnostics->setSourceManager(&SrcMgr);
365 const std::unique_ptr<driver::Driver> Driver(
372 Driver->setCheckInputsExist(
false);
373 const std::unique_ptr<driver::Compilation> Compilation(
374 Driver->BuildCompilation(llvm::makeArrayRef(Argv)));
378 &*Diagnostics, Compilation.get());
381 std::unique_ptr<CompilerInvocation> Invocation(
383 return runInvocation(BinaryName, Compilation.get(), std::move(Invocation),
384 std::move(PCHContainerOps));
387 bool ToolInvocation::runInvocation(
389 std::shared_ptr<CompilerInvocation> Invocation,
390 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
392 if (Invocation->getHeaderSearchOpts().Verbose) {
393 llvm::errs() <<
"clang Invocation:\n";
395 llvm::errs() <<
"\n";
399 std::move(PCHContainerOps), DiagConsumer);
403 std::shared_ptr<CompilerInvocation> Invocation,
FileManager *Files,
404 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
414 std::unique_ptr<FrontendAction> ScopedToolAction(
create());
423 const bool Success = Compiler.
ExecuteAction(*ScopedToolAction);
431 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
434 : Compilations(Compilations), SourcePaths(SourcePaths),
435 PCHContainerOps(
std::move(PCHContainerOps)),
436 OverlayFileSystem(new
llvm::vfs::OverlayFileSystem(
std::move(BaseFS))),
437 InMemoryFileSystem(new
llvm::vfs::InMemoryFileSystem),
440 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
445 Files->setVirtualFileSystem(OverlayFileSystem);
451 MappedFileContents.push_back(std::make_pair(FilePath, Content));
455 ArgsAdjuster =
combineAdjusters(std::move(ArgsAdjuster), std::move(Adjuster));
459 ArgsAdjuster =
nullptr;
465 for (StringRef Arg : Args)
466 if (Arg.startswith(
"-resource-dir"))
478 static int StaticSymbol;
482 if (SeenWorkingDirectories.insert(
"/").second)
483 for (
const auto &MappedFile : MappedFileContents)
484 if (llvm::sys::path::is_absolute(MappedFile.first))
485 InMemoryFileSystem->addFile(
487 llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
489 bool ProcessingFailed =
false;
490 bool FileSkipped =
false;
493 std::vector<std::string> AbsolutePaths;
494 AbsolutePaths.reserve(SourcePaths.size());
495 for (
const auto &SourcePath : SourcePaths) {
498 llvm::errs() <<
"Skipping " << SourcePath
499 <<
". Error while getting an absolute path: "
503 AbsolutePaths.push_back(std::move(*AbsPath));
509 if (
auto CWD = OverlayFileSystem->getCurrentWorkingDirectory()) {
510 InitialWorkingDir = std::move(*CWD);
512 llvm::errs() <<
"Could not get working directory: "
513 << CWD.getError().message() <<
"\n";
517 for (llvm::StringRef
File : AbsolutePaths) {
525 std::vector<CompileCommand> CompileCommandsForFile =
527 if (CompileCommandsForFile.empty()) {
528 llvm::errs() <<
"Skipping " <<
File <<
". Compile command not found.\n";
540 if (OverlayFileSystem->setCurrentWorkingDirectory(
542 llvm::report_fatal_error(
"Cannot chdir into \"" +
549 for (
const auto &MappedFile : MappedFileContents)
550 if (!llvm::sys::path::is_absolute(MappedFile.first))
551 InMemoryFileSystem->addFile(
553 llvm::MemoryBuffer::getMemBuffer(MappedFile.second));
558 assert(!CommandLine.empty());
572 LLVM_DEBUG({ llvm::dbgs() <<
"Processing: " <<
File <<
".\n"; });
573 ToolInvocation Invocation(std::move(CommandLine), Action, Files.get(),
575 Invocation.setDiagnosticConsumer(DiagConsumer);
577 if (!Invocation.run()) {
579 if (PrintErrorMessage)
580 llvm::errs() <<
"Error while processing " <<
File <<
".\n";
581 ProcessingFailed =
true;
586 if (!InitialWorkingDir.empty()) {
588 OverlayFileSystem->setCurrentWorkingDirectory(InitialWorkingDir))
589 llvm::errs() <<
"Error when trying to restore working dir: "
590 << EC.message() <<
"\n";
592 return ProcessingFailed ? 1 : (FileSkipped ? 2 : 0);
598 std::vector<std::unique_ptr<ASTUnit>> &ASTs;
601 ASTBuilderAction(std::vector<std::unique_ptr<ASTUnit>> &ASTs) : ASTs(ASTs) {}
603 bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
605 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
607 std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation(
608 Invocation, std::move(PCHContainerOps),
616 ASTs.push_back(std::move(AST));
624 ASTBuilderAction Action(ASTs);
629 this->RestoreCWD = RestoreCWD;
633 this->PrintErrorMessage = PrintErrorMessage;
639 std::unique_ptr<ASTUnit>
641 std::shared_ptr<PCHContainerOperations> PCHContainerOps) {
643 "clang-tool", std::move(PCHContainerOps));
647 StringRef Code,
const std::vector<std::string> &Args, StringRef FileName,
648 StringRef ToolName, std::shared_ptr<PCHContainerOperations> PCHContainerOps,
651 std::vector<std::unique_ptr<ASTUnit>> ASTs;
652 ASTBuilderAction Action(ASTs);
654 new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
656 new llvm::vfs::InMemoryFileSystem);
657 OverlayFileSystem->pushOverlay(InMemoryFileSystem);
663 &Action, Files.get(), std::move(PCHContainerOps));
666 InMemoryFileSystem->addFile(FileName, 0,
667 llvm::MemoryBuffer::getMemBufferCopy(Code));
668 for (
auto &FilenameWithContent : VirtualMappedFiles) {
669 InMemoryFileSystem->addFile(
670 FilenameWithContent.first, 0,
671 llvm::MemoryBuffer::getMemBuffer(FilenameWithContent.second));
674 if (!Invocation.
run())
677 assert(ASTs.size() == 1);
678 return std::move(ASTs[0]);