From 5d8bcb58722b250c296fc0324f9d06470fb3d7d0 Mon Sep 17 00:00:00 2001 From: Marcel Telka <marcel@telka.sk> Date: Mon, 08 Apr 2024 06:38:33 +0200 Subject: [PATCH] python/jaraco.context: update to 5.3.0 --- tools/bass-o-matic | 326 ++++++++++++++++++++++++++++++++++-------------------- 1 files changed, 206 insertions(+), 120 deletions(-) diff --git a/tools/bass-o-matic b/tools/bass-o-matic index dcefdf8..6128b46 100755 --- a/tools/bass-o-matic +++ b/tools/bass-o-matic @@ -1,4 +1,4 @@ -#!/usr/bin/python2.6 +#!/usr/bin/python3.9 # # CDDL HEADER START # @@ -30,157 +30,243 @@ import os import sys import re +import glob import subprocess +import argparse +import logging +import json +import multiprocessing + +try: + from scandir import walk +except ImportError: + from os import walk + +from bass.component import Component + +logger = logging.getLogger('bass-o-matic') # Locate SCM directories containing Userland components by searching from # from a supplied top of tree for .p5m files. Once a .p5m file is located, # that directory is added to the list and no children are searched. -def FindComponentPaths(path, debug=None): - expression = re.compile(".+\.p5m$", re.IGNORECASE) +def FindComponentPaths(path, debug=False, subdir='components', + incremental=False, begin_commit=None, end_commit=None): + expression = re.compile(r'.+\.p5m$', re.IGNORECASE) paths = [] if debug: - print >>debug, "searching %s for component directories" % path + logger.debug('searching %s for component directories', path) - for dirpath, dirnames, filenames in os.walk(path + '/components'): - found = 0 + workspace_path = os.path.join(path, subdir) - for name in filenames: - if expression.match(name): - if debug: - print >>debug, "found %s" % dirpath - paths.append(dirpath) - del dirnames[:] - break + if incremental: + cmd = ['git', '--no-pager', 'log', '--diff-filter=AMR', '--name-only', '--pretty=format:', + '..'.join([begin_commit, end_commit])] + + proc = subprocess.Popen(cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + cwd=workspace_path, + universal_newlines=True + ) + stdout, stderr = proc.communicate() + if debug: + if proc.returncode != 0: + logger.debug('exit: %d, %s', proc.returncode, stderr) + + for line in stdout.splitlines(): + line = line.rstrip() + # git output might contain empty lines, so we skip them. + if not line: + continue + + # Every time component is added, modified or moved, Makefile has to be + # present. However, this does not yet guarantee that the line is a + # real component. + filename = os.path.basename(line) + dirname = os.path.dirname(line).rsplit(subdir + '/')[-1] + + if filename == 'Makefile': + if glob.glob(os.path.join(workspace_path, dirname, '*.p5m')) and \ + not os.path.isfile(os.path.join(workspace_path, dirname, 'pkg5.ignore')): + paths.append(dirname) + + # Some components are using SCM checkout as a source code download method and + # COMPONENT_REVISION is not bumped. With this, we will never rebuild them. + # In order to rebuild them, we will look for such components and build them + # every run. These components are located in openindiana category and we care + # only about that category. One exception to this rule is meta-packages/history + # component, which holds obsoleted components. We add it to paths manually for + # that reason. + cmd = ['git', 'grep', '-l', 'GIT_REPO *='] + + proc = subprocess.Popen(cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + cwd=workspace_path, + universal_newlines=True + ) + + stdout, stderr = proc.communicate() + if debug: + if proc.returncode != 0: + logger.debug('exit: %d, %s', proc.returncode, stderr) + + for line in stdout.splitlines(): + line = line.rstrip() + + # Only 'openindiana' category. + category = line.split('/')[0] + if category == 'openindiana': + continue + + filename = os.path.basename(line) + dirname = os.path.dirname(line) + + if filename == 'Makefile': + if glob.glob(os.path.join(workspace_path, dirname, '*.p5m')) and \ + not os.path.isfile(os.path.join(workspace_path, dirname, 'pkg5.ignore')): + paths.append(os.path.dirname(line)) + + # Add meta-packages/history only if we build the main repository, where + # subdir is equal to 'components'. + if subdir == 'components': + paths.append('meta-packages/history') + # Add encumbered/meta-packages/history only if we build the encumbered repository + if subdir == 'components/encumbered': + paths.append('encumbered/meta-packages/history') + + paths = list(set(paths)) + + else: + for dirpath, dirnames, filenames in walk(workspace_path): + for name in filenames: + if expression.match(name): + if not os.path.isfile(os.path.join( dirpath, 'pkg5.ignore')): + if debug: + logger.debug('found %s', dirpath) + paths.append(dirpath) + del dirnames[:] + break return sorted(paths) -class BassComponent: - def __init__(self, path=None, debug=None): - self.debug = debug - self.path = path - if path: - # get supplied packages (cd path ; gmake print-package-names) - self.supplied_packages = self.run_make(path, 'print-package-names') - - # get supplied paths (cd path ; gmake print-package-paths) - self.supplied_paths = self.run_make(path, 'print-package-paths') - - # get required paths (cd path ; gmake print-required-paths) - self.required_paths = self.run_make(path, 'print-required-paths') - - def required(self, component): - result = False - - s1 = set(self.required_paths) - s2 = set(component.supplied_paths) - if s1.intersection(s2): - result = True - - return result - - def run_make(self, path, targets): - - result = list() - - if self.debug: - print >>self.debug, "Executing 'gmake %s' in %s" % (targets, path) - - proc = subprocess.Popen(['gmake', targets], cwd=path, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - p = proc.stdout - - for out in p: - result.append(out) - - if self.debug: - proc.wait() - if proc.returncode != 0: - print >>self.debug, "exit: %d, %s" % (proc.returncode, proc.stderr.read()) - - return result - - def __str__(self): - result = "Component:\n\tPath: %s\n" % self.path - result = result + "\tProvides Package(s):\n\t\t%s\n" % '\t\t'.join(self.supplied_packages) - result = result + "\tProvides Path(s):\n\t\t%s\n" % '\t\t'.join(self.supplied_paths) - result = result + "\tRequired Path(s):\n\t\t%s\n" % '\t\t'.join(self.required_paths) - - return result - -def usage(): - print "Usage: %s [-c|--components=(path|depend)] [-z|--zone (zone)]" % (sys.argv[0].split('/')[-1]) - sys.exit(1) def main(): - import getopt - import sys - - # FLUSH STDOUT - sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0) + sys.stdout.flush() components = {} - debug=None - components_arg=None - make_arg=None - component_arg=None - template_zone=None - workspace = os.getenv('WS_TOP') - try: - opts, args = getopt.getopt(sys.argv[1:], "w:c:d", - [ "debug", "workspace=", "components=", - "make", "component=", "template-zone=" ]) - except getopt.GetoptError, err: - print str(err) - usage() + COMPONENTS_ALLOWED_PATHS = ['paths', 'dirs'] + COMPONENTS_ALLOWED_FMRIS = ['fmris'] + COMPONENTS_ALLOWED_DEPENDENCIES = ['dependencies'] + COMPONENTS_ALLOWED_KEYWORDS = COMPONENTS_ALLOWED_PATHS + COMPONENTS_ALLOWED_FMRIS + COMPONENTS_ALLOWED_DEPENDENCIES - for opt, arg in opts: - if opt in [ "-w", "--workspace" ]: - workspace = arg - elif opt in [ "-l", "--components" ]: - components_arg = arg - elif opt in [ "--make" ]: - make_arg = True - elif opt in [ "--component" ]: - component_arg = arg - elif opt in [ "--template-zone" ]: - template_zone = arg - elif opt in [ "-d", "--debug" ]: - debug = sys.stdout - else: - assert False, "unknown option" + parser = argparse.ArgumentParser() + parser.add_argument('-w', '--workspace', default=os.getenv('WS_TOP'), help='Path to workspace') + parser.add_argument('-l', '--components', default=None, choices=COMPONENTS_ALLOWED_KEYWORDS) + parser.add_argument('--make', help='Makefile target to invoke') + parser.add_argument('--pkg5', action='store_true',help='Invoke generation of metadata') + parser.add_argument('--subdir', default='components', help='Directory holding components') + parser.add_argument('-d', '--debug', action='store_true', default=False) + parser.add_argument('--begin-commit', default=os.getenv('GIT_PREVIOUS_SUCCESSFUL_COMMIT', 'HEAD~1')) + parser.add_argument('--end-commit', default='HEAD') - component_paths = FindComponentPaths(workspace, debug) + args = parser.parse_args() + + workspace = args.workspace + components_arg = args.components + make_arg = args.make + pkg5_arg = args.pkg5 + subdir = args.subdir + begin_commit = args.begin_commit + end_commit = args.end_commit + debug = args.debug + log_level = logging.WARNING + + if debug: + log_level = logging.DEBUG + + logging.basicConfig(level=log_level, + format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s',) if make_arg: - if template_zone: - print "using template zone %s to create a build environment for %s to run '%s'" % (template_zone, component_arg, ['gmake'] + args) - proc = subprocess.Popen(['gmake'] + args) - proc.wait() + MAKE=os.getenv("MAKE","gmake") + + # https://www.gnu.org/software/make/manual/html_node/POSIX-Jobserver.html + JOBFLAGS=re.match('.* (--jobserver-auth=([0-9]+),([0-9]+)) ?.*',os.getenv("MAKEFLAGS","")) + if JOBFLAGS: + JOBFDS=( JOBFLAGS.group(2), JOBFLAGS.group(3) ) + JOBFLAGS=[JOBFLAGS.group(1)] + else: + JOBFDS=() + JOBFLAGS=[] + proc = subprocess.Popen([MAKE, '-s'] + [make_arg] + JOBFLAGS,pass_fds=JOBFDS) + rc = proc.wait() + sys.exit(rc) + + if pkg5_arg: + component_path = os.getcwd().split(os.path.join(workspace, subdir))[-1].replace('/', '', 1) + # the component may not be built directly but as a dependency of another component + if os.path.isfile(os.path.join( os.getcwd(), 'pkg5.ignore')): + sys.exit(0) + component_pkg5 = os.path.join( os.getcwd(), 'pkg5') + if os.path.isfile(component_pkg5): + os.remove(component_pkg5) + Component(FindComponentPaths(path=workspace, debug=debug, subdir=os.path.join(subdir, component_path))[0]) sys.exit(0) + incremental = False + if os.getenv('BASS_O_MATIC_MODE') == 'incremental': + incremental = True + + if incremental: + component_paths = FindComponentPaths(path=workspace, debug=debug, subdir=subdir, + incremental=incremental, begin_commit=begin_commit, end_commit=end_commit) + else: + component_paths = FindComponentPaths(path=workspace, debug=debug, subdir=subdir) + if components_arg: - if components_arg in [ 'path', 'paths', 'dir', 'dirs', 'directories' ]: + if components_arg in COMPONENTS_ALLOWED_PATHS: for path in component_paths: - print "%s" % path - - elif components_arg in [ 'depend', 'dependencies' ]: - for path in component_paths: - components[path] = BassComponent(path, debug) + print('{0}'.format(path)) - for c_path in components.keys(): - component = components[c_path] + elif components_arg in COMPONENTS_ALLOWED_FMRIS: + pool = multiprocessing.Pool(processes=multiprocessing.cpu_count()) + components = pool.map(Component, component_paths) - for d_path in components.keys(): - if (c_path != d_path and - component.required(components[d_path])): - print "%s: %s" % (c_path, d_path) + for component in components: + for fmri in component.supplied_packages: + print('{0}'.format(fmri)) + elif components_arg in COMPONENTS_ALLOWED_DEPENDENCIES: + dependencies = {} + + pool = multiprocessing.Pool(processes=multiprocessing.cpu_count()) + components = pool.map(Component, component_paths) + + with open(os.path.join(workspace, subdir, 'mapping.json'), "r") as f: + data = json.loads(f.read()) + component_path = {} + for entry in data: + component_path[entry['fmri']] = entry['path'] + + for component in components: + selfpath = component.path.split(os.path.join(workspace, subdir))[-1].replace('/', '', 1) + # Some packages from OpenSolaris only exist in binary form in the pkg repository + paths = set([component_path.get(i, "https://pkg.openindiana.org/hipster") for i in component.required_packages]) + # Remove self from the set of paths it depends on + paths.discard(selfpath) + dependencies[selfpath] = sorted(list(paths)) + + dependencies_file = os.path.join(workspace, subdir, 'dependencies.json') + with open(dependencies_file, 'w') as f: + f.write(json.dumps(dependencies, sort_keys=True, indent=4)) sys.exit(0) sys.exit(1) -if __name__ == "__main__": + +if __name__ == '__main__': main() -- Gitblit v1.9.3