Index: llvm/trunk/CMakeLists.txt =================================================================== --- llvm/trunk/CMakeLists.txt +++ llvm/trunk/CMakeLists.txt @@ -363,6 +363,8 @@ option(LLVM_ENABLE_TERMINFO "Use terminfo database if available." ON) +option(LLVM_ENABLE_LIBXML2 "Use libxml2 if available." ON) + option(LLVM_ENABLE_LIBEDIT "Use libedit if available." ON) option(LLVM_ENABLE_THREADS "Use threads if available." ON) Index: llvm/trunk/cmake/config-ix.cmake =================================================================== --- llvm/trunk/cmake/config-ix.cmake +++ llvm/trunk/cmake/config-ix.cmake @@ -155,6 +155,17 @@ else() set(HAVE_TERMINFO 0) endif() + + set(LLVM_LIBXML2_ENABLED 0) + set(LIBXML2_FOUND 0) + if(LLVM_ENABLE_LIBXML2) + find_package(LibXml2) + if (LIBXML2_FOUND) + set(LLVM_LIBXML2_ENABLED 1) + include_directories(${LIBXML2_INCLUDE_DIR}) + set(LIBXML2_LIBS "xml2") + endif() + endif() endif() check_library_exists(xar xar_open "" HAVE_LIBXAR) Index: llvm/trunk/include/llvm/Config/config.h.cmake =================================================================== --- llvm/trunk/include/llvm/Config/config.h.cmake +++ llvm/trunk/include/llvm/Config/config.h.cmake @@ -383,6 +383,9 @@ /* LLVM version string */ #define LLVM_VERSION_STRING "${PACKAGE_VERSION}" +/* Define if libxml2 is supported on this platform. */ +#cmakedefine LLVM_LIBXML2_ENABLED ${LLVM_LIBXML2_ENABLED} + /* Define to the extension used for shared libraries, say, ".so". */ #cmakedefine LTDL_SHLIB_EXT "${LTDL_SHLIB_EXT}" Index: llvm/trunk/include/llvm/Support/WindowsManifestMerger.h =================================================================== --- llvm/trunk/include/llvm/Support/WindowsManifestMerger.h +++ llvm/trunk/include/llvm/Support/WindowsManifestMerger.h @@ -0,0 +1,75 @@ +//===-- 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 + +#include "llvm/Config/config.h" +#include "llvm/Support/Error.h" + +#if LLVM_LIBXML2_ENABLED +#include +#endif + +namespace llvm { + +class MemoryBuffer; + +#if LLVM_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(const Twine &Msg); + void log(raw_ostream &OS) const override; + +private: + std::string Msg; +}; + +class WindowsManifestMerger { +public: + Error merge(const MemoryBuffer &Manifest); + + // Returns vector containing merged xml manifest, or uninitialized vector for + // empty manifest. + std::unique_ptr getMergedManifest(); + +private: + static void errorCallback(void *Ctx, const char *Format, ...); + Error getParseError(); + + XMLNodeImpl CombinedRoot = nullptr; + bool ParseErrorOccurred = false; +}; + +} // namespace llvm +#endif Index: llvm/trunk/lib/Support/CMakeLists.txt =================================================================== --- llvm/trunk/lib/Support/CMakeLists.txt +++ llvm/trunk/lib/Support/CMakeLists.txt @@ -27,6 +27,9 @@ if( UNIX AND NOT (BEOS OR HAIKU) ) set(system_libs ${system_libs} m) endif() + if( LLVM_LIBXML2_ENABLED ) + set(system_libs ${system_libs} ${LIBXML2_LIBS}) + endif() endif( MSVC OR MINGW ) add_llvm_library(LLVMSupport @@ -110,6 +113,7 @@ Triple.cpp Twine.cpp Unicode.cpp + WindowsManifestMerger.cpp YAMLParser.cpp YAMLTraits.cpp raw_os_ostream.cpp Index: llvm/trunk/lib/Support/WindowsManifestMerger.cpp =================================================================== --- llvm/trunk/lib/Support/WindowsManifestMerger.cpp +++ llvm/trunk/lib/Support/WindowsManifestMerger.cpp @@ -0,0 +1,70 @@ +//===-- 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(const Twine &Msg) : Msg(Msg.str()) {} + +void WindowsManifestError::log(raw_ostream &OS) const { OS << Msg; } + +Error WindowsManifestMerger::merge(const MemoryBuffer &Manifest) { +#if LLVM_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() { +#if LLVM_LIBXML2_ENABLED + unsigned char *XmlBuff; + int BufferSize = 0; + if (CombinedRoot) { + std::unique_ptr OutputDoc(xmlNewDoc((const unsigned char *)"1.0")); + xmlDocSetRootElement(OutputDoc.get(), CombinedRoot); + xmlDocDumpMemory(OutputDoc.get(), &XmlBuff, &BufferSize); + } + if (BufferSize == 0) + return nullptr; + return MemoryBuffer::getMemBuffer( + StringRef(reinterpret_cast(XmlBuff), (size_t)BufferSize)); +#else + return nullptr; +#endif +} + +void WindowsManifestMerger::errorCallback(void *Ctx, const char *Format, ...) { + auto *Merger = (WindowsManifestMerger *)Ctx; + Merger->ParseErrorOccurred = true; +} + +Error WindowsManifestMerger::getParseError() { + if (!ParseErrorOccurred) + return Error::success(); + return make_error("invalid xml document"); +} + +} // namespace llvm Index: llvm/trunk/test/CMakeLists.txt =================================================================== --- llvm/trunk/test/CMakeLists.txt +++ llvm/trunk/test/CMakeLists.txt @@ -59,7 +59,6 @@ llvm-mc llvm-mcmarkup llvm-modextract - llvm-mt llvm-nm llvm-objdump llvm-opt-report @@ -140,6 +139,12 @@ ) endif() +if (LLVM_LIBXML2_ENABLED) + 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/trunk/test/lit.cfg =================================================================== --- llvm/trunk/test/lit.cfg +++ llvm/trunk/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", @@ -550,3 +552,7 @@ if config.enable_abi_breaking_checks == "1": config.available_features.add('abi-breaking-checks') + +if config.llvm_disable_libxml2 == "OFF" and config.have_libxml2 == "TRUE": + config.available_features.add('libxml2') + \ No newline at end of file Index: llvm/trunk/test/lit.site.cfg.in =================================================================== --- llvm/trunk/test/lit.site.cfg.in +++ llvm/trunk/test/lit.site.cfg.in @@ -40,6 +40,8 @@ config.have_dia_sdk = @LLVM_ENABLE_DIA_SDK@ config.enable_ffi = @LLVM_ENABLE_FFI@ config.build_shared_libs = @BUILD_SHARED_LIBS@ +config.llvm_disable_libxml2 = "@LLVM_DISABLE_LIBXML2@" +config.have_libxml2 = "@LIBXML2_FOUND@" # Support substitution of the tools_dir with user parameters. This is # used when we can't determine the tool dir at configuration time. Index: llvm/trunk/test/tools/llvm-mt/Inputs/test_manifest.manifest =================================================================== --- llvm/trunk/test/tools/llvm-mt/Inputs/test_manifest.manifest +++ llvm/trunk/test/tools/llvm-mt/Inputs/test_manifest.manifest @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + Index: llvm/trunk/test/tools/llvm-mt/help.test =================================================================== --- llvm/trunk/test/tools/llvm-mt/help.test +++ llvm/trunk/test/tools/llvm-mt/help.test @@ -1,7 +1,3 @@ RUN: llvm-mt /h | FileCheck %s -check-prefix=HELP -RUN: llvm-mt /inputresource:foo.res /manifest foo.manifest | FileCheck %s -check-prefix=NOT_SUPPORTED - HELP: OVERVIEW: Manifest Tool - -NOT_SUPPORTED: llvm-mt: ignoring unsupported 'inputresource:' option Index: llvm/trunk/test/tools/llvm-mt/single_file.test =================================================================== --- llvm/trunk/test/tools/llvm-mt/single_file.test +++ llvm/trunk/test/tools/llvm-mt/single_file.test @@ -0,0 +1,5 @@ +REQUIRES: libxml2 +UNSUPPORTED: windows + +RUN: llvm-mt /manifest %p/Inputs/test_manifest.manifest /out:%t +RUN: diff %p/Inputs/test_manifest.manifest %t Index: llvm/trunk/test/tools/llvm-mt/xml_error.test =================================================================== --- llvm/trunk/test/tools/llvm-mt/xml_error.test +++ llvm/trunk/test/tools/llvm-mt/xml_error.test @@ -0,0 +1,11 @@ +REQUIRES: libxml2 +UNSUPPORTED: windows + +RUN: not llvm-mt /manifest %p/Inputs/bad.manifest 2>&1 >/dev/null | FileCheck %s + +CHECK: llvm-mt error: invalid xml document + +RUN: llvm-mt /inputresource:foo.res /manifest \ +RUN: %p/Inputs/test_manifest.manifest | FileCheck %s -check-prefix=NOT_SUPPORTED + +NOT_SUPPORTED: llvm-mt: ignoring unsupported 'inputresource:' option Index: llvm/trunk/tools/LLVMBuild.txt =================================================================== --- llvm/trunk/tools/LLVMBuild.txt +++ llvm/trunk/tools/LLVMBuild.txt @@ -38,6 +38,7 @@ llvm-mc llvm-mcmarkup llvm-modextract + llvm-mt llvm-nm llvm-objdump llvm-pdbutil Index: llvm/trunk/tools/llvm-mt/llvm-mt.cpp =================================================================== --- llvm/trunk/tools/llvm-mt/llvm-mt.cpp +++ llvm/trunk/tools/llvm-mt/llvm-mt.cpp @@ -16,11 +16,13 @@ #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 @@ -67,6 +69,22 @@ exit(1); } +static void reportError(StringRef Input, std::error_code EC) { + reportError(Twine(Input) + ": " + EC.message()); +} + +void error(std::error_code EC) { + if (EC) + reportError(EC.message()); +} + +void error(Error EC) { + if (EC) + 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); @@ -104,7 +122,6 @@ } StringRef OutputFile; - if (InputArgs.hasArg(OPT_out)) { OutputFile = InputArgs.getLastArgValue(OPT_out); } else if (InputFiles.size() == 1) { @@ -113,5 +130,27 @@ 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) + reportError("empty manifest not written"); + 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; }