diff --git a/llvm/cmake/modules/GetErrcMessages.cmake b/llvm/cmake/modules/GetErrcMessages.cmake
--- a/llvm/cmake/modules/GetErrcMessages.cmake
+++ b/llvm/cmake/modules/GetErrcMessages.cmake
@@ -23,9 +23,9 @@
}
int main() {
- std::cout << getMessageFor(ENOENT) << ';' << getMessageFor(EISDIR);
- std::cout << ';' << getMessageFor(EINVAL) << ';' << getMessageFor(EACCES);
- return 0;
+ std::cout << getMessageFor(ENOENT) << ';' << getMessageFor(ENOTDIR) << ';' <<
+ getMessageFor(EISDIR) << ';' << getMessageFor(EINVAL) << ';' << getMessageFor(EACCES);
+ return 0;
}
")
diff --git a/llvm/docs/CommandGuide/llvm-ar.rst b/llvm/docs/CommandGuide/llvm-ar.rst
--- a/llvm/docs/CommandGuide/llvm-ar.rst
+++ b/llvm/docs/CommandGuide/llvm-ar.rst
@@ -274,6 +274,11 @@
This option allows for MRI scripts to be read through the standard input
stream. No other options are compatible with this option.
+.. option:: --output=
+
+ Specify a directory where archive members should be extracted to. By default the
+ current working directory is used.
+
.. option:: --rsp-quoting=
This option selects the quoting style ```` for response files, either
``posix`` or ``windows``. The default when on Windows is ``windows``, otherwise the
diff --git a/llvm/test/tools/llvm-ar/absolute-paths.test b/llvm/test/tools/llvm-ar/absolute-paths.test
--- a/llvm/test/tools/llvm-ar/absolute-paths.test
+++ b/llvm/test/tools/llvm-ar/absolute-paths.test
@@ -26,3 +26,7 @@
RUN: llvm-ar x %S/Inputs/absolute-paths.lib
RUN: llvm-nm a.o | FileCheck %s --check-prefix=CHECK-A
RUN: llvm-nm b.o | FileCheck %s --check-prefix=CHECK-B
+
+RUN: mkdir dir
+RUN: llvm-ar x %S/Inputs/absolute-paths.lib --output=dir/
+RUN: cmp a.o dir/a.o
diff --git a/llvm/test/tools/llvm-ar/extract.test b/llvm/test/tools/llvm-ar/extract.test
--- a/llvm/test/tools/llvm-ar/extract.test
+++ b/llvm/test/tools/llvm-ar/extract.test
@@ -34,3 +34,20 @@
# RUN: llvm-ar x %t/archive.a 2>&1 | count 0
# RUN: diff %t/a.txt %t/extracted/a.txt
# RUN: diff %t/b.txt %t/extracted/b.txt
+
+## --output specifies the directory to extract archive members to. `1/2` is created by llvm-ar.
+# RUN: llvm-ar --output=1/2 x %t/archive.a
+# RUN: diff %t/a.txt %t/extracted/1/2/a.txt
+# RUN: diff %t/b.txt %t/extracted/1/2/b.txt
+
+## --output can be used with an absolute path and can be specified elsewhere on the command line.
+# RUN: rm 1/2/a.txt
+# RUN: llvm-ar xv %t/archive.a a.txt --output=%t/extracted/1/2/../2 | FileCheck %s --check-prefix=OUTPUT-A
+# RUN: diff %t/a.txt %t/extracted/1/2/a.txt
+# OUTPUT-A: x - {{.*}}extracted{{.*}}a.txt
+
+# RUN: not llvm-ar x --output=%t/a.txt %t/archive.a b.txt 2>&1 | FileCheck %s --check-prefix=OUTPUT-IS-FILE -DMSG=%errc_ENOTDIR
+# OUTPUT-IS-FILE: error: b.txt: [[MSG]]
+
+# RUN: not llvm-ar rc --output=1/2 %t/archive.a %t/a.txt 2>&1 | FileCheck %s --check-prefix=OUTPUT-ERR
+# OUTPUT-ERR: error: --output is only applicable to the 'x' operation
diff --git a/llvm/tools/llvm-ar/llvm-ar.cpp b/llvm/tools/llvm-ar/llvm-ar.cpp
--- a/llvm/tools/llvm-ar/llvm-ar.cpp
+++ b/llvm/tools/llvm-ar/llvm-ar.cpp
@@ -85,6 +85,7 @@
=aix - aix (big archive)
--plugin= - ignored for compatibility
-h --help - display this help and exit
+ --output - the directory to extract archive members to
--rsp-quoting - quoting style for response files
=posix - posix
=windows - windows
@@ -237,6 +238,9 @@
// command line.
static std::string ArchiveName;
+// Output directory specified by --output.
+static std::string OutputDir;
+
static std::vector> ArchiveBuffers;
static std::vector> Archives;
@@ -449,6 +453,8 @@
"operations");
if (OriginalDates && Operation != Extract)
badUsage("the 'o' modifier is only applicable to the 'x' operation");
+ if (!OutputDir.empty() && Operation != Extract)
+ badUsage("--output is only applicable to the 'x' operation");
if (OnlyUpdate && Operation != ReplaceOrInsert)
badUsage("the 'u' modifier is only applicable to the 'r' operation");
if (AddLibrary && Operation != QuickAppend)
@@ -554,7 +560,16 @@
failIfError(ModeOrErr.takeError());
sys::fs::perms Mode = ModeOrErr.get();
- llvm::StringRef outputFilePath = sys::path::filename(Name);
+ StringRef outputFilePath;
+ SmallString<128> path;
+ if (OutputDir.empty()) {
+ outputFilePath = sys::path::filename(Name);
+ } else {
+ failIfError(sys::fs::create_directories(OutputDir));
+ sys::path::append(path, OutputDir, sys::path::filename(Name));
+ outputFilePath = path.str();
+ }
+
if (Verbose)
outs() << "x - " << outputFilePath << '\n';
@@ -1224,6 +1239,11 @@
continue;
}
+ if ((Match = matchFlagWithArg("output", ArgIt, Argv))) {
+ OutputDir = Match;
+ continue;
+ }
+
if (matchFlagWithArg("plugin", ArgIt, Argv) ||
matchFlagWithArg("rsp-quoting", ArgIt, Argv))
continue;
diff --git a/llvm/utils/lit/lit/llvm/config.py b/llvm/utils/lit/lit/llvm/config.py
--- a/llvm/utils/lit/lit/llvm/config.py
+++ b/llvm/utils/lit/lit/llvm/config.py
@@ -372,15 +372,17 @@
# Python's strerror may not supply the same message
# as C++ std::error_code. One example of such a platform is
# Visual Studio. errc_messages may be supplied which contains the error
- # messages for ENOENT, EISDIR, EINVAL and EACCES as a semi colon
- # separated string. LLVM testsuites can use get_errc_messages in cmake
- # to automatically get the messages and pass them into lit.
+ # messages for ENOENT, ENOTDIR, EISDIR, EINVAL and EACCES as a semi
+ # colon separated string. LLVM testsuites can use get_errc_messages in
+ # cmake to automatically get the messages and pass them into lit.
errc_messages = getattr(self.config, 'errc_messages', '')
if len(errc_messages) != 0:
- (errc_enoent, errc_eisdir,
+ (errc_enoent, errc_enotdir, errc_eisdir,
errc_einval, errc_eacces) = errc_messages.split(';')
self.config.substitutions.append(
('%errc_ENOENT', '\'' + errc_enoent + '\''))
+ self.config.substitutions.append(
+ ('%errc_ENOTDIR', '\'' + errc_enotdir + '\''))
self.config.substitutions.append(
('%errc_EISDIR', '\'' + errc_eisdir + '\''))
self.config.substitutions.append(
@@ -390,6 +392,8 @@
else:
self.config.substitutions.append(
('%errc_ENOENT', '\'' + os.strerror(errno.ENOENT) + '\''))
+ self.config.substitutions.append(
+ ('%errc_ENOTDIR', '\'' + os.strerror(errno.ENOTDIR) + '\''))
self.config.substitutions.append(
('%errc_EISDIR', '\'' + os.strerror(errno.EISDIR) + '\''))
self.config.substitutions.append(