diff --git a/clang/include/clang/Tooling/CompilationDatabase.h b/clang/include/clang/Tooling/CompilationDatabase.h --- a/clang/include/clang/Tooling/CompilationDatabase.h +++ b/clang/include/clang/Tooling/CompilationDatabase.h @@ -31,6 +31,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" +#include "llvm/Support/VirtualFileSystem.h" #include #include #include @@ -219,6 +220,12 @@ std::unique_ptr inferTargetAndDriverMode(std::unique_ptr Base); +/// Returns a wrapped CompilationDatabase that will expand all rsp(response) +/// files on commandline returned by underlying database. +std::unique_ptr +expandResponseFiles(std::unique_ptr Base, + llvm::IntrusiveRefCntPtr FS); + } // namespace tooling } // namespace clang diff --git a/clang/lib/Tooling/CMakeLists.txt b/clang/lib/Tooling/CMakeLists.txt --- a/clang/lib/Tooling/CMakeLists.txt +++ b/clang/lib/Tooling/CMakeLists.txt @@ -17,6 +17,7 @@ CommonOptionsParser.cpp CompilationDatabase.cpp Execution.cpp + ExpandResponseFilesCompilationDatabase.cpp FileMatchTrie.cpp FixIt.cpp GuessTargetAndModeCompilationDatabase.cpp diff --git a/clang/lib/Tooling/ExpandResponseFilesCompilationDatabase.cpp b/clang/lib/Tooling/ExpandResponseFilesCompilationDatabase.cpp new file mode 100644 --- /dev/null +++ b/clang/lib/Tooling/ExpandResponseFilesCompilationDatabase.cpp @@ -0,0 +1,92 @@ +//===- ExpandResponseFileCompilationDataBase.cpp --------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/CompilationDatabase.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ConvertUTF.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/StringSaver.h" + +namespace clang { +namespace tooling { +namespace { + +class ExpandResponseFilesDatabase : public CompilationDatabase { +public: + ExpandResponseFilesDatabase( + std::unique_ptr Base, + llvm::cl::TokenizerCallback Tokenizer, + llvm::IntrusiveRefCntPtr FS) + : Base(std::move(Base)), Tokenizer(Tokenizer), FS(FS) { + assert(this->Base != nullptr); + assert(this->Tokenizer != nullptr); + assert(this->FS != nullptr); + } + + std::vector getAllFiles() const override { + return Base->getAllFiles(); + } + + std::vector + getCompileCommands(StringRef FilePath) const override { + return expand(Base->getCompileCommands(FilePath)); + } + + std::vector getAllCompileCommands() const override { + return expand(Base->getAllCompileCommands()); + } + +private: + std::vector expand(std::vector Cmds) const { + llvm::ErrorOr PreWorkingDirectory = + FS->getCurrentWorkingDirectory(); + for (auto &Cmd : Cmds) { + bool SeenRSPFile = false; + llvm::SmallVector Argv; + Argv.reserve(Cmd.CommandLine.size()); + for (auto &Arg : Cmd.CommandLine) { + Argv.push_back(Arg.c_str()); + SeenRSPFile |= Arg.front() == '@'; + } + if (!SeenRSPFile) + continue; + if (FS->setCurrentWorkingDirectory(Cmd.Directory)) + continue; + llvm::BumpPtrAllocator Alloc; + llvm::StringSaver Saver(Alloc); + llvm::cl::ExpandResponseFiles(Saver, Tokenizer, Argv, false, false, *FS); + Cmd.CommandLine.assign(Argv.begin(), Argv.end()); + } + if (PreWorkingDirectory) + FS->setCurrentWorkingDirectory(*PreWorkingDirectory); + return Cmds; + } + +private: + std::unique_ptr Base; + llvm::cl::TokenizerCallback Tokenizer; + llvm::IntrusiveRefCntPtr FS; +}; + +} // namespace + +std::unique_ptr +expandResponseFiles(std::unique_ptr Base, + llvm::IntrusiveRefCntPtr FS) { + auto Tokenizer = llvm::Triple(llvm::sys::getProcessTriple()).isOSWindows() + ? llvm::cl::TokenizeWindowsCommandLine + : llvm::cl::TokenizeGNUCommandLine; + return std::make_unique(std::move(Base), + Tokenizer, FS); +} + +} // namespace tooling +} // namespace clang diff --git a/clang/lib/Tooling/JSONCompilationDatabase.cpp b/clang/lib/Tooling/JSONCompilationDatabase.cpp --- a/clang/lib/Tooling/JSONCompilationDatabase.cpp +++ b/clang/lib/Tooling/JSONCompilationDatabase.cpp @@ -168,7 +168,8 @@ auto Base = JSONCompilationDatabase::loadFromFile( JSONDatabasePath, ErrorMessage, JSONCommandLineSyntax::AutoDetect); return Base ? inferTargetAndDriverMode( - inferMissingCompileCommands(std::move(Base))) + inferMissingCompileCommands(expandResponseFiles( + std::move(Base), llvm::vfs::getRealFileSystem()))) : nullptr; } }; diff --git a/clang/unittests/Tooling/CompilationDatabaseTest.cpp b/clang/unittests/Tooling/CompilationDatabaseTest.cpp --- a/clang/unittests/Tooling/CompilationDatabaseTest.cpp +++ b/clang/unittests/Tooling/CompilationDatabaseTest.cpp @@ -859,5 +859,64 @@ "clang++ --driver-mode=g++ bar.cpp -D bar.cpp"); } +class ExpandResponseFilesTest : public MemDBTest { +protected: + void SetUp() override { + FS = new llvm::vfs::InMemoryFileSystem; + + InnerDir = path(StringRef("inner")); + + llvm::sys::path::append(RspFileName1, InnerDir, "rsp1.rsp"); + addFile(RspFileName1, "-Dflag1"); + + RspFileName2 = path(StringRef("rsp2.rsp")); + addFile(RspFileName2, "-Dflag2 @rsp3.rsp"); + + RspFileName3 = path(StringRef("rsp3.rsp")); + addFile(RspFileName3, "-Dflag3"); + + RspFileName4 = path(StringRef("rsp4.rsp")); + addFile(RspFileName4, "-Dflag4 @rsp4.rsp"); + + llvm::sys::path::append(RspFileName5, InnerDir, "rsp5.rsp"); + addFile(RspFileName5, "-Dflag5 @inner/rsp1.rsp"); + } + + void addFile(StringRef File, StringRef Context) { + ASSERT_TRUE(FS->addFile(File, 0, llvm::MemoryBuffer::getMemBufferCopy(Context))); + } + + std::string getCommand(llvm::StringRef F) { + auto Results = expandResponseFiles(std::make_unique(Entries), + FS) + ->getCompileCommands(path(F)); + if (Results.empty()) { + return "none"; + } + return llvm::join(Results[0].CommandLine, " "); + } + + SmallString<128> InnerDir; + SmallString<128> RspFileName1; + SmallString<128> RspFileName2; + SmallString<128> RspFileName3; + SmallString<128> RspFileName4; + SmallString<128> RspFileName5; + llvm::IntrusiveRefCntPtr FS; + +}; + +TEST_F(ExpandResponseFilesTest, ExpandResponseFiles) { + // clang-format off + add("foo.cpp", "clang", + ("@inner/rsp1.rsp @rsp2.rsp @rsp4.rsp " + "@" + RspFileName1 + " @inner/rsp5.rsp @rsp6.rsp") + .str()); + // clang-format on + EXPECT_EQ(getCommand("foo.cpp"), "clang foo.cpp -D foo.cpp -Dflag1 -Dflag2 " + "-Dflag3 -Dflag4 @rsp4.rsp -Dflag1 " + "-Dflag5 -Dflag1 @rsp6.rsp"); +} + } // end namespace tooling } // end namespace clang