Index: llvm/include/llvm/Support/WindowsManifestMerger.h =================================================================== --- llvm/include/llvm/Support/WindowsManifestMerger.h +++ llvm/include/llvm/Support/WindowsManifestMerger.h @@ -57,6 +57,8 @@ class WindowsManifestMerger { public: + ~WindowsManifestMerger(); + Error merge(const MemoryBuffer &Manifest); // Returns vector containing merged xml manifest, or uninitialized vector for @@ -68,7 +70,8 @@ Error getParseError(); #if LLVM_LIBXML2_ENABLED - XMLNodeImpl CombinedRoot = nullptr; + XMLDocumentImpl CombinedDoc = nullptr; + std::vector MergedDocs; #endif bool ParseErrorOccurred = false; }; Index: llvm/lib/Support/WindowsManifestMerger.cpp =================================================================== --- llvm/lib/Support/WindowsManifestMerger.cpp +++ llvm/lib/Support/WindowsManifestMerger.cpp @@ -16,6 +16,11 @@ #include +#define TO_XML_CHAR(X) reinterpret_cast(X) +#define FROM_XML_CHAR(X) reinterpret_cast(X) + +using namespace llvm; + namespace llvm { char WindowsManifestError::ID = 0; @@ -24,16 +29,148 @@ void WindowsManifestError::log(raw_ostream &OS) const { OS << Msg; } +static bool xmlStringsEqual(const unsigned char *A, const unsigned char *B) { + return strcmp(FROM_XML_CHAR(A), FROM_XML_CHAR(B)) == 0; +} + +bool isMergeableElement(const unsigned char *ElementName) { + for (StringRef S : {"application", "assembly", "assemblyIdentity", + "compatibility", "noInherit", "requestedExecutionLevel", + "requestedPrivileges", "security", "trustInfo"}) { + if (S == FROM_XML_CHAR(ElementName)) + return true; + } + return false; +} + +XMLNodeImpl getChildWithName(XMLNodeImpl Parent, + const unsigned char *ElementName) { +#if LLVM_LIBXML2_ENABLED + for (XMLNodeImpl Child = Parent->children; Child; Child = Child->next) + if (xmlStringsEqual(Child->name, ElementName)) { + return Child; + } +#endif + return nullptr; +} + +const unsigned char *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; + } +#endif + return nullptr; +} + +Error mergeAttributes(XMLNodeImpl OriginalNode, XMLNodeImpl AdditionalNode) { +#if LLVM_LIBXML2_ENABLED + for (xmlAttrPtr Attribute = AdditionalNode->properties; Attribute != nullptr; + Attribute = Attribute->next) { + if (const unsigned char *OriginalValue = + 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)) + return make_error( + Twine("conflicting attributes for ") + + FROM_XML_CHAR(OriginalNode->name)); + } 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)); + } + } +#endif + return Error::success(); +} + +Error treeMerge(XMLNodeImpl OriginalRoot, XMLNodeImpl AdditionalRoot) { +#if LLVM_LIBXML2_ENABLED + XMLNodeImpl AdditionalFirstChild = AdditionalRoot->children; + for (XMLNodeImpl Child = AdditionalFirstChild; Child; Child = Child->next) { + XMLNodeImpl OriginalChildWithName; + if (!isMergeableElement(Child->name) || + !(OriginalChildWithName = + getChildWithName(OriginalRoot, Child->name))) { + XMLNodeImpl NewChild = xmlCopyNode(Child, 1); + if (!NewChild) + return make_error(Twine("error when copying ") + + FROM_XML_CHAR(Child->name)); + if (NewChild->ns) + xmlFreeNs(NewChild->ns); // xmlCopyNode explicitly defines default + // namespace, undo this here. + if (!xmlAddChild(OriginalRoot, NewChild)) + return make_error(Twine("could not merge ") + + FROM_XML_CHAR(NewChild->name)); + } else if (auto E = treeMerge(OriginalChildWithName, Child)) { + return E; + } + } + if (auto E = mergeAttributes(OriginalRoot, AdditionalRoot)) + return E; +#endif + return Error::success(); +} + +void stripCommentsAndText(XMLNodeImpl Root) { + 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); + } else { + StoreNext.next = Child->next; + XMLNodeImpl Remove = Child; + Child = &StoreNext; + xmlUnlinkNode(Remove); + xmlFreeNode(Remove); + } + } +} + +WindowsManifestMerger::~WindowsManifestMerger() { +#if LLVM_LIBXML2_ENABLED + for (auto &Doc : MergedDocs) + xmlFreeDoc(Doc); +#endif +} + Error WindowsManifestMerger::merge(const MemoryBuffer &Manifest) { #if LLVM_LIBXML2_ENABLED + if (Manifest.getBufferSize() == 0) + return make_error( + "attempted to merge empty manifest"); xmlSetGenericErrorFunc((void *)this, WindowsManifestMerger::errorCallback); XMLDocumentImpl ManifestXML = xmlReadMemory(Manifest.getBufferStart(), Manifest.getBufferSize(), - "manifest.xml", nullptr, 0); + "manifest.xml", nullptr, XML_PARSE_NOBLANKS); xmlSetGenericErrorFunc(nullptr, nullptr); if (auto E = getParseError()) return E; - CombinedRoot = xmlDocGetRootElement(ManifestXML); + XMLNodeImpl AdditionalRoot = xmlDocGetRootElement(ManifestXML); + stripCommentsAndText(AdditionalRoot); + if (CombinedDoc == nullptr) { + CombinedDoc = ManifestXML; + } else { + XMLNodeImpl CombinedRoot = xmlDocGetRootElement(CombinedDoc); + if (xmlStringsEqual(CombinedRoot->name, AdditionalRoot->name) && + isMergeableElement(AdditionalRoot->name)) { + if (auto E = treeMerge(CombinedRoot, AdditionalRoot)) { + return E; + } + } else { + XMLNodeImpl NewChild = xmlCopyNode(AdditionalRoot, 1); + if (!NewChild) + return make_error("could not copy manifest"); + if (!xmlAddChild(CombinedRoot, NewChild)) + return make_error("could not append manifest"); + } + } + MergedDocs.push_back(ManifestXML); #endif return Error::success(); } @@ -42,15 +179,16 @@ #if LLVM_LIBXML2_ENABLED unsigned char *XmlBuff; int BufferSize = 0; - if (CombinedRoot) { + if (CombinedDoc) { std::unique_ptr OutputDoc(xmlNewDoc((const unsigned char *)"1.0")); - xmlDocSetRootElement(OutputDoc.get(), CombinedRoot); - xmlDocDumpMemory(OutputDoc.get(), &XmlBuff, &BufferSize); + xmlDocSetRootElement(OutputDoc.get(), xmlDocGetRootElement(CombinedDoc)); + xmlKeepBlanksDefault(0); + xmlDocDumpFormatMemory(OutputDoc.get(), &XmlBuff, &BufferSize, 1); } if (BufferSize == 0) return nullptr; return MemoryBuffer::getMemBuffer( - StringRef(reinterpret_cast(XmlBuff), (size_t)BufferSize)); + StringRef(FROM_XML_CHAR(XmlBuff), (size_t)BufferSize)); #else return nullptr; #endif Index: llvm/test/tools/llvm-mt/Inputs/additional.manifest =================================================================== --- /dev/null +++ llvm/test/tools/llvm-mt/Inputs/additional.manifest @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + Index: llvm/test/tools/llvm-mt/Inputs/conflicting.manifest =================================================================== --- /dev/null +++ llvm/test/tools/llvm-mt/Inputs/conflicting.manifest @@ -0,0 +1,10 @@ + + + + + + + + + + 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,9 +1,9 @@ - + - + @@ -12,4 +12,12 @@ + + + + + + + + Index: llvm/test/tools/llvm-mt/conflicting.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-mt/conflicting.test @@ -0,0 +1,7 @@ +REQUIRES: libxml2 +UNSUPPORTED: windows + +RUN: not llvm-mt /manifest %p/Inputs/test_manifest.manifest /manifest \ +RUN: %p/Inputs/conflicting.manifest /out:%t 2>&1 >/dev/null | FileCheck %s + +CHECK: llvm-mt error: conflicting attributes for requestedExecutionLevel Index: llvm/test/tools/llvm-mt/simple_merge.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-mt/simple_merge.test @@ -0,0 +1,39 @@ +REQUIRES: libxml2 +UNSUPPORTED: windows + +RUN: llvm-mt /manifest %p/Inputs/test_manifest.manifest /manifest \ +RUN: %p/Inputs/additional.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: +CHECK-NEXT: +CHECK-NEXT: +CHECK-NEXT: +CHECK-NEXT: +CHECK-NEXT: +CHECK-NEXT: Index: llvm/tools/llvm-mt/llvm-mt.cpp =================================================================== --- llvm/tools/llvm-mt/llvm-mt.cpp +++ llvm/tools/llvm-mt/llvm-mt.cpp @@ -95,7 +95,6 @@ SpecificBumpPtrAllocator ArgAllocator; ExitOnErr(errorCodeToError(sys::Process::GetArgumentVector( argv_buf, makeArrayRef(argv, argc), ArgAllocator))); - llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. CvtResOptTable T;