openSUSE-release-tools/biarchtool.py
Steve Kowalik 326a087168 Switch to python3 shebang
With the python shebang remaining as Python 2, switch the scripts to
using Python 3. This also allows us to clean up some imports.
2021-02-03 14:53:42 +11:00

401 lines
15 KiB
Python
Executable File

#!/usr/bin/python3
from xml.etree import cElementTree as ET
import sys
import cmdln
import logging
from urllib.error import HTTPError
import osc.core
import ToolBase
logger = logging.getLogger()
FACTORY = "openSUSE:Factory"
class BiArchTool(ToolBase.ToolBase):
def __init__(self, project):
ToolBase.ToolBase.__init__(self)
self.project = project
self.biarch_packages = None
self._has_baselibs = dict()
self.packages = []
self.arch = 'i586'
self.rdeps = None
self.package_metas = dict()
self.whitelist = {
'i586': set([
'bzr',
'git',
# _link to baselibs package
'libjpeg62-turbo',
'mercurial',
'subversion',
'ovmf'
]),
}
self.blacklist = {
'i586': set([
'belle-sip',
'release-notes-openSUSE',
'openSUSE-EULAs', # translate-toolkit
'skelcd-openSUSE',
'plasma5-workspace',
'patterns-base',
'patterns-fonts',
'patterns-rpm-macros',
'patterns-yast',
'000release-packages',
]),
}
def get_filelist(self, project, package, expand = False):
query = {}
if expand:
query['expand'] = 1
root = ET.fromstring(self.cached_GET(self.makeurl(['source', self.project, package], query)))
return [ node.get('name') for node in root.findall('entry') ]
def has_baselibs(self, package):
if package in self._has_baselibs:
return self._has_baselibs[package]
is_multibuild = False
srcpkgname = package
if ':' in package:
is_multibuild = True
srcpkgname = package.split(':')[0]
ret = False
files = self.get_filelist(self.project, srcpkgname)
if 'baselibs.conf' in files:
logger.debug('%s has baselibs', package)
if is_multibuild:
logger.warning('%s is multibuild and has baselibs. canot handle that!', package)
else:
ret = True
elif '_link' in files:
files = self.get_filelist(self.project, srcpkgname, expand = True)
if 'baselibs.conf' in files:
logger.warning('%s is linked to a baselibs package', package)
elif is_multibuild:
logger.warning('%s is multibuild', package)
self._has_baselibs[package] = ret
return ret
def is_biarch_recursive(self, package):
logger.debug(package)
if package in self.blacklist[self.arch]:
logger.debug('%s is blacklisted', package)
return False
if package in self.biarch_packages:
logger.debug('%s is known biarch package', package)
return True
if package in self.whitelist[self.arch]:
logger.debug('%s is whitelisted', package)
return True
r = self.has_baselibs(package)
if r:
return r
if package in self.rdeps:
for p in self.rdeps[package]:
r = self.is_biarch_recursive(p)
if r:
break
return r
def _init_biarch_packages(self):
if self.biarch_packages is None:
if ':Rings' in self.project:
self.biarch_packages = set()
else:
self.biarch_packages = set(self.meta_get_packagelist("%s:Rings:0-Bootstrap" % self.project))
self.biarch_packages |= set(self.meta_get_packagelist("%s:Rings:1-MinimalX" % self.project))
self._init_rdeps()
self.fill_package_meta()
def fill_package_meta(self):
url = self.makeurl(['search', 'package'], "match=[@project='%s']" % self.project)
root = ET.fromstring(self.cached_GET(url))
for p in root.findall('package'):
name = p.attrib['name']
self.package_metas[name] = p
def _init_rdeps(self):
if self.rdeps is not None:
return
self.rdeps = dict()
url = self.makeurl(['build', self.project, 'standard', self.arch, '_builddepinfo' ], {'view': 'revpkgnames'})
x = ET.fromstring(self.cached_GET(url))
for pnode in x.findall('package'):
name = pnode.get('name')
for depnode in pnode.findall('pkgdep'):
depname = depnode.text
if depname == name:
logger.warning('%s requires itself for build', name)
continue
self.rdeps.setdefault(name, set()).add(depname)
def select_packages(self, packages):
if packages == '__all__':
self.packages = self.meta_get_packagelist(self.project)
elif packages == '__latest__':
# only works when called in packagelists loop
#self.packages = self._filter_packages_by_time(self.latest_packages(self.project))
self.packages = self.latest_packages(self.project)
else:
self.packages = packages
# check when 000product was last changed, eg by packagelist
# generator. Yield only packges that got checked in after that
# point in time.
def _filter_packages_by_time(self, packages):
x = ET.fromstring(self.cached_GET(self.makeurl(['source', self.project, '000product', '_history'], {'limit': '1'})))
producttime = int(x.find('./revision/time').text)
for pkg in packages:
try:
x = ET.fromstring(self.cached_GET(self.makeurl(['source', self.project, pkg, '_history'], {'rev': '1'})))
# catch deleted packages
except HTTPError as e:
if e.code == 404:
continue
raise e
packagetime = int(x.find('./revision/time').text)
# if producttime > packagetime:
# continue
yield pkg
def remove_explicit_enable(self):
self._init_biarch_packages()
resulturl = self.makeurl(['build', self.project, '_result'])
result = ET.fromstring(self.cached_GET(resulturl))
packages = set()
for n in result.findall("./result[@arch='{}']/status".format(self.arch)):
if n.get('code') not in ('disabled', 'excluded'):
packages.add(n.get('package'))
for pkg in sorted(packages):
changed = False
logger.debug("processing %s", pkg)
if not pkg in self.package_metas:
logger.error("%s not found", pkg)
continue
pkgmeta = self.package_metas[pkg]
for build in pkgmeta.findall("./build"):
for n in build.findall("./enable[@arch='{}']".format(self.arch)):
logger.debug("disable %s", pkg)
build.remove(n)
changed = True
if changed:
try:
pkgmetaurl = self.makeurl(['source', self.project, pkg, '_meta'])
self.http_PUT(pkgmetaurl, data=ET.tostring(pkgmeta))
if self.caching:
self._invalidate__cached_GET(pkgmetaurl)
except HTTPError as e:
logger.error('failed to update %s: %s', pkg, e)
def add_explicit_disable(self, wipebinaries=False):
self._init_biarch_packages()
resulturl = self.makeurl(['source', self.project])
result = ET.fromstring(self.cached_GET(resulturl))
for pkg in self.packages:
changed = False
logger.debug("processing %s", pkg)
if not pkg in self.package_metas:
logger.error("%s not found", pkg)
continue
pkgmeta = self.package_metas[pkg]
build = pkgmeta.findall("./build")
if not build:
logger.debug('disable %s for %s', pkg, self.arch)
bn = pkgmeta.find('build')
if bn is None:
bn = ET.SubElement(pkgmeta, 'build')
ET.SubElement(bn, 'disable', { 'arch': self.arch })
changed = True
if changed:
try:
pkgmetaurl = self.makeurl(['source', self.project, pkg, '_meta'])
self.http_PUT(pkgmetaurl, data=ET.tostring(pkgmeta))
if self.caching:
self._invalidate__cached_GET(pkgmetaurl)
if wipebinaries:
self.http_POST(self.makeurl(['build', self.project], {
'cmd': 'wipe',
'arch': self.arch,
'package': pkg }))
except HTTPError as e:
logger.error('failed to update %s: %s', pkg, e)
def enable_baselibs_packages(self, force=False, wipebinaries=False):
self._init_biarch_packages()
todo = dict()
for pkg in self.packages:
logger.debug("processing %s", pkg)
if not pkg in self.package_metas:
logger.error("%s not found", pkg)
continue
pkgmeta = self.package_metas[pkg]
is_enabled = None
is_disabled = None
has_baselibs = None
must_disable = None
changed = None
for n in pkgmeta.findall("./build/enable[@arch='{}']".format(self.arch)):
is_enabled = True
for n in pkgmeta.findall("./build/disable[@arch='{}']".format(self.arch)):
is_disabled = True
if force:
must_disable = False
if must_disable is None:
if self.is_biarch_recursive(pkg):
must_disable = False
else:
must_disable = True
if must_disable == False:
if is_disabled:
logger.info('enabling %s for %s', pkg, self.arch)
for build in pkgmeta.findall("./build"):
for n in build.findall("./disable[@arch='{}']".format(self.arch)):
build.remove(n)
changed = True
if changed == False:
logger.error('build tag not found in %s/%s!?', pkg, self.arch)
else:
logger.debug('%s already enabled for %s', pkg, self.arch)
elif must_disable == True:
if not is_disabled:
logger.info('disabling %s for %s', pkg, self.arch)
bn = pkgmeta.find('build')
if bn is None:
bn = ET.SubElement(pkgmeta, 'build')
ET.SubElement(bn, 'disable', { 'arch': self.arch })
changed = True
else:
logger.debug('%s already disabled for %s', pkg, self.arch)
if is_enabled:
logger.info('removing explicit enable %s for %s', pkg, self.arch)
for build in pkgmeta.findall("./build"):
for n in build.findall("./enable[@arch='{}']".format(self.arch)):
build.remove(n)
changed = True
if changed == False:
logger.error('build tag not found in %s/%s!?', pkg, self.arch)
if changed:
todo[pkg] = pkgmeta
if todo:
logger.info("applying changes")
for pkg in sorted(todo.keys()):
pkgmeta = todo[pkg]
try:
pkgmetaurl = self.makeurl(['source', self.project, pkg, '_meta'])
self.http_PUT(pkgmetaurl, data=ET.tostring(pkgmeta))
if self.caching:
self._invalidate__cached_GET(pkgmetaurl)
if wipebinaries and pkgmeta.find("./build/disable[@arch='{}']".format(self.arch)) is not None:
logger.debug("wiping %s", pkg)
self.http_POST(self.makeurl(['build', self.project], {
'cmd': 'wipe',
'arch': self.arch,
'package': pkg }))
except HTTPError as e:
logger.error('failed to update %s: %s', pkg, e)
class CommandLineInterface(ToolBase.CommandLineInterface):
def __init__(self, *args, **kwargs):
ToolBase.CommandLineInterface.__init__(self, args, kwargs)
def get_optparser(self):
parser = ToolBase.CommandLineInterface.get_optparser(self)
parser.add_option('-p', '--project', dest='project', metavar='PROJECT',
help='project to process (default: %s)' % FACTORY,
default = FACTORY)
return parser
def setup_tool(self):
tool = BiArchTool(self.options.project)
return tool
def _select_packages(self, all, packages):
if packages:
self.tool.select_packages(packages)
elif all:
self.tool.select_packages('__all__')
else:
self.tool.select_packages('__latest__')
@cmdln.option('-n', '--interval', metavar="minutes", type="int", help="periodic interval in minutes")
@cmdln.option('-a', '--all', action='store_true', help='process all packages')
@cmdln.option('-f', '--force', action='store_true', help='enable in any case')
@cmdln.option('--wipe', action='store_true', help='also wipe binaries')
def do_enable_baselibs_packages(self, subcmd, opts, *packages):
"""${cmd_name}: enable build for packages in Ring 0 or 1 or with
baselibs.conf
${cmd_usage}
${cmd_option_list}
"""
def work():
self._select_packages(opts.all, packages)
self.tool.enable_baselibs_packages(force=opts.force, wipebinaries=opts.wipe)
self.runner(work, opts.interval)
@cmdln.option('-a', '--all', action='store_true', help='process all packages')
def do_remove_explicit_enable(self, subcmd, opts, *packages):
"""${cmd_name}: remove all explicit enable tags from packages
${cmd_usage}
${cmd_option_list}
"""
self.tool.remove_explicit_enable()
@cmdln.option('-a', '--all', action='store_true', help='process all packages')
@cmdln.option('-n', '--interval', metavar="minutes", type="int", help="periodic interval in minutes")
@cmdln.option('--wipe', action='store_true', help='also wipe binaries')
def do_add_explicit_disable(self, subcmd, opts, *packages):
"""${cmd_name}: add explicit disable to all packages
${cmd_usage}
${cmd_option_list}
"""
def work():
self._select_packages(opts.all, packages)
self.tool.add_explicit_disable(wipebinaries=opts.wipe)
self.runner(work, opts.interval)
if __name__ == "__main__":
app = CommandLineInterface()
sys.exit( app.main() )