Skip to content

Commit

Permalink
[llvm-ar] Fix llvm-ar response file reading on Windows
Browse files Browse the repository at this point in the history
Response files where not being correctly read on Windows, this change
fixes the issue and adds some tests.

Differential Revision: https://reviews.llvm.org/D69665
  • Loading branch information
gbreynoo committed Mar 3, 2020
1 parent 859bcf4 commit 62fa333
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 47 deletions.
11 changes: 11 additions & 0 deletions llvm/test/tools/llvm-ar/response-utf8.test
@@ -0,0 +1,11 @@
## Check that response files can cope with non-ascii characters.

# RUN: echo 'contents' > %t-£.txt

# RUN: rm -f %t-£.a
# RUN: echo 'r %t-£.a %t-£.txt' > %t-replace.txt
# RUN: llvm-ar @%t-replace.txt

# RUN: echo 'p %t-£.a %t-£.txt' > %t-print.txt
# RUN: llvm-ar @%t-print.txt | FileCheck %s
# CHECK: contents
34 changes: 34 additions & 0 deletions llvm/test/tools/llvm-ar/response.test
@@ -0,0 +1,34 @@
## llvm-ar should be able to consume response files.

# RUN: echo 'contents' > %t.txt
# RUN: echo 'rc %t1.a %t.txt' > %t.response1.txt
# RUN: llvm-ar @%t.response1.txt
# RUN: llvm-ar p %t1.a | FileCheck %s --check-prefix=CONTENTS

## Quotes and Spaces.
# RUN: echo 'contents' > '%t space.txt'
## Python is used here to ensure the quotes are written to the response file
# RUN: %python -c "import os; open(r'%t.response2.txt', 'w').write(r'%t2.a \"%t space.txt\"'+ '\n')"
# RUN: llvm-ar rc @%t.response2.txt
# RUN: llvm-ar p %t2.a | FileCheck %s --check-prefix=CONTENTS

## Arguments after the response file.
# RUN: echo 'rc %t3.a' > %t.response3.txt
# RUN: llvm-ar @%t.response3.txt %t.txt
# RUN: llvm-ar p %t3.a | FileCheck %s --check-prefix=CONTENTS

# CONTENTS: contents

## rsp-quoting
# RUN: not llvm-ar --rsp-quoting=foobar @%t.response1.txt 2>&1 | \
# RUN: FileCheck %s --check-prefix=ERROR
# ERROR: Invalid response file quoting style foobar

# RUN: echo -e 'rc %/t.a blah\\foo' > %t-rsp.txt
# RUN: not llvm-ar --rsp-quoting=windows @%t-rsp.txt 2>&1 | \
# RUN: FileCheck %s --check-prefix=WIN
# WIN: error: blah\foo: {{[Nn]}}o such file or directory

# RUN: not llvm-ar -rsp-quoting posix @%t-rsp.txt 2>&1 | \
# RUN: FileCheck %s --check-prefix=POSIX
# POSIX: error: blahfoo: {{[Nn]}}o such file or directory
141 changes: 94 additions & 47 deletions llvm/tools/llvm-ar/llvm-ar.cpp
Expand Up @@ -83,6 +83,9 @@ USAGE: llvm-ar [options] [-]<operation>[modifiers] [relpos] [count] <archive> [f
=bsd - bsd
--plugin=<string> - ignored for compatibility
-h --help - display this help and exit
--rsp-quoting - quoting style for response files
=posix - posix
=windows - windows
--version - print the version and exit
@<file> - read options from <file>
Expand Down Expand Up @@ -1096,61 +1099,105 @@ static bool handleGenericOption(StringRef arg) {
return false;
}

static const char *matchFlagWithArg(StringRef Expected,
ArrayRef<const char *>::iterator &ArgIt,
ArrayRef<const char *> Args) {
StringRef Arg = *ArgIt;

if (Arg.startswith("--"))
Arg = Arg.substr(2);
else if (Arg.startswith("-"))
Arg = Arg.substr(1);

size_t len = Expected.size();
if (Arg == Expected) {
if (++ArgIt == Args.end())
fail(std::string(Expected) + " requires an argument");

return *ArgIt;
}
if (Arg.startswith(Expected) && Arg.size() > len && Arg[len] == '=')
return Arg.data() + len + 1;

return nullptr;
}

static cl::TokenizerCallback getRspQuoting(ArrayRef<const char *> ArgsArr) {
cl::TokenizerCallback Ret =
Triple(sys::getProcessTriple()).getOS() == Triple::Win32
? cl::TokenizeWindowsCommandLine
: cl::TokenizeGNUCommandLine;

for (ArrayRef<const char *>::iterator ArgIt = ArgsArr.begin();
ArgIt != ArgsArr.end(); ++ArgIt) {
if (const char *Match = matchFlagWithArg("rsp-quoting", ArgIt, ArgsArr)) {
StringRef MatchRef = Match;
if (MatchRef == "posix")
Ret = cl::TokenizeGNUCommandLine;
else if (MatchRef == "windows")
Ret = cl::TokenizeWindowsCommandLine;
else
fail(std::string("Invalid response file quoting style ") + Match);
}
}

return Ret;
}

static int ar_main(int argc, char **argv) {
SmallVector<const char *, 0> Argv(argv, argv + argc);
SmallVector<const char *, 0> Argv(argv + 1, argv + argc);
StringSaver Saver(Alloc);
cl::ExpandResponseFiles(Saver, cl::TokenizeGNUCommandLine, Argv);
for (size_t i = 1; i < Argv.size(); ++i) {
StringRef Arg = Argv[i];
const char *match = nullptr;
auto MatchFlagWithArg = [&](const char *expected) {
size_t len = strlen(expected);
if (Arg == expected) {
if (++i >= Argv.size())
fail(std::string(expected) + " requires an argument");
match = Argv[i];
return true;
}
if (Arg.startswith(expected) && Arg.size() > len && Arg[len] == '=') {
match = Arg.data() + len + 1;
return true;
}
return false;
};
if (handleGenericOption(Argv[i]))

cl::ExpandResponseFiles(Saver, getRspQuoting(makeArrayRef(argv, argc)), Argv);

ArrayRef<const char *> ArgsArr = makeArrayRef(argv, argc);

for (ArrayRef<const char *>::iterator ArgIt = Argv.begin();
ArgIt != Argv.end(); ++ArgIt) {
const char *Match = nullptr;

if (handleGenericOption(*ArgIt))
return 0;
if (Arg == "--") {
for (; i < Argv.size(); ++i)
PositionalArgs.push_back(Argv[i]);
if (strcmp(*ArgIt, "--") == 0) {
++ArgIt;
for (; ArgIt != Argv.end(); ++ArgIt)
PositionalArgs.push_back(*ArgIt);
break;
}
if (Arg[0] == '-') {
if (Arg.startswith("--"))
Arg = Argv[i] + 2;

if (*ArgIt[0] != '-') {
if (Options.empty())
Options += *ArgIt;
else
Arg = Argv[i] + 1;
if (Arg == "M") {
MRI = true;
} else if (MatchFlagWithArg("format")) {
FormatType = StringSwitch<Format>(match)
.Case("default", Default)
.Case("gnu", GNU)
.Case("darwin", DARWIN)
.Case("bsd", BSD)
.Default(Unknown);
if (FormatType == Unknown)
fail(std::string("Invalid format ") + match);
} else if (MatchFlagWithArg("plugin")) {
// Ignored.
} else {
Options += Argv[i] + 1;
}
} else if (Options.empty()) {
Options += Argv[i];
} else {
PositionalArgs.push_back(Argv[i]);
PositionalArgs.push_back(*ArgIt);
continue;
}

if (strcmp(*ArgIt, "-M") == 0) {
MRI = true;
continue;
}

Match = matchFlagWithArg("format", ArgIt, Argv);
if (Match) {
FormatType = StringSwitch<Format>(Match)
.Case("default", Default)
.Case("gnu", GNU)
.Case("darwin", DARWIN)
.Case("bsd", BSD)
.Default(Unknown);
if (FormatType == Unknown)
fail(std::string("Invalid format ") + Match);
continue;
}

if (matchFlagWithArg("plugin", ArgIt, Argv) ||
matchFlagWithArg("rsp-quoting", ArgIt, Argv))
continue;

Options += *ArgIt + 1;
}

ArchiveOperation Operation = parseCommandLine();
return performOperation(Operation, nullptr);
}
Expand Down

0 comments on commit 62fa333

Please sign in to comment.