diff --git a/llvm/test/tools/llvm-libtool-darwin/filelist.test b/llvm/test/tools/llvm-libtool-darwin/filelist.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-libtool-darwin/filelist.test @@ -0,0 +1,94 @@ +## This test checks that the -filelist option works correctly. + +# RUN: yaml2obj %S/Inputs/input1.yaml -o %t-input1.o +# RUN: yaml2obj %S/Inputs/input2.yaml -o %t-input2.o + +## Passing files in a listfile: +# RUN: echo %t-input1.o > %t.files.txt +# RUN: echo %t-input2.o >> %t.files.txt +# RUN: llvm-libtool-darwin -static -o %t.lib -filelist %t.files.txt + +## Check that binaries are present: +# RUN: llvm-ar t %t.lib | \ +# RUN: FileCheck %s --check-prefix=CHECK-NAMES --implicit-check-not={{.}} -DPREFIX=%basename_t.tmp + +# CHECK-NAMES: [[PREFIX]]-input1.o +# CHECK-NAMES-NEXT: [[PREFIX]]-input2.o + +## Check that symbols are present: +# RUN: llvm-nm --print-armap %t.lib | \ +# RUN: FileCheck %s --check-prefix=CHECK-SYMBOLS -DPREFIX=%basename_t.tmp --match-full-lines + +# CHECK-SYMBOLS: Archive map +# CHECK-SYMBOLS-NEXT: _symbol1 in [[PREFIX]]-input1.o +# CHECK-SYMBOLS-NEXT: _symbol2 in [[PREFIX]]-input2.o +# CHECK-SYMBOLS-EMPTY: + +# RUN: rm -rf %t/dirname && mkdir -p %t/dirname +# RUN: rm -f %t-input1.o && rm -f %basename_t.tmp-input1.o +# RUN: yaml2obj %S/Inputs/input1.yaml -o %t/dirname/%basename_t.tmp-input1.o +# RUN: echo %basename_t.tmp-input1.o > %t.files.txt + +## Check that error is thrown on passing just filelist: +# RUN: not llvm-libtool-darwin -static -o %t.lib -filelist %t.files.txt 2>&1 | \ +# RUN: FileCheck %s --check-prefix=FILE-ERROR -DFILE=%basename_t.tmp-input1.o + +# FILE-ERROR: error: '[[FILE]]': {{[nN]}}o such file or directory + +## Passing in dirname: +# RUN: llvm-libtool-darwin -static -o %t.lib -filelist %t.files.txt,%t/dirname +# RUN: llvm-ar t %t.lib | \ +# RUN: FileCheck %s --check-prefix=DIRNAME-NAMES --implicit-check-not={{.}} -DPREFIX=%basename_t.tmp +# RUN: llvm-nm --print-armap %t.lib | \ +# RUN: FileCheck %s --check-prefix=DIRNAME-SYMBOLS -DPREFIX=%basename_t.tmp --match-full-lines + +# DIRNAME-NAMES: [[PREFIX]]-input1.o + +# DIRNAME-SYMBOLS: Archive map +# DIRNAME-SYMBOLS-NEXT: _symbol1 in [[PREFIX]]-input1.o +# DIRNAME-SYMBOLS-EMPTY: + +## Passing both -filelist option and object file as input: +# RUN: llvm-libtool-darwin -static -o %t.lib -filelist %t.files.txt,%t/dirname %t-input2.o +# RUN: llvm-ar t %t.lib | \ +# RUN: FileCheck %s --check-prefix=REVERSE-NAMES --implicit-check-not={{.}} -DPREFIX=%basename_t.tmp +# RUN: llvm-nm --print-armap %t.lib | \ +# RUN: FileCheck %s --check-prefix=REVERSE-SYMBOLS -DPREFIX=%basename_t.tmp --match-full-lines + +# REVERSE-NAMES: [[PREFIX]]-input2.o +# REVERSE-NAMES-NEXT: [[PREFIX]]-input1.o + +# REVERSE-SYMBOLS: Archive map +# REVERSE-SYMBOLS-NEXT: _symbol2 in [[PREFIX]]-input2.o +# REVERSE-SYMBOLS-NEXT: _symbol1 in [[PREFIX]]-input1.o +# REVERSE-SYMBOLS-EMPTY: + +## Check that error is thrown when file in filelist doesn't exist (without dir): +# RUN: echo 'no-such-file' > %t.invalid-list.txt +# RUN: not llvm-libtool-darwin -static -o %t.lib -filelist %t.invalid-list.txt 2>&1 | \ +# RUN: FileCheck %s --check-prefix=FILE-ERROR -DFILE=no-such-file + +## Check that error is thrown when file in filelist doesn't exist (with dir): +# RUN: rm -rf %t/Invalid-Dir && mkdir -p %t/Invalid-Dir +# RUN: echo 'no-such-file' > %t.invalid-list.txt +# RUN: not llvm-libtool-darwin -static -o %t.lib -filelist %t.invalid-list.txt,%t/Invalid-Dir 2>&1 | \ +# RUN: FileCheck %s --check-prefix=FILE-ERROR -DFILE=%t/Invalid-Dir/no-such-file + +## Check that error is thrown when filelist is empty: +# RUN: touch %t.empty-list +# RUN: not llvm-libtool-darwin -static -o %t.lib -filelist %t.empty-list 2>&1 | \ +# RUN: FileCheck %s --check-prefix=EMPTY-ERROR -DFILE=%t.empty-list + +# EMPTY-ERROR: error: file list file: '[[FILE]]' is empty + +## Check that error is thrown when filelist contains a blank line: +# RUN: echo %t-input2.o > %t.blank-line.txt +# RUN: echo -ne "\n" >> %t.blank-line.txt +# RUN: not llvm-libtool-darwin -static -o %t.lib -filelist %t.blank-line.txt 2>&1 | \ +# RUN: FileCheck %s --check-prefix=FILE-ERROR -DFILE='' + +## Check that error is thrown when filelist contains a line with only spaces: +# RUN: echo %t-input2.o > %t.space-line.txt +# RUN: echo -ne " \n" >> %t.space-line.txt +# RUN: not llvm-libtool-darwin -static -o %t.lib -filelist %t.space-line.txt 2>&1 | \ +# RUN: FileCheck %s --check-prefix=FILE-ERROR -DFILE=' ' diff --git a/llvm/test/tools/llvm-libtool-darwin/invalid-input-output-args.test b/llvm/test/tools/llvm-libtool-darwin/invalid-input-output-args.test --- a/llvm/test/tools/llvm-libtool-darwin/invalid-input-output-args.test +++ b/llvm/test/tools/llvm-libtool-darwin/invalid-input-output-args.test @@ -4,7 +4,7 @@ # RUN: not llvm-libtool-darwin -static -o %t.lib 2>&1 | \ # RUN: FileCheck %s --check-prefix=NO-INPUT -# NO-INPUT: Must specify at least 1 positional argument +# NO-INPUT: error: no input files specified ## Missing output file: # RUN: not llvm-libtool-darwin -static %t.input 2>&1 | \ diff --git a/llvm/tools/llvm-libtool-darwin/llvm-libtool-darwin.cpp b/llvm/tools/llvm-libtool-darwin/llvm-libtool-darwin.cpp --- a/llvm/tools/llvm-libtool-darwin/llvm-libtool-darwin.cpp +++ b/llvm/tools/llvm-libtool-darwin/llvm-libtool-darwin.cpp @@ -16,6 +16,7 @@ #include "llvm/Object/ObjectFile.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/InitLLVM.h" +#include "llvm/Support/LineIterator.h" #include "llvm/Support/WithColor.h" using namespace llvm; @@ -29,7 +30,7 @@ static cl::list InputFiles(cl::Positional, cl::desc(""), - cl::OneOrMore, + cl::ZeroOrMore, cl::cat(LibtoolCategory)); enum class Operation { Static }; @@ -41,6 +42,40 @@ "Produce a statically linked library from the input files")), cl::Required, cl::cat(LibtoolCategory)); +static cl::opt + FileList("filelist", + cl::desc("Pass in file containing a list of filenames."), + cl::value_desc("listfile[,dirname]"), cl::cat(LibtoolCategory)); + +static Error processFileList() { + StringRef FileName, DirName; + std::tie(FileName, DirName) = StringRef(FileList).rsplit(","); + + ErrorOr> FileOrErr = + MemoryBuffer::getFileOrSTDIN(FileName, /*FileSize=*/-1, + /*RequiresNullTerminator=*/false); + if (std::error_code EC = FileOrErr.getError()) + return createFileError(FileName, errorCodeToError(EC)); + const MemoryBuffer &Ref = *FileOrErr.get(); + + line_iterator I(Ref, /*SkipBlanks=*/false), E; + if (I == E) + return createStringError(std::errc::invalid_argument, + "file list file: '%s' is empty", + FileName.str().c_str()); + for (; I != E; ++I) { + StringRef Line = *I; + SmallString<128> Path; + if (!DirName.empty()) + sys::path::append(Path, DirName, Line); + else + sys::path::append(Path, Line); + InputFiles.push_back(Twine(Path).str()); + } + + return Error::success(); +} + static Error verifyMachOObject(const NewArchiveMember &Member) { auto MBRef = Member.Buf->getMemBufferRef(); Expected> ObjOrErr = @@ -135,6 +170,19 @@ InitLLVM X(Argc, Argv); cl::HideUnrelatedOptions({&LibtoolCategory, &ColorCategory}); cl::ParseCommandLineOptions(Argc, Argv, "llvm-libtool-darwin\n"); + if (!FileList.empty()) { + if (Error E = processFileList()) { + WithColor::defaultErrorHandler(std::move(E)); + exit(EXIT_FAILURE); + } + } + + if (InputFiles.empty()) { + Error E = createStringError(std::errc::invalid_argument, + "no input files specified"); + WithColor::defaultErrorHandler(std::move(E)); + exit(EXIT_FAILURE); + } switch (LibraryOperation) { case Operation::Static: