| # -*- coding:utf-8 -*- |
| |
| from __future__ import print_function, unicode_literals |
| |
| import sys |
| import xml |
| |
| # import our initialized portage instance |
| from repoman._portage import portage |
| |
| from portage import os |
| from portage.output import red |
| from portage.process import find_binary |
| |
| from repoman.metadata import fetch_metadata_dtd |
| from repoman._subprocess import repoman_getstatusoutput |
| |
| |
| class _XMLParser(xml.etree.ElementTree.XMLParser): |
| |
| def __init__(self, data, **kwargs): |
| xml.etree.ElementTree.XMLParser.__init__(self, **kwargs) |
| self._portage_data = data |
| if hasattr(self, 'parser'): |
| self._base_XmlDeclHandler = self.parser.XmlDeclHandler |
| self.parser.XmlDeclHandler = self._portage_XmlDeclHandler |
| self._base_StartDoctypeDeclHandler = \ |
| self.parser.StartDoctypeDeclHandler |
| self.parser.StartDoctypeDeclHandler = \ |
| self._portage_StartDoctypeDeclHandler |
| |
| def _portage_XmlDeclHandler(self, version, encoding, standalone): |
| if self._base_XmlDeclHandler is not None: |
| self._base_XmlDeclHandler(version, encoding, standalone) |
| self._portage_data["XML_DECLARATION"] = (version, encoding, standalone) |
| |
| def _portage_StartDoctypeDeclHandler( |
| self, doctypeName, systemId, publicId, has_internal_subset): |
| if self._base_StartDoctypeDeclHandler is not None: |
| self._base_StartDoctypeDeclHandler( |
| doctypeName, systemId, publicId, has_internal_subset) |
| self._portage_data["DOCTYPE"] = (doctypeName, systemId, publicId) |
| |
| |
| class _MetadataTreeBuilder(xml.etree.ElementTree.TreeBuilder): |
| """ |
| Implements doctype() as required to avoid deprecation warnings with |
| >=python-2.7. |
| """ |
| def doctype(self, name, pubid, system): |
| pass |
| |
| |
| class XmlLint(object): |
| |
| def __init__(self, options, repoman_settings, metadata_dtd=None): |
| self.metadata_dtd = (metadata_dtd or |
| os.path.join(repoman_settings["DISTDIR"], 'metadata.dtd')) |
| self.options = options |
| self.repoman_settings = repoman_settings |
| self._is_capable = metadata_dtd is not None |
| self.binary = None |
| self._check_capable() |
| |
| def _check_capable(self): |
| if self.options.mode == "manifest": |
| return |
| self.binary = find_binary('xmllint') |
| if not self.binary: |
| print(red("!!! xmllint not found. Can't check metadata.xml.\n")) |
| self._is_capable = False |
| elif not self._is_capable: |
| if not fetch_metadata_dtd(self.metadata_dtd, self.repoman_settings): |
| sys.exit(1) |
| # this can be problematic if xmllint changes their output |
| self._is_capable = True |
| |
| @property |
| def capable(self): |
| return self._is_capable |
| |
| def check(self, checkdir, repolevel): |
| '''Runs checks on the package metadata.xml file |
| |
| @param checkdir: string, path |
| @param repolevel: integer |
| @return boolean, False == bad metadata |
| ''' |
| if not self.capable: |
| if self.options.xml_parse or repolevel == 3: |
| print("%s sorry, xmllint is needed. failing\n" % red("!!!")) |
| sys.exit(1) |
| return True |
| # xmlint can produce garbage output even on success, so only dump |
| # the ouput when it fails. |
| st, out = repoman_getstatusoutput( |
| self.binary + " --nonet --noout --dtdvalid %s %s" % ( |
| portage._shell_quote(self.metadata_dtd), |
| portage._shell_quote( |
| os.path.join(checkdir, "metadata.xml")))) |
| if st != os.EX_OK: |
| print(red("!!!") + " metadata.xml is invalid:") |
| for z in out.splitlines(): |
| print(red("!!! ") + z) |
| return False |
| return True |