#!/usr/bin/python3.9 # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # Copyright (c) 2010, Oracle and/or it's affiliates. All rights reserved. # # # bass-o-matic.py # A simple program to enumerate components in the userland gate and report # on dependency related information. # import os import subprocess import json class Component(object): def __init__(self, path=None, debug=False): self.debug = debug self.path = path self.name = None self.supplied_packages = [] self.required_packages = [] if path: component_pkg5_file = os.path.join(self.path, 'pkg5') if not os.path.isfile(component_pkg5_file): # get component name component_name = self.run_make(path, 'print-value-COMPONENT_NAME') if not component_name: raise ValueError('Component returns empty name at ' + self.path + '.') else: self.name = component_name[0] # get supplied packages, this may trigger the creation of a pkg5.fmris file self.supplied_packages = self.run_make(path, 'print-package-names') # always update fmris if list is overriden component_pkg5_fmris_file = os.path.join(self.path, 'pkg5.fmris') if os.path.isfile(component_pkg5_fmris_file): with open(component_pkg5_fmris_file, 'r') as f: self.supplied_packages = f.read().splitlines() # get dependencies self.required_packages = self.run_make(path, 'print-required-packages') data = { 'name' : self.name, 'fmris': self.supplied_packages, 'dependencies' : self.required_packages } with open(component_pkg5_file, 'w') as f: f.write(json.dumps(data, sort_keys=True, indent=4)) f.write('\n') else: with open(component_pkg5_file, 'r') as f: data = json.loads(f.read()) if not data: raise ValueError('Component pkg5 data is empty for path ' + self.path + '.') self.name = data['name'] self.supplied_packages = data['fmris'] self.required_packages = data['dependencies'] def required(self, component): result = False s1 = set(self.required_packages) s2 = set(component.supplied_packages) if s1.intersection(s2): result = True return result def run_make(self, path, targets): result = [] if self.debug: logger.debug('Executing \'gmake %s\' in %s', targets, path) proc = subprocess.Popen(['gmake', '-s', targets], stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=path, universal_newlines=True) stdout, stderr = proc.communicate() for out in stdout.splitlines(): result.append(out.rstrip()) if self.debug: if proc.returncode != 0: logger.debug('exit: %d, %s', proc.returncode, stderr) return result def __str__(self): result = 'Component:\n\tPath: %s\n' % self.path result += '\tProvides Package(s):\n\t\t%s\n' % '\t\t'.join(self.supplied_packages) result += '\tRequired Package(s):\n\t\t%s\n' % '\t\t'.join(self.required_packages) return result