diff --git a/clang/include/clang/Tooling/ArgumentsAdjusters.h b/clang/include/clang/Tooling/ArgumentsAdjusters.h --- a/clang/include/clang/Tooling/ArgumentsAdjusters.h +++ b/clang/include/clang/Tooling/ArgumentsAdjusters.h @@ -33,7 +33,7 @@ /// Command line argument adjuster is responsible for command line arguments /// modification before the arguments are used to run a frontend action. using ArgumentsAdjuster = std::function; + const CommandLineArguments &, StringRef Filename, StringRef Directory)>; /// Gets an argument adjuster that converts input command line arguments /// to the "syntax check only" variant. diff --git a/clang/lib/Tooling/ArgumentsAdjusters.cpp b/clang/lib/Tooling/ArgumentsAdjusters.cpp --- a/clang/lib/Tooling/ArgumentsAdjusters.cpp +++ b/clang/lib/Tooling/ArgumentsAdjusters.cpp @@ -21,7 +21,7 @@ /// Add -fsyntax-only option to the command line arguments. ArgumentsAdjuster getClangSyntaxOnlyAdjuster() { - return [](const CommandLineArguments &Args, StringRef /*unused*/) { + return [](const CommandLineArguments &Args, StringRef /*unused*/, StringRef /*unused*/) { CommandLineArguments AdjustedArgs; bool HasSyntaxOnly = false; for (size_t i = 0, e = Args.size(); i < e; ++i) { @@ -40,7 +40,7 @@ } ArgumentsAdjuster getClangStripOutputAdjuster() { - return [](const CommandLineArguments &Args, StringRef /*unused*/) { + return [](const CommandLineArguments &Args, StringRef /*unused*/, StringRef /*unused*/) { CommandLineArguments AdjustedArgs; for (size_t i = 0, e = Args.size(); i < e; ++i) { StringRef Arg = Args[i]; @@ -58,7 +58,7 @@ } ArgumentsAdjuster getClangStripSerializeDiagnosticAdjuster() { - return [](const CommandLineArguments &Args, StringRef /*unused*/) { + return [](const CommandLineArguments &Args, StringRef /*unused*/, StringRef /*unused*/) { CommandLineArguments AdjustedArgs; for (size_t i = 0, e = Args.size(); i < e; ++i) { StringRef Arg = Args[i]; @@ -74,7 +74,7 @@ } ArgumentsAdjuster getClangStripDependencyFileAdjuster() { - return [](const CommandLineArguments &Args, StringRef /*unused*/) { + return [](const CommandLineArguments &Args, StringRef /*unused*/, StringRef /*unused*/) { CommandLineArguments AdjustedArgs; for (size_t i = 0, e = Args.size(); i < e; ++i) { StringRef Arg = Args[i]; @@ -95,7 +95,7 @@ ArgumentsAdjuster getInsertArgumentAdjuster(const CommandLineArguments &Extra, ArgumentInsertPosition Pos) { - return [Extra, Pos](const CommandLineArguments &Args, StringRef /*unused*/) { + return [Extra, Pos](const CommandLineArguments &Args, StringRef /*unused*/, StringRef /*unused*/) { CommandLineArguments Return(Args); CommandLineArguments::iterator I; @@ -122,13 +122,13 @@ return Second; if (!Second) return First; - return [First, Second](const CommandLineArguments &Args, StringRef File) { - return Second(First(Args, File), File); + return [First, Second](const CommandLineArguments &Args, StringRef File, StringRef Directory) { + return Second(First(Args, File, Directory), File, Directory); }; } ArgumentsAdjuster getStripPluginsAdjuster() { - return [](const CommandLineArguments &Args, StringRef /*unused*/) { + return [](const CommandLineArguments &Args, StringRef /*unused*/, StringRef /*unused*/) { CommandLineArguments AdjustedArgs; for (size_t I = 0, E = Args.size(); I != E; I++) { // According to https://clang.llvm.org/docs/ClangPlugins.html diff --git a/clang/lib/Tooling/CommonOptionsParser.cpp b/clang/lib/Tooling/CommonOptionsParser.cpp --- a/clang/lib/Tooling/CommonOptionsParser.cpp +++ b/clang/lib/Tooling/CommonOptionsParser.cpp @@ -76,7 +76,7 @@ std::vector Commands) const { for (CompileCommand &Command : Commands) for (const auto &Adjuster : Adjusters) - Command.CommandLine = Adjuster(Command.CommandLine, Command.Filename); + Command.CommandLine = Adjuster(Command.CommandLine, Command.Filename, Command.Directory); return Commands; } diff --git a/clang/lib/Tooling/Tooling.cpp b/clang/lib/Tooling/Tooling.cpp --- a/clang/lib/Tooling/Tooling.cpp +++ b/clang/lib/Tooling/Tooling.cpp @@ -189,8 +189,11 @@ llvm::IntrusiveRefCntPtr Files( new FileManager(FileSystemOptions(), VFS)); ArgumentsAdjuster Adjuster = getClangStripDependencyFileAdjuster(); + const llvm::ErrorOr &WD = VFS->getCurrentWorkingDirectory(); + if (!WD) + return false; ToolInvocation Invocation( - getSyntaxOnlyToolArgs(ToolName, Adjuster(Args, FileNameRef), FileNameRef), + getSyntaxOnlyToolArgs(ToolName, Adjuster(Args, FileNameRef, WD.get()), FileNameRef), std::move(ToolAction), Files.get(), std::move(PCHContainerOps)); return Invocation.run(); } @@ -527,7 +530,7 @@ std::vector CommandLine = CompileCommand.CommandLine; if (ArgsAdjuster) - CommandLine = ArgsAdjuster(CommandLine, CompileCommand.Filename); + CommandLine = ArgsAdjuster(CommandLine, CompileCommand.Filename, CompileCommand.Directory); assert(!CommandLine.empty()); // Add the resource dir based on the binary of this tool. argv[0] in the @@ -630,8 +633,11 @@ llvm::IntrusiveRefCntPtr Files( new FileManager(FileSystemOptions(), OverlayFileSystem)); + const auto &CWD = OverlayFileSystem->getCurrentWorkingDirectory(); + if (!CWD) + return nullptr; ToolInvocation Invocation( - getSyntaxOnlyToolArgs(ToolName, Adjuster(Args, FileName), FileName), + getSyntaxOnlyToolArgs(ToolName, Adjuster(Args, FileName, CWD.get()), FileName), &Action, Files.get(), std::move(PCHContainerOps)); InMemoryFileSystem->addFile(FileName, 0, diff --git a/clang/tools/clang-scan-deps/ClangScanDeps.cpp b/clang/tools/clang-scan-deps/ClangScanDeps.cpp --- a/clang/tools/clang-scan-deps/ClangScanDeps.cpp +++ b/clang/tools/clang-scan-deps/ClangScanDeps.cpp @@ -12,6 +12,7 @@ #include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h" #include "clang/Tooling/JSONCompilationDatabase.h" #include "llvm/Support/InitLLVM.h" +#include "llvm/Support/FileUtilities.h" #include "llvm/Support/Options.h" #include "llvm/Support/Program.h" #include "llvm/Support/Signals.h" @@ -38,6 +39,64 @@ raw_ostream &OS; }; +class ResourceDirectoryCache { +public: + /// FindResourceDir finds the resource directory relative to the clang compiler + /// being used in Args, by running it with "-print-resource-dir" option and + /// cache the results for reuse. + /// \returns resource directory path associated with the given invocation command. + std::string FindResourceDir(const tooling::CommandLineArguments &Args, StringRef Directory) { + if (Args.size() < 1) + return ""; + + const std::string &ClangBinaryPath = MakeAbsolutePath(Directory, Args[0]); + + std::unique_lock LockGuard(CacheLock); + const auto& CachedResourceDir = Cache.find(ClangBinaryPath); + if (CachedResourceDir != Cache.end()) + return CachedResourceDir->second; + + std::vector PrintResourceDirArgs{"clang", "-print-resource-dir"}; + llvm::SmallString<64> OutputFile, ErrorFile; + llvm::sys::fs::createTemporaryFile("print-resource-dir-output", "" /*no-suffix*/, OutputFile); + llvm::sys::fs::createTemporaryFile("print-resource-dir-error", ""/*no-suffix*/, ErrorFile); + llvm::FileRemover OutputRemover(OutputFile.c_str()); + llvm::FileRemover ErrorRemover(ErrorFile.c_str()); + llvm::Optional Redirects[] = { + {""}, //Stdin + StringRef(OutputFile), + StringRef(ErrorFile), + }; + if (const int RC = llvm::sys::ExecuteAndWait(ClangBinaryPath, PrintResourceDirArgs, {}, Redirects)) { + auto ErrorBuf = llvm::MemoryBuffer::getFile(ErrorFile.c_str()); + llvm::errs() << ErrorBuf.get()->getBuffer(); + return ""; + } + + auto OutputBuf = llvm::MemoryBuffer::getFile(OutputFile.c_str()); + if (!OutputBuf) + return ""; + StringRef Output = OutputBuf.get()->getBuffer().rtrim('\n'); + + Cache[ClangBinaryPath] = Output.str(); + return Cache[ClangBinaryPath]; + } + +private: + /// \returns the absolute path formed by joining the current directory with the given path. + std::string MakeAbsolutePath(StringRef CurrentDir, StringRef Path) { + if (Path.empty()) + return ""; + llvm::SmallString<256> InitialDirectory(CurrentDir); + llvm::SmallString<256> AbsolutePath(Path); + llvm::sys::fs::make_absolute(InitialDirectory, AbsolutePath); + return AbsolutePath.str(); + } + + std::map Cache; + std::mutex CacheLock; +}; + /// The high-level implementation of the dependency discovery tool that runs on /// an individual worker thread. class DependencyScanningTool { @@ -218,12 +277,14 @@ auto AdjustingCompilations = std::make_unique( std::move(Compilations)); + ResourceDirectoryCache ResourceDirCache; AdjustingCompilations->appendArgumentsAdjuster( - [](const tooling::CommandLineArguments &Args, StringRef FileName) { + [&ResourceDirCache](const tooling::CommandLineArguments &Args, StringRef FileName, StringRef Directory) { std::string LastO = ""; bool HasMT = false; bool HasMQ = false; bool HasMD = false; + bool HasResourceDir = false; // We need to find the last -o value. if (!Args.empty()) { std::size_t Idx = Args.size() - 1; @@ -237,6 +298,8 @@ HasMQ = true; if (Args[Idx] == "-MD") HasMD = true; + if (Args[Idx] == "-resource-dir") + HasResourceDir = true; } --Idx; } @@ -264,6 +327,14 @@ AdjustedArgs.push_back("-Xclang"); AdjustedArgs.push_back("-sys-header-deps"); AdjustedArgs.push_back("-Wno-error"); + + if (!HasResourceDir) { + const std::string &ResourceDir = ResourceDirCache.FindResourceDir(Args, Directory); + if (!ResourceDir.empty()) { + AdjustedArgs.push_back("-resource-dir"); + AdjustedArgs.push_back(ResourceDir); + } + } return AdjustedArgs; }); AdjustingCompilations->appendArgumentsAdjuster(