Index: llvm/include/llvm/WindowsManifest/WindowsManifestMerger.h =================================================================== --- llvm/include/llvm/WindowsManifest/WindowsManifestMerger.h +++ llvm/include/llvm/WindowsManifest/WindowsManifestMerger.h @@ -37,14 +37,29 @@ class MemoryBuffer; +namespace windows_manifest { + #if LLVM_LIBXML2_ENABLED typedef xmlDocPtr XMLDocumentImpl; typedef xmlNodePtr XMLNodeImpl; +typedef xmlNsPtr XMLNsImpl; +typedef xmlAttrPtr XMLAttrImpl; #else typedef void *XMLDocumentImpl; typedef void *XMLNodeImpl; +typedef void *XMLNsImpl; +typedef void *XMLAttrImpl; #endif +static const std::vector> + 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"}}; + class WindowsManifestError : public ErrorInfo { public: static char ID; @@ -76,5 +91,6 @@ bool ParseErrorOccurred = false; }; +} // namespace windows_manifest } // namespace llvm #endif Index: llvm/lib/WindowsManifest/WindowsManifestMerger.cpp =================================================================== --- llvm/lib/WindowsManifest/WindowsManifestMerger.cpp +++ llvm/lib/WindowsManifest/WindowsManifestMerger.cpp @@ -20,8 +20,7 @@ #define FROM_XML_CHAR(X) reinterpret_cast(X) using namespace llvm; - -namespace llvm { +using namespace windows_manifest; char WindowsManifestError::ID = 0; @@ -31,6 +30,8 @@ #if LLVM_LIBXML2_ENABLED static bool xmlStringsEqual(const unsigned char *A, const unsigned char *B) { + if (!(A && B)) + return !A && !B; return strcmp(FROM_XML_CHAR(A), FROM_XML_CHAR(B)) == 0; } #endif @@ -56,72 +57,552 @@ return nullptr; } -const unsigned char *getAttribute(XMLNodeImpl Node, - const unsigned char *AttributeName) { +XMLAttrImpl getAttribute(XMLNodeImpl Node, const unsigned char *AttributeName) { #if LLVM_LIBXML2_ENABLED for (xmlAttrPtr Attribute = Node->properties; Attribute != nullptr; Attribute = Attribute->next) { if (xmlStringsEqual(Attribute->name, AttributeName)) - return Attribute->children->content; + return Attribute; + } +#endif + return nullptr; +} + +bool namespaceOverrides(const unsigned char *Ns1, const unsigned char *Ns2) { + auto Ns1Position = std::find_if( + MtNsHrefsPrefixes.begin(), MtNsHrefsPrefixes.end(), + [Ns1](const std::pair &element) { + return xmlStringsEqual(Ns1, TO_XML_CHAR(element.first.c_str())); + }); + if (Ns1Position == MtNsHrefsPrefixes.end()) + return false; + auto Ns2Position = std::find_if( + MtNsHrefsPrefixes.begin(), MtNsHrefsPrefixes.end(), + [Ns2](const std::pair &element) { + return xmlStringsEqual(Ns2, TO_XML_CHAR(element.first.c_str())); + }); + if (Ns2Position == MtNsHrefsPrefixes.end()) + return true; + int Ns1Priority = std::distance(Ns1Position, MtNsHrefsPrefixes.end()); + int Ns2Priority = std::distance(Ns2Position, MtNsHrefsPrefixes.end()); + if (Ns1Priority > Ns2Priority) + return true; + else + return false; +} + +XMLNsImpl search(const unsigned char *HRef, XMLNodeImpl Node) { +#if LLVM_LIBXML2_ENABLED + // outs() << "searching for " << FROM_XML_CHAR(HRef) << " on " << + // FROM_XML_CHAR(Node->name) << "\n"; + for (XMLNsImpl Def = Node->nsDef; Def; Def = Def->next) { + // outs() << "checking " << FROM_XML_CHAR(Def->prefix) << "\n"; + if (Def->prefix != nullptr && xmlStringsEqual(Def->href, HRef)) + return Def; + } + if (Node->parent) { + return search(HRef, Node->parent); + } +#endif + return nullptr; +} + +const unsigned char *getPrefixForHref(const unsigned char *HRef) { + for (auto &Ns : MtNsHrefsPrefixes) { + if (xmlStringsEqual(HRef, TO_XML_CHAR(Ns.first.c_str()))) { + return TO_XML_CHAR(Ns.second.c_str()); + } + } + outs() << "could not find prefix\n"; + return HRef; +} + +Expected searchOrDefine(const unsigned char *HRef, + XMLNodeImpl Node) { + // outs() << "search or define for " << FROM_XML_CHAR(HRef) << " on " << + // FROM_XML_CHAR(Node->name) << "\n"; + XMLNsImpl Def = search(HRef, Node); + if (Def) + return Def; + Def = xmlNewNs(Node, HRef, getPrefixForHref(HRef)); + // outs() << "create namespace def with prefix " << FROM_XML_CHAR(Def->prefix) + // << " on " << FROM_XML_CHAR(Node->name) << "\n"; + if (!Def) + return make_error("failed to create new namespace"); + return Def; +} + +Error copyAttributeNamespace(XMLAttrImpl OriginalAttribute, + XMLNodeImpl OriginalNode, + XMLAttrImpl AdditionalAttribute) { + Expected ExplicitOrError = + searchOrDefine(AdditionalAttribute->ns->href, OriginalNode); + if (!ExplicitOrError) + return ExplicitOrError.takeError(); + OriginalAttribute->ns = std::move(ExplicitOrError.get()); + return Error::success(); +} + +XMLNsImpl getNamespaceWithPrefix(const unsigned char *Prefix, + XMLNodeImpl Node) { +#if LLVM_LIBXML2_ENABLED + // outs() << "looking for namespace with prefix " << FROM_XML_CHAR(Prefix) << + // "\n"; + if (Node == nullptr) + return nullptr; + for (XMLNsImpl Def = Node->nsDef; Def; Def = Def->next) { + if (xmlStringsEqual(Def->prefix, Prefix)) + return Def; } #endif return nullptr; } +XMLNsImpl getClosestDefault(XMLNodeImpl Node) { +#if LLVM_LIBXML2_ENABLED + if (XMLNsImpl Ret = getNamespaceWithPrefix(nullptr, Node)) + return Ret; + if (Node->parent == nullptr) + return nullptr; + return getClosestDefault(Node->parent); +#else + return nullptr; +#endif +} + Error mergeAttributes(XMLNodeImpl OriginalNode, XMLNodeImpl AdditionalNode) { #if LLVM_LIBXML2_ENABLED - for (xmlAttrPtr Attribute = AdditionalNode->properties; Attribute != nullptr; + XMLNsImpl ClosestDefault = getClosestDefault(OriginalNode); + for (xmlAttrPtr Attribute = AdditionalNode->properties; Attribute; Attribute = Attribute->next) { - if (const unsigned char *OriginalValue = + if (xmlAttrPtr OriginalAttribute = getAttribute(OriginalNode, Attribute->name)) { - // Attributes of the same name must also have the same value. Otherwise - // an error is thrown. - if (!xmlStringsEqual(OriginalValue, Attribute->children->content)) + // outs() << "about to print ns\n"; + // outs() << FROM_XML_CHAR(Attribute->ns) << "\n"; + // outs() << "About to print namespace href\n"; + // outs() << FROM_XML_CHAR(Attribute->ns->href) << "\n"; + // outs() << "about to check namespace overrides\n"; + if (!xmlStringsEqual(OriginalAttribute->children->content, + Attribute->children->content)) return make_error( Twine("conflicting attributes for ") + FROM_XML_CHAR(OriginalNode->name)); + + // if (Attribute->ns && (!OriginalAttribute->ns || + // namespaceOverrides(Attribute->ns->href, OriginalAttribute->ns->href))) + // { + // Expected ExplicitOrError = + // searchOrDefine(Attribute->ns->href, OriginalNode); if + // (!ExplicitOrError) + // return ExplicitOrError.takeError(); + // XMLNsImpl Explicit = std::move(ExplicitOrError.get()); + // OriginalAttribute->ns = Explicit; + + if (!Attribute->ns) { + outs() << "no original namespace\n"; + continue; + } + if (!OriginalAttribute->ns) { + outs() << "no attribute namespace\n"; + if (auto E = copyAttributeNamespace(OriginalAttribute, OriginalNode, + Attribute)) + return E; + } else if (namespaceOverrides(OriginalAttribute->ns->href, + Attribute->ns->href)) { + // outs() << "original namespace overrides\n"; + if (!OriginalAttribute->ns->prefix && !Attribute->ns->prefix && + ClosestDefault && + xmlStringsEqual(Attribute->ns->href, ClosestDefault->href)) { + // outs() << "hit the case where we hvae to copy non-dominnat + // namespace\n"; + if (auto E = copyAttributeNamespace(OriginalAttribute, OriginalNode, + Attribute)) + return E; + } + } else if (Attribute->ns->prefix || OriginalAttribute->ns->prefix || + (ClosestDefault && + !xmlStringsEqual(OriginalAttribute->ns->href, + ClosestDefault->href))) { + outs() << "hitting the case where we have to copy the additional " + "namespace\n"; + outs() << "incoming prefix " << FROM_XML_CHAR(Attribute->ns->prefix) + << "\n"; + outs() << "original prefix " + << FROM_XML_CHAR(OriginalAttribute->ns->prefix) << "\n"; + outs() << "ClosestDefault " << FROM_XML_CHAR(ClosestDefault->href) + << "\n"; + outs() << "incoming href " << FROM_XML_CHAR(Attribute->ns->href) + << "\n"; + if (auto E = copyAttributeNamespace(OriginalAttribute, OriginalNode, + Attribute)) + return E; + } + + // if (OriginalAttribute->ns && (!Attribute->ns || + // namespaceOverrides(OriginalAttribute->ns->href, Attribute->ns->href))) + // { + // if(!Attribute->ns || (!OriginalAttribute->ns->prefix && + // !Attribute->ns->prefix && xmlStringsEqual(ClosestDefault->ns->href, ) + // } + // } + // Attributes of the same name must also have the same value. Otherwise + // an error is thrown. } else { - char *NameCopy = strdup(FROM_XML_CHAR(Attribute->name)); - char *ContentCopy = strdup(FROM_XML_CHAR(Attribute->children->content)); - xmlNewProp(OriginalNode, TO_XML_CHAR(NameCopy), TO_XML_CHAR(ContentCopy)); + XMLAttrImpl NewProp = xmlNewProp(OriginalNode, Attribute->name, + Attribute->children->content); + Expected ExplicitOrError = + searchOrDefine(Attribute->ns->href, OriginalNode); + if (!ExplicitOrError) + return ExplicitOrError.takeError(); + NewProp->ns = std::move(ExplicitOrError.get()); } } #endif return Error::success(); } +XMLNodeImpl getDominantNode(XMLNodeImpl Node1, XMLNodeImpl Node2) { + if (!Node1 || !Node1->ns) + return Node2; + else if (!Node2 || !Node2->ns) + return Node1; + else if (namespaceOverrides(Node1->ns->href, Node2->ns->href)) + return Node1; + else + return Node2; +} + +bool hasInheritedNs(XMLNodeImpl Node) { +#if LLVM_LIBXML2_ENABLED + if (Node->ns && Node->ns != getNamespaceWithPrefix(Node->ns->prefix, Node)) + return true; + else +#endif + return false; +} + +bool hasInheritedDefaultNs(XMLNodeImpl Node) { +#if LLVM_LIBXML2_ENABLED + if (hasInheritedNs(Node) && Node->ns->prefix == nullptr) + return true; + else +#endif + return false; +} + +bool hasDefinedDefaultNamespace(XMLNodeImpl Node) { +#if LLVM_LIBXML2_ENABLED + if (Node->ns && (Node->ns == getNamespaceWithPrefix(nullptr, Node))) + return true; + else +#endif + return false; +} + +void explicateNamespace(XMLNsImpl PrefixDef, XMLNodeImpl Node) { +#if LLVM_LIBXML2_ENABLED + // outs() << "about to explicate on " << FROM_XML_CHAR(Node->name) << "\n"; + 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 (XMLNodeImpl Child = Node->children; Child; Child = Child->next) { + explicateNamespace(PrefixDef, Child); + } +#endif +} + +Error mergeNamespaces(XMLNodeImpl OriginalNode, XMLNodeImpl AdditionalNode) { +#if LLVM_LIBXML2_ENABLED + const unsigned char *OriginalDefinedDefaultHref = nullptr; + if (XMLNsImpl OriginalDefinedDefaultNs = + getNamespaceWithPrefix(nullptr, OriginalNode)) { + OriginalDefinedDefaultHref = xmlStrdup(OriginalDefinedDefaultNs->href); + // outs() << "found defined default ns " << + // FROM_XML_CHAR(OriginalDefinedDefaultHref) << "\n"; + } + for (XMLNsImpl Def = AdditionalNode->nsDef; Def; Def = Def->next) { + // outs() << "merging namespace\n"; + if (XMLNsImpl OriginalNsDef = + getNamespaceWithPrefix(Def->prefix, OriginalNode)) { + // outs() << "found existing def with prefix\n"; + if (!Def->prefix) { + if (namespaceOverrides(Def->href, OriginalNsDef->href)) { + // outs() << "namespace overrides\n"; + xmlFree(const_cast(OriginalNsDef->href)); + OriginalNsDef->href = TO_XML_CHAR(strdup(FROM_XML_CHAR(Def->href))); + // outs() << "def overrides so replace\n"; + } + } else if (!xmlStringsEqual(OriginalNsDef->href, Def->href)) { + // outs() << "conflicting namespace error\n"; + return make_error( + Twine("conflicting namespace definitions for ") + + FROM_XML_CHAR(Def->prefix)); + } + } else { + XMLNsImpl NewDef = xmlCopyNamespace(Def); + NewDef->next = OriginalNode->nsDef; + OriginalNode->nsDef = NewDef; + } + } + // outs() << "Finished merging defs\n"; + XMLNodeImpl DominantNode = getDominantNode(OriginalNode, AdditionalNode); + XMLNodeImpl NonDominantNode = + DominantNode == OriginalNode ? AdditionalNode : OriginalNode; + if (DominantNode == OriginalNode) { + // outs() << "original dominant\n"; + if (OriginalDefinedDefaultHref) { + XMLNsImpl NonDominantDefinedDefault = + getNamespaceWithPrefix(nullptr, NonDominantNode); + if (NonDominantDefinedDefault && + namespaceOverrides(NonDominantDefinedDefault->href, + OriginalDefinedDefaultHref)) { + Expected EC = + searchOrDefine(OriginalDefinedDefaultHref, DominantNode); + if (!EC) + return EC.takeError(); + XMLNsImpl PrefixDominantDefinedDefault = std::move(EC.get()); + explicateNamespace(PrefixDominantDefinedDefault, DominantNode); + } + } else if (getNamespaceWithPrefix(nullptr, NonDominantNode)) { + // outs() << "found default namespace on NonDominantNode\n"; + if (DominantNode->parent) { + XMLNsImpl ClosestDefault = getClosestDefault(DominantNode->parent); + Expected EC = + searchOrDefine(ClosestDefault->href, DominantNode); + if (!EC) + return EC.takeError(); + XMLNsImpl ExplicitDefault = std::move(EC.get()); + explicateNamespace(ExplicitDefault, DominantNode); + } + } + } else { + // outs() << "additional dominant\n"; + if (hasDefinedDefaultNamespace(DominantNode)) { + NonDominantNode->ns = getNamespaceWithPrefix(nullptr, NonDominantNode); + } else { + Expected EC = + searchOrDefine(DominantNode->ns->href, NonDominantNode); + if (!EC) + return EC.takeError(); + XMLNsImpl Explicit = std::move(EC.get()); + NonDominantNode->ns = Explicit; + // outs() << "set explicit with prefix " << + // FROM_XML_CHAR(Explicit->prefix) << " on node " << + // FROM_XML_CHAR(NonDominantNode->name) << "\n"; + } + if (XMLNsImpl DominantDefaultDefined = + getNamespaceWithPrefix(nullptr, DominantNode)) { + // outs() << "dominant defined default\n"; + if (OriginalDefinedDefaultHref) { + if (namespaceOverrides(DominantDefaultDefined->href, + OriginalDefinedDefaultHref)) { + Expected EC = + searchOrDefine(OriginalDefinedDefaultHref, NonDominantNode); + if (!EC) + return EC.takeError(); + XMLNsImpl ExplicitDefault = std::move(EC.get()); + explicateNamespace(ExplicitDefault, NonDominantNode); + } + } else { + XMLNsImpl ClosestDefault = getClosestDefault(NonDominantNode); + Expected EC = + searchOrDefine(ClosestDefault->href, NonDominantNode); + if (!EC) + return EC.takeError(); + XMLNsImpl ExplicitDefault = std::move(EC.get()); + explicateNamespace(ExplicitDefault, NonDominantNode); + } + } + } + xmlFree(const_cast(OriginalDefinedDefaultHref)); +#endif + return Error::success(); +} + +bool isRecognizedNamespace(const unsigned char *NsHref) { + for (auto &Ns : MtNsHrefsPrefixes) { + if (xmlStringsEqual(NsHref, TO_XML_CHAR(Ns.first.c_str()))) + return true; + } + return false; +} + +bool hasRecognizedNamespace(XMLNodeImpl Node) { +#if LLVM_LIBXML2_ENABLED + return isRecognizedNamespace(Node->ns->href); +#else + return false; +#endif +} + +Error reconcileNamespaces(XMLNodeImpl Node) { +#if LLVM_LIBXML2_ENABLED + if (Node == nullptr) + return Error::success(); + if (hasInheritedNs(Node)) { + Expected ExplicitOrError = searchOrDefine(Node->ns->href, Node); + if (!ExplicitOrError) + return ExplicitOrError.takeError(); + XMLNsImpl Explicit = std::move(ExplicitOrError.get()); + Node->ns = Explicit; + } + for (XMLNodeImpl Child = Node->children; Child; Child = Child->next) { + if (auto E = reconcileNamespaces(Child)) + return E; + } +#endif + return Error::success(); +} + +// Error mergeNamespaces(XMLNodeImpl OriginalNode, XMLNodeImpl AdditionalNode) { +// #if LLVM_LIBXML2_ENABLED +// for (XMLNsImpl Def = AdditionalNode->nsDef; Def; Def = Def->next) { +// if (XMLNsImpl OriginalNsDef = getNamespaceWithPrefix(Def->prefix, +// OriginalNode)) { +// if (Def->prefix == nullptr && namespaceOverrides(Def, OriginalNsDef)) { +// xmlFree(OriginalDefinedDefaultNs->href); +// OriginalDefinedDefaultNs->href = +// TO_XML_CHAR(strdup(FROM_XML_CHAR(Def->href))); +// } else if (!xmlStringsEqual(OriginalNsDef->href, Def->href)) { +// return make_error(Twine("conflicting namespace +// definitions for ") + FROM_XML_CHAR(Def->prefix)); +// } +// } else { +// XMLNsImpl NewDef = xmlCopyNamespace(Def); +// NewDef->next = OriginalNode->nsDef; +// OriginalNode->nsDef = NewDef; +// } +// } + +// XMLNodeImpl DominantNode = getDominantNode(OriginalNode, AdditionalNode); +// XMLNodeImpl NonDominantNode = DominantNode == OriginalNode ? AdditionalNode +// : DominantNode; if (hasInheritedDefaultNs(NonDominantNode)) { +// XMLNsImpl NewDef = searchOrAddNsDef(NonDominantNode, +// NonDominantNode->ns); propagateNamespace(NonDominantNode, NewDef); +// } +// if (hasDefinedDefaultNs(DominantNode) && DominantNode != OriginalNode) { +// XMLNsImpl NewDef = addNsDef(OriginalNode, DominantNode->ns->href, +// nullptr); OriginalNode->ns = NewDef; +// if(defaultNamespaceDefinedOn(OriginalNode)) +// propagateNamespace(NewDef); +// } else if (hasInheritedNs(DominantNode) && +// (defaultNamespaceDefinedOn(NonDominantNode) || NonDominantNode == +// OriginalNode)) { +// XMLNsImpl NewNs = searchOrAddNsDef(OriginalNode, DominantNode->ns); +// OriginalNode->ns = newNs; +// if (hasInheritedDefaultNs(DominantNode)) { +// XMLNsImpl NewDef = searchOrAddNsDef(DominantNode, DominantNode->ns); +// propagateNamespace(DominantNode, NewDef); +// } +// } +// #endif +// } + +// XMLNodeImpl DominantNode = getDominantNode(OriginalNode, AdditionalNode); +// OriginalNode->ns = DominantNode->ns; +// if (isInheritedNamespace(DominantNode->ns) && OriginalNode != DominantNode) { +// if (isDefaultNamespace(DominantNode->ns)) { +// XMLNsImpl NewNs = defineNamespace(OriginalNode, DominantNode->ns); +// OriginalNode->ns = NewNs; +// explicateNamespace(DominantNode, NewNs); +// } else { +// if (XMLNsImpl PreviousDef = searchAncestorsForNsDef(OriginalNode, +// DominantNode->ns->ref)) { +// OriginalNode->ns = PreviousDef; +// } else { +// XMLNsImpl NewNs = defineNamespace(OriginalNode, DominantNode->ns); +// OriginalNode->ns = NewNs; +// } +// } +// } +// XMLNodeImpl NonDominantNode = OriginalNode == DominantNode ? AdditionalNode : +// OriginalNode; if (isInheritedNamespace(NonDominantNode->ns) && +// isDefaultNamespace(NonDominantNode->ns)) { +// XMLNsImpl OldNs = defineNamespace(OriginalNode, NonDominantNode->ns); +// explicateNamespace(NonDominantNode, OldNs); +// } + +// void mergeNamespaces(XMLNodeImpl OriginalNode, XMLNodeImpl AdditionalNode) { +// #if LLVM_LIBXML2_ENABLED +// XMLNodeImpl DominantNode = getDominantNode(OriginalNode, AdditionalNode); +// XMLNodeImpl NonDominantNode = DominantNode == OriginalNode ? AdditionalNode +// : OriginalNode; if (isDefaultNamespace(NonDominantNode->ns, +// NonDominantNode)) { +// XMLNsImpl NonDominantNs = xmlNewNs(NonDominantNode, +// NonDominantNode->ns->href, +// getCorrespondingPrefix(NonDominantNode->ns->href)); +// explicateNamespace(NonDominantNode, NonDominantNs); +// if (DominantNode == OriginalNode) return; +// } +// if (isDefaultNamespace(DominantNode->ns, DominantNode)) { +// unsigned char *Prefix = getCorrespondingPrefix(DominantNs->href); +// DominantNode->ns = xmlNewNs(DominantNode, DominantNs->href, Prefix); +// explicateNamespace(DominantNode, DominantNode->ns); +// if (OriginalNode != DominantNode) +// OriginalNode->ns = xmlNewNs(OriginalNode, DominantNs->href, Prefix); +// } else { +// xmlFree(OriginalNode->ns->href); +// OriginalNode->ns->href = +// TO_XML_CHAR(strdup(FROM_XML_CHAR(DominantNs->href))); +// OriginalNode->ns->prefix = +// TO_XML_CHAR(strdup(FROM_XML_CHAR(DominantNs->prefix))); +// } +// #endif +// } + +// 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. Error treeMerge(XMLNodeImpl OriginalRoot, XMLNodeImpl AdditionalRoot) { + + outs() << "merging node " << FROM_XML_CHAR(AdditionalRoot->name) << "\n"; #if LLVM_LIBXML2_ENABLED + if (auto E = mergeAttributes(OriginalRoot, AdditionalRoot)) + return E; + if (auto E = mergeNamespaces(OriginalRoot, AdditionalRoot)) + return E; XMLNodeImpl AdditionalFirstChild = AdditionalRoot->children; xmlNode StoreNext; for (XMLNodeImpl Child = AdditionalFirstChild; Child; Child = Child->next) { XMLNodeImpl OriginalChildWithName; if (!isMergeableElement(Child->name) || !(OriginalChildWithName = - getChildWithName(OriginalRoot, Child->name))) { + getChildWithName(OriginalRoot, Child->name)) || + !hasRecognizedNamespace(Child)) { StoreNext.next = Child->next; xmlUnlinkNode(Child); if (!xmlAddChild(OriginalRoot, Child)) return make_error(Twine("could not merge ") + FROM_XML_CHAR(Child->name)); + if (auto E = reconcileNamespaces(Child)) + return E; Child = &StoreNext; } else if (auto E = treeMerge(OriginalChildWithName, Child)) { return E; } } - if (auto E = mergeAttributes(OriginalRoot, AdditionalRoot)) - return E; #endif + + outs() << "finished merging node " << FROM_XML_CHAR(AdditionalRoot->name) + << "\n"; return Error::success(); } -void stripCommentsAndText(XMLNodeImpl Root) { +void stripComments(XMLNodeImpl Root) { #if LLVM_LIBXML2_ENABLED xmlNode StoreNext; for (XMLNodeImpl Child = Root->children; Child; Child = Child->next) { - if (!xmlStringsEqual(Child->name, TO_XML_CHAR("text")) && - !xmlStringsEqual(Child->name, TO_XML_CHAR("comment"))) { - stripCommentsAndText(Child); + if (!xmlStringsEqual(Child->name, TO_XML_CHAR("comment"))) { + stripComments(Child); } else { StoreNext.next = Child->next; XMLNodeImpl Remove = Child; @@ -140,6 +621,18 @@ #endif } +void setAttributeNamespaces(XMLNodeImpl Node) { + for (XMLAttrImpl Attribute = Node->properties; Attribute; + Attribute = Attribute->next) { + if (!Attribute->ns) { + Attribute->ns = getClosestDefault(Node); + } + } + for (XMLNodeImpl Child = Node->children; Child; Child = Child->next) { + setAttributeNamespaces(Child); + } +} + Error WindowsManifestMerger::merge(const MemoryBuffer &Manifest) { #if LLVM_LIBXML2_ENABLED if (Manifest.getBufferSize() == 0) @@ -153,13 +646,15 @@ if (auto E = getParseError()) return E; XMLNodeImpl AdditionalRoot = xmlDocGetRootElement(ManifestXML); - stripCommentsAndText(AdditionalRoot); + stripComments(AdditionalRoot); + setAttributeNamespaces(AdditionalRoot); if (CombinedDoc == nullptr) { CombinedDoc = ManifestXML; } else { XMLNodeImpl CombinedRoot = xmlDocGetRootElement(CombinedDoc); if (xmlStringsEqual(CombinedRoot->name, AdditionalRoot->name) && - isMergeableElement(AdditionalRoot->name)) { + isMergeableElement(AdditionalRoot->name) && + hasRecognizedNamespace(AdditionalRoot)) { if (auto E = treeMerge(CombinedRoot, AdditionalRoot)) { return E; } @@ -169,21 +664,87 @@ } MergedDocs.push_back(ManifestXML); #endif + // outs() << "finished merge\n"; return Error::success(); } +void checkAndStripPrefixes(XMLNodeImpl Node, + std::vector &RequiredPrefixes) { +#if LLVM_LIBXML2_ENABLED + for (XMLNodeImpl Child = Node->children; Child; Child = Child->next) { + checkAndStripPrefixes(Child, RequiredPrefixes); + } + if (Node->ns && Node->ns->prefix != nullptr) { + // outs() << "about to get closest default\n"; + XMLNsImpl ClosestDefault = getClosestDefault(Node); + // outs() << "got closest default\n"; + if (ClosestDefault && + xmlStringsEqual(ClosestDefault->href, Node->ns->href)) { + Node->ns = ClosestDefault; + } else if (std::find(RequiredPrefixes.begin(), RequiredPrefixes.end(), + Node->ns) == RequiredPrefixes.end()) { + RequiredPrefixes.push_back(Node->ns); + } + } + for (XMLAttrImpl Attribute = Node->properties; Attribute; + Attribute = Attribute->next) { + if (Attribute->ns && Attribute->ns->prefix != nullptr) { + // outs() << "about to get closest default\n"; + XMLNsImpl ClosestDefault = getClosestDefault(Node); + // outs() << "got closest default\n"; + if (ClosestDefault && + xmlStringsEqual(ClosestDefault->href, Attribute->ns->href)) { + Attribute->ns = ClosestDefault; + } else if (std::find(RequiredPrefixes.begin(), RequiredPrefixes.end(), + Node->ns) == RequiredPrefixes.end()) { + RequiredPrefixes.push_back(Attribute->ns); + } + } + } + XMLNsImpl Prev; + xmlNs Temp; + for (XMLNsImpl Def = Node->nsDef; Def; Def = Def->next) { + if (Def->prefix && + (std::find(RequiredPrefixes.begin(), RequiredPrefixes.end(), Def) == + RequiredPrefixes.end())) { + if (Def == Node->nsDef) + Node->nsDef = Def->next; + else + Prev->next = Def->next; + Temp.next = Def->next; + // outs() << "about to free ns\n"; + xmlFreeNs(Def); + Def = &Temp; + // outs() << "freed ns\n"; + } else { + Prev = Def; + } + } + // outs() << "returned check and strip\n"; +#endif +} + std::unique_ptr WindowsManifestMerger::getMergedManifest() { +// outs() << "getting merged manifest\n"; #if LLVM_LIBXML2_ENABLED unsigned char *XmlBuff; int BufferSize = 0; if (CombinedDoc) { + CombinedDoc->standalone = 1; + XMLNodeImpl CombinedRoot = xmlDocGetRootElement(CombinedDoc); + std::vector RequiredPrefixes; + // outs() << "about to check and strip\n"; + checkAndStripPrefixes(CombinedRoot, RequiredPrefixes); + // outs() << "finished checking and stripping\n"; std::unique_ptr OutputDoc(xmlNewDoc((const unsigned char *)"1.0")); - xmlDocSetRootElement(OutputDoc.get(), xmlDocGetRootElement(CombinedDoc)); + xmlDocSetRootElement(OutputDoc.get(), CombinedRoot); xmlKeepBlanksDefault(0); - xmlDocDumpFormatMemory(OutputDoc.get(), &XmlBuff, &BufferSize, 1); + xmlDocDumpFormatMemoryEnc(OutputDoc.get(), &XmlBuff, &BufferSize, "UTF-8", + 1); } if (BufferSize == 0) return nullptr; + // outs() << "about to return membuffer\n"; return MemoryBuffer::getMemBuffer( StringRef(FROM_XML_CHAR(XmlBuff), (size_t)BufferSize)); #else @@ -201,5 +762,3 @@ return Error::success(); return make_error("invalid xml document"); } - -} // namespace llvm Index: llvm/test/tools/llvm-mt/Inputs/assembly_identity.manifest =================================================================== --- /dev/null +++ llvm/test/tools/llvm-mt/Inputs/assembly_identity.manifest @@ -0,0 +1,8 @@ + + + + + + + + Index: llvm/test/tools/llvm-mt/Inputs/compatibility.manifest =================================================================== --- /dev/null +++ llvm/test/tools/llvm-mt/Inputs/compatibility.manifest @@ -0,0 +1,9 @@ + + + + + + + + + Index: llvm/test/tools/llvm-mt/Inputs/expected_big.manifest =================================================================== --- /dev/null +++ llvm/test/tools/llvm-mt/Inputs/expected_big.manifest @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + true/pm + + + Index: llvm/test/tools/llvm-mt/Inputs/test_manifest.manifest =================================================================== --- llvm/test/tools/llvm-mt/Inputs/test_manifest.manifest +++ llvm/test/tools/llvm-mt/Inputs/test_manifest.manifest @@ -1,4 +1,4 @@ - + Index: llvm/test/tools/llvm-mt/Inputs/trust_and_identity.manifest =================================================================== --- /dev/null +++ llvm/test/tools/llvm-mt/Inputs/trust_and_identity.manifest @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file Index: llvm/test/tools/llvm-mt/Inputs/trust_info.manifest =================================================================== --- /dev/null +++ llvm/test/tools/llvm-mt/Inputs/trust_info.manifest @@ -0,0 +1,9 @@ + + + + + + + + + Index: llvm/test/tools/llvm-mt/Inputs/windows_settings.manifest =================================================================== --- /dev/null +++ llvm/test/tools/llvm-mt/Inputs/windows_settings.manifest @@ -0,0 +1,8 @@ + + + + + true/pm + + + Index: llvm/test/tools/llvm-mt/big_merge.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-mt/big_merge.test @@ -0,0 +1,39 @@ +REQUIRES: libxml2 +UNSUPPORTED: windows + +RUN: llvm-mt /manifest %p/Inputs/trust_info.manifest /manifest \ +RUN: %p/Inputs/assembly_identity.manifest /manifest \ +RUN: %p/Inputs/trust_and_identity.manifest /manifest \ +RUN: %p/Inputs/compatibility.manifest /manifest \ +RUN: %p/Inputs/windows_settings.manifest /out:%t +RUN: FileCheck %s -input-file=%t + +CHECK: +CHECK-NEXT: +CHECK-NEXT: +CHECK-NEXT: +CHECK-NEXT: +CHECK-NEXT: +CHECK-NEXT: +CHECK-NEXT: +CHECK-NEXT: +CHECK-NEXT: +CHECK-NEXT: +CHECK-NEXT: +CHECK-NEXT: +CHECK-NEXT: +CHECK-NEXT: +CHECK-NEXT: +CHECK-NEXT: +CHECK-NEXT: +CHECK-NEXT: +CHECK-NEXT: +CHECK-NEXT: +CHECK-NEXT: +CHECK-NEXT: +CHECK-NEXT: +CHECK-NEXT: +CHECK-NEXT: true/pm +CHECK-NEXT: +CHECK-NEXT: +CHECK-NEXT: Index: llvm/test/tools/llvm-mt/simple_merge.test =================================================================== --- llvm/test/tools/llvm-mt/simple_merge.test +++ llvm/test/tools/llvm-mt/simple_merge.test @@ -5,7 +5,7 @@ RUN: %p/Inputs/additional.manifest /out:%t RUN: FileCheck %s -input-file=%t -CHECK: +CHECK: CHECK-NEXT: CHECK-NEXT: CHECK-NEXT: Index: llvm/test/tools/llvm-mt/single_file.test =================================================================== --- llvm/test/tools/llvm-mt/single_file.test +++ llvm/test/tools/llvm-mt/single_file.test @@ -4,7 +4,7 @@ RUN: llvm-mt /manifest %p/Inputs/test_manifest.manifest /out:%t RUN: FileCheck %s --input-file=%t -CHECK: +CHECK: CHECK-NEXT: CHECK-NEXT: CHECK-NEXT: Index: llvm/tools/llvm-mt/Opts.td =================================================================== --- llvm/tools/llvm-mt/Opts.td +++ llvm/tools/llvm-mt/Opts.td @@ -1,7 +1,8 @@ include "llvm/Option/OptParser.td" +def supported : OptionGroup<"supported">; def unsupported : OptionGroup<"unsupported">; -def manifest : Separate<["/", "-"], "manifest">, HelpText<"Used to specify each manifest that need to be processed">, MetaVarName<"manifest">; +def manifest : Separate<["/", "-"], "manifest">, HelpText<"Used to specify each manifest that need to be processed">, MetaVarName<"manifest">, Group; def identity : Joined<["/", "-"], "identity:">, HelpText<"Not supported">, MetaVarName<"identity">, Group; def rgs : Joined<["/", "-"], "rgs:">, HelpText<"Not supported">, MetaVarName<"script">, Group; def tlb : Joined<["/", "-"], "tlb:">, HelpText<"Not supported">, MetaVarName<"file">, Group; @@ -10,8 +11,8 @@ def managed_assembly_name : Joined<["/", "-"], "managedassemblyname:">, HelpText<"Not supported">, MetaVarName<"assembly">, Group; def no_dependency : Flag<["/", "-"], "nodependency">, HelpText<"Not supported">, Group; def category : Flag<["/", "-"], "category">, HelpText<"Not supported">, Group; -def no_logo : Flag<["/", "-"], "nologo">, HelpText<"No effect as this tool never writes copyright data. Included for parity">; -def out : Joined<["/", "-"], "out:">, HelpText<"Name of the output manifest. If this is skipped and only one manifest is being operated upon by the tool, that manifest is modified in place">, MetaVarName<"manifest">; +def no_logo : Flag<["/", "-"], "nologo">, HelpText<"No effect as this tool never writes copyright data. Included for parity">, Group; +def out : Joined<["/", "-"], "out:">, HelpText<"Name of the output manifest. If this is skipped and only one manifest is being operated upon by the tool, that manifest is modified in place">, MetaVarName<"manifest">, Group; def input_resource : Joined<["/", "-"], "inputresource:">, HelpText<"Not supported">, MetaVarName<"file">, Group; def output_resource : Joined<["/", "-"], "outputresource:">, HelpText<"Not supported">, MetaVarName<"file">, Group; def output_resource_flag : Flag<["/", "-"], "outputresource">, Alias, HelpText<"Not supported">, Group; @@ -24,6 +25,6 @@ def check_for_duplicates : Flag<["/", "-"], "check_for_duplicates:">, HelpText<"Not supported">, Group; def make_cdfs : Flag<["/", "-"], "makecdfs:">, HelpText<"Not supported">, Group; def verbose : Flag<["/", "-"], "verbose">, HelpText<"Not supported">, Group; -def help : Flag<["/", "-"], "?">; -def help_long : Flag<["/", "-"], "help">, Alias; -def h : Flag<["/", "-"], "h">, Alias; +def help : Flag<["/", "-"], "?">, Group; +def help_long : Flag<["/", "-"], "help">, Alias, Group; +def h : Flag<["/", "-"], "h">, Alias, Group; Index: llvm/tools/llvm-mt/llvm-mt.cpp =================================================================== --- llvm/tools/llvm-mt/llvm-mt.cpp +++ llvm/tools/llvm-mt/llvm-mt.cpp @@ -102,6 +102,25 @@ ArrayRef ArgsArr = makeArrayRef(argv + 1, argc); opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MAI, MAC); + if (MAC) { + outs() << "invalid\n"; + } + + // for (auto &Arg : InputArgs) { + // outs() << "checking valid\n"; + // outs() << Arg->getOption().getID() << "\n"; + // if (!Arg->getOption().isValid()) { + // reportError(Twine("invalid option ") + Arg->getOption().getName()); + // } + // } + + for (auto &Arg : InputArgs) { + if (!(Arg->getOption().matches(OPT_unsupported) || + Arg->getOption().matches(OPT_supported))) { + reportError(Twine("invalid option ") + Arg->getSpelling()); + } + } + for (auto &Arg : InputArgs) { if (Arg->getOption().matches(OPT_unsupported)) { outs() << "llvm-mt: ignoring unsupported '" << Arg->getOption().getName() @@ -116,6 +135,95 @@ std::vector InputFiles = InputArgs.getAllArgValues(OPT_manifest); + // for (int i = 0; i < 2; i++) { + // switch (i) { + // case 0: + // outs() << "If additional dominant\n"; + // break; + // case 1: + // outs() << "Else\n"; + // break; + // } + // for (int j = 0; j < 2; j++) { + // outs() << "\t"; + // switch (j) { + // case 0: + // outs() << "If dominant defines default\n"; + // break; + // case 1: + // outs() << "Else\n"; + // break; + // } + // for (int k = 0; k < 2; k++) { + // outs() << "\t\t"; + // switch (k) { + // case 0: + // outs() << "If dominant defines prefix\n"; + // break; + // case 1: + // outs() << "Else\n"; + // break; + // } + // for (int l = 0; l < 4; l++) { + // outs() << "\t\t\t"; + // switch (l) { + // case 0: + // outs() << "If dominant namespace is inherited default\n"; + // break; + // case 1: + // outs() << "If dominant namespace is inherited prefix\n"; + // break; + // case 2: + // outs() << "If dominant namespace is defined default\n"; + // break; + // case 3: + // outs() << "If dominant namespace is defined prefix\n"; + // break; + // } + // for (int m = 0; m < 2; m++) { + // outs() << "\t\t\t\t"; + // switch (m) { + // case 0: + // outs() << "If nondominant defines default\n"; + // break; + // case 1: + // outs() << "Else\n"; + // break; + // } + // for (int n = 0; n < 2; n++) { + // outs() << "\t\t\t\t\t"; + // switch (n) { + // case 0: + // outs() << "If nondominant defines prefix\n"; + // break; + // case 1: + // outs() << "Else\n"; + // break; + // } + // for (int o = 0; o < 4; o++) { + // outs() << "\t\t\t\t\t\t"; + // switch (o) { + // case 0: + // outs() << "If nondominant namespace is inherited + // default\n"; break; + // case 1: + // outs() << "If nondominant namespace is inherited prefix\n"; + // break; + // case 2: + // outs() << "If nondominant namespace is defined default\n"; + // break; + // case 3: + // outs() << "If nondominant namespace is defined prefix\n"; + // break; + // } + // } + // } + // } + // } + // } + // } + // } + if (InputFiles.size() == 0) { reportError("no input file specified"); } @@ -129,7 +237,7 @@ reportError("no output file specified"); } - WindowsManifestMerger Merger; + windows_manifest::WindowsManifestMerger Merger; for (const auto &File : InputFiles) { ErrorOr> ManifestOrErr =