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(