sepavloff created this revision.
sepavloff added a reviewer: rnk.
sepavloff added a subscriber: cfe-commits.

If a response file included by construct @file itself includes a response file
and that file is specified by relative file name, resolve the name relative
to the including file.  With this behavior a set of related response files may
be kept together and reference each other with short position-independent names.
This is convenient for implementation of configuration files.

https://reviews.llvm.org/D24917

Files:
  lib/Support/CommandLine.cpp
  unittests/Support/CommandLineTest.cpp

Index: unittests/Support/CommandLineTest.cpp
===================================================================
--- unittests/Support/CommandLineTest.cpp
+++ 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>
 
@@ -476,4 +480,64 @@
   EXPECT_FALSE(cl::ParseCommandLineOptions(3, args2, nullptr, true));
 }
 
+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);
+  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);
+}
+
 }  // anonymous namespace
Index: lib/Support/CommandLine.cpp
===================================================================
--- lib/Support/CommandLine.cpp
+++ lib/Support/CommandLine.cpp
@@ -865,7 +865,7 @@
   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) {
@@ -893,6 +893,25 @@
   // Tokenize the contents into NewArgv.
   Tokenizer(Str, Saver, NewArgv, MarkEOLs);
 
+  // Scan expanded arguments. If any of them is itself an expansion of a
+  // response file and that file was specified by relative name, replace the
+  // response file name with the full path obtained by resolution of the
+  // specified name relative to the path of current file.
+  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());
+        }
+      }
+    }
+
   return true;
 }
 
@@ -924,8 +943,6 @@
 
     // 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)) {
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to