Index: lld/COFF/CMakeLists.txt =================================================================== --- lld/COFF/CMakeLists.txt +++ lld/COFF/CMakeLists.txt @@ -39,6 +39,7 @@ Target Option Support + WindowsManifest LINK_LIBS lldCore Index: lld/COFF/DriverUtils.cpp =================================================================== --- lld/COFF/DriverUtils.cpp +++ lld/COFF/DriverUtils.cpp @@ -20,6 +20,7 @@ #include "Symbols.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/Triple.h" #include "llvm/BinaryFormat/COFF.h" #include "llvm/Object/COFF.h" #include "llvm/Object/WindowsResource.h" @@ -32,6 +33,7 @@ #include "llvm/Support/Process.h" #include "llvm/Support/Program.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/WindowsManifest/WindowsManifestMerger.h" #include using namespace llvm::COFF; @@ -328,16 +330,9 @@ }; } -// Create the default manifest file as a temporary file. -TemporaryFile createDefaultXml() { - // Create a temporary file. - TemporaryFile File("defaultxml", "manifest"); - - // Open the temporary file for writing. - std::error_code EC; - raw_fd_ostream OS(File.Path, EC, sys::fs::F_Text); - if (EC) - fatal(EC, "failed to open " + File.Path); +static std::string createDefaultXml() { + std::string Ret; + raw_string_ostream OS(Ret); // Emit the XML. Note that we do *not* verify that the XML attributes are // syntactically correct. This is intentional for link.exe compatibility. @@ -362,37 +357,85 @@ << " \n"; } OS << "\n"; - OS.close(); - return File; + return Ret; } -static std::string readFile(StringRef Path) { - std::unique_ptr MB = - check(MemoryBuffer::getFile(Path), "could not open " + Path); - return MB->getBuffer(); +static Expected> +createManifestXmlWithInternalMt(std::string &DefaultXml) { + std::unique_ptr DefaultXmlCopy = + MemoryBuffer::getMemBufferCopy(DefaultXml); + + windows_manifest::WindowsManifestMerger Merger; + if (auto E = Merger.merge(*DefaultXmlCopy.get())) + return std::move(E); + + for (StringRef Filename : Config->ManifestInput) { + std::unique_ptr Manifest = + check(MemoryBuffer::getFile(Filename)); + if (auto E = Merger.merge(*Manifest.get())) { + warn("internal manifest tool failed on file " + Filename); + return std::move(E); + } + } + + return Merger.getMergedManifest(); } -static std::string createManifestXml() { - // Create the default manifest file. - TemporaryFile File1 = createDefaultXml(); - if (Config->ManifestInput.empty()) - return readFile(File1.Path); +static std::unique_ptr +createManifestXmlWithExternalMt(std::string &DefaultXml) { + const Triple HostTriple(Triple::normalize(LLVM_HOST_TRIPLE)); + if (!HostTriple.isOSWindows()) { + warn("manifest ignored because no external manifest tool available"); + return MemoryBuffer::getMemBufferCopy(std::string()); + } + // Create the default manifest file as a temporary file. + TemporaryFile Default("defaultxml", "manifest"); + std::error_code EC; + raw_fd_ostream OS(Default.Path, EC, sys::fs::F_Text); + if (EC) + fatal(EC, "failed to open " + Default.Path); + OS << DefaultXml; + OS.close(); - // If manifest files are supplied by the user using /MANIFESTINPUT - // option, we need to merge them with the default manifest. - TemporaryFile File2("user", "manifest"); + // Merge user-supplied manifests if they are given. Since libxml2 is not + // enabled, we must shell out to Microsoft's mt.exe tool. + TemporaryFile User("user", "manifest"); Executor E("mt.exe"); E.add("/manifest"); - E.add(File1.Path); + E.add(Default.Path); for (StringRef Filename : Config->ManifestInput) { E.add("/manifest"); E.add(Filename); } E.add("/nologo"); - E.add("/out:" + StringRef(File2.Path)); + E.add("/out:" + StringRef(User.Path)); E.run(); - return readFile(File2.Path); + + return check(MemoryBuffer::getFile(User.Path), "could not open " + User.Path); +} + +static std::string createManifestXml() { + std::string DefaultXml = createDefaultXml(); + if (Config->ManifestInput.empty()) + return DefaultXml; + + // If manifest files are supplied by the user using /MANIFESTINPUT + // option, we need to merge them with the default manifest. If libxml2 + // is enabled, we may merge them with LLVM's own library. + Expected> OutputBufferOrError = + createManifestXmlWithInternalMt(DefaultXml); + if (OutputBufferOrError) + return OutputBufferOrError.get()->getBuffer(); + // Using built-in library failed, possibly because libxml2 is not installed. + // Shell out to mt.exe instead. + handleAllErrors(std::move(OutputBufferOrError.takeError()), + [&](ErrorInfoBase &EIB) { + warn("error with internal manifest tool: " + EIB.message()); + }); + std::unique_ptr OutputBuffer; + OutputBuffer = createManifestXmlWithExternalMt(DefaultXml); + return OutputBuffer->getBuffer(); } static std::unique_ptr Index: lld/test/COFF/manifestinput.test =================================================================== --- lld/test/COFF/manifestinput.test +++ lld/test/COFF/manifestinput.test @@ -1,4 +1,4 @@ -# REQUIRES: win_mt +# REQUIRES: manifest_tool # RUN: yaml2obj %p/Inputs/ret42.yaml > %t.obj # RUN: lld-link /out:%t.exe /entry:main \ Index: lld/test/lit.cfg =================================================================== --- lld/test/lit.cfg +++ lld/test/lit.cfg @@ -265,6 +265,8 @@ config.environment['LLD_VERSION'] = 'LLD 1.0' # Indirectly check if the mt.exe Microsoft utility exists by searching for -# cvtres, which always accompanies it. -if lit.util.which('cvtres', config.environment['PATH']): - config.available_features.add('win_mt') +# cvtres, which always accompanies it. Alternatively, check if we can use +# libxml2 to merge manifests. +if (lit.util.which('cvtres', config.environment['PATH'])) or \ + (config.llvm_libxml2_enabled == "1"): + config.available_features.add('manifest_tool') Index: lld/test/lit.site.cfg.in =================================================================== --- lld/test/lit.site.cfg.in +++ lld/test/lit.site.cfg.in @@ -4,6 +4,7 @@ config.llvm_obj_root = "@LLVM_BINARY_DIR@" config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" config.llvm_libs_dir = "@LLVM_LIBS_DIR@" +config.llvm_libxml2_enabled = "@LLVM_LIBXML2_ENABLED@" config.lit_tools_dir = "@LLVM_LIT_TOOLS_DIR@" config.lld_obj_root = "@LLD_BINARY_DIR@" config.lld_libs_dir = "@LLVM_LIBRARY_OUTPUT_INTDIR@" Index: llvm/test/tools/llvm-mt/Inputs/test_manifest.manifest =================================================================== --- llvm/test/tools/llvm-mt/Inputs/test_manifest.manifest +++ llvm/test/tools/llvm-mt/Inputs/test_manifest.manifest @@ -1,4 +1,4 @@ - + Index: llvm/test/tools/llvm-mt/big_merge.test =================================================================== --- llvm/test/tools/llvm-mt/big_merge.test +++ llvm/test/tools/llvm-mt/big_merge.test @@ -1,11 +1,11 @@ REQUIRES: libxml2 UNSUPPORTED: windows -RUN: llvm-mt /manifest %p/Inputs/trust_info.manifest \ -RUN: /manifest %p/Inputs/assembly_identity.manifest \ -RUN: /manifest %p/Inputs/trust_and_identity.manifest \ -RUN: /manifest %p/Inputs/compatibility.manifest \ -RUN: /manifest %p/Inputs/windows_settings.manifest /out:%t +RUN: llvm-mt /manifest %p/Inputs/trust_info.manifest /manifest \ +RUN: %p/Inputs/assembly_identity.manifest /manifest \ +RUN: %p/Inputs/trust_and_identity.manifest /manifest \ +RUN: %p/Inputs/compatibility.manifest /manifest \ +RUN: %p/Inputs/windows_settings.manifest /out:%t RUN: FileCheck %s -input-file=%t CHECK: Index: llvm/tools/llvm-mt/Opts.td =================================================================== --- llvm/tools/llvm-mt/Opts.td +++ llvm/tools/llvm-mt/Opts.td @@ -1,7 +1,8 @@ include "llvm/Option/OptParser.td" +def supported : OptionGroup<"supported">; def unsupported : OptionGroup<"unsupported">; -def manifest : Separate<["/", "-"], "manifest">, HelpText<"Used to specify each manifest that need to be processed">, MetaVarName<"manifest">; +def manifest : Separate<["/", "-"], "manifest">, HelpText<"Used to specify each manifest that need to be processed">, MetaVarName<"manifest">, Group; def identity : Joined<["/", "-"], "identity:">, HelpText<"Not supported">, MetaVarName<"identity">, Group; def rgs : Joined<["/", "-"], "rgs:">, HelpText<"Not supported">, MetaVarName<"script">, Group; def tlb : Joined<["/", "-"], "tlb:">, HelpText<"Not supported">, MetaVarName<"file">, Group; @@ -10,8 +11,8 @@ def managed_assembly_name : Joined<["/", "-"], "managedassemblyname:">, HelpText<"Not supported">, MetaVarName<"assembly">, Group; def no_dependency : Flag<["/", "-"], "nodependency">, HelpText<"Not supported">, Group; def category : Flag<["/", "-"], "category">, HelpText<"Not supported">, Group; -def no_logo : Flag<["/", "-"], "nologo">, HelpText<"No effect as this tool never writes copyright data. Included for parity">; -def out : Joined<["/", "-"], "out:">, HelpText<"Name of the output manifest. If this is skipped and only one manifest is being operated upon by the tool, that manifest is modified in place">, MetaVarName<"manifest">; +def no_logo : Flag<["/", "-"], "nologo">, HelpText<"No effect as this tool never writes copyright data. Included for parity">, Group; +def out : Joined<["/", "-"], "out:">, HelpText<"Name of the output manifest. If this is skipped and only one manifest is being operated upon by the tool, that manifest is modified in place">, MetaVarName<"manifest">, Group; def input_resource : Joined<["/", "-"], "inputresource:">, HelpText<"Not supported">, MetaVarName<"file">, Group; def output_resource : Joined<["/", "-"], "outputresource:">, HelpText<"Not supported">, MetaVarName<"file">, Group; def output_resource_flag : Flag<["/", "-"], "outputresource">, Alias, HelpText<"Not supported">, Group; @@ -24,6 +25,6 @@ def check_for_duplicates : Flag<["/", "-"], "check_for_duplicates:">, HelpText<"Not supported">, Group; def make_cdfs : Flag<["/", "-"], "makecdfs:">, HelpText<"Not supported">, Group; def verbose : Flag<["/", "-"], "verbose">, HelpText<"Not supported">, Group; -def help : Flag<["/", "-"], "?">; -def help_long : Flag<["/", "-"], "help">, Alias; -def h : Flag<["/", "-"], "h">, Alias; +def help : Flag<["/", "-"], "?">, Group; +def help_long : Flag<["/", "-"], "help">, Alias, Group; +def h : Flag<["/", "-"], "h">, Alias, Group; Index: llvm/tools/llvm-mt/llvm-mt.cpp =================================================================== --- llvm/tools/llvm-mt/llvm-mt.cpp +++ llvm/tools/llvm-mt/llvm-mt.cpp @@ -105,6 +105,13 @@ for (auto *Arg : InputArgs.filtered(OPT_INPUT)) reportError(Twine("invalid option ") + Arg->getSpelling()); + for (auto &Arg : InputArgs) { + if (!(Arg->getOption().matches(OPT_unsupported) || + Arg->getOption().matches(OPT_supported))) { + reportError(Twine("invalid option ") + Arg->getSpelling()); + } + } + for (auto &Arg : InputArgs) { if (Arg->getOption().matches(OPT_unsupported)) { outs() << "llvm-mt: ignoring unsupported '" << Arg->getOption().getName()