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 @@ -42,6 +42,10 @@ Specify the output file name. Must be specified exactly once. +.. option:: -static + + Produces a static library from the input files. + EXIT STATUS ----------- diff --git a/llvm/test/tools/llvm-libtool-darwin/basic.test b/llvm/test/tools/llvm-libtool-darwin/basic.test deleted file mode 100644 --- a/llvm/test/tools/llvm-libtool-darwin/basic.test +++ /dev/null @@ -1,10 +0,0 @@ -## This test checks that main exits normally (EC 0) for correct input/output args. - -# RUN: yaml2obj %S/Inputs/input1.yaml -o %t-input1.o -# RUN: yaml2obj %S/Inputs/input2.yaml -o %t-input2.o - -## Pass single input: -# RUN: llvm-libtool-darwin -o %t.lib %t-input1.o - -## Pass multiple inputs: -# RUN: llvm-libtool-darwin -o %t.lib %t-input1.o %t-input2.o diff --git a/llvm/test/tools/llvm-libtool-darwin/create-static-lib.test b/llvm/test/tools/llvm-libtool-darwin/create-static-lib.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-libtool-darwin/create-static-lib.test @@ -0,0 +1,62 @@ +## This test checks that a correct static library is created. + +# RUN: yaml2obj %S/Inputs/input1.yaml -o %t-input1.o +# RUN: yaml2obj %S/Inputs/input2.yaml -o %t-input2.o + +# RUN: rm -rf %t.lib +# RUN: llvm-libtool-darwin -static -o %t.lib %t-input1.o %t-input2.o + +## 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 + +# 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 output archive is in Darwin format: +# RUN: llvm-objdump --macho --archive-headers %t.lib | \ +# RUN: FileCheck %s --check-prefix=FORMAT -DPREFIX=%basename_t.tmp + +# FORMAT: __.SYMDEF +# FORMAT-NEXT: [[PREFIX]]-input1.o +# FORMAT-NEXT: [[PREFIX]]-input2.o +# FORMAT_NOT: {{.}} + +## Check that the output file is overwritten: +# RUN: llvm-libtool-darwin -static -o %t.lib %t-input2.o +# RUN: llvm-ar t %t.lib | \ +# RUN: FileCheck %s --check-prefix=OVERWRITE-NAMES --implicit-check-not={{.}} -DPREFIX=%basename_t.tmp +# RUN: llvm-nm --print-armap %t.lib | \ +# RUN: FileCheck %s --check-prefix=OVERWRITE-SYMBOLS -DPREFIX=%basename_t.tmp + +# OVERWRITE-NAMES: [[PREFIX]]-input2.o + +# OVERWRITE-SYMBOLS: Archive map +# OVERWRITE-SYMBOLS-NEXT: _symbol2 in [[PREFIX]]-input2.o +# OVERWRITE-SYMBOLS-EMPTY: + +## Duplicate a binary: +# RUN: llvm-libtool-darwin -static -o %t.lib %t-input1.o %t-input2.o %t-input1.o +# RUN: llvm-ar t %t.lib | \ +# RUN: FileCheck %s --check-prefix=DUPLICATE-NAMES --implicit-check-not={{.}} -DPREFIX=%basename_t.tmp +# RUN: llvm-nm --print-armap %t.lib | \ +# RUN: FileCheck %s --check-prefix=DUPLICATE-SYMBOLS -DPREFIX=%basename_t.tmp + +# DUPLICATE-NAMES: [[PREFIX]]-input1.o +# DUPLICATE-NAMES-NEXT: [[PREFIX]]-input2.o +# DUPLICATE-NAMES-NEXT: [[PREFIX]]-input1.o + +# DUPLICATE-SYMBOLS: Archive map +# DUPLICATE-SYMBOLS-NEXT: _symbol1 in [[PREFIX]]-input1.o +# DUPLICATE-SYMBOLS-NEXT: _symbol2 in [[PREFIX]]-input2.o +# DUPLICATE-SYMBOLS-NEXT: _symbol1 in [[PREFIX]]-input1.o +# DUPLICATE-SYMBOLS-EMPTY: diff --git a/llvm/test/tools/llvm-libtool-darwin/hide-unrelated-options.test b/llvm/test/tools/llvm-libtool-darwin/hide-unrelated-options.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-libtool-darwin/hide-unrelated-options.test @@ -0,0 +1,9 @@ +## This test checks that unrelated options are hidden in help text. + +## Hide --safepoint-ir-verifier-print-only: +# RUN: llvm-libtool-darwin -h | \ +# RUN: FileCheck %s --implicit-check-not=--safepoint-ir-verifier-print-only +# RUN: llvm-libtool-darwin -help | \ +# RUN: FileCheck %s --implicit-check-not=--safepoint-ir-verifier-print-only +# RUN: llvm-libtool-darwin --help | \ +# RUN: FileCheck %s --implicit-check-not=--safepoint-ir-verifier-print-only 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 @@ -23,3 +23,30 @@ # RUN: FileCheck %s --check-prefix=DOUBLE-OUTPUT # DOUBLE-OUTPUT: for the --output option: must occur exactly one time! + +## Input file not found: +# RUN: not llvm-libtool-darwin -static -o %t.lib %t.missing 2>&1 | \ +# RUN: FileCheck %s --check-prefix=NO-FILE -DFILE=%t.missing + +# NO-FILE: error: '[[FILE]]': {{[nN]}}o such file or directory + +## Input file is not an object file: +# RUN: touch %t.invalid +# RUN: not llvm-libtool-darwin -static -o %t.lib %t.invalid 2>&1 | \ +# RUN: FileCheck %s --check-prefix=NOT-OBJECT -DFILE=%basename_t.tmp.invalid + +# NOT-OBJECT: error: '[[FILE]]': The file was not recognized as a valid object file + +## Input file is not a MachO object file: +# RUN: yaml2obj %s -o %t.elf +# RUN: not llvm-libtool-darwin -static -o %t.lib %t.elf 2>&1 | \ +# RUN: FileCheck %s --check-prefix=NOT-MACHO -DFILE=%basename_t.tmp.elf + +# NOT-MACHO: error: '[[FILE]]': format not supported + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 diff --git a/llvm/test/tools/llvm-libtool-darwin/missing-library-type.test b/llvm/test/tools/llvm-libtool-darwin/missing-library-type.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-libtool-darwin/missing-library-type.test @@ -0,0 +1,5 @@ +## Missing library type option: +# RUN: not llvm-libtool-darwin -o %t.lib %t.input 2>&1 | \ +# RUN: FileCheck %s --check-prefix=MISSING-OPERATION --strict-whitespace + +# MISSING-OPERATION: Library Type: option: must be specified at least once! diff --git a/llvm/tools/llvm-libtool-darwin/CMakeLists.txt b/llvm/tools/llvm-libtool-darwin/CMakeLists.txt --- a/llvm/tools/llvm-libtool-darwin/CMakeLists.txt +++ b/llvm/tools/llvm-libtool-darwin/CMakeLists.txt @@ -1,4 +1,5 @@ set(LLVM_LINK_COMPONENTS + Object Support ) diff --git a/llvm/tools/llvm-libtool-darwin/LLVMBuild.txt b/llvm/tools/llvm-libtool-darwin/LLVMBuild.txt --- a/llvm/tools/llvm-libtool-darwin/LLVMBuild.txt +++ b/llvm/tools/llvm-libtool-darwin/LLVMBuild.txt @@ -17,4 +17,4 @@ type = Tool name = llvm-libtool-darwin parent = Tools -required_libraries = Support +required_libraries = Object Support 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 @@ -10,22 +10,102 @@ // //===----------------------------------------------------------------------===// +#include "llvm/Object/ArchiveWriter.h" +#include "llvm/Object/MachO.h" +#include "llvm/Object/ObjectFile.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/InitLLVM.h" +#include "llvm/Support/WithColor.h" using namespace llvm; +using namespace llvm::object; + +cl::OptionCategory LibtoolCategory("llvm-libtool-darwin options"); static cl::opt OutputFile("output", cl::desc("Specify output filename"), - cl::value_desc("filename"), - cl::Required); + cl::value_desc("filename"), cl::Required, + cl::cat(LibtoolCategory)); static cl::alias OutputFileShort("o", cl::desc("Alias for -output"), cl::aliasopt(OutputFile), cl::NotHidden); -static cl::list - InputFiles(cl::Positional, cl::desc(""), cl::OneOrMore); +static cl::list InputFiles(cl::Positional, + cl::desc(""), + cl::OneOrMore, + cl::cat(LibtoolCategory)); + +enum Operation { Static }; +static cl::opt LibraryOperation( + cl::desc("Library Type: "), + cl::values( + clEnumValN(Static, "static", + "Produce a statically linked library from the input files")), + cl::Required, cl::cat(LibtoolCategory)); + +static Error verifyDarwinObject(const NewArchiveMember &Member) { + auto MBRef = Member.Buf->getMemBufferRef(); + Expected> ObjOrErr = + object::ObjectFile::createObjectFile(MBRef); + + // Throw error if not a valid object file. + if (!ObjOrErr) + return createFileError(Member.MemberName, ObjOrErr.takeError()); + + // Throw error if not in darwin format. + if (!isa(**ObjOrErr)) + return createStringError(std::errc::invalid_argument, + "'%s': format not supported", + Member.MemberName.data()); + + return Error::success(); +} + +static Error addMember(std::vector &Members, + StringRef FileName) { + Expected NMOrErr = + NewArchiveMember::getFile(FileName, /*Deterministic=*/true); + + if (!NMOrErr) + return createFileError(FileName, NMOrErr.takeError()); + + // For regular archives, use the basename of the object path for the member + // name. + NMOrErr->MemberName = sys::path::filename(NMOrErr->MemberName); + + // Verify that Member is a darwin object file. + if (Error E = verifyDarwinObject(*NMOrErr)) + return E; + + Members.push_back(std::move(*NMOrErr)); + return Error::success(); +} + +static Error createStaticLibrary() { + std::vector NewMembers; + for (const StringRef Member : InputFiles) + if (Error E = addMember(NewMembers, Member)) + return E; + + if (Error E = writeArchive(OutputFile, NewMembers, + /*WriteSymtab=*/true, + /*Kind=*/object::Archive::K_DARWIN, + /*Deterministic=*/true, + /*Thin=*/false)) + return E; + return Error::success(); +} int main(int Argc, char **Argv) { InitLLVM X(Argc, Argv); + cl::HideUnrelatedOptions({&LibtoolCategory, &ColorCategory}); cl::ParseCommandLineOptions(Argc, Argv, "llvm-libtool-darwin\n"); + + switch (LibraryOperation) { + case Static: + if (Error E = createStaticLibrary()) { + WithColor::defaultErrorHandler(std::move(E)); + exit(EXIT_FAILURE); + } + break; + } }