diff options
| -rw-r--r-- | llvm/include/llvm/Support/CommandLine.h | 4 | ||||
| -rw-r--r-- | llvm/lib/Support/CommandLine.cpp | 29 | ||||
| -rw-r--r-- | llvm/unittests/Support/CommandLineTest.cpp | 65 |
3 files changed, 91 insertions, 7 deletions
diff --git a/llvm/include/llvm/Support/CommandLine.h b/llvm/include/llvm/Support/CommandLine.h index 692e16df041..fc675930760 100644 --- a/llvm/include/llvm/Support/CommandLine.h +++ b/llvm/include/llvm/Support/CommandLine.h @@ -1803,10 +1803,12 @@ typedef void (*TokenizerCallback)(StringRef Source, StringSaver &Saver, /// \param [in,out] Argv Command line into which to expand response files. /// \param [in] MarkEOLs Mark end of lines and the end of the response file /// with nullptrs in the Argv vector. +/// \param [in] RelativeNames true if names of nested response files must be +/// resolved relative to including file. /// \return true if all @files were expanded successfully or there were none. bool ExpandResponseFiles(StringSaver &Saver, TokenizerCallback Tokenizer, SmallVectorImpl<const char *> &Argv, - bool MarkEOLs = false); + bool MarkEOLs = false, bool RelativeNames = false); /// \brief Mark all options not part of this category as cl::ReallyHidden. /// diff --git a/llvm/lib/Support/CommandLine.cpp b/llvm/lib/Support/CommandLine.cpp index 07114ddcb8f..dccb5d6962f 100644 --- a/llvm/lib/Support/CommandLine.cpp +++ b/llvm/lib/Support/CommandLine.cpp @@ -871,10 +871,10 @@ static bool hasUTF8ByteOrderMark(ArrayRef<char> S) { return (S.size() >= 3 && S[0] == '\xef' && S[1] == '\xbb' && S[2] == '\xbf'); } -static bool ExpandResponseFile(const char *FName, StringSaver &Saver, +static bool ExpandResponseFile(StringRef FName, StringSaver &Saver, TokenizerCallback Tokenizer, SmallVectorImpl<const char *> &NewArgv, - bool MarkEOLs = false) { + bool MarkEOLs, bool RelativeNames) { ErrorOr<std::unique_ptr<MemoryBuffer>> MemBufOrErr = MemoryBuffer::getFile(FName); if (!MemBufOrErr) @@ -899,6 +899,25 @@ static bool ExpandResponseFile(const char *FName, StringSaver &Saver, // Tokenize the contents into NewArgv. Tokenizer(Str, Saver, NewArgv, MarkEOLs); + // If names of nested response files should be resolved relative to including + // file, replace the included response file names with their full paths + // obtained by required resolution. + if (RelativeNames) + for (unsigned I = 0; I < NewArgv.size(); ++I) + if (NewArgv[I]) { + StringRef Arg = NewArgv[I]; + if (Arg.front() == '@') { + StringRef FileName = Arg.drop_front(); + if (llvm::sys::path::is_relative(FileName)) { + SmallString<128> ResponseFile; + ResponseFile.append(1, '@'); + llvm::sys::path::append( + ResponseFile, llvm::sys::path::parent_path(FName), FileName); + NewArgv[I] = Saver.save(ResponseFile.c_str()).data(); + } + } + } + return true; } @@ -906,7 +925,7 @@ static bool ExpandResponseFile(const char *FName, StringSaver &Saver, /// StringSaver and tokenization strategy. bool cl::ExpandResponseFiles(StringSaver &Saver, TokenizerCallback Tokenizer, SmallVectorImpl<const char *> &Argv, - bool MarkEOLs) { + bool MarkEOLs, bool RelativeNames) { unsigned RspFiles = 0; bool AllExpanded = true; @@ -930,11 +949,9 @@ bool cl::ExpandResponseFiles(StringSaver &Saver, TokenizerCallback Tokenizer, // Replace this response file argument with the tokenization of its // contents. Nested response files are expanded in subsequent iterations. - // FIXME: If a nested response file uses a relative path, is it relative to - // the cwd of the process or the response file? SmallVector<const char *, 0> ExpandedArgv; if (!ExpandResponseFile(Arg + 1, Saver, Tokenizer, ExpandedArgv, - MarkEOLs)) { + MarkEOLs, RelativeNames)) { // We couldn't read this file, so we leave it in the argument stream and // move on. AllExpanded = false; diff --git a/llvm/unittests/Support/CommandLineTest.cpp b/llvm/unittests/Support/CommandLineTest.cpp index 72c9e32a60d..e0613250d2a 100644 --- a/llvm/unittests/Support/CommandLineTest.cpp +++ b/llvm/unittests/Support/CommandLineTest.cpp @@ -7,11 +7,15 @@ // //===----------------------------------------------------------------------===// +#include "llvm/ADT/SmallString.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Config/config.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" #include "llvm/Support/StringSaver.h" #include "gtest/gtest.h" +#include <fstream> #include <stdlib.h> #include <string> @@ -505,4 +509,65 @@ TEST(CommandLineTest, GetRegisteredSubcommands) { } } +TEST(CommandLineTest, ResponseFiles) { + llvm::SmallString<128> TestDir; + std::error_code EC = + llvm::sys::fs::createUniqueDirectory("unittest", TestDir); + EXPECT_TRUE(!EC); + + // Create included response file of first level. + llvm::SmallString<128> IncludedFileName; + llvm::sys::path::append(IncludedFileName, TestDir, "resp1"); + std::ofstream IncludedFile(IncludedFileName.c_str()); + EXPECT_TRUE(IncludedFile.is_open()); + IncludedFile << "-option_1 -option_2\n" + "@incdir/resp2\n" + "-option_3=abcd\n"; + IncludedFile.close(); + + // Directory for included file. + llvm::SmallString<128> IncDir; + llvm::sys::path::append(IncDir, TestDir, "incdir"); + EC = llvm::sys::fs::create_directory(IncDir); + EXPECT_TRUE(!EC); + + // Create included response file of second level. + llvm::SmallString<128> IncludedFileName2; + llvm::sys::path::append(IncludedFileName2, IncDir, "resp2"); + std::ofstream IncludedFile2(IncludedFileName2.c_str()); + EXPECT_TRUE(IncludedFile2.is_open()); + IncludedFile2 << "-option_21 -option_22\n"; + IncludedFile2 << "-option_23=abcd\n"; + IncludedFile2.close(); + + // Prepare 'file' with reference to response file. + SmallString<128> IncRef; + IncRef.append(1, '@'); + IncRef.append(IncludedFileName.c_str()); + llvm::SmallVector<const char *, 4> Argv = + { "test/test", "-flag_1", IncRef.c_str(), "-flag_2" }; + + // Expand response files. + llvm::BumpPtrAllocator A; + llvm::StringSaver Saver(A); + bool Res = llvm::cl::ExpandResponseFiles( + Saver, llvm::cl::TokenizeGNUCommandLine, Argv, false, true); + EXPECT_TRUE(Res); + EXPECT_EQ(Argv.size(), 9); + EXPECT_STREQ(Argv[0], "test/test"); + EXPECT_STREQ(Argv[1], "-flag_1"); + EXPECT_STREQ(Argv[2], "-option_1"); + EXPECT_STREQ(Argv[3], "-option_2"); + EXPECT_STREQ(Argv[4], "-option_21"); + EXPECT_STREQ(Argv[5], "-option_22"); + EXPECT_STREQ(Argv[6], "-option_23=abcd"); + EXPECT_STREQ(Argv[7], "-option_3=abcd"); + EXPECT_STREQ(Argv[8], "-flag_2"); + + llvm::sys::fs::remove(IncludedFileName2); + llvm::sys::fs::remove(IncDir); + llvm::sys::fs::remove(IncludedFileName); + llvm::sys::fs::remove(TestDir); +} + } // anonymous namespace |

