Index: llvm/lib/ToolDrivers/llvm-lib/LibDriver.cpp =================================================================== --- llvm/lib/ToolDrivers/llvm-lib/LibDriver.cpp +++ llvm/lib/ToolDrivers/llvm-lib/LibDriver.cpp @@ -55,23 +55,23 @@ } -static std::string getOutputPath(opt::InputArgList *Args, +static std::string getOutputPath(const opt::InputArgList &Args, const NewArchiveMember &FirstMember) { - if (auto *Arg = Args->getLastArg(OPT_out)) + if (auto *Arg = Args.getLastArg(OPT_out)) return Arg->getValue(); SmallString<128> Val = StringRef(FirstMember.Buf->getBufferIdentifier()); sys::path::replace_extension(Val, ".lib"); return Val.str(); } -static std::vector getSearchPaths(opt::InputArgList *Args, +static std::vector getSearchPaths(const opt::InputArgList &Args, StringSaver &Saver) { std::vector Ret; // Add current directory as first item of the search path. Ret.push_back(""); // Add /libpath flags. - for (auto *Arg : Args->filtered(OPT_libpath)) + for (auto *Arg : Args.filtered(OPT_libpath)) Ret.push_back(Arg->getValue()); // Add $LIB. @@ -97,6 +97,101 @@ return ""; } +// Open input files and return them as MemoryBuffers. +static std::vector> +openFiles(const opt::InputArgList &Args, StringSaver &Saver) { + std::vector SearchPaths = getSearchPaths(Args, Saver); + + std::vector> Ret; + + for (auto *Arg : Args.filtered(OPT_INPUT)) { + std::string Path = findInputFile(Arg->getValue(), SearchPaths); + if (Path.empty()) { + llvm::errs() << Arg->getValue() << ": no such file or directory\n"; + return {}; + } + + ErrorOr> MBOrErr = + MemoryBuffer::getFile(Path, -1, false); + if (!MBOrErr) { + llvm::errs() << "cannot open " << Arg->getValue() << ": " + << MBOrErr.getError().message() << "\n"; + return {}; + } + + file_magic Magic = identify_magic((*MBOrErr)->getBuffer()); + if (Magic != file_magic::coff_object && Magic != file_magic::bitcode && + Magic != file_magic::archive && Magic != file_magic::windows_resource) { + llvm::errs() << Arg->getValue() + << ": not a COFF object, bitcode, archive or " + << "resource file\n"; + return {}; + } + + Ret.push_back(std::move(*MBOrErr)); + } + return Ret; +} + +// Create NewArchiveMembers for a given input file and add them to Members. +// Returns true on success. +static bool addMembers(std::vector &Members, + MemoryBufferRef MB) { + // Regular path -- if a given file is not an archive, add it as a member. + if (identify_magic(MB.getBuffer()) != file_magic::archive) { + Members.push_back(NewArchiveMember(MB)); + Members.back().IsNew = true; + return true; + } + + // If a given file is an archive file, copy its members. + // This is Windows lib.exe-specific feature. Unix's "ar" doesn't do this. + // First, create an archive file instance. + Expected> FileOrErr = + object::Archive::create(MB); + if (!FileOrErr) { + handleAllErrors(FileOrErr.takeError(), [&](const ErrorInfoBase &EIB) { + llvm::errs() << "cannot open " << MB.getBufferIdentifier() << ": " + << EIB.message() << "\n"; + }); + return false; + } + object::Archive *File = FileOrErr->get(); + + // Iterate over archive members to copy them. + Error Err = Error::success(); + for (const ErrorOr &COrErr : File->children(Err)) { + if (!COrErr) { + llvm::errs() << MB.getBufferIdentifier() + << ": could not get the child of the archive: " + << COrErr.getError().message() << "\n"; + return false; + } + + Expected MBOrErr = COrErr->getMemoryBufferRef(); + if (!MBOrErr) { + handleAllErrors(FileOrErr.takeError(), [&](const ErrorInfoBase &EIB) { + llvm::errs() << MB.getBufferIdentifier() + << ": could not get the buffer for a child of the archive" + << EIB.message() << "\n"; + }); + return false; + } + + Members.push_back(NewArchiveMember(MBOrErr.get())); + Members.back().IsNew = true; + } + + if (Err) { + handleAllErrors(FileOrErr.takeError(), [&](const ErrorInfoBase &EIB) { + llvm::errs() << MB.getBufferIdentifier() + << ": Archive::children failed: " << EIB.message() << "\n"; + }); + return false; + } + return true; +} + int llvm::libDriverMain(ArrayRef ArgsArr) { BumpPtrAllocator Alloc; StringSaver Saver(Alloc); @@ -125,38 +220,18 @@ if (!Args.hasArgNoClaim(OPT_INPUT)) return 0; - std::vector SearchPaths = getSearchPaths(&Args, Saver); + // Create NewArchiveMembers from input files. + std::vector> Files = openFiles(Args, Saver); + if (Files.empty()) + return 1; - // Create a NewArchiveMember for each input file. std::vector Members; - for (auto *Arg : Args.filtered(OPT_INPUT)) { - std::string Path = findInputFile(Arg->getValue(), SearchPaths); - if (Path.empty()) { - llvm::errs() << Arg->getValue() << ": no such file or directory\n"; + for (std::unique_ptr &MB : Files) + if (!addMembers(Members, MB->getMemBufferRef())) return 1; - } - - Expected MOrErr = - NewArchiveMember::getFile(Saver.save(Path), /*Deterministic=*/true); - if (!MOrErr) { - handleAllErrors(MOrErr.takeError(), [&](const ErrorInfoBase &EIB) { - llvm::errs() << Arg->getValue() << ": " << EIB.message() << "\n"; - }); - return 1; - } - - file_magic Magic = identify_magic(MOrErr->Buf->getBuffer()); - if (Magic != file_magic::coff_object && Magic != file_magic::bitcode && - Magic != file_magic::windows_resource) { - llvm::errs() << Arg->getValue() - << ": not a COFF object, bitcode or resource file\n"; - return 1; - } - Members.emplace_back(std::move(*MOrErr)); - } // Create an archive file. - std::string OutputPath = getOutputPath(&Args, Members[0]); + std::string OutputPath = getOutputPath(Args, Members[0]); std::error_code EC = writeArchive(OutputPath, Members, /*WriteSymtab=*/true, object::Archive::K_GNU, @@ -166,6 +241,5 @@ llvm::errs() << OutputPath << ": " << EC.message() << "\n"; return 1; } - return 0; } Index: llvm/test/LibDriver/invalid.test =================================================================== --- llvm/test/LibDriver/invalid.test +++ llvm/test/LibDriver/invalid.test @@ -1,2 +1,2 @@ RUN: not llvm-lib %S/Inputs/cl-gl.obj 2>&1 | FileCheck %s -CHECK: not a COFF object, bitcode or resource file +CHECK: not a COFF object, bitcode, archive or resource file Index: llvm/test/LibDriver/merge.test =================================================================== --- /dev/null +++ llvm/test/LibDriver/merge.test @@ -0,0 +1,18 @@ +If a given file is an archive file, MSVC lib.exe adds its members to a resulting +archive instead of adding the input archive file itself to a resulting archive. + +Clean up a working directory. +RUN: rm -rf %t +RUN: mkdir -p %t/foo +RUN: cd %t + +Make foo/a.obj, foo/b.obj, and b.lib. +RUN: llvm-mc -triple=x86_64-pc-windows-msvc -filetype=obj -o foo/a.obj %S/Inputs/a.s +RUN: llvm-mc -triple=x86_64-pc-windows-msvc -filetype=obj -o foo/b.obj %S/Inputs/b.s +RUN: llvm-lib -out:b.lib foo/b.obj + +RUN: llvm-lib -out:foo.lib foo/a.obj b.lib +RUN: llvm-ar t foo.lib | FileCheck %s + +CHECK: foo/a.obj +CHECK: foo/b.obj