Index: llvm/include/llvm/Support/WindowsManifestMerger.h =================================================================== --- llvm/include/llvm/Support/WindowsManifestMerger.h +++ llvm/include/llvm/Support/WindowsManifestMerger.h @@ -35,6 +35,15 @@ namespace llvm { +static const char *const MT_Strings[]{ + "application", "assembly", "assemblyIdentity", + "compatibility", "noInherit", "requestedExecutionLevel", + "requestedPrivileges", "security", "trustInfo", +}; + +static const std::vector MTMergeableElements(MT_Strings, + std::end(MT_Strings)); + class MemoryBuffer; #if LLVM_LIBXML2_ENABLED @@ -49,6 +58,7 @@ public: static char ID; WindowsManifestError(const Twine &Msg); + WindowsManifestError(const Twine &Prefix, const Twine &Message); void log(raw_ostream &OS) const override; private: @@ -57,6 +67,10 @@ class WindowsManifestMerger { public: +#if LLVM_LIBXML2_ENABLED + ~WindowsManifestMerger(); +#endif + Error merge(const MemoryBuffer &Manifest); // Returns vector containing merged xml manifest, or uninitialized vector for @@ -68,7 +82,7 @@ Error getParseError(); #if LLVM_LIBXML2_ENABLED - XMLNodeImpl CombinedRoot = nullptr; + XMLDocumentImpl CombinedDoc = nullptr; #endif bool ParseErrorOccurred = false; }; Index: llvm/lib/Support/WindowsManifestMerger.cpp =================================================================== --- llvm/lib/Support/WindowsManifestMerger.cpp +++ llvm/lib/Support/WindowsManifestMerger.cpp @@ -16,24 +16,139 @@ #include +using namespace llvm; + namespace llvm { char WindowsManifestError::ID = 0; WindowsManifestError::WindowsManifestError(const Twine &Msg) : Msg(Msg.str()) {} +WindowsManifestError::WindowsManifestError(const Twine &Prefix, + const Twine &Message) + : Msg((Prefix.concat(Message)).str()) {} + void WindowsManifestError::log(raw_ostream &OS) const { OS << Msg; } +bool isMergeableElement(const unsigned char *ElementName) { + bool result = + std::find(MTMergeableElements.begin(), MTMergeableElements.end(), + std::string(reinterpret_cast(ElementName))) != + MTMergeableElements.end(); + return result; +} + +XMLNodeImpl hasChildWithName(XMLNodeImpl Parent, + const unsigned char *ElementName) { +#if LLVM_LIBXML2_ENABLED + for (XMLNodeImpl Child = Parent->children; Child; Child = Child->next) + if (strcmp(reinterpret_cast(Child->name), + reinterpret_cast(ElementName)) == 0) { + return Child; + } +#endif + return nullptr; +} + +const unsigned char *hasAttribute(XMLNodeImpl Node, + const unsigned char *AttributeName) { +#if LLVM_LIBXML2_ENABLED + for (xmlAttrPtr Attribute = Node->properties; Attribute != nullptr; + Attribute = Attribute->next) { + if (strcmp(reinterpret_cast(Attribute->name), + reinterpret_cast(AttributeName)) == 0) + 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 = + hasAttribute(OriginalNode, Attribute->name)) { + if (strcmp(reinterpret_cast(OriginalValue), + reinterpret_cast( + Attribute->children->content)) != 0) + return make_error( + "conflicting attributes for ", + reinterpret_cast(OriginalNode->name)); + } else { + char *NameCopy = strdup(reinterpret_cast(Attribute->name)); + char *ContentCopy = + strdup(reinterpret_cast(Attribute->children->content)); + xmlNewProp(OriginalNode, + reinterpret_cast(NameCopy), + reinterpret_cast(ContentCopy)); + } + } +#endif + return Error::success(); +} + +Error treeMerge(XMLNodeImpl OriginalRoot, XMLNodeImpl AdditionalRoot) { +#if LLVM_LIBXML2_ENABLED + XMLNodeImpl AdditionalFirstChild = AdditionalRoot->children; + if (AdditionalFirstChild != nullptr) { + for (XMLNodeImpl Child = AdditionalFirstChild; Child; Child = Child->next) { + XMLNodeImpl OriginalChildWithName; + if ((strcmp(reinterpret_cast(Child->name), "text") != 0) && + (strcmp(reinterpret_cast(Child->name), "comment") != + 0)) { + if (!(OriginalChildWithName = + hasChildWithName(OriginalRoot, Child->name)) || + !isMergeableElement(Child->name)) { + XMLNodeImpl NewChild = xmlCopyNode(Child, 1); + if (NewChild->ns) + xmlFreeNs(NewChild->ns); // xmlCopyNode explicitly defines default + // namespace, undo this here. + xmlAddChild(OriginalRoot, NewChild); + } else { + if (auto E = treeMerge(OriginalChildWithName, Child)) + return E; + } + } + } + } + if (auto E = mergeAttributes(OriginalRoot, AdditionalRoot)) + return E; + xmlTextMerge(OriginalRoot, AdditionalRoot); +#endif + return Error::success(); +} + +#if LLVM_LIBXML2_ENABLED +WindowsManifestMerger::~WindowsManifestMerger() { xmlFreeDoc(CombinedDoc); } +#endif + Error WindowsManifestMerger::merge(const MemoryBuffer &Manifest) { #if LLVM_LIBXML2_ENABLED xmlSetGenericErrorFunc((void *)this, WindowsManifestMerger::errorCallback); XMLDocumentImpl ManifestXML = xmlReadMemory(Manifest.getBufferStart(), Manifest.getBufferSize(), - "manifest.xml", nullptr, 0); + "manifest.xml", nullptr, XML_PARSE_NOBLANKS); xmlSetGenericErrorFunc(nullptr, nullptr); if (auto E = getParseError()) return E; - CombinedRoot = xmlDocGetRootElement(ManifestXML); + if (CombinedDoc == nullptr) + CombinedDoc = ManifestXML; + else { + XMLNodeImpl CombinedRoot = xmlDocGetRootElement(CombinedDoc); + XMLNodeImpl AdditionalRoot = xmlDocGetRootElement(ManifestXML); + if (strcmp(reinterpret_cast(CombinedRoot->name), + reinterpret_cast(AdditionalRoot->name)) == 0 && + isMergeableElement(AdditionalRoot->name)) { + if (auto E = treeMerge(CombinedRoot, AdditionalRoot)) { + xmlFreeDoc(ManifestXML); + return E; + } + } else { + XMLNodeImpl NewChild = xmlCopyNode(AdditionalRoot, 1); + xmlAddChild(CombinedRoot, NewChild); + } + } #endif return Error::success(); } @@ -42,10 +157,13 @@ #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); + errs() << "about to dump memory\n"; + xmlDocDumpFormatMemory(OutputDoc.get(), &XmlBuff, &BufferSize, 1); + errs() << "dumped\n"; } if (BufferSize == 0) return nullptr; Index: llvm/test/tools/llvm-mt/Inputs/additional.manifest =================================================================== --- llvm/test/tools/llvm-mt/Inputs/additional.manifest +++ llvm/test/tools/llvm-mt/Inputs/additional.manifest @@ -3,13 +3,21 @@ - + - + + + + + + + + + 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;