Index: COFF/Driver.h =================================================================== --- COFF/Driver.h +++ COFF/Driver.h @@ -20,6 +20,7 @@ #include "llvm/Object/COFF.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Support/TarWriter.h" #include #include @@ -48,18 +49,21 @@ class ArgParser { public: - // Parses command line options. - llvm::opt::InputArgList parse(llvm::ArrayRef Args); + // Tokenizes a given string and then parses as command line options. + llvm::opt::InputArgList parse(StringRef S, + llvm::cl::TokenizerCallback Tokenizer) { + return parse(tokenize(S, Tokenizer)); + } // Concatenate LINK environment varirable and given arguments and parse them. llvm::opt::InputArgList parseLINK(std::vector Args); - // Tokenizes a given string and then parses as command line options. - llvm::opt::InputArgList parse(StringRef S) { return parse(tokenize(S)); } - private: - std::vector tokenize(StringRef S); - std::vector replaceResponseFiles(std::vector); + // Parses command line options. + llvm::opt::InputArgList parse(llvm::ArrayRef Args); + + std::vector tokenize(StringRef S, + llvm::cl::TokenizerCallback Tokenizer); COFFOptTable Table; }; Index: COFF/Driver.cpp =================================================================== --- COFF/Driver.cpp +++ COFF/Driver.cpp @@ -202,7 +202,8 @@ // specified by /defaultlib. void LinkerDriver::parseDirectives(StringRef S) { ArgParser Parser; - opt::InputArgList Args = Parser.parse(S); + // .drectve is always tokenized using Windows shell rules. + opt::InputArgList Args = Parser.parse(S, cl::TokenizeWindowsCommandLine); for (auto *Arg : Args) { switch (Arg->getOption().getUnaliasedOption().getID()) { Index: COFF/DriverUtils.cpp =================================================================== --- COFF/DriverUtils.cpp +++ COFF/DriverUtils.cpp @@ -38,8 +38,6 @@ using namespace llvm::COFF; using namespace llvm; -using llvm::cl::ExpandResponseFiles; -using llvm::cl::TokenizeWindowsCommandLine; using llvm::sys::Process; namespace lld { @@ -718,20 +716,40 @@ COFFOptTable::COFFOptTable() : OptTable(InfoTable, true) {} -// Parses a given list of options. -opt::InputArgList ArgParser::parse(ArrayRef ArgsArr) { - // First, replace respnose files (@-style options). - std::vector Argv = replaceResponseFiles(ArgsArr); +static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &Args) { + if (auto *Arg = Args.getLastArg(OPT_rsp_quoting)) { + StringRef S = Arg->getValue(); + if (S != "windows" && S != "posix") + error("invalid response file quoting: " + S); + if (S == "windows") + return cl::TokenizeWindowsCommandLine; + return cl::TokenizeGNUCommandLine; + } + // The COFF linker always defaults to Windows quoting. + return cl::TokenizeWindowsCommandLine; +} +// Parses a given list of options. +opt::InputArgList ArgParser::parse(ArrayRef Argv) { // Make InputArgList from string vectors. unsigned MissingIndex; unsigned MissingCount; - opt::InputArgList Args = Table.ParseArgs(Argv, MissingIndex, MissingCount); + SmallVector Vec(Argv.data(), Argv.data() + Argv.size()); + + // We need to get the quoting style for response files before parsing all + // options so we parse here before and ignore all the options but + // --rsp-quoting. + opt::InputArgList Args = Table.ParseArgs(Vec, MissingIndex, MissingCount); + + // Expand response files (arguments in the form of @) + // and then parse the argument again. + cl::ExpandResponseFiles(Saver, getQuotingStyle(Args), Vec); + Args = Table.ParseArgs(Vec, MissingIndex, MissingCount); // Print the real command line if response files are expanded. - if (Args.hasArg(OPT_verbose) && ArgsArr.size() != Argv.size()) { + if (Args.hasArg(OPT_verbose) && Argv.size() != Vec.size()) { std::string Msg = "Command line:"; - for (const char *S : Argv) + for (const char *S : Vec) Msg += " " + std::string(S); message(Msg); } @@ -746,31 +764,32 @@ // link.exe has an interesting feature. If LINK or _LINK_ environment // variables exist, their contents are handled as command line strings. // So you can pass extra arguments using them. -opt::InputArgList ArgParser::parseLINK(std::vector Args) { +opt::InputArgList ArgParser::parseLINK(std::vector Argv) { + // Make InputArgList from string vectors. + unsigned MissingIndex; + unsigned MissingCount; + SmallVector Vec(Argv.data(), Argv.data() + Argv.size()); + + // We need to get the quoting style for response files before parsing + // LINK and _LINK_. + opt::InputArgList Args = Table.ParseArgs(Vec, MissingIndex, MissingCount); + // Concatenate LINK env and command line arguments, and then parse them. if (Optional S = Process::GetEnv("LINK")) { - std::vector V = tokenize(*S); - Args.insert(Args.begin(), V.begin(), V.end()); + std::vector V = tokenize(*S, getQuotingStyle(Args)); + Argv.insert(Argv.begin(), V.begin(), V.end()); } if (Optional S = Process::GetEnv("_LINK_")) { - std::vector V = tokenize(*S); - Args.insert(Args.begin(), V.begin(), V.end()); + std::vector V = tokenize(*S, getQuotingStyle(Args)); + Argv.insert(Argv.begin(), V.begin(), V.end()); } - return parse(Args); + return parse(Argv); } -std::vector ArgParser::tokenize(StringRef S) { +std::vector ArgParser::tokenize(StringRef S, + cl::TokenizerCallback tokenizer) { SmallVector Tokens; - cl::TokenizeWindowsCommandLine(S, Saver, Tokens); - return std::vector(Tokens.begin(), Tokens.end()); -} - -// Creates a new command line by replacing options starting with '@' -// character. '@' is replaced by the file's contents. -std::vector -ArgParser::replaceResponseFiles(std::vector Argv) { - SmallVector Tokens(Argv.data(), Argv.data() + Argv.size()); - ExpandResponseFiles(Saver, TokenizeWindowsCommandLine, Tokens); + tokenizer(S, Saver, Tokens, /*MarkEOLs=*/false); return std::vector(Tokens.begin(), Tokens.end()); } Index: COFF/Options.td =================================================================== --- COFF/Options.td +++ COFF/Options.td @@ -101,6 +101,8 @@ def nopdb : F<"nopdb">, HelpText<"Disable PDB generation for DWARF users">; def nosymtab : F<"nosymtab">; def msvclto : F<"msvclto">; +def rsp_quoting : Joined<["--"], "rsp-quoting=">, + HelpText<"Quoting style for response files, 'windows' (default) or 'posix'">; // Flags for debugging def lldmap : F<"lldmap">; Index: test/COFF/linkenv.test =================================================================== --- test/COFF/linkenv.test +++ test/COFF/linkenv.test @@ -2,3 +2,12 @@ # RUN: env _LINK_=-help lld-link | FileCheck %s CHECK: OVERVIEW: LLVM Linker + +# RUN: env LINK='foo\bar' not lld-link 2>&1 | FileCheck %s --check-prefix=DEFL +DEFL: error: could not open foo\bar + +# RUN: env LINK='foo\bar' not lld-link --rsp-quoting=windows 2>&1 | FileCheck %s --check-prefix=WINL +WINL: error: could not open foo\bar + +# RUN: env LINK='foo\bar' not lld-link --rsp-quoting=posix 2>&1 | FileCheck %s --check-prefix=POSL +POSL: error: could not open foobar Index: test/COFF/responsefile.test =================================================================== --- test/COFF/responsefile.test +++ test/COFF/responsefile.test @@ -3,5 +3,23 @@ # RUN: echo /out:%t.exe /entry:main %t.obj > %t.rsp # RUN: lld-link @%t.rsp /heap:0x3000 # RUN: llvm-readobj -file-headers %t.exe | FileCheck %s - CHECK: SizeOfHeapReserve: 12288 + +# RUN: not lld-link --rsp-quoting=foobar @%t.rsp 2>&1 | \ +# RUN: FileCheck --check-prefix=INVRSP %s +INVRSP: invalid response file quoting: foobar + +# RUN: echo "blah\foo" > %t.rsp +# RUN: not lld-link @%t.rsp 2>&1 | \ +# RUN: FileCheck --check-prefix=DEFRSP %s +DEFRSP: error: could not open blah\foo + +# RUN: echo "blah\foo" > %t.rsp +# RUN: not lld-link --rsp-quoting=windows @%t.rsp 2>&1 | \ +# RUN: FileCheck --check-prefix=WINRSP %s +WINRSP: error: could not open blah\foo + +# RUN: echo "blah\foo" > %t.rsp +# RUN: not lld-link --rsp-quoting=posix @%t.rsp 2>&1 | \ +# RUN: FileCheck --check-prefix=POSRSP %s +POSRSP: error: could not open blahfoo