Index: lld/COFF/CMakeLists.txt =================================================================== --- lld/COFF/CMakeLists.txt +++ lld/COFF/CMakeLists.txt @@ -39,6 +39,7 @@ Target Option Support + WindowsManifest LINK_LIBS lldCore Index: lld/COFF/DriverUtils.cpp =================================================================== --- lld/COFF/DriverUtils.cpp +++ lld/COFF/DriverUtils.cpp @@ -20,6 +20,7 @@ #include "Symbols.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/Triple.h" #include "llvm/BinaryFormat/COFF.h" #include "llvm/Object/COFF.h" #include "llvm/Object/WindowsResource.h" @@ -32,6 +33,7 @@ #include "llvm/Support/Process.h" #include "llvm/Support/Program.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/WindowsManifest/WindowsManifestMerger.h" #include using namespace llvm::COFF; @@ -328,16 +330,9 @@ }; } -// Create the default manifest file as a temporary file. -TemporaryFile createDefaultXml() { - // Create a temporary file. - TemporaryFile File("defaultxml", "manifest"); - - // Open the temporary file for writing. - std::error_code EC; - raw_fd_ostream OS(File.Path, EC, sys::fs::F_Text); - if (EC) - fatal(EC, "failed to open " + File.Path); +static std::string createDefaultXml() { + std::string Ret; + raw_string_ostream OS(Ret); // Emit the XML. Note that we do *not* verify that the XML attributes are // syntactically correct. This is intentional for link.exe compatibility. @@ -362,37 +357,85 @@ << " \n"; } OS << "\n"; - OS.close(); - return File; + return Ret; } -static std::string readFile(StringRef Path) { - std::unique_ptr MB = - check(MemoryBuffer::getFile(Path), "could not open " + Path); - return MB->getBuffer(); +static Expected> +createManifestXmlWithInternalMt(std::string &DefaultXml) { + std::unique_ptr DefaultXmlCopy = + MemoryBuffer::getMemBufferCopy(DefaultXml); + + windows_manifest::WindowsManifestMerger Merger; + if (auto E = Merger.merge(*DefaultXmlCopy.get())) + return std::move(E); + + for (StringRef Filename : Config->ManifestInput) { + std::unique_ptr Manifest = + check(MemoryBuffer::getFile(Filename)); + if (auto E = Merger.merge(*Manifest.get())) { + warn("internal manifest tool failed on file " + Filename); + return std::move(E); + } + } + + return Merger.getMergedManifest(); } -static std::string createManifestXml() { - // Create the default manifest file. - TemporaryFile File1 = createDefaultXml(); - if (Config->ManifestInput.empty()) - return readFile(File1.Path); +static std::unique_ptr +createManifestXmlWithExternalMt(std::string &DefaultXml) { + const Triple HostTriple(Triple::normalize(LLVM_HOST_TRIPLE)); + if (!HostTriple.isOSWindows()) { + warn("manifest ignored because no external manifest tool available"); + return MemoryBuffer::getMemBufferCopy(std::string()); + } + // Create the default manifest file as a temporary file. + TemporaryFile Default("defaultxml", "manifest"); + std::error_code EC; + raw_fd_ostream OS(Default.Path, EC, sys::fs::F_Text); + if (EC) + fatal(EC, "failed to open " + Default.Path); + OS << DefaultXml; + OS.close(); - // If manifest files are supplied by the user using /MANIFESTINPUT - // option, we need to merge them with the default manifest. - TemporaryFile File2("user", "manifest"); + // Merge user-supplied manifests if they are given. Since libxml2 is not + // enabled, we must shell out to Microsoft's mt.exe tool. + TemporaryFile User("user", "manifest"); Executor E("mt.exe"); E.add("/manifest"); - E.add(File1.Path); + E.add(Default.Path); for (StringRef Filename : Config->ManifestInput) { E.add("/manifest"); E.add(Filename); } E.add("/nologo"); - E.add("/out:" + StringRef(File2.Path)); + E.add("/out:" + StringRef(User.Path)); E.run(); - return readFile(File2.Path); + + return check(MemoryBuffer::getFile(User.Path), "could not open " + User.Path); +} + +static std::string createManifestXml() { + std::string DefaultXml = createDefaultXml(); + if (Config->ManifestInput.empty()) + return DefaultXml; + + // If manifest files are supplied by the user using /MANIFESTINPUT + // option, we need to merge them with the default manifest. If libxml2 + // is enabled, we may merge them with LLVM's own library. + Expected> OutputBufferOrError = + createManifestXmlWithInternalMt(DefaultXml); + if (OutputBufferOrError) + return OutputBufferOrError.get()->getBuffer(); + // Using built-in library failed, possibly because libxml2 is not installed. + // Shell out to mt.exe instead. + handleAllErrors(std::move(OutputBufferOrError.takeError()), + [&](ErrorInfoBase &EIB) { + warn("error with internal manifest tool: " + EIB.message()); + }); + std::unique_ptr OutputBuffer; + OutputBuffer = createManifestXmlWithExternalMt(DefaultXml); + return OutputBuffer->getBuffer(); } static std::unique_ptr Index: lld/test/COFF/manifestinput.test =================================================================== --- lld/test/COFF/manifestinput.test +++ lld/test/COFF/manifestinput.test @@ -1,4 +1,4 @@ -# REQUIRES: win_mt +# REQUIRES: manifest_tool # RUN: yaml2obj %p/Inputs/ret42.yaml > %t.obj # RUN: lld-link /out:%t.exe /entry:main \ Index: lld/test/lit.cfg =================================================================== --- lld/test/lit.cfg +++ lld/test/lit.cfg @@ -265,6 +265,8 @@ config.environment['LLD_VERSION'] = 'LLD 1.0' # Indirectly check if the mt.exe Microsoft utility exists by searching for -# cvtres, which always accompanies it. -if lit.util.which('cvtres', config.environment['PATH']): - config.available_features.add('win_mt') +# cvtres, which always accompanies it. Alternatively, check if we can use +# libxml2 to merge manifests. +if (lit.util.which('cvtres', config.environment['PATH'])) or \ + (config.llvm_libxml2_enabled == "1"): + config.available_features.add('manifest_tool') Index: lld/test/lit.site.cfg.in =================================================================== --- lld/test/lit.site.cfg.in +++ lld/test/lit.site.cfg.in @@ -4,6 +4,7 @@ config.llvm_obj_root = "@LLVM_BINARY_DIR@" config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" config.llvm_libs_dir = "@LLVM_LIBS_DIR@" +config.llvm_libxml2_enabled = "@LLVM_LIBXML2_ENABLED@" config.lit_tools_dir = "@LLVM_LIT_TOOLS_DIR@" config.lld_obj_root = "@LLD_BINARY_DIR@" config.lld_libs_dir = "@LLVM_LIBRARY_OUTPUT_INTDIR@" Index: llvm/include/llvm/WindowsManifest/WindowsManifestMerger.h =================================================================== --- llvm/include/llvm/WindowsManifest/WindowsManifestMerger.h +++ llvm/include/llvm/WindowsManifest/WindowsManifestMerger.h @@ -46,6 +46,9 @@ }; class WindowsManifestMerger { +private: + class WindowsManifestMergerImpl; + std::unique_ptr Impl; public: WindowsManifestMerger(); ~WindowsManifestMerger(); Index: llvm/lib/WindowsManifest/WindowsManifestMerger.cpp =================================================================== --- llvm/lib/WindowsManifest/WindowsManifestMerger.cpp +++ llvm/lib/WindowsManifest/WindowsManifestMerger.cpp @@ -14,8 +14,6 @@ #include "llvm/WindowsManifest/WindowsManifestMerger.h" #include "llvm/Support/MemoryBuffer.h" -#include - #if LLVM_LIBXML2_ENABLED #include #endif @@ -37,7 +35,6 @@ ~WindowsManifestMergerImpl(); Error merge(const MemoryBuffer &Manifest); std::unique_ptr getMergedManifest(); - private: static void errorCallback(void *Ctx, const char *Format, ...); Error getParseError(); @@ -50,19 +47,21 @@ #if LLVM_LIBXML2_ENABLED -static const std::pair 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 const std::pair + 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) { // 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; } @@ -70,56 +69,61 @@ for (StringRef S : {"application", "assembly", "assemblyIdentity", "compatibility", "noInherit", "requestedExecutionLevel", "requestedPrivileges", "security", "trustInfo"}) { - if (S == FROM_XML_CHAR(ElementName)) { + if (S == FROM_XML_CHAR(ElementName)) return true; - } } return false; } static xmlNodePtr getChildWithName(xmlNodePtr Parent, - const unsigned char *ElementName) { - for (xmlNodePtr Child = Parent->children; Child; Child = Child->next) { + const unsigned char *ElementName) { + for (xmlNodePtr Child = Parent->children; Child; Child = Child->next) if (xmlStringsEqual(Child->name, ElementName)) { return Child; } - } return nullptr; } -static xmlAttrPtr getAttribute(xmlNodePtr Node, - const unsigned char *AttributeName) { +static xmlAttrPtr getAttribute(xmlNodePtr Node, const unsigned char *AttributeName) { for (xmlAttrPtr Attribute = Node->properties; Attribute != nullptr; Attribute = Attribute->next) { - if (xmlStringsEqual(Attribute->name, AttributeName)) { + if (xmlStringsEqual(Attribute->name, AttributeName)) return Attribute; - } } return nullptr; } // Check if namespace specified by HRef1 overrides that of HRef2. static bool namespaceOverrides(const unsigned char *HRef1, - const unsigned char *HRef2) { - auto HRef1Position = llvm::find_if( - MtNsHrefsPrefixes, [=](const std::pair &Element) { - return xmlStringsEqual(HRef1, TO_XML_CHAR(Element.first.data())); + const unsigned char *HRef2) { + auto HRef1Position = std::find_if( + std::begin(MtNsHrefsPrefixes), std::end(MtNsHrefsPrefixes), + [HRef1](const std::pair &element) { + return xmlStringsEqual(HRef1, TO_XML_CHAR(element.first.c_str())); }); - auto HRef2Position = llvm::find_if( - MtNsHrefsPrefixes, [=](const std::pair &Element) { - return xmlStringsEqual(HRef2, TO_XML_CHAR(Element.first.data())); + if (HRef1Position == std::end(MtNsHrefsPrefixes)) + return false; + auto HRef2Position = std::find_if( + std::begin(MtNsHrefsPrefixes), std::end(MtNsHrefsPrefixes), + [HRef2](const std::pair &element) { + return xmlStringsEqual(HRef2, TO_XML_CHAR(element.first.c_str())); }); - return HRef1Position < HRef2Position; + if (HRef2Position == std::end(MtNsHrefsPrefixes)) + return true; + int HRef1Priority = std::distance(HRef1Position, std::end(MtNsHrefsPrefixes)); + int HRef2Priority = std::distance(HRef2Position, std::end(MtNsHrefsPrefixes)); + if (HRef1Priority > HRef2Priority) + return true; + return false; } // Search for prefix-defined namespace specified by HRef, starting on Node and -// continuing recursively upwards. Returns the namespace or nullptr if not +// continuing recurisvely 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)) { + if (Def->prefix && xmlStringsEqual(Def->href, HRef)) return Def; - } } if (Node->parent) { return search(HRef, Node->parent); @@ -131,31 +135,33 @@ // 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()); + if (xmlStringsEqual(HRef, TO_XML_CHAR(Ns.first.c_str()))) { + return TO_XML_CHAR(Ns.second.c_str()); } } 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 +// continuing recurisvely 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 searchOrDefine(const unsigned char *HRef, - xmlNodePtr Node) { - if (xmlNsPtr Def = search(HRef, Node)) - return Def; - if (xmlNsPtr Def = xmlNewNs(Node, HRef, getPrefixForHref(HRef))) + xmlNodePtr Node) { + xmlNsPtr Def = search(HRef, Node); + if (Def) return Def; - return make_error("failed to create new namespace"); + Def = xmlNewNs(Node, HRef, getPrefixForHref(HRef)); + if (!Def) + return make_error("failed to create new namespace"); + return Def; } // Set the namespace of OrigionalAttribute on OriginalNode to be that of // AdditionalAttribute's. static Error copyAttributeNamespace(xmlAttrPtr OriginalAttribute, - xmlNodePtr OriginalNode, - xmlAttrPtr AdditionalAttribute) { + xmlNodePtr OriginalNode, + xmlAttrPtr AdditionalAttribute) { Expected ExplicitOrError = searchOrDefine(AdditionalAttribute->ns->href, OriginalNode); @@ -168,13 +174,13 @@ // Return the corresponding namespace definition for the prefix, defined on the // given Node. Returns nullptr if there is no such definition. static xmlNsPtr getNamespaceWithPrefix(const unsigned char *Prefix, - xmlNodePtr Node) { + xmlNodePtr Node) { + if (Node == nullptr) return nullptr; for (xmlNsPtr Def = Node->nsDef; Def; Def = Def->next) { - if (xmlStringsEqual(Def->prefix, Prefix)) { + if (xmlStringsEqual(Def->prefix, Prefix)) return Def; - } } return nullptr; } @@ -196,72 +202,62 @@ // 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) { +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)) { + Attribute->children->content)) return make_error( Twine("conflicting attributes for ") + FROM_XML_CHAR(OriginalNode->name)); - } if (!Attribute->ns) { continue; } if (!OriginalAttribute->ns) { if (auto E = copyAttributeNamespace(OriginalAttribute, OriginalNode, - Attribute)) { + 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. + } else if (namespaceOverrides(OriginalAttribute->ns->href, + Attribute->ns->href)) { if (!OriginalAttribute->ns->prefix && !Attribute->ns->prefix && ClosestDefault && xmlStringsEqual(Attribute->ns->href, ClosestDefault->href)) { if (auto E = copyAttributeNamespace(OriginalAttribute, OriginalNode, - Attribute)) { + 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))) { + } else if (Attribute->ns->prefix || OriginalAttribute->ns->prefix || + (ClosestDefault && + !xmlStringsEqual(OriginalAttribute->ns->href, + ClosestDefault->href))) { if (auto E = copyAttributeNamespace(OriginalAttribute, OriginalNode, - Attribute)) { + 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. + } else { + xmlAttrPtr 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()); } - // 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 ExplicitOrError = - searchOrDefine(Attribute->ns->href, OriginalNode); - if (!ExplicitOrError) - return ExplicitOrError.takeError(); - NewProp->ns = std::move(ExplicitOrError.get()); } return Error::success(); } @@ -271,27 +267,37 @@ if (!Node1 || !Node1->ns) return Node2; - if (!Node2 || !Node2->ns) + else if (!Node2 || !Node2->ns) return Node1; - if (namespaceOverrides(Node1->ns->href, Node2->ns->href)) + else if (namespaceOverrides(Node1->ns->href, Node2->ns->href)) return Node1; - return Node2; + else + 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); + if (Node->ns && Node->ns != getNamespaceWithPrefix(Node->ns->prefix, Node)) + return true; + else + return false; } // 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; + if (hasInheritedNs(Node) && Node->ns->prefix == nullptr) + return true; + else + return false; } // 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)); + if (Node->ns && (Node->ns == getNamespaceWithPrefix(nullptr, Node))) + return true; + else + return false; } // For the given explicit prefix-definition of a namespace, travel downwards @@ -321,8 +327,7 @@ } // Perform the namespace merge between two nodes. -static Error mergeNamespaces(xmlNodePtr OriginalNode, - xmlNodePtr AdditionalNode) { +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; @@ -375,9 +380,8 @@ OriginalDefinedDefaultHref)) { Expected EC = searchOrDefine(OriginalDefinedDefaultHref, DominantNode); - if (!EC) { + if (!EC) return EC.takeError(); - } xmlNsPtr PrefixDominantDefinedDefault = std::move(EC.get()); explicateNamespace(PrefixDominantDefinedDefault, DominantNode); } @@ -393,9 +397,8 @@ xmlNsPtr ClosestDefault = getClosestDefault(DominantNode->parent); Expected EC = searchOrDefine(ClosestDefault->href, DominantNode); - if (!EC) { + if (!EC) return EC.takeError(); - } xmlNsPtr ExplicitDefault = std::move(EC.get()); explicateNamespace(ExplicitDefault, DominantNode); } @@ -413,9 +416,8 @@ // for it, and then set the namespace equal to it. Expected EC = searchOrDefine(DominantNode->ns->href, NonDominantNode); - if (!EC) { + if (!EC) return EC.takeError(); - } xmlNsPtr Explicit = std::move(EC.get()); NonDominantNode->ns = Explicit; } @@ -431,9 +433,8 @@ // definition must be updated accordingly. Expected EC = searchOrDefine(OriginalDefinedDefaultHref, NonDominantNode); - if (!EC) { + if (!EC) return EC.takeError(); - } xmlNsPtr ExplicitDefault = std::move(EC.get()); explicateNamespace(ExplicitDefault, NonDominantNode); } @@ -445,9 +446,8 @@ xmlNsPtr ClosestDefault = getClosestDefault(NonDominantNode); Expected EC = searchOrDefine(ClosestDefault->href, NonDominantNode); - if (!EC) { + if (!EC) return EC.takeError(); - } xmlNsPtr ExplicitDefault = std::move(EC.get()); explicateNamespace(ExplicitDefault, NonDominantNode); } @@ -464,9 +464,8 @@ static bool isRecognizedNamespace(const unsigned char *NsHref) { for (auto &Ns : MtNsHrefsPrefixes) { - if (xmlStringsEqual(NsHref, TO_XML_CHAR(Ns.first.data()))) { + if (xmlStringsEqual(NsHref, TO_XML_CHAR(Ns.first.c_str()))) return true; - } } return false; } @@ -478,21 +477,18 @@ // Ensure a node's inherited namespace is actually defined in the tree it // resides in. static Error reconcileNamespaces(xmlNodePtr Node) { - if (!Node) { + if (Node == nullptr) return Error::success(); - } if (hasInheritedNs(Node)) { Expected ExplicitOrError = searchOrDefine(Node->ns->href, Node); - if (!ExplicitOrError) { + 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)) { + if (auto E = reconcileNamespaces(Child)) return E; - } } return Error::success(); } @@ -515,13 +511,11 @@ !hasRecognizedNamespace(Child)) { StoreNext.next = Child->next; xmlUnlinkNode(Child); - if (!xmlAddChild(OriginalRoot, Child)) { + if (!xmlAddChild(OriginalRoot, Child)) return make_error(Twine("could not merge ") + FROM_XML_CHAR(Child->name)); - } - if (auto E = reconcileNamespaces(Child)) { + if (auto E = reconcileNamespaces(Child)) return E; - } Child = &StoreNext; } else if (auto E = treeMerge(OriginalChildWithName, Child)) { return E; @@ -535,19 +529,20 @@ for (xmlNodePtr Child = Root->children; Child; Child = Child->next) { if (!xmlStringsEqual(Child->name, TO_XML_CHAR("comment"))) { stripComments(Child); - continue; + } else { + StoreNext.next = Child->next; + xmlNodePtr Remove = Child; + Child = &StoreNext; + xmlUnlinkNode(Remove); + xmlFreeNode(Remove); } - StoreNext.next = Child->next; - xmlNodePtr Remove = Child; - Child = &StoreNext; - xmlUnlinkNode(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. +// The 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) { @@ -563,7 +558,7 @@ // The merging process may create too many prefix defined namespaces. This // function removes all unnecessary ones from the tree. static void checkAndStripPrefixes(xmlNodePtr Node, - std::vector &RequiredPrefixes) { + std::vector &RequiredPrefixes) { for (xmlNodePtr Child = Node->children; Child; Child = Child->next) { checkAndStripPrefixes(Child, RequiredPrefixes); } @@ -572,7 +567,8 @@ if (ClosestDefault && xmlStringsEqual(ClosestDefault->href, Node->ns->href)) { Node->ns = ClosestDefault; - } else if (!llvm::is_contained(RequiredPrefixes, Node->ns)) { + } else if (std::find(RequiredPrefixes.begin(), RequiredPrefixes.end(), + Node->ns) == RequiredPrefixes.end()) { RequiredPrefixes.push_back(Node->ns); } } @@ -583,7 +579,8 @@ if (ClosestDefault && xmlStringsEqual(ClosestDefault->href, Attribute->ns->href)) { Attribute->ns = ClosestDefault; - } else if (!llvm::is_contained(RequiredPrefixes, Node->ns)) { + } else if (std::find(RequiredPrefixes.begin(), RequiredPrefixes.end(), + Node->ns) == RequiredPrefixes.end()) { RequiredPrefixes.push_back(Attribute->ns); } } @@ -591,18 +588,19 @@ 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; + 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; + xmlFreeNs(Def); + Def = &Temp; } else { - Prev->next = Def->next; + Prev = Def; } - Temp.next = Def->next; - xmlFreeNs(Def); - Def = &Temp; } } @@ -611,13 +609,11 @@ xmlFreeDoc(Doc); } -Error WindowsManifestMerger::WindowsManifestMergerImpl::merge( - const MemoryBuffer &Manifest) { +Error WindowsManifestMerger::WindowsManifestMergerImpl::merge(const MemoryBuffer &Manifest) { if (Manifest.getBufferSize() == 0) return make_error( "attempted to merge empty manifest"); - xmlSetGenericErrorFunc((void *)this, - WindowsManifestMergerImpl::errorCallback); + xmlSetGenericErrorFunc((void *)this, WindowsManifestMergerImpl::errorCallback); xmlDocPtr ManifestXML = xmlReadMemory(Manifest.getBufferStart(), Manifest.getBufferSize(), "manifest.xml", nullptr, XML_PARSE_NOBLANKS); @@ -631,21 +627,21 @@ CombinedDoc = ManifestXML; } else { xmlNodePtr CombinedRoot = xmlDocGetRootElement(CombinedDoc); - if (!xmlStringsEqual(CombinedRoot->name, AdditionalRoot->name) || - !isMergeableElement(AdditionalRoot->name) || - !hasRecognizedNamespace(AdditionalRoot)) { + if (xmlStringsEqual(CombinedRoot->name, AdditionalRoot->name) && + isMergeableElement(AdditionalRoot->name) && + hasRecognizedNamespace(AdditionalRoot)) { + if (auto E = treeMerge(CombinedRoot, AdditionalRoot)) { + return E; + } + } else { return make_error("multiple root nodes"); } - if (auto E = treeMerge(CombinedRoot, AdditionalRoot)) { - return E; - } } MergedDocs.push_back(ManifestXML); return Error::success(); } -std::unique_ptr -WindowsManifestMerger::WindowsManifestMergerImpl::getMergedManifest() { +std::unique_ptr WindowsManifestMerger::WindowsManifestMergerImpl::getMergedManifest() { unsigned char *XmlBuff; int BufferSize = 0; if (CombinedDoc) { @@ -666,36 +662,27 @@ #else -WindowsManifestMerger::WindowsManifestMergerImpl::~WindowsManifestMergerImpl() { -} +WindowsManifestMerger::WindowsManifestMergerImpl::~WindowsManifestMergerImpl() {} -Error WindowsManifestMerger::WindowsManifestMergerImpl::merge( - const MemoryBuffer &Manifest) { - return Error::success(); +Error WindowsManifestMerger::WindowsManifestMergerImpl::merge(const MemoryBuffer &Manifest) { + return make_error("no libxml2"); } -std::unique_ptr -WindowsManifestMerger::WindowsManifestMergerImpl::getMergedManifest() { +std::unique_ptr WindowsManifestMerger::WindowsManifestMergerImpl::getMergedManifest() { return nullptr; } -#endif +#endif -WindowsManifestMerger::WindowsManifestMerger() - : Impl(make_unique()) {} +WindowsManifestMerger::WindowsManifestMerger() : Impl(make_unique()) {} WindowsManifestMerger::~WindowsManifestMerger() {} -Error WindowsManifestMerger::merge(const MemoryBuffer &Manifest) { - return Impl->merge(Manifest); -} +Error WindowsManifestMerger::merge(const MemoryBuffer &Manifest) { return Impl->merge(Manifest); } -std::unique_ptr WindowsManifestMerger::getMergedManifest() { - return Impl->getMergedManifest(); -} +std::unique_ptr WindowsManifestMerger::getMergedManifest() { return Impl->getMergedManifest(); } -void WindowsManifestMerger::WindowsManifestMergerImpl::errorCallback( - void *Ctx, const char *Format, ...) { +void WindowsManifestMerger::WindowsManifestMergerImpl::errorCallback(void *Ctx, const char *Format, ...) { auto *Merger = (WindowsManifestMergerImpl *)Ctx; Merger->ParseErrorOccurred = true; } 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/big_merge.test =================================================================== --- llvm/test/tools/llvm-mt/big_merge.test +++ llvm/test/tools/llvm-mt/big_merge.test @@ -1,11 +1,11 @@ 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: 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: 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 @@ -105,6 +105,13 @@ for (auto *Arg : InputArgs.filtered(OPT_INPUT)) reportError(Twine("invalid option ") + Arg->getSpelling()); + 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()