[llvm] r308679 - Implement parsing and writing of a single xml manifest file.

Eric Beckmann via llvm-commits llvm-commits at lists.llvm.org
Thu Jul 20 14:42:04 PDT 2017


Author: ecbeckmann
Date: Thu Jul 20 14:42:04 2017
New Revision: 308679

URL: http://llvm.org/viewvc/llvm-project?rev=308679&view=rev
Log:
Implement parsing and writing of a single xml manifest file.

Summary: Implement parsing and writing of a single xml manifest file.

Subscribers: mgorny, llvm-commits, hiraditya

Differential Revision: https://reviews.llvm.org/D35425

Added:
    llvm/trunk/include/llvm/Support/WindowsManifestMerger.h
    llvm/trunk/lib/Support/WindowsManifestMerger.cpp
    llvm/trunk/test/tools/llvm-mt/Inputs/
    llvm/trunk/test/tools/llvm-mt/Inputs/bad.manifest   (with props)
    llvm/trunk/test/tools/llvm-mt/Inputs/test_manifest.manifest
    llvm/trunk/test/tools/llvm-mt/single_file.test
    llvm/trunk/test/tools/llvm-mt/xml_error.test
Modified:
    llvm/trunk/CMakeLists.txt
    llvm/trunk/cmake/config-ix.cmake
    llvm/trunk/include/llvm/Config/config.h.cmake
    llvm/trunk/lib/Support/CMakeLists.txt
    llvm/trunk/test/CMakeLists.txt
    llvm/trunk/test/lit.cfg
    llvm/trunk/test/lit.site.cfg.in
    llvm/trunk/test/tools/llvm-mt/help.test
    llvm/trunk/tools/LLVMBuild.txt
    llvm/trunk/tools/llvm-mt/llvm-mt.cpp

Modified: llvm/trunk/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/CMakeLists.txt?rev=308679&r1=308678&r2=308679&view=diff
==============================================================================
--- llvm/trunk/CMakeLists.txt (original)
+++ llvm/trunk/CMakeLists.txt Thu Jul 20 14:42:04 2017
@@ -363,6 +363,8 @@ set(LLVM_TARGET_ARCH "host"
 
 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)

Modified: llvm/trunk/cmake/config-ix.cmake
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/cmake/config-ix.cmake?rev=308679&r1=308678&r2=308679&view=diff
==============================================================================
--- llvm/trunk/cmake/config-ix.cmake (original)
+++ llvm/trunk/cmake/config-ix.cmake Thu Jul 20 14:42:04 2017
@@ -155,6 +155,17 @@ if( NOT PURE_WINDOWS AND NOT LLVM_USE_SA
   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)

Modified: llvm/trunk/include/llvm/Config/config.h.cmake
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Config/config.h.cmake?rev=308679&r1=308678&r2=308679&view=diff
==============================================================================
--- llvm/trunk/include/llvm/Config/config.h.cmake (original)
+++ llvm/trunk/include/llvm/Config/config.h.cmake Thu Jul 20 14:42:04 2017
@@ -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}"
 

Added: llvm/trunk/include/llvm/Support/WindowsManifestMerger.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Support/WindowsManifestMerger.h?rev=308679&view=auto
==============================================================================
--- llvm/trunk/include/llvm/Support/WindowsManifestMerger.h (added)
+++ llvm/trunk/include/llvm/Support/WindowsManifestMerger.h Thu Jul 20 14:42:04 2017
@@ -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 <libxml/xmlreader.h>
+#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<WindowsManifestError, ECError> {
+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<MemoryBuffer> getMergedManifest();
+
+private:
+  static void errorCallback(void *Ctx, const char *Format, ...);
+  Error getParseError();
+
+  XMLNodeImpl CombinedRoot = nullptr;
+  bool ParseErrorOccurred = false;
+};
+
+} // namespace llvm
+#endif

Modified: llvm/trunk/lib/Support/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/CMakeLists.txt?rev=308679&r1=308678&r2=308679&view=diff
==============================================================================
--- llvm/trunk/lib/Support/CMakeLists.txt (original)
+++ llvm/trunk/lib/Support/CMakeLists.txt Thu Jul 20 14:42:04 2017
@@ -27,6 +27,9 @@ elseif( CMAKE_HOST_UNIX )
   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 @@ add_llvm_library(LLVMSupport
   Triple.cpp
   Twine.cpp
   Unicode.cpp
+  WindowsManifestMerger.cpp
   YAMLParser.cpp
   YAMLTraits.cpp
   raw_os_ostream.cpp

Added: llvm/trunk/lib/Support/WindowsManifestMerger.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/WindowsManifestMerger.cpp?rev=308679&view=auto
==============================================================================
--- llvm/trunk/lib/Support/WindowsManifestMerger.cpp (added)
+++ llvm/trunk/lib/Support/WindowsManifestMerger.cpp Thu Jul 20 14:42:04 2017
@@ -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 <stdarg.h>
+
+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<MemoryBuffer> WindowsManifestMerger::getMergedManifest() {
+#if LLVM_LIBXML2_ENABLED
+  unsigned char *XmlBuff;
+  int BufferSize = 0;
+  if (CombinedRoot) {
+    std::unique_ptr<xmlDoc> 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<const char *>(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<WindowsManifestError>("invalid xml document");
+}
+
+} // namespace llvm

Modified: llvm/trunk/test/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CMakeLists.txt?rev=308679&r1=308678&r2=308679&view=diff
==============================================================================
--- llvm/trunk/test/CMakeLists.txt (original)
+++ llvm/trunk/test/CMakeLists.txt Thu Jul 20 14:42:04 2017
@@ -59,7 +59,6 @@ set(LLVM_TEST_DEPENDS
           llvm-mc
           llvm-mcmarkup
           llvm-modextract
-          llvm-mt
           llvm-nm
           llvm-objdump
           llvm-opt-report
@@ -140,6 +139,12 @@ if(TARGET ocaml_llvm)
         )
 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")
 

Modified: llvm/trunk/test/lit.cfg
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/lit.cfg?rev=308679&r1=308678&r2=308679&view=diff
==============================================================================
--- llvm/trunk/test/lit.cfg (original)
+++ llvm/trunk/test/lit.cfg Thu Jul 20 14:42:04 2017
@@ -288,6 +288,7 @@ for pattern in [r"\bbugpoint\b(?!-)",
                 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 pattern in [r"\bbugpoint\b(?!-)",
 # 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.have_libxar:
 
 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

Modified: llvm/trunk/test/lit.site.cfg.in
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/lit.site.cfg.in?rev=308679&r1=308678&r2=308679&view=diff
==============================================================================
--- llvm/trunk/test/lit.site.cfg.in (original)
+++ llvm/trunk/test/lit.site.cfg.in Thu Jul 20 14:42:04 2017
@@ -40,6 +40,8 @@ config.have_libxar = @HAVE_LIBXAR@
 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.

Added: llvm/trunk/test/tools/llvm-mt/Inputs/bad.manifest
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-mt/Inputs/bad.manifest?rev=308679&view=auto
==============================================================================
Binary file - no diff available.

Propchange: llvm/trunk/test/tools/llvm-mt/Inputs/bad.manifest
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: llvm/trunk/test/tools/llvm-mt/Inputs/test_manifest.manifest
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-mt/Inputs/test_manifest.manifest?rev=308679&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-mt/Inputs/test_manifest.manifest (added)
+++ llvm/trunk/test/tools/llvm-mt/Inputs/test_manifest.manifest Thu Jul 20 14:42:04 2017
@@ -0,0 +1,15 @@
+<?xml version="1.0"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+  <trustInfo>
+    <security>
+      <requestedPrivileges>
+         <requestedExecutionLevel level="3" uiAccess="1"/>
+      </requestedPrivileges>
+    </security>
+  </trustInfo>
+  <dependency>
+    <dependentAssembly>
+      <assemblyIdentity program="displayDriver"/>
+    </dependentAssembly>
+  </dependency>
+</assembly>

Modified: llvm/trunk/test/tools/llvm-mt/help.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-mt/help.test?rev=308679&r1=308678&r2=308679&view=diff
==============================================================================
--- llvm/trunk/test/tools/llvm-mt/help.test (original)
+++ llvm/trunk/test/tools/llvm-mt/help.test Thu Jul 20 14:42:04 2017
@@ -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

Added: llvm/trunk/test/tools/llvm-mt/single_file.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-mt/single_file.test?rev=308679&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-mt/single_file.test (added)
+++ llvm/trunk/test/tools/llvm-mt/single_file.test Thu Jul 20 14:42:04 2017
@@ -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

Added: llvm/trunk/test/tools/llvm-mt/xml_error.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-mt/xml_error.test?rev=308679&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-mt/xml_error.test (added)
+++ llvm/trunk/test/tools/llvm-mt/xml_error.test Thu Jul 20 14:42:04 2017
@@ -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

Modified: llvm/trunk/tools/LLVMBuild.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/LLVMBuild.txt?rev=308679&r1=308678&r2=308679&view=diff
==============================================================================
--- llvm/trunk/tools/LLVMBuild.txt (original)
+++ llvm/trunk/tools/LLVMBuild.txt Thu Jul 20 14:42:04 2017
@@ -38,6 +38,7 @@ subdirectories =
  llvm-mc
  llvm-mcmarkup
  llvm-modextract
+ llvm-mt
  llvm-nm
  llvm-objdump
  llvm-pdbutil

Modified: llvm/trunk/tools/llvm-mt/llvm-mt.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-mt/llvm-mt.cpp?rev=308679&r1=308678&r2=308679&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-mt/llvm-mt.cpp (original)
+++ llvm/trunk/tools/llvm-mt/llvm-mt.cpp Thu Jul 20 14:42:04 2017
@@ -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 <system_error>
@@ -67,6 +69,22 @@ LLVM_ATTRIBUTE_NORETURN void reportError
   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 @@ int main(int argc, const char **argv) {
   }
 
   StringRef OutputFile;
-
   if (InputArgs.hasArg(OPT_out)) {
     OutputFile = InputArgs.getLastArgValue(OPT_out);
   } else if (InputFiles.size() == 1) {
@@ -113,5 +130,27 @@ int main(int argc, const char **argv) {
     reportError("no output file specified");
   }
 
+  WindowsManifestMerger Merger;
+
+  for (const auto &File : InputFiles) {
+    ErrorOr<std::unique_ptr<MemoryBuffer>> ManifestOrErr =
+        MemoryBuffer::getFile(File);
+    if (!ManifestOrErr)
+      reportError(File, ManifestOrErr.getError());
+    MemoryBuffer &Manifest = *ManifestOrErr.get();
+    error(Merger.merge(Manifest));
+  }
+
+  std::unique_ptr<MemoryBuffer> OutputBuffer = Merger.getMergedManifest();
+  if (!OutputBuffer)
+    reportError("empty manifest not written");
+  ErrorOr<std::unique_ptr<FileOutputBuffer>> FileOrErr =
+      FileOutputBuffer::create(OutputFile, OutputBuffer->getBufferSize());
+  if (!FileOrErr)
+    reportError(OutputFile, FileOrErr.getError());
+  std::unique_ptr<FileOutputBuffer> FileBuffer = std::move(*FileOrErr);
+  std::copy(OutputBuffer->getBufferStart(), OutputBuffer->getBufferEnd(),
+            FileBuffer->getBufferStart());
+  error(FileBuffer->commit());
   return 0;
 }




More information about the llvm-commits mailing list