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,85 @@ +## 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 + +# RUN: rm -rf %t/dirname && mkdir -p %t/dirname +# RUN: llvm-ar cr %t/dirname/archive.o %t-input1.o %t-input2.o + +## Check that the library is recognised when it ends with '.o': +# RUN: llvm-libtool-darwin -static -o %t.lib -larchive.o -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 + +# 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: + +# RUN: llvm-ar cr %t/dirname/libsingle-archive.a %t-input2.o + +## Check that the library is recognosed when prepended with 'lib' and appended with '.a': +# 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 option specified multiple times: +# RUN: llvm-libtool-darwin -static -o %t.lib -lsingle-archive -larchive.o -L%t/dirname +# RUN: llvm-ar t %t.lib | \ +# RUN: FileCheck %s --check-prefix=MULTIPLE-NAMES --implicit-check-not={{.}} -DPREFIX=%basename_t.tmp +# RUN: llvm-nm --print-armap %t.lib | \ +# RUN: FileCheck %s --check-prefix=MULTIPLE-SYMBOLS -DPREFIX=%basename_t.tmp --match-full-lines + +# MULTIPLE-NAMES: [[PREFIX]]-input2.o +# MULTIPLE-NAMES-NEXT: [[PREFIX]]-input1.o +# MULTIPLE-NAMES-NEXT: [[PREFIX]]-input2.o + +# MULTIPLE-SYMBOLS: Archive map +# MULTIPLE-SYMBOLS-NEXT: _symbol2 in [[PREFIX]]-input2.o +# MULTIPLE-SYMBOLS-NEXT: _symbol1 in [[PREFIX]]-input1.o +# MULTIPLE-SYMBOLS-NEXT: _symbol2 in [[PREFIX]]-input2.o +# MULTIPLE-SYMBOLS-EMPTY: + +# RUN: rm -rf %t/otherDirname && mkdir -p %t/otherDirname +# RUN: llvm-ar cr %t/otherDirname/libother-single-archive.a %t-input1.o + +## -l and -L option specified multiple times: +# 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 an error is thrown when the input library cannot be found: +# RUN: not llvm-libtool-darwin -static -o %t.lib -larchive.o 2>&1 | \ +# RUN: FileCheck %s --check-prefix=NOT-FOUND -DFILE=archive.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 %tlib -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 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,10 +66,77 @@ 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 LDirs( + "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::vector StandardDirs{ + "/lib/", + "/usr/lib/", + "/usr/local/lib/", +}; + struct Config { bool Deterministic = true; // Updated by 'D' and 'U' modifiers. }; +static Expected searchForFile(StringRef FileName) { + SmallString<128> Path; + + for (StringRef Dir : LDirs) { + sys::path::append(Path, Dir, FileName); + StringRef PathString = Path; + + if (sys::fs::exists(PathString)) { + return PathString.str(); + } + Path.clear(); + } + + for (StringRef Dir : StandardDirs) { + sys::path::append(Path, Dir, FileName); + StringRef PathString = Path; + + if (sys::fs::exists(PathString)) + return PathString.str(); + Path.clear(); + } + + return createStringError(std::errc::invalid_argument, + "cannot locate file for '%s'", + FileName.str().c_str()); +} + +static Error processCommandLineLibraries() { + for (std::string s : Libraries) { + StringRef BaseName = StringRef(s); + + std::string FullBaseName; + if (BaseName.endswith(".o")) + FullBaseName = BaseName.str(); + else + FullBaseName = ("lib" + BaseName + ".a").str(); + + Expected FullPath = searchForFile(FullBaseName); + 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(","); @@ -399,6 +466,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);