diff --git a/llvm/lib/ToolDrivers/llvm-lib/LibDriver.cpp b/llvm/lib/ToolDrivers/llvm-lib/LibDriver.cpp --- a/llvm/lib/ToolDrivers/llvm-lib/LibDriver.cpp +++ b/llvm/lib/ToolDrivers/llvm-lib/LibDriver.cpp @@ -19,6 +19,7 @@ #include "llvm/Bitcode/BitcodeReader.h" #include "llvm/Object/ArchiveWriter.h" #include "llvm/Object/COFF.h" +#include "llvm/Object/COFFModuleDefinition.h" #include "llvm/Object/WindowsMachineFlag.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" @@ -31,6 +32,7 @@ #include using namespace llvm; +using namespace llvm::object; namespace { @@ -60,7 +62,7 @@ public: LibOptTable() : opt::GenericOptTable(InfoTable, true) {} }; -} +} // namespace static std::string getDefaultOutputPath(const NewArchiveMember &FirstMember) { SmallString<128> Val = StringRef(FirstMember.Buf->getBufferIdentifier()); @@ -91,6 +93,18 @@ return Ret; } +// Opens a file. Path has to be resolved already. (used for def file) +std::unique_ptr openFile(const Twine &Path) { + ErrorOr> MB = MemoryBuffer::getFile(Path); + + if (std::error_code EC = MB.getError()) { + llvm::errs() << "cannot open file " << Path << ": " << EC.message() << "\n"; + return nullptr; + } + + return std::move(*MB); +} + static std::string findInputFile(StringRef File, ArrayRef Paths) { for (StringRef Dir : Paths) { SmallString<128> Path = Dir; @@ -110,7 +124,7 @@ }); } -static void doList(opt::InputArgList& Args) { +static void doList(opt::InputArgList &Args) { // lib.exe prints the contents of the first archive file. std::unique_ptr B; for (auto *Arg : Args.filtered(OPT_INPUT)) { @@ -302,6 +316,63 @@ for (auto *Arg : Args.filtered(OPT_ignore)) IgnoredWarnings.insert(Arg->getValue()); + // get output library path, if any + std::string OutputPath; + if (auto *Arg = Args.getLastArg(OPT_out)) { + OutputPath = Arg->getValue(); + } + + COFF::MachineTypes LibMachine = COFF::IMAGE_FILE_MACHINE_UNKNOWN; + std::string LibMachineSource; + if (auto *Arg = Args.getLastArg(OPT_machine)) { + LibMachine = getMachineType(Arg->getValue()); + if (LibMachine == COFF::IMAGE_FILE_MACHINE_UNKNOWN) { + llvm::errs() << "unknown /machine: arg " << Arg->getValue() << '\n'; + return 1; + } + LibMachineSource = + std::string(" (from '/machine:") + Arg->getValue() + "' flag)"; + } + + // create an import library + if (Args.hasArg(OPT_deffile)) { + + if (OutputPath.empty()) { + llvm::errs() << "no output path given\n"; + return 1; + } + + if (LibMachine == COFF::IMAGE_FILE_MACHINE_UNKNOWN) { + llvm::errs() << "/def option requires /machine to be specified" << '\n'; + return 1; + } + + std::unique_ptr MB = + openFile(Args.getLastArg(OPT_deffile)->getValue()); + if (!MB) + return 1; + + if (!MB->getBufferSize()) { + llvm::errs() << "definition file empty\n"; + return 1; + } + + Expected Def = + parseCOFFModuleDefinition(*MB, LibMachine, true); + + if (!Def) { + llvm::errs() << "error parsing definition\n" + << errorToErrorCode(Def.takeError()).message(); + return 1; + } + + return writeImportLibrary(Def->OutputFile, OutputPath, Def->Exports, + LibMachine, + /*MinGW=*/false) + ? 1 + : 0; + } + // If no input files and not told otherwise, silently do nothing to match // lib.exe if (!Args.hasArgNoClaim(OPT_INPUT) && !Args.hasArg(OPT_llvmlibempty)) { @@ -324,18 +395,6 @@ std::vector SearchPaths = getSearchPaths(&Args, Saver); - COFF::MachineTypes LibMachine = COFF::IMAGE_FILE_MACHINE_UNKNOWN; - std::string LibMachineSource; - if (auto *Arg = Args.getLastArg(OPT_machine)) { - LibMachine = getMachineType(Arg->getValue()); - if (LibMachine == COFF::IMAGE_FILE_MACHINE_UNKNOWN) { - llvm::errs() << "unknown /machine: arg " << Arg->getValue() << '\n'; - return 1; - } - LibMachineSource = - std::string(" (from '/machine:") + Arg->getValue() + "' flag)"; - } - std::vector> MBs; StringSet<> Seen; std::vector Members; @@ -373,14 +432,13 @@ } // Create an archive file. - std::string OutputPath; - if (auto *Arg = Args.getLastArg(OPT_out)) { - OutputPath = Arg->getValue(); - } else if (!Members.empty()) { - OutputPath = getDefaultOutputPath(Members[0]); - } else { - llvm::errs() << "no output path given, and cannot infer with no inputs\n"; - return 1; + if (OutputPath.empty()) { + if (!Members.empty()) { + OutputPath = getDefaultOutputPath(Members[0]); + } else { + llvm::errs() << "no output path given, and cannot infer with no inputs\n"; + return 1; + } } // llvm-lib uses relative paths for both regular and thin archives, unlike // standard GNU ar, which only uses relative paths for thin archives and diff --git a/llvm/lib/ToolDrivers/llvm-lib/Options.td b/llvm/lib/ToolDrivers/llvm-lib/Options.td --- a/llvm/lib/ToolDrivers/llvm-lib/Options.td +++ b/llvm/lib/ToolDrivers/llvm-lib/Options.td @@ -22,6 +22,7 @@ // Can't be called "list" since that's a keyword. def lst : F<"list">, HelpText<"List contents of .lib file on stdout">; def out : P<"out", "Path to file to write output">; +def deffile : P<"def", "def file to use to generate import library">; def llvmlibthin : F<"llvmlibthin">, HelpText<"Make .lib point to .obj files instead of copying their contents">; diff --git a/llvm/test/tools/llvm-lib/implibs.test b/llvm/test/tools/llvm-lib/implibs.test --- a/llvm/test/tools/llvm-lib/implibs.test +++ b/llvm/test/tools/llvm-lib/implibs.test @@ -10,3 +10,10 @@ RUN: llvm-ar t %t/newlib.lib | FileCheck %s CHECK: lib.dll + +Test that import libraries can be created from a def file + +RUN: echo -e "NAME lib.dll\nEXPORTS\nMyFunc" > %t/implib.def +RUN: llvm-lib -out:%t/implib.lib -def:%t/implib.def -machine:x64 + +RUN: llvm-ar t %t/implib.lib | FileCheck %s