forked from OSchip/llvm-project
llvm-mt: Merge manifest namespaces.
mt.exe performs a tree merge where certain element nodes are combined into one. This introduces the possibility of xml namespaces conflicting with each other. The original mt.exe has a hierarchy whereby certain namespace names can override others, and nodes that would then end up in ambigious namespaces have their namespaces explicitly defined. This namespace handles this merging process. llvm-svn: 311215
This commit is contained in:
parent
42479e02ca
commit
91d8af5386
|
@ -29,21 +29,11 @@
|
||||||
#include "llvm/Config/config.h"
|
#include "llvm/Config/config.h"
|
||||||
#include "llvm/Support/Error.h"
|
#include "llvm/Support/Error.h"
|
||||||
|
|
||||||
#if LLVM_LIBXML2_ENABLED
|
|
||||||
#include <libxml/xmlreader.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace llvm {
|
namespace llvm {
|
||||||
|
|
||||||
class MemoryBuffer;
|
class MemoryBuffer;
|
||||||
|
|
||||||
#if LLVM_LIBXML2_ENABLED
|
namespace windows_manifest {
|
||||||
typedef xmlDocPtr XMLDocumentImpl;
|
|
||||||
typedef xmlNodePtr XMLNodeImpl;
|
|
||||||
#else
|
|
||||||
typedef void *XMLDocumentImpl;
|
|
||||||
typedef void *XMLNodeImpl;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class WindowsManifestError : public ErrorInfo<WindowsManifestError, ECError> {
|
class WindowsManifestError : public ErrorInfo<WindowsManifestError, ECError> {
|
||||||
public:
|
public:
|
||||||
|
@ -57,8 +47,8 @@ private:
|
||||||
|
|
||||||
class WindowsManifestMerger {
|
class WindowsManifestMerger {
|
||||||
public:
|
public:
|
||||||
|
WindowsManifestMerger();
|
||||||
~WindowsManifestMerger();
|
~WindowsManifestMerger();
|
||||||
|
|
||||||
Error merge(const MemoryBuffer &Manifest);
|
Error merge(const MemoryBuffer &Manifest);
|
||||||
|
|
||||||
// Returns vector containing merged xml manifest, or uninitialized vector for
|
// Returns vector containing merged xml manifest, or uninitialized vector for
|
||||||
|
@ -66,15 +56,10 @@ public:
|
||||||
std::unique_ptr<MemoryBuffer> getMergedManifest();
|
std::unique_ptr<MemoryBuffer> getMergedManifest();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void errorCallback(void *Ctx, const char *Format, ...);
|
class WindowsManifestMergerImpl;
|
||||||
Error getParseError();
|
std::unique_ptr<WindowsManifestMergerImpl> Impl;
|
||||||
|
|
||||||
#if LLVM_LIBXML2_ENABLED
|
|
||||||
XMLDocumentImpl CombinedDoc = nullptr;
|
|
||||||
std::vector<XMLDocumentImpl> MergedDocs;
|
|
||||||
#endif
|
|
||||||
bool ParseErrorOccurred = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // namespace windows_manifest
|
||||||
} // namespace llvm
|
} // namespace llvm
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -14,14 +14,17 @@
|
||||||
#include "llvm/WindowsManifest/WindowsManifestMerger.h"
|
#include "llvm/WindowsManifest/WindowsManifestMerger.h"
|
||||||
#include "llvm/Support/MemoryBuffer.h"
|
#include "llvm/Support/MemoryBuffer.h"
|
||||||
|
|
||||||
#include <stdarg.h>
|
#include <map>
|
||||||
|
|
||||||
|
#if LLVM_LIBXML2_ENABLED
|
||||||
|
#include <libxml/xmlreader.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#define TO_XML_CHAR(X) reinterpret_cast<const unsigned char *>(X)
|
#define TO_XML_CHAR(X) reinterpret_cast<const unsigned char *>(X)
|
||||||
#define FROM_XML_CHAR(X) reinterpret_cast<const char *>(X)
|
#define FROM_XML_CHAR(X) reinterpret_cast<const char *>(X)
|
||||||
|
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
|
using namespace windows_manifest;
|
||||||
namespace llvm {
|
|
||||||
|
|
||||||
char WindowsManifestError::ID = 0;
|
char WindowsManifestError::ID = 0;
|
||||||
|
|
||||||
|
@ -29,177 +32,676 @@ WindowsManifestError::WindowsManifestError(const Twine &Msg) : Msg(Msg.str()) {}
|
||||||
|
|
||||||
void WindowsManifestError::log(raw_ostream &OS) const { OS << Msg; }
|
void WindowsManifestError::log(raw_ostream &OS) const { OS << Msg; }
|
||||||
|
|
||||||
|
class WindowsManifestMerger::WindowsManifestMergerImpl {
|
||||||
|
public:
|
||||||
|
~WindowsManifestMergerImpl();
|
||||||
|
Error merge(const MemoryBuffer &Manifest);
|
||||||
|
std::unique_ptr<MemoryBuffer> getMergedManifest();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void errorCallback(void *Ctx, const char *Format, ...);
|
||||||
|
Error getParseError();
|
||||||
#if LLVM_LIBXML2_ENABLED
|
#if LLVM_LIBXML2_ENABLED
|
||||||
|
xmlDocPtr CombinedDoc = nullptr;
|
||||||
|
std::vector<xmlDocPtr> MergedDocs;
|
||||||
|
#endif
|
||||||
|
bool ParseErrorOccurred = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
#if LLVM_LIBXML2_ENABLED
|
||||||
|
|
||||||
|
static const std::pair<StringRef, StringRef> MtNsHrefsPrefixes[] = {
|
||||||
|
{"urn:schemas-microsoft-com:asm.v1", "ms_asmv1"},
|
||||||
|
{"urn:schemas-microsoft-com:asm.v2", "ms_asmv2"},
|
||||||
|
{"urn:schemas-microsoft-com:asm.v3", "ms_asmv3"},
|
||||||
|
{"http://schemas.microsoft.com/SMI/2005/WindowsSettings",
|
||||||
|
"ms_windowsSettings"},
|
||||||
|
{"urn:schemas-microsoft-com:compatibility.v1", "ms_compatibilityv1"}};
|
||||||
|
|
||||||
static bool xmlStringsEqual(const unsigned char *A, const unsigned char *B) {
|
static bool xmlStringsEqual(const unsigned char *A, const unsigned char *B) {
|
||||||
|
// Handle null pointers. Comparison of 2 null pointers returns true because
|
||||||
|
// this indicates the prefix of a default namespace.
|
||||||
|
if (!A || !B)
|
||||||
|
return A == B;
|
||||||
return strcmp(FROM_XML_CHAR(A), FROM_XML_CHAR(B)) == 0;
|
return strcmp(FROM_XML_CHAR(A), FROM_XML_CHAR(B)) == 0;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
bool isMergeableElement(const unsigned char *ElementName) {
|
static bool isMergeableElement(const unsigned char *ElementName) {
|
||||||
for (StringRef S : {"application", "assembly", "assemblyIdentity",
|
for (StringRef S : {"application", "assembly", "assemblyIdentity",
|
||||||
"compatibility", "noInherit", "requestedExecutionLevel",
|
"compatibility", "noInherit", "requestedExecutionLevel",
|
||||||
"requestedPrivileges", "security", "trustInfo"}) {
|
"requestedPrivileges", "security", "trustInfo"}) {
|
||||||
if (S == FROM_XML_CHAR(ElementName))
|
if (S == FROM_XML_CHAR(ElementName)) {
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
XMLNodeImpl getChildWithName(XMLNodeImpl Parent,
|
static xmlNodePtr getChildWithName(xmlNodePtr Parent,
|
||||||
const unsigned char *ElementName) {
|
const unsigned char *ElementName) {
|
||||||
#if LLVM_LIBXML2_ENABLED
|
for (xmlNodePtr Child = Parent->children; Child; Child = Child->next) {
|
||||||
for (XMLNodeImpl Child = Parent->children; Child; Child = Child->next)
|
|
||||||
if (xmlStringsEqual(Child->name, ElementName)) {
|
if (xmlStringsEqual(Child->name, ElementName)) {
|
||||||
return Child;
|
return Child;
|
||||||
}
|
}
|
||||||
#endif
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const unsigned char *getAttribute(XMLNodeImpl Node,
|
static xmlAttrPtr getAttribute(xmlNodePtr Node,
|
||||||
const unsigned char *AttributeName) {
|
const unsigned char *AttributeName) {
|
||||||
#if LLVM_LIBXML2_ENABLED
|
|
||||||
for (xmlAttrPtr Attribute = Node->properties; Attribute != nullptr;
|
for (xmlAttrPtr Attribute = Node->properties; Attribute != nullptr;
|
||||||
Attribute = Attribute->next) {
|
Attribute = Attribute->next) {
|
||||||
if (xmlStringsEqual(Attribute->name, AttributeName))
|
if (xmlStringsEqual(Attribute->name, AttributeName)) {
|
||||||
return Attribute->children->content;
|
return Attribute;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Error mergeAttributes(XMLNodeImpl OriginalNode, XMLNodeImpl AdditionalNode) {
|
// Check if namespace specified by HRef1 overrides that of HRef2.
|
||||||
#if LLVM_LIBXML2_ENABLED
|
static bool namespaceOverrides(const unsigned char *HRef1,
|
||||||
for (xmlAttrPtr Attribute = AdditionalNode->properties; Attribute != nullptr;
|
const unsigned char *HRef2) {
|
||||||
Attribute = Attribute->next) {
|
auto HRef1Position = llvm::find_if(
|
||||||
if (const unsigned char *OriginalValue =
|
MtNsHrefsPrefixes, [=](const std::pair<StringRef, StringRef> &Element) {
|
||||||
getAttribute(OriginalNode, Attribute->name)) {
|
return xmlStringsEqual(HRef1, TO_XML_CHAR(Element.first.data()));
|
||||||
// Attributes of the same name must also have the same value. Otherwise
|
});
|
||||||
// an error is thrown.
|
auto HRef2Position = llvm::find_if(
|
||||||
if (!xmlStringsEqual(OriginalValue, Attribute->children->content))
|
MtNsHrefsPrefixes, [=](const std::pair<StringRef, StringRef> &Element) {
|
||||||
return make_error<WindowsManifestError>(
|
return xmlStringsEqual(HRef2, TO_XML_CHAR(Element.first.data()));
|
||||||
Twine("conflicting attributes for ") +
|
});
|
||||||
FROM_XML_CHAR(OriginalNode->name));
|
return HRef1Position < HRef2Position;
|
||||||
} else {
|
}
|
||||||
char *NameCopy = strdup(FROM_XML_CHAR(Attribute->name));
|
|
||||||
char *ContentCopy = strdup(FROM_XML_CHAR(Attribute->children->content));
|
// Search for prefix-defined namespace specified by HRef, starting on Node and
|
||||||
xmlNewProp(OriginalNode, TO_XML_CHAR(NameCopy), TO_XML_CHAR(ContentCopy));
|
// continuing recursively upwards. Returns the namespace or nullptr if not
|
||||||
|
// found.
|
||||||
|
static xmlNsPtr search(const unsigned char *HRef, xmlNodePtr Node) {
|
||||||
|
for (xmlNsPtr Def = Node->nsDef; Def; Def = Def->next) {
|
||||||
|
if (Def->prefix && xmlStringsEqual(Def->href, HRef)) {
|
||||||
|
return Def;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
if (Node->parent) {
|
||||||
|
return search(HRef, Node->parent);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the prefix that corresponds to the HRef. If HRef is not a recognized
|
||||||
|
// URI, then just return the HRef itself to use as the prefix.
|
||||||
|
static const unsigned char *getPrefixForHref(const unsigned char *HRef) {
|
||||||
|
for (auto &Ns : MtNsHrefsPrefixes) {
|
||||||
|
if (xmlStringsEqual(HRef, TO_XML_CHAR(Ns.first.data()))) {
|
||||||
|
return TO_XML_CHAR(Ns.second.data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return HRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search for prefix-defined namespace specified by HRef, starting on Node and
|
||||||
|
// continuing recursively upwards. If it is found, then return it. If it is
|
||||||
|
// not found, then prefix-define that namespace on the node and return a
|
||||||
|
// reference to it.
|
||||||
|
static Expected<xmlNsPtr> searchOrDefine(const unsigned char *HRef,
|
||||||
|
xmlNodePtr Node) {
|
||||||
|
if (xmlNsPtr Def = search(HRef, Node))
|
||||||
|
return Def;
|
||||||
|
if (xmlNsPtr Def = xmlNewNs(Node, HRef, getPrefixForHref(HRef)))
|
||||||
|
return Def;
|
||||||
|
return make_error<WindowsManifestError>("failed to create new namespace");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the namespace of OrigionalAttribute on OriginalNode to be that of
|
||||||
|
// AdditionalAttribute's.
|
||||||
|
static Error copyAttributeNamespace(xmlAttrPtr OriginalAttribute,
|
||||||
|
xmlNodePtr OriginalNode,
|
||||||
|
xmlAttrPtr AdditionalAttribute) {
|
||||||
|
|
||||||
|
Expected<xmlNsPtr> ExplicitOrError =
|
||||||
|
searchOrDefine(AdditionalAttribute->ns->href, OriginalNode);
|
||||||
|
if (!ExplicitOrError)
|
||||||
|
return ExplicitOrError.takeError();
|
||||||
|
OriginalAttribute->ns = std::move(ExplicitOrError.get());
|
||||||
return Error::success();
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
Error treeMerge(XMLNodeImpl OriginalRoot, XMLNodeImpl AdditionalRoot) {
|
// Return the corresponding namespace definition for the prefix, defined on the
|
||||||
#if LLVM_LIBXML2_ENABLED
|
// given Node. Returns nullptr if there is no such definition.
|
||||||
XMLNodeImpl AdditionalFirstChild = AdditionalRoot->children;
|
static xmlNsPtr getNamespaceWithPrefix(const unsigned char *Prefix,
|
||||||
|
xmlNodePtr Node) {
|
||||||
|
if (Node == nullptr)
|
||||||
|
return nullptr;
|
||||||
|
for (xmlNsPtr Def = Node->nsDef; Def; Def = Def->next) {
|
||||||
|
if (xmlStringsEqual(Def->prefix, Prefix)) {
|
||||||
|
return Def;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search for the closest inheritable default namespace, starting on (and
|
||||||
|
// including) the Node and traveling upwards through parent nodes. Returns
|
||||||
|
// nullptr if there are no inheritable default namespaces.
|
||||||
|
static xmlNsPtr getClosestDefault(xmlNodePtr Node) {
|
||||||
|
if (xmlNsPtr Ret = getNamespaceWithPrefix(nullptr, Node))
|
||||||
|
return Ret;
|
||||||
|
if (Node->parent == nullptr)
|
||||||
|
return nullptr;
|
||||||
|
return getClosestDefault(Node->parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge the attributes of AdditionalNode into OriginalNode. If attributes
|
||||||
|
// with identical types are present, they are not duplicated but rather if
|
||||||
|
// their values are not consistent and error is thrown. In addition, the
|
||||||
|
// higher priority namespace is used for each attribute, EXCEPT in the case
|
||||||
|
// of merging two default namespaces and the lower priority namespace
|
||||||
|
// definition occurs closer than the higher priority one.
|
||||||
|
static Error mergeAttributes(xmlNodePtr OriginalNode,
|
||||||
|
xmlNodePtr AdditionalNode) {
|
||||||
|
xmlNsPtr ClosestDefault = getClosestDefault(OriginalNode);
|
||||||
|
for (xmlAttrPtr Attribute = AdditionalNode->properties; Attribute;
|
||||||
|
Attribute = Attribute->next) {
|
||||||
|
if (xmlAttrPtr OriginalAttribute =
|
||||||
|
getAttribute(OriginalNode, Attribute->name)) {
|
||||||
|
if (!xmlStringsEqual(OriginalAttribute->children->content,
|
||||||
|
Attribute->children->content)) {
|
||||||
|
return make_error<WindowsManifestError>(
|
||||||
|
Twine("conflicting attributes for ") +
|
||||||
|
FROM_XML_CHAR(OriginalNode->name));
|
||||||
|
}
|
||||||
|
if (!Attribute->ns) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!OriginalAttribute->ns) {
|
||||||
|
if (auto E = copyAttributeNamespace(OriginalAttribute, OriginalNode,
|
||||||
|
Attribute)) {
|
||||||
|
return E;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (namespaceOverrides(OriginalAttribute->ns->href,
|
||||||
|
Attribute->ns->href)) {
|
||||||
|
// In this case, the original attribute has a higher priority namespace
|
||||||
|
// than the incomiing attribute, however the namespace definition of
|
||||||
|
// the lower priority namespace occurs first traveling upwards in the
|
||||||
|
// tree. Therefore the lower priority namespace is applied.
|
||||||
|
if (!OriginalAttribute->ns->prefix && !Attribute->ns->prefix &&
|
||||||
|
ClosestDefault &&
|
||||||
|
xmlStringsEqual(Attribute->ns->href, ClosestDefault->href)) {
|
||||||
|
if (auto E = copyAttributeNamespace(OriginalAttribute, OriginalNode,
|
||||||
|
Attribute)) {
|
||||||
|
return E;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
// This covers the case where the incoming attribute has the higher
|
||||||
|
// priority. The higher priority namespace is applied in all cases
|
||||||
|
// EXCEPT when both of the namespaces are default inherited, and the
|
||||||
|
// closest inherited default is the lower priority one.
|
||||||
|
}
|
||||||
|
if (Attribute->ns->prefix || OriginalAttribute->ns->prefix ||
|
||||||
|
(ClosestDefault && !xmlStringsEqual(OriginalAttribute->ns->href,
|
||||||
|
ClosestDefault->href))) {
|
||||||
|
if (auto E = copyAttributeNamespace(OriginalAttribute, OriginalNode,
|
||||||
|
Attribute)) {
|
||||||
|
return E;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// If the incoming attribute is not already found on the node, append it
|
||||||
|
// to the end of the properties list. Also explicitly apply its
|
||||||
|
// namespace as a prefix because it might be contained in a separate
|
||||||
|
// namespace that doesn't use the attribute.
|
||||||
|
xmlAttrPtr NewProp =
|
||||||
|
xmlNewProp(OriginalNode, Attribute->name, Attribute->children->content);
|
||||||
|
Expected<xmlNsPtr> ExplicitOrError =
|
||||||
|
searchOrDefine(Attribute->ns->href, OriginalNode);
|
||||||
|
if (!ExplicitOrError)
|
||||||
|
return ExplicitOrError.takeError();
|
||||||
|
NewProp->ns = std::move(ExplicitOrError.get());
|
||||||
|
}
|
||||||
|
return Error::success();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given two nodes, return the one with the higher priority namespace.
|
||||||
|
static xmlNodePtr getDominantNode(xmlNodePtr Node1, xmlNodePtr Node2) {
|
||||||
|
|
||||||
|
if (!Node1 || !Node1->ns)
|
||||||
|
return Node2;
|
||||||
|
if (!Node2 || !Node2->ns)
|
||||||
|
return Node1;
|
||||||
|
if (namespaceOverrides(Node1->ns->href, Node2->ns->href))
|
||||||
|
return Node1;
|
||||||
|
return Node2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if this Node's namespace is inherited or one it defined itself.
|
||||||
|
static bool hasInheritedNs(xmlNodePtr Node) {
|
||||||
|
return Node->ns && Node->ns != getNamespaceWithPrefix(Node->ns->prefix, Node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this Node's namespace is a default namespace that it inherited, as
|
||||||
|
// opposed to defining itself.
|
||||||
|
static bool hasInheritedDefaultNs(xmlNodePtr Node) {
|
||||||
|
return hasInheritedNs(Node) && Node->ns->prefix == nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this Node's namespace is a default namespace it defined itself.
|
||||||
|
static bool hasDefinedDefaultNamespace(xmlNodePtr Node) {
|
||||||
|
return Node->ns && (Node->ns == getNamespaceWithPrefix(nullptr, Node));
|
||||||
|
}
|
||||||
|
|
||||||
|
// For the given explicit prefix-definition of a namespace, travel downwards
|
||||||
|
// from a node recursively, and for every implicit, inherited default usage of
|
||||||
|
// that namespace replace it with that explicit prefix use. This is important
|
||||||
|
// when namespace overriding occurs when merging, so that elements unique to a
|
||||||
|
// namespace will still stay in that namespace.
|
||||||
|
static void explicateNamespace(xmlNsPtr PrefixDef, xmlNodePtr Node) {
|
||||||
|
// If a node as its own default namespace definition it clearly cannot have
|
||||||
|
// inherited the given default namespace, and neither will any of its
|
||||||
|
// children.
|
||||||
|
if (hasDefinedDefaultNamespace(Node))
|
||||||
|
return;
|
||||||
|
if (Node->ns && xmlStringsEqual(Node->ns->href, PrefixDef->href) &&
|
||||||
|
hasInheritedDefaultNs(Node))
|
||||||
|
Node->ns = PrefixDef;
|
||||||
|
for (xmlAttrPtr Attribute = Node->properties; Attribute;
|
||||||
|
Attribute = Attribute->next) {
|
||||||
|
if (Attribute->ns &&
|
||||||
|
xmlStringsEqual(Attribute->ns->href, PrefixDef->href)) {
|
||||||
|
Attribute->ns = PrefixDef;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (xmlNodePtr Child = Node->children; Child; Child = Child->next) {
|
||||||
|
explicateNamespace(PrefixDef, Child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform the namespace merge between two nodes.
|
||||||
|
static Error mergeNamespaces(xmlNodePtr OriginalNode,
|
||||||
|
xmlNodePtr AdditionalNode) {
|
||||||
|
// Save the original default namespace definition in case the incoming node
|
||||||
|
// overrides it.
|
||||||
|
const unsigned char *OriginalDefinedDefaultHref = nullptr;
|
||||||
|
if (xmlNsPtr OriginalDefinedDefaultNs =
|
||||||
|
getNamespaceWithPrefix(nullptr, OriginalNode)) {
|
||||||
|
OriginalDefinedDefaultHref = xmlStrdup(OriginalDefinedDefaultNs->href);
|
||||||
|
}
|
||||||
|
const unsigned char *NewDefinedDefaultHref = nullptr;
|
||||||
|
// Copy all namespace definitions. There can only be one default namespace
|
||||||
|
// definition per node, so the higher priority one takes precedence in the
|
||||||
|
// case of collision.
|
||||||
|
for (xmlNsPtr Def = AdditionalNode->nsDef; Def; Def = Def->next) {
|
||||||
|
if (xmlNsPtr OriginalNsDef =
|
||||||
|
getNamespaceWithPrefix(Def->prefix, OriginalNode)) {
|
||||||
|
if (!Def->prefix) {
|
||||||
|
if (namespaceOverrides(Def->href, OriginalNsDef->href)) {
|
||||||
|
NewDefinedDefaultHref = TO_XML_CHAR(strdup(FROM_XML_CHAR(Def->href)));
|
||||||
|
}
|
||||||
|
} else if (!xmlStringsEqual(OriginalNsDef->href, Def->href)) {
|
||||||
|
return make_error<WindowsManifestError>(
|
||||||
|
Twine("conflicting namespace definitions for ") +
|
||||||
|
FROM_XML_CHAR(Def->prefix));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
xmlNsPtr NewDef = xmlCopyNamespace(Def);
|
||||||
|
NewDef->next = OriginalNode->nsDef;
|
||||||
|
OriginalNode->nsDef = NewDef;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether the original node or the incoming node has the higher
|
||||||
|
// priority namespace. Depending on which one is dominant, we will have
|
||||||
|
// to recursively apply namespace changes down to children of the original
|
||||||
|
// node.
|
||||||
|
xmlNodePtr DominantNode = getDominantNode(OriginalNode, AdditionalNode);
|
||||||
|
xmlNodePtr NonDominantNode =
|
||||||
|
DominantNode == OriginalNode ? AdditionalNode : OriginalNode;
|
||||||
|
if (DominantNode == OriginalNode) {
|
||||||
|
if (OriginalDefinedDefaultHref) {
|
||||||
|
xmlNsPtr NonDominantDefinedDefault =
|
||||||
|
getNamespaceWithPrefix(nullptr, NonDominantNode);
|
||||||
|
// In this case, both the nodes defined a default namespace. However
|
||||||
|
// the lower priority node ended up having a higher priority default
|
||||||
|
// definition. This can occur if the higher priority node is prefix
|
||||||
|
// namespace defined. In this case we have to define an explicit
|
||||||
|
// prefix for the overridden definition and apply it to all children
|
||||||
|
// who relied on that definition.
|
||||||
|
if (NonDominantDefinedDefault &&
|
||||||
|
namespaceOverrides(NonDominantDefinedDefault->href,
|
||||||
|
OriginalDefinedDefaultHref)) {
|
||||||
|
Expected<xmlNsPtr> EC =
|
||||||
|
searchOrDefine(OriginalDefinedDefaultHref, DominantNode);
|
||||||
|
if (!EC) {
|
||||||
|
return EC.takeError();
|
||||||
|
}
|
||||||
|
xmlNsPtr PrefixDominantDefinedDefault = std::move(EC.get());
|
||||||
|
explicateNamespace(PrefixDominantDefinedDefault, DominantNode);
|
||||||
|
}
|
||||||
|
// In this case the node with a higher priority namespace did not have a
|
||||||
|
// default namespace definition, but the lower priority node did. In this
|
||||||
|
// case the new default namespace definition is copied. A side effect of
|
||||||
|
// this is that all children will suddenly find themselves in a different
|
||||||
|
// default namespace. To maintain correctness we need to ensure that all
|
||||||
|
// children now explicitly refer to the namespace that they had previously
|
||||||
|
// implicitly inherited.
|
||||||
|
} else if (getNamespaceWithPrefix(nullptr, NonDominantNode)) {
|
||||||
|
if (DominantNode->parent) {
|
||||||
|
xmlNsPtr ClosestDefault = getClosestDefault(DominantNode->parent);
|
||||||
|
Expected<xmlNsPtr> EC =
|
||||||
|
searchOrDefine(ClosestDefault->href, DominantNode);
|
||||||
|
if (!EC) {
|
||||||
|
return EC.takeError();
|
||||||
|
}
|
||||||
|
xmlNsPtr ExplicitDefault = std::move(EC.get());
|
||||||
|
explicateNamespace(ExplicitDefault, DominantNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Covers case where the incoming node has a default namespace definition
|
||||||
|
// that overrides the original node's namespace. This always leads to
|
||||||
|
// the original node receiving that new default namespace.
|
||||||
|
if (hasDefinedDefaultNamespace(DominantNode)) {
|
||||||
|
NonDominantNode->ns = getNamespaceWithPrefix(nullptr, NonDominantNode);
|
||||||
|
} else {
|
||||||
|
// This covers the case where the incoming node either has a prefix
|
||||||
|
// namespace, or an inherited default namespace. Since the namespace
|
||||||
|
// may not yet be defined in the original tree we do a searchOrDefine
|
||||||
|
// for it, and then set the namespace equal to it.
|
||||||
|
Expected<xmlNsPtr> EC =
|
||||||
|
searchOrDefine(DominantNode->ns->href, NonDominantNode);
|
||||||
|
if (!EC) {
|
||||||
|
return EC.takeError();
|
||||||
|
}
|
||||||
|
xmlNsPtr Explicit = std::move(EC.get());
|
||||||
|
NonDominantNode->ns = Explicit;
|
||||||
|
}
|
||||||
|
// This covers cases where the incoming dominant node HAS a default
|
||||||
|
// namespace definition, but MIGHT NOT NECESSARILY be in that namespace.
|
||||||
|
if (xmlNsPtr DominantDefaultDefined =
|
||||||
|
getNamespaceWithPrefix(nullptr, DominantNode)) {
|
||||||
|
if (OriginalDefinedDefaultHref) {
|
||||||
|
if (namespaceOverrides(DominantDefaultDefined->href,
|
||||||
|
OriginalDefinedDefaultHref)) {
|
||||||
|
// In this case, the incoming node's default definition overrides
|
||||||
|
// the original default definition, all children who relied on that
|
||||||
|
// definition must be updated accordingly.
|
||||||
|
Expected<xmlNsPtr> EC =
|
||||||
|
searchOrDefine(OriginalDefinedDefaultHref, NonDominantNode);
|
||||||
|
if (!EC) {
|
||||||
|
return EC.takeError();
|
||||||
|
}
|
||||||
|
xmlNsPtr ExplicitDefault = std::move(EC.get());
|
||||||
|
explicateNamespace(ExplicitDefault, NonDominantNode);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// The original did not define a default definition, however the new
|
||||||
|
// default definition still applies to all children, so they must be
|
||||||
|
// updated to explicitly refer to the namespace they had previously
|
||||||
|
// been inheriting implicitly.
|
||||||
|
xmlNsPtr ClosestDefault = getClosestDefault(NonDominantNode);
|
||||||
|
Expected<xmlNsPtr> EC =
|
||||||
|
searchOrDefine(ClosestDefault->href, NonDominantNode);
|
||||||
|
if (!EC) {
|
||||||
|
return EC.takeError();
|
||||||
|
}
|
||||||
|
xmlNsPtr ExplicitDefault = std::move(EC.get());
|
||||||
|
explicateNamespace(ExplicitDefault, NonDominantNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (NewDefinedDefaultHref) {
|
||||||
|
xmlNsPtr OriginalNsDef = getNamespaceWithPrefix(nullptr, OriginalNode);
|
||||||
|
xmlFree(const_cast<unsigned char *>(OriginalNsDef->href));
|
||||||
|
OriginalNsDef->href = NewDefinedDefaultHref;
|
||||||
|
}
|
||||||
|
xmlFree(const_cast<unsigned char *>(OriginalDefinedDefaultHref));
|
||||||
|
return Error::success();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isRecognizedNamespace(const unsigned char *NsHref) {
|
||||||
|
for (auto &Ns : MtNsHrefsPrefixes) {
|
||||||
|
if (xmlStringsEqual(NsHref, TO_XML_CHAR(Ns.first.data()))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool hasRecognizedNamespace(xmlNodePtr Node) {
|
||||||
|
return isRecognizedNamespace(Node->ns->href);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure a node's inherited namespace is actually defined in the tree it
|
||||||
|
// resides in.
|
||||||
|
static Error reconcileNamespaces(xmlNodePtr Node) {
|
||||||
|
if (!Node) {
|
||||||
|
return Error::success();
|
||||||
|
}
|
||||||
|
if (hasInheritedNs(Node)) {
|
||||||
|
Expected<xmlNsPtr> ExplicitOrError = searchOrDefine(Node->ns->href, Node);
|
||||||
|
if (!ExplicitOrError) {
|
||||||
|
return ExplicitOrError.takeError();
|
||||||
|
}
|
||||||
|
xmlNsPtr Explicit = std::move(ExplicitOrError.get());
|
||||||
|
Node->ns = Explicit;
|
||||||
|
}
|
||||||
|
for (xmlNodePtr Child = Node->children; Child; Child = Child->next) {
|
||||||
|
if (auto E = reconcileNamespaces(Child)) {
|
||||||
|
return E;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Error::success();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively merge the two given manifest trees, depending on which elements
|
||||||
|
// are of a mergeable type, and choose namespaces according to which have
|
||||||
|
// higher priority.
|
||||||
|
static Error treeMerge(xmlNodePtr OriginalRoot, xmlNodePtr AdditionalRoot) {
|
||||||
|
if (auto E = mergeAttributes(OriginalRoot, AdditionalRoot))
|
||||||
|
return E;
|
||||||
|
if (auto E = mergeNamespaces(OriginalRoot, AdditionalRoot))
|
||||||
|
return E;
|
||||||
|
xmlNodePtr AdditionalFirstChild = AdditionalRoot->children;
|
||||||
xmlNode StoreNext;
|
xmlNode StoreNext;
|
||||||
for (XMLNodeImpl Child = AdditionalFirstChild; Child; Child = Child->next) {
|
for (xmlNodePtr Child = AdditionalFirstChild; Child; Child = Child->next) {
|
||||||
XMLNodeImpl OriginalChildWithName;
|
xmlNodePtr OriginalChildWithName;
|
||||||
if (!isMergeableElement(Child->name) ||
|
if (!isMergeableElement(Child->name) ||
|
||||||
!(OriginalChildWithName =
|
!(OriginalChildWithName =
|
||||||
getChildWithName(OriginalRoot, Child->name))) {
|
getChildWithName(OriginalRoot, Child->name)) ||
|
||||||
|
!hasRecognizedNamespace(Child)) {
|
||||||
StoreNext.next = Child->next;
|
StoreNext.next = Child->next;
|
||||||
xmlUnlinkNode(Child);
|
xmlUnlinkNode(Child);
|
||||||
if (!xmlAddChild(OriginalRoot, Child))
|
if (!xmlAddChild(OriginalRoot, Child)) {
|
||||||
return make_error<WindowsManifestError>(Twine("could not merge ") +
|
return make_error<WindowsManifestError>(Twine("could not merge ") +
|
||||||
FROM_XML_CHAR(Child->name));
|
FROM_XML_CHAR(Child->name));
|
||||||
|
}
|
||||||
|
if (auto E = reconcileNamespaces(Child)) {
|
||||||
|
return E;
|
||||||
|
}
|
||||||
Child = &StoreNext;
|
Child = &StoreNext;
|
||||||
} else if (auto E = treeMerge(OriginalChildWithName, Child)) {
|
} else if (auto E = treeMerge(OriginalChildWithName, Child)) {
|
||||||
return E;
|
return E;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (auto E = mergeAttributes(OriginalRoot, AdditionalRoot))
|
|
||||||
return E;
|
|
||||||
#endif
|
|
||||||
return Error::success();
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
void stripCommentsAndText(XMLNodeImpl Root) {
|
static void stripComments(xmlNodePtr Root) {
|
||||||
#if LLVM_LIBXML2_ENABLED
|
|
||||||
xmlNode StoreNext;
|
xmlNode StoreNext;
|
||||||
for (XMLNodeImpl Child = Root->children; Child; Child = Child->next) {
|
for (xmlNodePtr Child = Root->children; Child; Child = Child->next) {
|
||||||
if (!xmlStringsEqual(Child->name, TO_XML_CHAR("text")) &&
|
if (!xmlStringsEqual(Child->name, TO_XML_CHAR("comment"))) {
|
||||||
!xmlStringsEqual(Child->name, TO_XML_CHAR("comment"))) {
|
stripComments(Child);
|
||||||
stripCommentsAndText(Child);
|
continue;
|
||||||
} else {
|
}
|
||||||
StoreNext.next = Child->next;
|
StoreNext.next = Child->next;
|
||||||
XMLNodeImpl Remove = Child;
|
xmlNodePtr Remove = Child;
|
||||||
Child = &StoreNext;
|
Child = &StoreNext;
|
||||||
xmlUnlinkNode(Remove);
|
xmlUnlinkNode(Remove);
|
||||||
xmlFreeNode(Remove);
|
xmlFreeNode(Remove);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// libxml2 assumes that attributes do not inherit default namespaces, whereas
|
||||||
|
// the original mt.exe does make this assumption. This function reconciles
|
||||||
|
// this by setting all attributes to have the inherited default namespace.
|
||||||
|
static void setAttributeNamespaces(xmlNodePtr Node) {
|
||||||
|
for (xmlAttrPtr Attribute = Node->properties; Attribute;
|
||||||
|
Attribute = Attribute->next) {
|
||||||
|
if (!Attribute->ns) {
|
||||||
|
Attribute->ns = getClosestDefault(Node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
for (xmlNodePtr Child = Node->children; Child; Child = Child->next) {
|
||||||
|
setAttributeNamespaces(Child);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WindowsManifestMerger::~WindowsManifestMerger() {
|
// The merging process may create too many prefix defined namespaces. This
|
||||||
#if LLVM_LIBXML2_ENABLED
|
// function removes all unnecessary ones from the tree.
|
||||||
|
static void checkAndStripPrefixes(xmlNodePtr Node,
|
||||||
|
std::vector<xmlNsPtr> &RequiredPrefixes) {
|
||||||
|
for (xmlNodePtr Child = Node->children; Child; Child = Child->next) {
|
||||||
|
checkAndStripPrefixes(Child, RequiredPrefixes);
|
||||||
|
}
|
||||||
|
if (Node->ns && Node->ns->prefix != nullptr) {
|
||||||
|
xmlNsPtr ClosestDefault = getClosestDefault(Node);
|
||||||
|
if (ClosestDefault &&
|
||||||
|
xmlStringsEqual(ClosestDefault->href, Node->ns->href)) {
|
||||||
|
Node->ns = ClosestDefault;
|
||||||
|
} else if (!llvm::is_contained(RequiredPrefixes, Node->ns)) {
|
||||||
|
RequiredPrefixes.push_back(Node->ns);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (xmlAttrPtr Attribute = Node->properties; Attribute;
|
||||||
|
Attribute = Attribute->next) {
|
||||||
|
if (Attribute->ns && Attribute->ns->prefix != nullptr) {
|
||||||
|
xmlNsPtr ClosestDefault = getClosestDefault(Node);
|
||||||
|
if (ClosestDefault &&
|
||||||
|
xmlStringsEqual(ClosestDefault->href, Attribute->ns->href)) {
|
||||||
|
Attribute->ns = ClosestDefault;
|
||||||
|
} else if (!llvm::is_contained(RequiredPrefixes, Node->ns)) {
|
||||||
|
RequiredPrefixes.push_back(Attribute->ns);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xmlNsPtr Prev;
|
||||||
|
xmlNs Temp;
|
||||||
|
for (xmlNsPtr Def = Node->nsDef; Def; Def = Def->next) {
|
||||||
|
if (!Def->prefix || llvm::is_contained(RequiredPrefixes, Def)) {
|
||||||
|
Prev = Def;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (Def == Node->nsDef) {
|
||||||
|
Node->nsDef = Def->next;
|
||||||
|
} else {
|
||||||
|
Prev->next = Def->next;
|
||||||
|
}
|
||||||
|
Temp.next = Def->next;
|
||||||
|
xmlFreeNs(Def);
|
||||||
|
Def = &Temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowsManifestMerger::WindowsManifestMergerImpl::~WindowsManifestMergerImpl() {
|
||||||
for (auto &Doc : MergedDocs)
|
for (auto &Doc : MergedDocs)
|
||||||
xmlFreeDoc(Doc);
|
xmlFreeDoc(Doc);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Error WindowsManifestMerger::merge(const MemoryBuffer &Manifest) {
|
Error WindowsManifestMerger::WindowsManifestMergerImpl::merge(
|
||||||
#if LLVM_LIBXML2_ENABLED
|
const MemoryBuffer &Manifest) {
|
||||||
if (Manifest.getBufferSize() == 0)
|
if (Manifest.getBufferSize() == 0)
|
||||||
return make_error<WindowsManifestError>(
|
return make_error<WindowsManifestError>(
|
||||||
"attempted to merge empty manifest");
|
"attempted to merge empty manifest");
|
||||||
xmlSetGenericErrorFunc((void *)this, WindowsManifestMerger::errorCallback);
|
xmlSetGenericErrorFunc((void *)this,
|
||||||
XMLDocumentImpl ManifestXML =
|
WindowsManifestMergerImpl::errorCallback);
|
||||||
|
xmlDocPtr ManifestXML =
|
||||||
xmlReadMemory(Manifest.getBufferStart(), Manifest.getBufferSize(),
|
xmlReadMemory(Manifest.getBufferStart(), Manifest.getBufferSize(),
|
||||||
"manifest.xml", nullptr, XML_PARSE_NOBLANKS);
|
"manifest.xml", nullptr, XML_PARSE_NOBLANKS);
|
||||||
xmlSetGenericErrorFunc(nullptr, nullptr);
|
xmlSetGenericErrorFunc(nullptr, nullptr);
|
||||||
if (auto E = getParseError())
|
if (auto E = getParseError())
|
||||||
return E;
|
return E;
|
||||||
XMLNodeImpl AdditionalRoot = xmlDocGetRootElement(ManifestXML);
|
xmlNodePtr AdditionalRoot = xmlDocGetRootElement(ManifestXML);
|
||||||
stripCommentsAndText(AdditionalRoot);
|
stripComments(AdditionalRoot);
|
||||||
|
setAttributeNamespaces(AdditionalRoot);
|
||||||
if (CombinedDoc == nullptr) {
|
if (CombinedDoc == nullptr) {
|
||||||
CombinedDoc = ManifestXML;
|
CombinedDoc = ManifestXML;
|
||||||
} else {
|
} else {
|
||||||
XMLNodeImpl CombinedRoot = xmlDocGetRootElement(CombinedDoc);
|
xmlNodePtr CombinedRoot = xmlDocGetRootElement(CombinedDoc);
|
||||||
if (xmlStringsEqual(CombinedRoot->name, AdditionalRoot->name) &&
|
if (!xmlStringsEqual(CombinedRoot->name, AdditionalRoot->name) ||
|
||||||
isMergeableElement(AdditionalRoot->name)) {
|
!isMergeableElement(AdditionalRoot->name) ||
|
||||||
if (auto E = treeMerge(CombinedRoot, AdditionalRoot)) {
|
!hasRecognizedNamespace(AdditionalRoot)) {
|
||||||
return E;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return make_error<WindowsManifestError>("multiple root nodes");
|
return make_error<WindowsManifestError>("multiple root nodes");
|
||||||
}
|
}
|
||||||
|
if (auto E = treeMerge(CombinedRoot, AdditionalRoot)) {
|
||||||
|
return E;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
MergedDocs.push_back(ManifestXML);
|
MergedDocs.push_back(ManifestXML);
|
||||||
#endif
|
|
||||||
return Error::success();
|
return Error::success();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<MemoryBuffer> WindowsManifestMerger::getMergedManifest() {
|
std::unique_ptr<MemoryBuffer>
|
||||||
#if LLVM_LIBXML2_ENABLED
|
WindowsManifestMerger::WindowsManifestMergerImpl::getMergedManifest() {
|
||||||
unsigned char *XmlBuff;
|
unsigned char *XmlBuff;
|
||||||
int BufferSize = 0;
|
int BufferSize = 0;
|
||||||
if (CombinedDoc) {
|
if (CombinedDoc) {
|
||||||
|
xmlNodePtr CombinedRoot = xmlDocGetRootElement(CombinedDoc);
|
||||||
|
std::vector<xmlNsPtr> RequiredPrefixes;
|
||||||
|
checkAndStripPrefixes(CombinedRoot, RequiredPrefixes);
|
||||||
std::unique_ptr<xmlDoc> OutputDoc(xmlNewDoc((const unsigned char *)"1.0"));
|
std::unique_ptr<xmlDoc> OutputDoc(xmlNewDoc((const unsigned char *)"1.0"));
|
||||||
xmlDocSetRootElement(OutputDoc.get(), xmlDocGetRootElement(CombinedDoc));
|
xmlDocSetRootElement(OutputDoc.get(), CombinedRoot);
|
||||||
xmlKeepBlanksDefault(0);
|
xmlKeepBlanksDefault(0);
|
||||||
xmlDocDumpFormatMemory(OutputDoc.get(), &XmlBuff, &BufferSize, 1);
|
xmlDocDumpFormatMemoryEnc(OutputDoc.get(), &XmlBuff, &BufferSize, "UTF-8",
|
||||||
|
1);
|
||||||
}
|
}
|
||||||
if (BufferSize == 0)
|
if (BufferSize == 0)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
return MemoryBuffer::getMemBuffer(
|
return MemoryBuffer::getMemBuffer(
|
||||||
StringRef(FROM_XML_CHAR(XmlBuff), (size_t)BufferSize));
|
StringRef(FROM_XML_CHAR(XmlBuff), (size_t)BufferSize));
|
||||||
#else
|
|
||||||
return nullptr;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WindowsManifestMerger::errorCallback(void *Ctx, const char *Format, ...) {
|
#else
|
||||||
auto *Merger = (WindowsManifestMerger *)Ctx;
|
|
||||||
|
WindowsManifestMerger::WindowsManifestMergerImpl::~WindowsManifestMergerImpl() {
|
||||||
|
}
|
||||||
|
|
||||||
|
Error WindowsManifestMerger::WindowsManifestMergerImpl::merge(
|
||||||
|
const MemoryBuffer &Manifest) {
|
||||||
|
return Error::success();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<MemoryBuffer>
|
||||||
|
WindowsManifestMerger::WindowsManifestMergerImpl::getMergedManifest() {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
WindowsManifestMerger::WindowsManifestMerger()
|
||||||
|
: Impl(make_unique<WindowsManifestMergerImpl>()) {}
|
||||||
|
|
||||||
|
WindowsManifestMerger::~WindowsManifestMerger() {}
|
||||||
|
|
||||||
|
Error WindowsManifestMerger::merge(const MemoryBuffer &Manifest) {
|
||||||
|
return Impl->merge(Manifest);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<MemoryBuffer> WindowsManifestMerger::getMergedManifest() {
|
||||||
|
return Impl->getMergedManifest();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WindowsManifestMerger::WindowsManifestMergerImpl::errorCallback(
|
||||||
|
void *Ctx, const char *Format, ...) {
|
||||||
|
auto *Merger = (WindowsManifestMergerImpl *)Ctx;
|
||||||
Merger->ParseErrorOccurred = true;
|
Merger->ParseErrorOccurred = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Error WindowsManifestMerger::getParseError() {
|
Error WindowsManifestMerger::WindowsManifestMergerImpl::getParseError() {
|
||||||
if (!ParseErrorOccurred)
|
if (!ParseErrorOccurred)
|
||||||
return Error::success();
|
return Error::success();
|
||||||
return make_error<WindowsManifestError>("invalid xml document");
|
return make_error<WindowsManifestError>("invalid xml document");
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace llvm
|
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<assembly xmlns="urn:schemas-microsoft-com:asm.v2" manifestVersion="1.0">
|
||||||
|
<dependency>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity foo="bar" name="identity1" version="1.2.3.4"></assemblyIdentity>
|
||||||
|
</dependentAssembly>
|
||||||
|
</dependency>
|
||||||
|
</assembly>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
|
<application>
|
||||||
|
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||||
|
<supportedOS Id="FooOS"/>
|
||||||
|
<supportedOS Id="BarOS"/>
|
||||||
|
</compatibility>
|
||||||
|
</application>
|
||||||
|
</assembly>
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||||
|
<ms_asmv1:trustInfo xmlns="urn:schemas-microsoft-com:asm.v3" xmlns:ms_asmv1="urn:schemas-microsoft-com:asm.v1">
|
||||||
|
<ms_asmv1:security>
|
||||||
|
<ms_asmv1:requestedPrivileges>
|
||||||
|
<ms_asmv1:requestedExecutionLevel level="trust1" ms_asmv1:access="false"></ms_asmv1:requestedExecutionLevel>
|
||||||
|
</ms_asmv1:requestedPrivileges>
|
||||||
|
</ms_asmv1:security>
|
||||||
|
</ms_asmv1:trustInfo>
|
||||||
|
<ms_asmv2:dependency xmlns:ms_asmv2="urn:schemas-microsoft-com:asm.v2">
|
||||||
|
<ms_asmv2:dependentAssembly>
|
||||||
|
<ms_asmv2:assemblyIdentity foo="bar" name="identity1" version="1.2.3.4"></assemblyIdentity>
|
||||||
|
</ms_asmv2:dependentAssembly>
|
||||||
|
</ms_asmv2:dependency>
|
||||||
|
<dependency>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity bar="foo" name="my assembly" version="5"></assemblyIdentity>
|
||||||
|
</dependentAssembly>
|
||||||
|
</dependency>
|
||||||
|
<application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||||
|
<supportedOS Id="FooOS"/>
|
||||||
|
<supportedOS Id="BarOS"/>
|
||||||
|
</compatibility>
|
||||||
|
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
|
||||||
|
<dpiAware>true/pm</dpiAware>
|
||||||
|
</asmv3:windowsSettings>
|
||||||
|
</application>
|
||||||
|
</assembly>
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||||
|
|
||||||
|
<!--random comment -->
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity bar="foo" name="my assembly" version="5"></assemblyIdentity>
|
||||||
|
</dependentAssembly>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<trustInfo>
|
||||||
|
<security>
|
||||||
|
<requestedPrivileges>
|
||||||
|
<requestedExecutionLevel level="trust1" access="false"/>
|
||||||
|
</requestedPrivileges>
|
||||||
|
</security>
|
||||||
|
</trustInfo>
|
||||||
|
|
||||||
|
</assembly>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||||
|
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<security>
|
||||||
|
<requestedPrivileges>
|
||||||
|
<requestedExecutionLevel level="trust1"></requestedExecutionLevel>
|
||||||
|
</requestedPrivileges>
|
||||||
|
</security>
|
||||||
|
</trustInfo></assembly>
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<assembly xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings" manifestVersion="1.0">
|
||||||
|
<asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
|
||||||
|
<dpiAware>true/pm</dpiAware>
|
||||||
|
</asmv3:windowsSettings>
|
||||||
|
</asmv3:application>
|
||||||
|
</assembly>
|
|
@ -0,0 +1,39 @@
|
||||||
|
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: FileCheck %s -input-file=%t
|
||||||
|
|
||||||
|
CHECK: <?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
CHECK-NEXT: <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||||
|
CHECK-NEXT: <ms_asmv1:trustInfo xmlns="urn:schemas-microsoft-com:asm.v3" xmlns:ms_asmv1="urn:schemas-microsoft-com:asm.v1">
|
||||||
|
CHECK-NEXT: <ms_asmv1:security>
|
||||||
|
CHECK-NEXT: <ms_asmv1:requestedPrivileges>
|
||||||
|
CHECK-NEXT: <ms_asmv1:requestedExecutionLevel level="trust1" ms_asmv1:access="false"/>
|
||||||
|
CHECK-NEXT: </ms_asmv1:requestedPrivileges>
|
||||||
|
CHECK-NEXT: </ms_asmv1:security>
|
||||||
|
CHECK-NEXT: </ms_asmv1:trustInfo>
|
||||||
|
CHECK-NEXT: <ms_asmv2:dependency xmlns:ms_asmv2="urn:schemas-microsoft-com:asm.v2">
|
||||||
|
CHECK-NEXT: <ms_asmv2:dependentAssembly>
|
||||||
|
CHECK-NEXT: <ms_asmv2:assemblyIdentity foo="bar" name="identity1" version="1.2.3.4"/>
|
||||||
|
CHECK-NEXT: </ms_asmv2:dependentAssembly>
|
||||||
|
CHECK-NEXT: </ms_asmv2:dependency>
|
||||||
|
CHECK-NEXT: <dependency>
|
||||||
|
CHECK-NEXT: <dependentAssembly>
|
||||||
|
CHECK-NEXT: <assemblyIdentity bar="foo" name="my assembly" version="5"/>
|
||||||
|
CHECK-NEXT: </dependentAssembly>
|
||||||
|
CHECK-NEXT: </dependency>
|
||||||
|
CHECK-NEXT: <application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||||
|
CHECK-NEXT: <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||||
|
CHECK-NEXT: <supportedOS Id="FooOS"/>
|
||||||
|
CHECK-NEXT: <supportedOS Id="BarOS"/>
|
||||||
|
CHECK-NEXT: </compatibility>
|
||||||
|
CHECK-NEXT: <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
|
||||||
|
CHECK-NEXT: <dpiAware>true/pm</dpiAware>
|
||||||
|
CHECK-NEXT: </asmv3:windowsSettings>
|
||||||
|
CHECK-NEXT: </application>
|
||||||
|
CHECK-NEXT: </assembly>
|
|
@ -1,3 +1,7 @@
|
||||||
RUN: llvm-mt /h | FileCheck %s -check-prefix=HELP
|
RUN: llvm-mt /h | FileCheck %s -check-prefix=HELP
|
||||||
|
|
||||||
HELP: OVERVIEW: Manifest Tool
|
HELP: OVERVIEW: Manifest Tool
|
||||||
|
|
||||||
|
RUN: not llvm-mt /foo 2>&1 >/dev/null | FileCheck %s -check-prefix=INVALID
|
||||||
|
|
||||||
|
INVALID: llvm-mt error: invalid option /foo
|
||||||
|
|
|
@ -5,7 +5,7 @@ RUN: llvm-mt /manifest %p/Inputs/test_manifest.manifest /manifest \
|
||||||
RUN: %p/Inputs/additional.manifest /out:%t
|
RUN: %p/Inputs/additional.manifest /out:%t
|
||||||
RUN: FileCheck %s -input-file=%t
|
RUN: FileCheck %s -input-file=%t
|
||||||
|
|
||||||
CHECK: <?xml version="1.0"?>
|
CHECK: <?xml version="1.0" encoding="UTF-8"?>
|
||||||
CHECK-NEXT: <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
CHECK-NEXT: <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||||
CHECK-NEXT: <trustInfo>
|
CHECK-NEXT: <trustInfo>
|
||||||
CHECK-NEXT: <security>
|
CHECK-NEXT: <security>
|
||||||
|
|
|
@ -4,7 +4,7 @@ UNSUPPORTED: windows
|
||||||
RUN: llvm-mt /manifest %p/Inputs/test_manifest.manifest /out:%t
|
RUN: llvm-mt /manifest %p/Inputs/test_manifest.manifest /out:%t
|
||||||
RUN: FileCheck %s --input-file=%t
|
RUN: FileCheck %s --input-file=%t
|
||||||
|
|
||||||
CHECK: <?xml version="1.0"?>
|
CHECK: <?xml version="1.0" encoding="UTF-8"?>
|
||||||
CHECK-NEXT: <assembly xmlns="urn:schemas-microsoft-com:asm.v1">
|
CHECK-NEXT: <assembly xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
CHECK-NEXT: <trustInfo>
|
CHECK-NEXT: <trustInfo>
|
||||||
CHECK-NEXT: <security>
|
CHECK-NEXT: <security>
|
||||||
|
|
|
@ -11,8 +11,8 @@ RUN: FileCheck %s -check-prefix=EMPTY
|
||||||
|
|
||||||
EMPTY: llvm-mt error: attempted to merge empty manifest
|
EMPTY: llvm-mt error: attempted to merge empty manifest
|
||||||
|
|
||||||
RUN: llvm-mt /inputresource:foo.res /manifest \
|
RUN: llvm-mt /inputresource:foo.res \
|
||||||
RUN: %p/Inputs/test_manifest.manifest | FileCheck %s \
|
RUN: /manifest %p/Inputs/test_manifest.manifest \
|
||||||
RUN: -check-prefix=NOT_SUPPORTED
|
RUN: /out:%t | FileCheck %s -check-prefix=NOT_SUPPORTED
|
||||||
|
|
||||||
NOT_SUPPORTED: llvm-mt: ignoring unsupported 'inputresource:' option
|
NOT_SUPPORTED: llvm-mt: ignoring unsupported 'inputresource:' option
|
||||||
|
|
|
@ -102,6 +102,9 @@ int main(int argc, const char **argv) {
|
||||||
ArrayRef<const char *> ArgsArr = makeArrayRef(argv + 1, argc);
|
ArrayRef<const char *> ArgsArr = makeArrayRef(argv + 1, argc);
|
||||||
opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MAI, MAC);
|
opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MAI, MAC);
|
||||||
|
|
||||||
|
for (auto *Arg : InputArgs.filtered(OPT_INPUT))
|
||||||
|
reportError(Twine("invalid option ") + Arg->getSpelling());
|
||||||
|
|
||||||
for (auto &Arg : InputArgs) {
|
for (auto &Arg : InputArgs) {
|
||||||
if (Arg->getOption().matches(OPT_unsupported)) {
|
if (Arg->getOption().matches(OPT_unsupported)) {
|
||||||
outs() << "llvm-mt: ignoring unsupported '" << Arg->getOption().getName()
|
outs() << "llvm-mt: ignoring unsupported '" << Arg->getOption().getName()
|
||||||
|
@ -129,7 +132,7 @@ int main(int argc, const char **argv) {
|
||||||
reportError("no output file specified");
|
reportError("no output file specified");
|
||||||
}
|
}
|
||||||
|
|
||||||
WindowsManifestMerger Merger;
|
windows_manifest::WindowsManifestMerger Merger;
|
||||||
|
|
||||||
for (const auto &File : InputFiles) {
|
for (const auto &File : InputFiles) {
|
||||||
ErrorOr<std::unique_ptr<MemoryBuffer>> ManifestOrErr =
|
ErrorOr<std::unique_ptr<MemoryBuffer>> ManifestOrErr =
|
||||||
|
|
Loading…
Reference in New Issue