From 082986daf95047182ba08c219df2d8987a5a222e Mon Sep 17 00:00:00 2001 From: Daniel Mach Date: Tue, 10 May 2022 09:41:08 +0200 Subject: [PATCH] core: Add functions for glob matching of multibuild flavors --- osc/core.py | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/osc/core.py b/osc/core.py index a3fadb3b..cec62202 100644 --- a/osc/core.py +++ b/osc/core.py @@ -8155,4 +8155,114 @@ def vc_export_env(apiurl, quiet=False): os.environ[env] = val +class MultibuildFlavorResolver: + def __init__(self, apiurl, project, package, use_local=False): + self.apiurl = apiurl + self.project = project + self.package = package + # whether to use local _multibuild file or download it from server + self.use_local = use_local + + def get_multibuild_data(self): + """ + Retrieve contents of _multibuild file from given project/package. + Return None if the file doesn't exist. + """ + + # use local _multibuild file + if self.use_local: + try: + with open("_multibuild", "r") as f: + return f.read() + except IOError as e: + if e.errno != errno.EEXIST: + raise + return None + + # use _multibuild file from server + query = {} + query['expand'] = 1 + u = makeurl(self.apiurl, ['source', self.project, self.package, '_multibuild'], query=query) + + try: + f = http_GET(u) + except HTTPError as e: + if e.code == 404: + return None + raise + return f.read() + + @staticmethod + def parse_multibuild_data(s): + """ + Return set of flavors from a string with multibuild xml. + """ + result = set() + + # handle empty string and None + if not s: + return result + + root = ET.fromstring(s) + for node in root.findall("flavor"): + result.add(node.text) + return result + + def resolve(self, patterns): + """ + Return list of flavors based on given flavor `patterns`. + If `patterns` contain a glob, it's resolved according to _multibuild file, + values without globs are passed through. + """ + + # determine if we're using globs + # yes: contact server and do glob matching + # no: use the specified values directly + use_globs = False + for pattern in patterns: + if '*' in pattern: + use_globs = True + break + + if use_globs: + import fnmatch + multibuild_xml = self.get_multibuild_data() + all_flavors = self.parse_multibuild_data(multibuild_xml) + flavors = set() + for pattern in patterns: + # not a glob, use it as it is + if '*' not in pattern: + flavors.add(pattern) + continue + + # match the globs with flavors from server + for flavor in all_flavors: + if fnmatch.fnmatch(flavor, pattern): + flavors.add(flavor) + + else: + flavors = patterns + + return sorted(flavors) + + def resolve_as_packages(self, patterns): + """ + Return list of package:flavor based on given flavor `patterns`. + If a value from `patterns` contains a glob, it is resolved according to the _multibuild + file. Values without globs are passed through. If a value is empty string, package + without flavor is returned. + """ + + flavors = self.resolve(patterns) + + packages = [] + for flavor in flavors: + if flavor: + packages.append(self.package + ':' + flavor) + else: + # special case: no flavor + packages.append(self.package) + return packages + + # vim: sw=4 et