Index: llvm/cmake/config-ix.cmake =================================================================== --- llvm/cmake/config-ix.cmake +++ llvm/cmake/config-ix.cmake @@ -600,3 +600,13 @@ endif() string(REPLACE " " ";" LLVM_BINDINGS_LIST "${LLVM_BINDINGS}") + +if (NOT LIBXML2_FOUND AND NOT LLVM_DISABLE_LIBXML2 AND NOT (CMAKE_SYSTEM_NAME MATCHES "Windows")) + find_package(LibXml2) +endif() + +if (LIBXML2_FOUND AND NOT LLVM_DISABLE_LIBXML2) + add_definitions( -DLIBXML2_ENABLED ) + list(APPEND CMAKE_REQUIRED_LIBRARIES ${LIBXML2_LIBRARIES}) + include_directories(${LIBXML2_INCLUDE_DIR}) +endif() Index: llvm/include/llvm/Support/WindowsManifestMerger.h =================================================================== --- /dev/null +++ llvm/include/llvm/Support/WindowsManifestMerger.h @@ -0,0 +1,74 @@ +//===-- WindowsManifestMerger.h ---------------------------------*- C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===---------------------------------------------------------------------===// +// +// This file provides a utility for merging Microsoft .manifest files. These +// files are xml documents which contain meta-information about applications, +// such as whether or not admin access is required, system compatibility, +// versions, etc. Part of the linking process of an executable may require +// merging several of these .manifest files using a tree-merge following +// specific rules. Unfortunately, these rules are not documented well +// anywhere. However, a careful investigation of the behavior of the original +// Microsoft Manifest Tool (mt.exe) revealed the rules of this merge. As the +// saying goes, code is the best documentation, so please look below if you are +// interested in the exact merging requirements. +// +// Ref: +// https://msdn.microsoft.com/en-us/library/windows/desktop/aa374191(v=vs.85).aspx +// +//===---------------------------------------------------------------------===// + +#ifndef LLVM_INCLUDE_LLVM_SUPPORT_WINDOWS_MANIFEST_MERGER_H +#define LLVM_INCLUDE_LLVM_SUPPORT_WINDOWS_MANIFEST_MERGER_H +#endif + +#include "llvm/Support/Error.h" + +#if defined(LIBXML2_ENABLED) +#include +#endif + +namespace llvm { + +class MemoryBuffer; + +#if defined(LIBXML2_ENABLED) +typedef xmlDocPtr XMLDocumentImpl; +typedef xmlNodePtr XMLNodeImpl; +#else +typedef void *XMLDocumentImpl; +typedef void *XMLNodeImpl; +#endif + +class WindowsManifestError : public ErrorInfo { +public: + static char ID; + WindowsManifestError(Twine Msg); + void log(raw_ostream &OS) const override; + +private: + std::string Msg; +}; + +class WindowsManifestMerger { +public: + WindowsManifestMerger() = default; + + Error merge(MemoryBuffer &Manifest); + + std::unique_ptr getMergedManifest(); + +private: + static void errorCallback(void *Ctx, const char *Format, ...); + Error getParseError(); + + XMLNodeImpl CombinedRoot = nullptr; + bool ParseErrorOccurred = false; +}; + +} // namespace llvm Index: llvm/lib/Support/CMakeLists.txt =================================================================== --- llvm/lib/Support/CMakeLists.txt +++ llvm/lib/Support/CMakeLists.txt @@ -29,6 +29,8 @@ endif() endif( MSVC OR MINGW ) +set(system_libs ${system_libs} ${LIBXML2_LIBRARIES}) + add_llvm_library(LLVMSupport AMDGPUCodeObjectMetadata.cpp APFloat.cpp @@ -110,6 +112,7 @@ Triple.cpp Twine.cpp Unicode.cpp + WindowsManifestMerger.cpp YAMLParser.cpp YAMLTraits.cpp raw_os_ostream.cpp Index: llvm/lib/Support/WindowsManifestMerger.cpp =================================================================== --- /dev/null +++ llvm/lib/Support/WindowsManifestMerger.cpp @@ -0,0 +1,74 @@ +//===-- WindowsManifestMerger.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===---------------------------------------------------------------------===// +// +// This file implements the .manifest merger class. +// +//===---------------------------------------------------------------------===// + +#include "llvm/Support/WindowsManifestMerger.h" +#include "llvm/Support/MemoryBuffer.h" + +#include + +namespace llvm { + +char WindowsManifestError::ID = 0; + +WindowsManifestError::WindowsManifestError(Twine Msg) : Msg(Msg.str()) {} + +void WindowsManifestError::log(raw_ostream &OS) const { OS << Msg; } + +Error WindowsManifestMerger::merge(MemoryBuffer &Manifest) { +#if defined(LIBXML2_ENABLED) + xmlSetGenericErrorFunc((void *)this, WindowsManifestMerger::errorCallback); + XMLDocumentImpl ManifestXML = + xmlReadMemory(Manifest.getBufferStart(), Manifest.getBufferSize(), + "manifest.xml", nullptr, 0); + xmlSetGenericErrorFunc(nullptr, nullptr); + if (auto E = getParseError()) + return E; + CombinedRoot = xmlDocGetRootElement(ManifestXML); +#endif + return Error::success(); +} + +std::unique_ptr WindowsManifestMerger::getMergedManifest() { + std::unique_ptr OutputBuffer; + unsigned char *XmlBuff; + int BufferSize = 0; +#if defined(LIBXML2_ENABLED) + if (CombinedRoot) { + std::unique_ptr OutputDoc(xmlNewDoc((const unsigned char *)"1.0")); + xmlDocSetRootElement(OutputDoc.get(), CombinedRoot); + xmlDocDumpMemory(OutputDoc.get(), &XmlBuff, &BufferSize); + } +#endif + + if (BufferSize > 0) + OutputBuffer = MemoryBuffer::getMemBuffer( + StringRef(reinterpret_cast(XmlBuff), (size_t)BufferSize)); + else + OutputBuffer = MemoryBuffer::getNewMemBuffer(0); + + return OutputBuffer; +} + +void WindowsManifestMerger::errorCallback(void *Ctx, const char *Format, ...) { + auto *Merger = (WindowsManifestMerger *)Ctx; + Merger->ParseErrorOccurred = true; +} + +Error WindowsManifestMerger::getParseError() { + if (!ParseErrorOccurred) + return Error::success(); + else + return make_error("Invalid xml document"); +} + +} // namespace llvm Index: llvm/test/CMakeLists.txt =================================================================== --- llvm/test/CMakeLists.txt +++ llvm/test/CMakeLists.txt @@ -137,6 +137,12 @@ ) endif() +if (LIBXML2_FOUND AND NOT LLVM_DISABLE_LIBXML2) + set(LLVM_TEST_DEPENDS ${LLVM_TEST_DEPENDS} + llvm-mt + ) +endif() + add_custom_target(llvm-test-depends DEPENDS ${LLVM_TEST_DEPENDS}) set_target_properties(llvm-test-depends PROPERTIES FOLDER "Tests") Index: llvm/test/lit.cfg =================================================================== --- llvm/test/lit.cfg +++ llvm/test/lit.cfg @@ -288,6 +288,7 @@ r"\bllvm-config\b", r"\bllvm-cov\b", r"\bllvm-cxxdump\b", + r"\bllvm-cvtres\b", r"\bllvm-diff\b", r"\bllvm-dis\b", r"\bllvm-dsymutil\b", @@ -337,6 +338,7 @@ # For tools that are optional depending on the config, we won't warn # if they're missing. for pattern in [r"\bllvm-go\b", + r"\bllvm-mt\b", r"\bKaleidoscope-Ch3\b", r"\bKaleidoscope-Ch4\b", r"\bKaleidoscope-Ch5\b", Index: llvm/test/tools/llvm-mt/Inputs/bad.manifest =================================================================== --- /dev/null +++ llvm/test/tools/llvm-mt/Inputs/bad.manifest @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + Index: llvm/test/tools/llvm-mt/Inputs/test_manifest.manifest =================================================================== --- /dev/null +++ llvm/test/tools/llvm-mt/Inputs/test_manifest.manifest @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + Index: llvm/test/tools/llvm-mt/help.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-mt/help.test @@ -0,0 +1,8 @@ +RUN: llvm-mt /? | FileCheck %s -check-prefix=HELP + +RUN: llvm-mt /inputresource:foo.res /manifest \ +RUN: %p/Inputs/test_manifest.manifest | FileCheck %s -check-prefix=NOT_SUPPORTED + +HELP: OVERVIEW: Manifest Tool + +NOT_SUPPORTED: Ignoring INPUTRESOURCE: option; not supported. Index: llvm/test/tools/llvm-mt/single_file.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-mt/single_file.test @@ -0,0 +1,2 @@ +RUN: llvm-mt /manifest %p/Inputs/test_manifest.manifest /out:%t +RUN: diff %p/Inputs/test_manifest.manifest %t Index: llvm/test/tools/llvm-mt/xml_error.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-mt/xml_error.test @@ -0,0 +1,3 @@ +RUN: not llvm-mt /manifest %p/Inputs/bad.manifest 2>&1 >/dev/null | FileCheck %s + +CHECK: llvm-mt error: Invalid xml document. Index: llvm/tools/llvm-mt/CMakeLists.txt =================================================================== --- /dev/null +++ llvm/tools/llvm-mt/CMakeLists.txt @@ -0,0 +1,13 @@ +set(LLVM_LINK_COMPONENTS + Option + Support + ) + +set(LLVM_TARGET_DEFINITIONS Opts.td) + +tablegen(LLVM Opts.inc -gen-opt-parser-defs) +add_public_tablegen_target(MtTableGen) + +add_llvm_tool(llvm-mt + llvm-mt.cpp + ) Index: llvm/tools/llvm-mt/LLVMBuild.txt =================================================================== --- /dev/null +++ llvm/tools/llvm-mt/LLVMBuild.txt @@ -0,0 +1,22 @@ +;===- ./tools/llvm-mt/LLVMBuild.txt ---------------------------*- Conf -*--===; +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Tool +name = llvm-mt +parent = Tools +required_libraries = Option Support Index: llvm/tools/llvm-mt/Opts.td =================================================================== --- /dev/null +++ llvm/tools/llvm-mt/Opts.td @@ -0,0 +1,29 @@ +include "llvm/Option/OptParser.td" + +def UNSUPPORTED : OptionGroup<"UNSUPPORTED">; +def MANIFEST : Separate<["/", "-"], "MANIFEST">, HelpText<"Used to specify each manifest that need to be processed.">, MetaVarName<"manifest">; +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; +def DLL : Joined<["/", "-"], "DLL:">, HelpText<"Not supported.">, MetaVarName<"dll">, Group; +def REPLACEMENTS : Joined<["/", "-"], "REPLACEMENTS:">, HelpText<"Not supported.">, MetaVarName<"file">, Group; +def MANAGEDASSEMBLYNAME : Joined<["/", "-"], "MANAGEDASSEMBLYNAME:">, HelpText<"Not supported.">, MetaVarName<"assembly">, Group; +def NODEPENDENCY : Flag<["/", "-"], "NODEPENDENCY">, HelpText<"Not supported.">, Group; +def CATEGORY : Flag<["/", "-"], "CATEGORY">, HelpText<"Not supported.">, Group; +def NOLOGO : 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 INPUTRESOURCE : Joined<["/", "-"], "INPUTRESOURCE:">, HelpText<"Not supported.">, MetaVarName<"file">, Group; +def OUTPUTRESOURCE : Joined<["/", "-"], "OUTPUTRESOURCE:">, HelpText<"Not supported.">, MetaVarName<"file">, Group; +def OUTPUTRESOURCE_FLAG : Flag<["/", "-"], "OUTPUTRESOURCE">, Alias, HelpText<"Not supported.">, Group; +def UPDATERESOURCE : Joined<["/", "-"], "UPDATERESOURCE:">, HelpText<"Not supported.">, MetaVarName<"file">, Group; +def HASHUPDATE : Joined<["/", "-"], "HASHUPDATE:">, HelpText<"Not supported.">, MetaVarName<"file">, Group; +def HASHUPDATE_FLAG : Flag<["/", "-"], "HASHUPDATE">, Alias, HelpText<"Not supported.">, Group; +def VALIDATE_MANIFEST : Flag<["/", "-"], "VALIDATE_MANIFEST">, HelpText<"Not supported.">, Group; +def VALIDATE_FILE_HASHES : Joined<["/", "-"], "VALIDATE_FILE_HASHES:">, HelpText<"Not supported.">, MetaVarName<"">, Group; +def CANONICALIZE : Flag<["/", "-"], "HASHUPDATE:">, HelpText<"Not supported.">, Group; +def CHECK_FOR_DUPLICATES : Flag<["/", "-"], "CHECK_FOR_DUPLICATES:">, HelpText<"Not supported.">, Group; +def MAKECDFS : 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; Index: llvm/tools/llvm-mt/llvm-mt.cpp =================================================================== --- /dev/null +++ llvm/tools/llvm-mt/llvm-mt.cpp @@ -0,0 +1,161 @@ +//===- llvm-mt.cpp - Merge .manifest files ---------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===---------------------------------------------------------------------===// +// +// Merge .manifest files. This is intended to be a platform-independent port +// of Microsoft's mt.exe. +// +//===---------------------------------------------------------------------===// + +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/WindowsManifestMerger.h" +#include "llvm/Support/raw_ostream.h" + +#include + +using namespace llvm; + +namespace { + +enum ID { + OPT_INVALID = 0, // This is not an option ID. +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + OPT_##ID, +#include "Opts.inc" +#undef OPTION +}; + +#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; +#include "Opts.inc" +#undef PREFIX + +static const opt::OptTable::Info InfoTable[] = { +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + { \ + PREFIX, NAME, HELPTEXT, \ + METAVAR, OPT_##ID, opt::Option::KIND##Class, \ + PARAM, FLAGS, OPT_##GROUP, \ + OPT_##ALIAS, ALIASARGS, VALUES}, +#include "Opts.inc" +#undef OPTION +}; + +class CvtResOptTable : public opt::OptTable { +public: + CvtResOptTable() : OptTable(InfoTable, true) {} +}; + +static ExitOnError ExitOnErr; +} // namespace + +LLVM_ATTRIBUTE_NORETURN void reportError(Twine Msg) { + errs() << "llvm-mt error: " << Msg << ".\n"; + exit(1); +} + +static void reportError(StringRef Input, std::error_code EC) { + reportError(Twine(Input) + ": " + EC.message()); +} + +void error(std::error_code EC) { + if (!EC) + return; + reportError(EC.message()); +} + +void error(Error EC) { + if (!EC) + return; + handleAllErrors(std::move(EC), + [&](const ErrorInfoBase &EI) { reportError(EI.message()); }); +} + +int main(int argc_, const char *argv_[]) { + sys::PrintStackTraceOnErrorSignal(argv_[0]); + PrettyStackTraceProgram X(argc_, argv_); + + ExitOnErr.setBanner("llvm-mt: "); + + SmallVector argv; + SpecificBumpPtrAllocator ArgAllocator; + ExitOnErr(errorCodeToError(sys::Process::GetArgumentVector( + argv, makeArrayRef(argv_, argc_), ArgAllocator))); + + llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + + CvtResOptTable T; + unsigned MAI, MAC; + ArrayRef ArgsArr = makeArrayRef(argv_ + 1, argc_); + opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MAI, MAC); + + for (auto &Arg : InputArgs) { + if (Arg->getOption().matches(OPT_UNSUPPORTED)) { + outs() << "Ignoring " << Arg->getOption().getName() + << " option; not supported.\n"; + } + } + + if (InputArgs.hasArg(OPT_HELP)) { + T.PrintHelp(outs(), "mt", "Manifest Tool", false); + return 0; + } + + std::vector InputFiles = InputArgs.getAllArgValues(OPT_MANIFEST); + + if (InputFiles.size() == 0) { + reportError("No input file specified"); + } + + StringRef OutputFile; + + if (InputArgs.hasArg(OPT_OUT)) { + OutputFile = InputArgs.getLastArgValue(OPT_OUT); + } else if (InputFiles.size() == 1) { + OutputFile = InputFiles[0]; + } else { + reportError("No output file specified"); + } + + WindowsManifestMerger Merger; + + for (const auto &File : InputFiles) { + ErrorOr> ManifestOrErr = + MemoryBuffer::getFile(File); + if (!ManifestOrErr) + reportError(File, ManifestOrErr.getError()); + MemoryBuffer &Manifest = *ManifestOrErr.get(); + error(Merger.merge(Manifest)); + } + + std::unique_ptr OutputBuffer = Merger.getMergedManifest(); + if (OutputBuffer->getBufferSize() == 0) { + outs() << "Empty manifest not written.\n"; + } else { + ErrorOr> FileOrErr = + FileOutputBuffer::create(OutputFile, OutputBuffer->getBufferSize()); + if (!FileOrErr) + reportError(OutputFile, FileOrErr.getError()); + std::unique_ptr FileBuffer = std::move(*FileOrErr); + std::copy(OutputBuffer->getBufferStart(), OutputBuffer->getBufferEnd(), + FileBuffer->getBufferStart()); + error(FileBuffer->commit()); + } + + return 0; +}