diff --git a/llvm/docs/CommandGuide/llvm-libtool-darwin.rst b/llvm/docs/CommandGuide/llvm-libtool-darwin.rst --- a/llvm/docs/CommandGuide/llvm-libtool-darwin.rst +++ b/llvm/docs/CommandGuide/llvm-libtool-darwin.rst @@ -50,6 +50,20 @@ Show help and usage for this command without grouping the options into categories. +.. option:: -l + + Searches for the library libx.a in the library search path. If the string `` + ends with '.o', then the library 'x' is searched for without prepending 'lib' + or appending '.a'. If the library is found, it is added to the list of input + files. Otherwise, an error is raised. + +.. option:: -L + + Adds `` to the list of directories in which to search for libraries. The + directories are searched in the order in which they are specified with + :option:`-L` and before the default search path. The default search path + includes directories `/lib/`, `/usr/lib` and `/usr/local/lib`. + .. option:: -o Specify the output file name. Must be specified exactly once. diff --git a/llvm/test/tools/llvm-libtool-darwin/L-and-l.test b/llvm/test/tools/llvm-libtool-darwin/L-and-l.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-libtool-darwin/L-and-l.test @@ -0,0 +1,112 @@ +## This test checks that -l and -L options work correctly. + +# RUN: yaml2obj %S/Inputs/input1.yaml -o %t-input1.o +# RUN: yaml2obj %S/Inputs/input2.yaml -o %t-input2.o + +## Check that the library is recognised when it ends with '.o': +# RUN: llvm-libtool-darwin -static -o %t.lib -l%basename_t.tmp-input1.o -l%basename_t.tmp-input2.o -L%T +# RUN: llvm-ar t %t.lib | \ +# RUN: FileCheck %s --check-prefix=CHECK-NAMES --implicit-check-not={{.}} -DPREFIX=%basename_t.tmp +# RUN: llvm-nm --print-armap %t.lib | \ +# RUN: FileCheck %s --check-prefix=CHECK-SYMBOLS -DPREFIX=%basename_t.tmp --match-full-lines + +# CHECK-NAMES: [[PREFIX]]-input1.o +# CHECK-NAMES-NEXT: [[PREFIX]]-input2.o + +# CHECK-SYMBOLS: Archive map +# CHECK-SYMBOLS-NEXT: _symbol1 in [[PREFIX]]-input1.o +# CHECK-SYMBOLS-NEXT: _symbol2 in [[PREFIX]]-input2.o +# CHECK-SYMBOLS-EMPTY: + +## Check that the library is recognised when prepended with 'lib' and appended with '.a': +# RUN: rm -rf %t/dirname && mkdir -p %t/dirname +# RUN: llvm-ar cr %t/dirname/libsingle-archive.a %t-input2.o + +# RUN: llvm-libtool-darwin -static -o %t.lib -lsingle-archive -L%t/dirname +# RUN: llvm-ar t %t.lib | \ +# RUN: FileCheck %s --check-prefix=SINGLE-NAMES --implicit-check-not={{.}} -DPREFIX=%basename_t.tmp +# RUN: llvm-nm --print-armap %t.lib | \ +# RUN: FileCheck %s --check-prefix=SINGLE-SYMBOLS -DPREFIX=%basename_t.tmp --match-full-lines + +# SINGLE-NAMES: [[PREFIX]]-input2.o + +# SINGLE-SYMBOLS: Archive map +# SINGLE-SYMBOLS-NEXT: _symbol2 in [[PREFIX]]-input2.o +# SINGLE-SYMBOLS-EMPTY: + +## -l and -L option specified multiple times: +# RUN: rm -rf %t/otherDirname && mkdir -p %t/otherDirname +# RUN: llvm-ar cr %t/otherDirname/libother-single-archive.a %t-input1.o + +# RUN: llvm-libtool-darwin -static -o %t.lib -lsingle-archive -lother-single-archive -L%t/dirname -L%t/otherDirname +# RUN: llvm-ar t %t.lib | \ +# RUN: FileCheck %s --check-prefix=OTHER-NAMES --implicit-check-not={{.}} -DPREFIX=%basename_t.tmp +# RUN: llvm-nm --print-armap %t.lib | \ +# RUN: FileCheck %s --check-prefix=OTHER-SYMBOLS -DPREFIX=%basename_t.tmp --match-full-lines + +# OTHER-NAMES: [[PREFIX]]-input2.o +# OTHER-NAMES-NEXT: [[PREFIX]]-input1.o + +# OTHER-SYMBOLS: Archive map +# OTHER-SYMBOLS-NEXT: _symbol2 in [[PREFIX]]-input2.o +# OTHER-SYMBOLS-NEXT: _symbol1 in [[PREFIX]]-input1.o +# OTHER-SYMBOLS-EMPTY: + +## Check that if multiple directories specified with -L have the same named file +## in them, the file from the first directory is selected. +# RUN: llvm-ar cr %t/dirname/libmultiple-dirs.a %t-input1.o +# RUN: llvm-ar cr %t/otherDirname/libmultiple-dirs.a %t-input2.o + +# RUN: llvm-libtool-darwin -static -o %t.lib -lmultiple-dirs -L%t/dirname -L%t/otherDirname +# RUN: llvm-ar t %t.lib | \ +# RUN: FileCheck %s --check-prefix=MULTIPLE-DIR-NAMES --implicit-check-not={{.}} -DPREFIX=%basename_t.tmp +# RUN: llvm-nm --print-armap %t.lib | \ +# RUN: FileCheck %s --check-prefix=MULTIPLE-DIR-SYMBOLS -DPREFIX=%basename_t.tmp --match-full-lines + +# MULTIPLE-DIR-NAMES: [[PREFIX]]-input1.o + +# MULTIPLE-DIR-SYMBOLS: Archive map +# MULTIPLE-DIR-SYMBOLS-NEXT: _symbol1 in [[PREFIX]]-input1.o +# MULTIPLE-DIR-SYMBOLS-EMPTY: + +## -l option combined with an input file: +# RUN: llvm-libtool-darwin -static -o %t.lib %t-input1.o -lsingle-archive -L%t/dirname +# RUN: llvm-ar t %t.lib | \ +# RUN: FileCheck %s --check-prefix=CHECK-NAMES --implicit-check-not={{.}} -DPREFIX=%basename_t.tmp +# RUN: llvm-nm --print-armap %t.lib | \ +# RUN: FileCheck %s --check-prefix=CHECK-SYMBOLS -DPREFIX=%basename_t.tmp --match-full-lines + +## Specify same -l option twice: +## cctools' libtool raises a warning in this case. +## The warning is not yet implemented for llvm-libtool-darwin. +# RUN: llvm-libtool-darwin -static -o %t.lib -l%basename_t.tmp-input2.o -l%basename_t.tmp-input2.o -L%T +# RUN: llvm-ar t %t.lib | \ +# RUN: FileCheck %s --check-prefix=DOUBLE-NAMES --implicit-check-not={{.}} -DPREFIX=%basename_t.tmp +# RUN: llvm-nm --print-armap %t.lib | \ +# RUN: FileCheck %s --check-prefix=DOUBLE-SYMBOLS -DPREFIX=%basename_t.tmp --match-full-lines + +# DOUBLE-NAMES: [[PREFIX]]-input2.o +# DOUBLE-NAMES-NEXT: [[PREFIX]]-input2.o + +# DOUBLE-SYMBOLS: Archive map +# DOUBLE-SYMBOLS-NEXT: _symbol2 in [[PREFIX]]-input2.o +# DOUBLE-SYMBOLS-NEXT: _symbol2 in [[PREFIX]]-input2.o +# DOUBLE-SYMBOLS-EMPTY: + +## Check that an error is thrown when the input library cannot be found: +# RUN: not llvm-libtool-darwin -static -o %t.lib -lfile-will-not-exist.o 2>&1 | \ +# RUN: FileCheck %s --check-prefix=NOT-FOUND -DFILE=file-will-not-exist.o + +# NOT-FOUND: error: cannot locate file for '[[FILE]]' + +## Check that an error is thrown when the input library is not valid: +# RUN: touch %t/dirname/not-valid.o +# RUN: not llvm-libtool-darwin -static -o %t.lib -lnot-valid.o -L%t/dirname 2>&1 | \ +# RUN: FileCheck %s --check-prefix=NOT-VALID -DFILE=not-valid.o + +# NOT-VALID: error: '[[FILE]]': The file was not recognized as a valid object file + +## Check that 'lib' and '.a' are not added to a file ending in '.o': +# RUN: llvm-ar cr %t/dirname/libfoo.o.a %t-input1.o +# RUN: not llvm-libtool-darwin -static -o %t.lib -lfoo.o 2>&1 | \ +# RUN: FileCheck %s --check-prefix=NOT-FOUND -DFILE=foo.o 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 @@ -66,12 +66,69 @@ cl::desc("Pass in file containing a list of filenames"), cl::value_desc("listfile[,dirname]"), cl::cat(LibtoolCategory)); +static cl::list Libraries( + "l", + cl::desc( + "l searches for the library libx.a in the library search path. If" + " the string 'x' ends with '.o', then the library 'x' is searched for" + " without prepending 'lib' or appending '.a'"), + cl::ZeroOrMore, cl::Prefix, cl::cat(LibtoolCategory)); + +static cl::list LibrarySearchDirs( + "L", + cl::desc( + "L adds to the list of directories in which to search for" + " libraries"), + cl::ZeroOrMore, cl::Prefix, cl::cat(LibtoolCategory)); + +static const std::array StandardSearchDirs{ + "/lib", + "/usr/lib", + "/usr/local/lib", +}; + struct Config { bool Deterministic = true; // Updated by 'D' and 'U' modifiers. uint32_t ArchCPUType; uint32_t ArchCPUSubtype; }; +static Expected searchForFile(const Twine &FileName) { + + auto FindLib = [FileName](ArrayRef SearchDirs) { + for (StringRef Dir : SearchDirs) { + SmallString<128> Path; + sys::path::append(Path, Dir, FileName); + + if (sys::fs::exists(Path)) + return std::string(Path); + } + return std::string(); + }; + + std::string Found = FindLib(LibrarySearchDirs); + if (Found.empty()) + Found = FindLib(StandardSearchDirs); + if (!Found.empty()) + return Found; + + return createStringError(std::errc::invalid_argument, + "cannot locate file for '%s'", + FileName.str().c_str()); +} + +static Error processCommandLineLibraries() { + for (const StringRef BaseName : Libraries) { + Expected FullPath = searchForFile( + BaseName.endswith(".o") ? BaseName.str() : "lib" + BaseName + ".a"); + if (!FullPath) + return FullPath.takeError(); + InputFiles.push_back(FullPath.get()); + } + + return Error::success(); +} + static Error processFileList() { StringRef FileName, DirName; std::tie(FileName, DirName) = StringRef(FileList).rsplit(","); @@ -396,6 +453,10 @@ else if (NonDeterministicOption) C.Deterministic = false; + if (!Libraries.empty()) + if (Error E = processCommandLineLibraries()) + return std::move(E); + if (!FileList.empty()) if (Error E = processFileList()) return std::move(E);