1*4882a593Smuzhiyun#!/usr/bin/env python3 2*4882a593Smuzhiyun 3*4882a593Smuzhiyun# Script to extract information from image manifests 4*4882a593Smuzhiyun# 5*4882a593Smuzhiyun# Copyright (C) 2018 Intel Corporation 6*4882a593Smuzhiyun# Copyright (C) 2021 Wind River Systems, Inc. 7*4882a593Smuzhiyun# 8*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0-only 9*4882a593Smuzhiyun# 10*4882a593Smuzhiyun 11*4882a593Smuzhiyunimport sys 12*4882a593Smuzhiyunimport os 13*4882a593Smuzhiyunimport argparse 14*4882a593Smuzhiyunimport logging 15*4882a593Smuzhiyunimport json 16*4882a593Smuzhiyunimport shutil 17*4882a593Smuzhiyunimport tempfile 18*4882a593Smuzhiyunimport tarfile 19*4882a593Smuzhiyunfrom collections import OrderedDict 20*4882a593Smuzhiyun 21*4882a593Smuzhiyunscripts_path = os.path.dirname(__file__) 22*4882a593Smuzhiyunlib_path = scripts_path + '/../lib' 23*4882a593Smuzhiyunsys.path = sys.path + [lib_path] 24*4882a593Smuzhiyun 25*4882a593Smuzhiyunimport scriptutils 26*4882a593Smuzhiyunlogger = scriptutils.logger_create(os.path.basename(__file__)) 27*4882a593Smuzhiyun 28*4882a593Smuzhiyunimport argparse_oe 29*4882a593Smuzhiyunimport scriptpath 30*4882a593Smuzhiyunbitbakepath = scriptpath.add_bitbake_lib_path() 31*4882a593Smuzhiyunif not bitbakepath: 32*4882a593Smuzhiyun logger.error("Unable to find bitbake by searching parent directory of this script or PATH") 33*4882a593Smuzhiyun sys.exit(1) 34*4882a593Smuzhiyunlogger.debug('Using standard bitbake path %s' % bitbakepath) 35*4882a593Smuzhiyunscriptpath.add_oe_lib_path() 36*4882a593Smuzhiyun 37*4882a593Smuzhiyunimport bb.tinfoil 38*4882a593Smuzhiyunimport bb.utils 39*4882a593Smuzhiyunimport oe.utils 40*4882a593Smuzhiyunimport oe.recipeutils 41*4882a593Smuzhiyun 42*4882a593Smuzhiyundef get_pkg_list(manifest): 43*4882a593Smuzhiyun pkglist = [] 44*4882a593Smuzhiyun with open(manifest, 'r') as f: 45*4882a593Smuzhiyun for line in f: 46*4882a593Smuzhiyun linesplit = line.split() 47*4882a593Smuzhiyun if len(linesplit) == 3: 48*4882a593Smuzhiyun # manifest file 49*4882a593Smuzhiyun pkglist.append(linesplit[0]) 50*4882a593Smuzhiyun elif len(linesplit) == 1: 51*4882a593Smuzhiyun # build dependency file 52*4882a593Smuzhiyun pkglist.append(linesplit[0]) 53*4882a593Smuzhiyun return sorted(pkglist) 54*4882a593Smuzhiyun 55*4882a593Smuzhiyundef list_packages(args): 56*4882a593Smuzhiyun pkglist = get_pkg_list(args.manifest) 57*4882a593Smuzhiyun for pkg in pkglist: 58*4882a593Smuzhiyun print('%s' % pkg) 59*4882a593Smuzhiyun 60*4882a593Smuzhiyundef pkg2recipe(tinfoil, pkg): 61*4882a593Smuzhiyun if "-native" in pkg: 62*4882a593Smuzhiyun logger.info('skipping %s' % pkg) 63*4882a593Smuzhiyun return None 64*4882a593Smuzhiyun 65*4882a593Smuzhiyun pkgdata_dir = tinfoil.config_data.getVar('PKGDATA_DIR') 66*4882a593Smuzhiyun pkgdatafile = os.path.join(pkgdata_dir, 'runtime-reverse', pkg) 67*4882a593Smuzhiyun logger.debug('pkgdatafile %s' % pkgdatafile) 68*4882a593Smuzhiyun try: 69*4882a593Smuzhiyun f = open(pkgdatafile, 'r') 70*4882a593Smuzhiyun for line in f: 71*4882a593Smuzhiyun if line.startswith('PN:'): 72*4882a593Smuzhiyun recipe = line.split(':', 1)[1].strip() 73*4882a593Smuzhiyun return recipe 74*4882a593Smuzhiyun except Exception: 75*4882a593Smuzhiyun logger.warning('%s is missing' % pkgdatafile) 76*4882a593Smuzhiyun return None 77*4882a593Smuzhiyun 78*4882a593Smuzhiyundef get_recipe_list(manifest, tinfoil): 79*4882a593Smuzhiyun pkglist = get_pkg_list(manifest) 80*4882a593Smuzhiyun recipelist = [] 81*4882a593Smuzhiyun for pkg in pkglist: 82*4882a593Smuzhiyun recipe = pkg2recipe(tinfoil,pkg) 83*4882a593Smuzhiyun if recipe: 84*4882a593Smuzhiyun if not recipe in recipelist: 85*4882a593Smuzhiyun recipelist.append(recipe) 86*4882a593Smuzhiyun 87*4882a593Smuzhiyun return sorted(recipelist) 88*4882a593Smuzhiyun 89*4882a593Smuzhiyundef list_recipes(args): 90*4882a593Smuzhiyun import bb.tinfoil 91*4882a593Smuzhiyun with bb.tinfoil.Tinfoil() as tinfoil: 92*4882a593Smuzhiyun tinfoil.logger.setLevel(logger.getEffectiveLevel()) 93*4882a593Smuzhiyun tinfoil.prepare(config_only=True) 94*4882a593Smuzhiyun recipelist = get_recipe_list(args.manifest, tinfoil) 95*4882a593Smuzhiyun for recipe in sorted(recipelist): 96*4882a593Smuzhiyun print('%s' % recipe) 97*4882a593Smuzhiyun 98*4882a593Smuzhiyundef list_layers(args): 99*4882a593Smuzhiyun 100*4882a593Smuzhiyun def find_git_repo(pth): 101*4882a593Smuzhiyun checkpth = pth 102*4882a593Smuzhiyun while checkpth != os.sep: 103*4882a593Smuzhiyun if os.path.exists(os.path.join(checkpth, '.git')): 104*4882a593Smuzhiyun return checkpth 105*4882a593Smuzhiyun checkpth = os.path.dirname(checkpth) 106*4882a593Smuzhiyun return None 107*4882a593Smuzhiyun 108*4882a593Smuzhiyun def get_git_remote_branch(repodir): 109*4882a593Smuzhiyun try: 110*4882a593Smuzhiyun stdout, _ = bb.process.run(['git', 'rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{u}'], cwd=repodir) 111*4882a593Smuzhiyun except bb.process.ExecutionError as e: 112*4882a593Smuzhiyun stdout = None 113*4882a593Smuzhiyun if stdout: 114*4882a593Smuzhiyun return stdout.strip() 115*4882a593Smuzhiyun else: 116*4882a593Smuzhiyun return None 117*4882a593Smuzhiyun 118*4882a593Smuzhiyun def get_git_head_commit(repodir): 119*4882a593Smuzhiyun try: 120*4882a593Smuzhiyun stdout, _ = bb.process.run(['git', 'rev-parse', 'HEAD'], cwd=repodir) 121*4882a593Smuzhiyun except bb.process.ExecutionError as e: 122*4882a593Smuzhiyun stdout = None 123*4882a593Smuzhiyun if stdout: 124*4882a593Smuzhiyun return stdout.strip() 125*4882a593Smuzhiyun else: 126*4882a593Smuzhiyun return None 127*4882a593Smuzhiyun 128*4882a593Smuzhiyun def get_git_repo_url(repodir, remote='origin'): 129*4882a593Smuzhiyun import bb.process 130*4882a593Smuzhiyun # Try to get upstream repo location from origin remote 131*4882a593Smuzhiyun try: 132*4882a593Smuzhiyun stdout, _ = bb.process.run(['git', 'remote', '-v'], cwd=repodir) 133*4882a593Smuzhiyun except bb.process.ExecutionError as e: 134*4882a593Smuzhiyun stdout = None 135*4882a593Smuzhiyun if stdout: 136*4882a593Smuzhiyun for line in stdout.splitlines(): 137*4882a593Smuzhiyun splitline = line.split() 138*4882a593Smuzhiyun if len(splitline) > 1: 139*4882a593Smuzhiyun if splitline[0] == remote and scriptutils.is_src_url(splitline[1]): 140*4882a593Smuzhiyun return splitline[1] 141*4882a593Smuzhiyun return None 142*4882a593Smuzhiyun 143*4882a593Smuzhiyun with bb.tinfoil.Tinfoil() as tinfoil: 144*4882a593Smuzhiyun tinfoil.logger.setLevel(logger.getEffectiveLevel()) 145*4882a593Smuzhiyun tinfoil.prepare(config_only=False) 146*4882a593Smuzhiyun layers = OrderedDict() 147*4882a593Smuzhiyun for layerdir in tinfoil.config_data.getVar('BBLAYERS').split(): 148*4882a593Smuzhiyun layerdata = OrderedDict() 149*4882a593Smuzhiyun layername = os.path.basename(layerdir) 150*4882a593Smuzhiyun logger.debug('layername %s, layerdir %s' % (layername, layerdir)) 151*4882a593Smuzhiyun if layername in layers: 152*4882a593Smuzhiyun logger.warning('layername %s is not unique in configuration' % layername) 153*4882a593Smuzhiyun layername = os.path.basename(os.path.dirname(layerdir)) + '_' + os.path.basename(layerdir) 154*4882a593Smuzhiyun logger.debug('trying layername %s' % layername) 155*4882a593Smuzhiyun if layername in layers: 156*4882a593Smuzhiyun logger.error('Layer name %s is not unique in configuration' % layername) 157*4882a593Smuzhiyun sys.exit(2) 158*4882a593Smuzhiyun repodir = find_git_repo(layerdir) 159*4882a593Smuzhiyun if repodir: 160*4882a593Smuzhiyun remotebranch = get_git_remote_branch(repodir) 161*4882a593Smuzhiyun remote = 'origin' 162*4882a593Smuzhiyun if remotebranch and '/' in remotebranch: 163*4882a593Smuzhiyun rbsplit = remotebranch.split('/', 1) 164*4882a593Smuzhiyun layerdata['actual_branch'] = rbsplit[1] 165*4882a593Smuzhiyun remote = rbsplit[0] 166*4882a593Smuzhiyun layerdata['vcs_url'] = get_git_repo_url(repodir, remote) 167*4882a593Smuzhiyun if os.path.abspath(repodir) != os.path.abspath(layerdir): 168*4882a593Smuzhiyun layerdata['vcs_subdir'] = os.path.relpath(layerdir, repodir) 169*4882a593Smuzhiyun commit = get_git_head_commit(repodir) 170*4882a593Smuzhiyun if commit: 171*4882a593Smuzhiyun layerdata['vcs_commit'] = commit 172*4882a593Smuzhiyun layers[layername] = layerdata 173*4882a593Smuzhiyun 174*4882a593Smuzhiyun json.dump(layers, args.output, indent=2) 175*4882a593Smuzhiyun 176*4882a593Smuzhiyundef get_recipe(args): 177*4882a593Smuzhiyun with bb.tinfoil.Tinfoil() as tinfoil: 178*4882a593Smuzhiyun tinfoil.logger.setLevel(logger.getEffectiveLevel()) 179*4882a593Smuzhiyun tinfoil.prepare(config_only=True) 180*4882a593Smuzhiyun 181*4882a593Smuzhiyun recipe = pkg2recipe(tinfoil, args.package) 182*4882a593Smuzhiyun print(' %s package provided by %s' % (args.package, recipe)) 183*4882a593Smuzhiyun 184*4882a593Smuzhiyundef pkg_dependencies(args): 185*4882a593Smuzhiyun def get_recipe_info(tinfoil, recipe): 186*4882a593Smuzhiyun try: 187*4882a593Smuzhiyun info = tinfoil.get_recipe_info(recipe) 188*4882a593Smuzhiyun except Exception: 189*4882a593Smuzhiyun logger.error('Failed to get recipe info for: %s' % recipe) 190*4882a593Smuzhiyun sys.exit(1) 191*4882a593Smuzhiyun if not info: 192*4882a593Smuzhiyun logger.warning('No recipe info found for: %s' % recipe) 193*4882a593Smuzhiyun sys.exit(1) 194*4882a593Smuzhiyun append_files = tinfoil.get_file_appends(info.fn) 195*4882a593Smuzhiyun appends = True 196*4882a593Smuzhiyun data = tinfoil.parse_recipe_file(info.fn, appends, append_files) 197*4882a593Smuzhiyun data.pn = info.pn 198*4882a593Smuzhiyun data.pv = info.pv 199*4882a593Smuzhiyun return data 200*4882a593Smuzhiyun 201*4882a593Smuzhiyun def find_dependencies(tinfoil, assume_provided, recipe_info, packages, rn, order): 202*4882a593Smuzhiyun spaces = ' ' * order 203*4882a593Smuzhiyun data = recipe_info[rn] 204*4882a593Smuzhiyun if args.native: 205*4882a593Smuzhiyun logger.debug('%s- %s' % (spaces, data.pn)) 206*4882a593Smuzhiyun elif "-native" not in data.pn: 207*4882a593Smuzhiyun if "cross" not in data.pn: 208*4882a593Smuzhiyun logger.debug('%s- %s' % (spaces, data.pn)) 209*4882a593Smuzhiyun 210*4882a593Smuzhiyun depends = [] 211*4882a593Smuzhiyun for dep in data.depends: 212*4882a593Smuzhiyun if dep not in assume_provided: 213*4882a593Smuzhiyun depends.append(dep) 214*4882a593Smuzhiyun 215*4882a593Smuzhiyun # First find all dependencies not in package list. 216*4882a593Smuzhiyun for dep in depends: 217*4882a593Smuzhiyun if dep not in packages: 218*4882a593Smuzhiyun packages.append(dep) 219*4882a593Smuzhiyun dep_data = get_recipe_info(tinfoil, dep) 220*4882a593Smuzhiyun # Do this once now to reduce the number of bitbake calls. 221*4882a593Smuzhiyun dep_data.depends = dep_data.getVar('DEPENDS').split() 222*4882a593Smuzhiyun recipe_info[dep] = dep_data 223*4882a593Smuzhiyun 224*4882a593Smuzhiyun # Then recursively analyze all of the dependencies for the current recipe. 225*4882a593Smuzhiyun for dep in depends: 226*4882a593Smuzhiyun find_dependencies(tinfoil, assume_provided, recipe_info, packages, dep, order + 1) 227*4882a593Smuzhiyun 228*4882a593Smuzhiyun with bb.tinfoil.Tinfoil() as tinfoil: 229*4882a593Smuzhiyun tinfoil.logger.setLevel(logger.getEffectiveLevel()) 230*4882a593Smuzhiyun tinfoil.prepare() 231*4882a593Smuzhiyun 232*4882a593Smuzhiyun assume_provided = tinfoil.config_data.getVar('ASSUME_PROVIDED').split() 233*4882a593Smuzhiyun logger.debug('assumed provided:') 234*4882a593Smuzhiyun for ap in sorted(assume_provided): 235*4882a593Smuzhiyun logger.debug(' - %s' % ap) 236*4882a593Smuzhiyun 237*4882a593Smuzhiyun recipe = pkg2recipe(tinfoil, args.package) 238*4882a593Smuzhiyun data = get_recipe_info(tinfoil, recipe) 239*4882a593Smuzhiyun data.depends = [] 240*4882a593Smuzhiyun depends = data.getVar('DEPENDS').split() 241*4882a593Smuzhiyun for dep in depends: 242*4882a593Smuzhiyun if dep not in assume_provided: 243*4882a593Smuzhiyun data.depends.append(dep) 244*4882a593Smuzhiyun 245*4882a593Smuzhiyun recipe_info = dict([(recipe, data)]) 246*4882a593Smuzhiyun packages = [] 247*4882a593Smuzhiyun find_dependencies(tinfoil, assume_provided, recipe_info, packages, recipe, order=1) 248*4882a593Smuzhiyun 249*4882a593Smuzhiyun print('\nThe following packages are required to build %s' % recipe) 250*4882a593Smuzhiyun for p in sorted(packages): 251*4882a593Smuzhiyun data = recipe_info[p] 252*4882a593Smuzhiyun if "-native" not in data.pn: 253*4882a593Smuzhiyun if "cross" not in data.pn: 254*4882a593Smuzhiyun print(" %s (%s)" % (data.pn,p)) 255*4882a593Smuzhiyun 256*4882a593Smuzhiyun if args.native: 257*4882a593Smuzhiyun print('\nThe following native packages are required to build %s' % recipe) 258*4882a593Smuzhiyun for p in sorted(packages): 259*4882a593Smuzhiyun data = recipe_info[p] 260*4882a593Smuzhiyun if "-native" in data.pn: 261*4882a593Smuzhiyun print(" %s(%s)" % (data.pn,p)) 262*4882a593Smuzhiyun if "cross" in data.pn: 263*4882a593Smuzhiyun print(" %s(%s)" % (data.pn,p)) 264*4882a593Smuzhiyun 265*4882a593Smuzhiyundef default_config(): 266*4882a593Smuzhiyun vlist = OrderedDict() 267*4882a593Smuzhiyun vlist['PV'] = 'yes' 268*4882a593Smuzhiyun vlist['SUMMARY'] = 'no' 269*4882a593Smuzhiyun vlist['DESCRIPTION'] = 'no' 270*4882a593Smuzhiyun vlist['SECTION'] = 'no' 271*4882a593Smuzhiyun vlist['LICENSE'] = 'yes' 272*4882a593Smuzhiyun vlist['HOMEPAGE'] = 'no' 273*4882a593Smuzhiyun vlist['BUGTRACKER'] = 'no' 274*4882a593Smuzhiyun vlist['PROVIDES'] = 'no' 275*4882a593Smuzhiyun vlist['BBCLASSEXTEND'] = 'no' 276*4882a593Smuzhiyun vlist['DEPENDS'] = 'no' 277*4882a593Smuzhiyun vlist['PACKAGECONFIG'] = 'no' 278*4882a593Smuzhiyun vlist['SRC_URI'] = 'yes' 279*4882a593Smuzhiyun vlist['SRCREV'] = 'yes' 280*4882a593Smuzhiyun vlist['EXTRA_OECONF'] = 'no' 281*4882a593Smuzhiyun vlist['EXTRA_OESCONS'] = 'no' 282*4882a593Smuzhiyun vlist['EXTRA_OECMAKE'] = 'no' 283*4882a593Smuzhiyun vlist['EXTRA_OEMESON'] = 'no' 284*4882a593Smuzhiyun 285*4882a593Smuzhiyun clist = OrderedDict() 286*4882a593Smuzhiyun clist['variables'] = vlist 287*4882a593Smuzhiyun clist['filepath'] = 'no' 288*4882a593Smuzhiyun clist['sha256sum'] = 'no' 289*4882a593Smuzhiyun clist['layerdir'] = 'no' 290*4882a593Smuzhiyun clist['layer'] = 'no' 291*4882a593Smuzhiyun clist['inherits'] = 'no' 292*4882a593Smuzhiyun clist['source_urls'] = 'no' 293*4882a593Smuzhiyun clist['packageconfig_opts'] = 'no' 294*4882a593Smuzhiyun clist['patches'] = 'no' 295*4882a593Smuzhiyun clist['packagedir'] = 'no' 296*4882a593Smuzhiyun return clist 297*4882a593Smuzhiyun 298*4882a593Smuzhiyundef dump_config(args): 299*4882a593Smuzhiyun config = default_config() 300*4882a593Smuzhiyun f = open('default_config.json', 'w') 301*4882a593Smuzhiyun json.dump(config, f, indent=2) 302*4882a593Smuzhiyun logger.info('Default config list dumped to default_config.json') 303*4882a593Smuzhiyun 304*4882a593Smuzhiyundef export_manifest_info(args): 305*4882a593Smuzhiyun 306*4882a593Smuzhiyun def handle_value(value): 307*4882a593Smuzhiyun if value: 308*4882a593Smuzhiyun return oe.utils.squashspaces(value) 309*4882a593Smuzhiyun else: 310*4882a593Smuzhiyun return value 311*4882a593Smuzhiyun 312*4882a593Smuzhiyun if args.config: 313*4882a593Smuzhiyun logger.debug('config: %s' % args.config) 314*4882a593Smuzhiyun f = open(args.config, 'r') 315*4882a593Smuzhiyun config = json.load(f, object_pairs_hook=OrderedDict) 316*4882a593Smuzhiyun else: 317*4882a593Smuzhiyun config = default_config() 318*4882a593Smuzhiyun if logger.isEnabledFor(logging.DEBUG): 319*4882a593Smuzhiyun print('Configuration:') 320*4882a593Smuzhiyun json.dump(config, sys.stdout, indent=2) 321*4882a593Smuzhiyun print('') 322*4882a593Smuzhiyun 323*4882a593Smuzhiyun tmpoutdir = tempfile.mkdtemp(prefix=os.path.basename(__file__)+'-') 324*4882a593Smuzhiyun logger.debug('tmp dir: %s' % tmpoutdir) 325*4882a593Smuzhiyun 326*4882a593Smuzhiyun # export manifest 327*4882a593Smuzhiyun shutil.copy2(args.manifest,os.path.join(tmpoutdir, "manifest")) 328*4882a593Smuzhiyun 329*4882a593Smuzhiyun with bb.tinfoil.Tinfoil(tracking=True) as tinfoil: 330*4882a593Smuzhiyun tinfoil.logger.setLevel(logger.getEffectiveLevel()) 331*4882a593Smuzhiyun tinfoil.prepare(config_only=False) 332*4882a593Smuzhiyun 333*4882a593Smuzhiyun pkglist = get_pkg_list(args.manifest) 334*4882a593Smuzhiyun # export pkg list 335*4882a593Smuzhiyun f = open(os.path.join(tmpoutdir, "pkgs"), 'w') 336*4882a593Smuzhiyun for pkg in pkglist: 337*4882a593Smuzhiyun f.write('%s\n' % pkg) 338*4882a593Smuzhiyun f.close() 339*4882a593Smuzhiyun 340*4882a593Smuzhiyun recipelist = [] 341*4882a593Smuzhiyun for pkg in pkglist: 342*4882a593Smuzhiyun recipe = pkg2recipe(tinfoil,pkg) 343*4882a593Smuzhiyun if recipe: 344*4882a593Smuzhiyun if not recipe in recipelist: 345*4882a593Smuzhiyun recipelist.append(recipe) 346*4882a593Smuzhiyun recipelist.sort() 347*4882a593Smuzhiyun # export recipe list 348*4882a593Smuzhiyun f = open(os.path.join(tmpoutdir, "recipes"), 'w') 349*4882a593Smuzhiyun for recipe in recipelist: 350*4882a593Smuzhiyun f.write('%s\n' % recipe) 351*4882a593Smuzhiyun f.close() 352*4882a593Smuzhiyun 353*4882a593Smuzhiyun try: 354*4882a593Smuzhiyun rvalues = OrderedDict() 355*4882a593Smuzhiyun for pn in sorted(recipelist): 356*4882a593Smuzhiyun logger.debug('Package: %s' % pn) 357*4882a593Smuzhiyun rd = tinfoil.parse_recipe(pn) 358*4882a593Smuzhiyun 359*4882a593Smuzhiyun rvalues[pn] = OrderedDict() 360*4882a593Smuzhiyun 361*4882a593Smuzhiyun for varname in config['variables']: 362*4882a593Smuzhiyun if config['variables'][varname] == 'yes': 363*4882a593Smuzhiyun rvalues[pn][varname] = handle_value(rd.getVar(varname)) 364*4882a593Smuzhiyun 365*4882a593Smuzhiyun fpth = rd.getVar('FILE') 366*4882a593Smuzhiyun layerdir = oe.recipeutils.find_layerdir(fpth) 367*4882a593Smuzhiyun if config['filepath'] == 'yes': 368*4882a593Smuzhiyun rvalues[pn]['filepath'] = os.path.relpath(fpth, layerdir) 369*4882a593Smuzhiyun if config['sha256sum'] == 'yes': 370*4882a593Smuzhiyun rvalues[pn]['sha256sum'] = bb.utils.sha256_file(fpth) 371*4882a593Smuzhiyun 372*4882a593Smuzhiyun if config['layerdir'] == 'yes': 373*4882a593Smuzhiyun rvalues[pn]['layerdir'] = layerdir 374*4882a593Smuzhiyun 375*4882a593Smuzhiyun if config['layer'] == 'yes': 376*4882a593Smuzhiyun rvalues[pn]['layer'] = os.path.basename(layerdir) 377*4882a593Smuzhiyun 378*4882a593Smuzhiyun if config['inherits'] == 'yes': 379*4882a593Smuzhiyun gr = set(tinfoil.config_data.getVar("__inherit_cache") or []) 380*4882a593Smuzhiyun lr = set(rd.getVar("__inherit_cache") or []) 381*4882a593Smuzhiyun rvalues[pn]['inherits'] = sorted({os.path.splitext(os.path.basename(r))[0] for r in lr if r not in gr}) 382*4882a593Smuzhiyun 383*4882a593Smuzhiyun if config['source_urls'] == 'yes': 384*4882a593Smuzhiyun rvalues[pn]['source_urls'] = [] 385*4882a593Smuzhiyun for url in (rd.getVar('SRC_URI') or '').split(): 386*4882a593Smuzhiyun if not url.startswith('file://'): 387*4882a593Smuzhiyun url = url.split(';')[0] 388*4882a593Smuzhiyun rvalues[pn]['source_urls'].append(url) 389*4882a593Smuzhiyun 390*4882a593Smuzhiyun if config['packageconfig_opts'] == 'yes': 391*4882a593Smuzhiyun rvalues[pn]['packageconfig_opts'] = OrderedDict() 392*4882a593Smuzhiyun for key in rd.getVarFlags('PACKAGECONFIG').keys(): 393*4882a593Smuzhiyun if key == 'doc': 394*4882a593Smuzhiyun continue 395*4882a593Smuzhiyun rvalues[pn]['packageconfig_opts'][key] = rd.getVarFlag('PACKAGECONFIG', key) 396*4882a593Smuzhiyun 397*4882a593Smuzhiyun if config['patches'] == 'yes': 398*4882a593Smuzhiyun patches = oe.recipeutils.get_recipe_patches(rd) 399*4882a593Smuzhiyun rvalues[pn]['patches'] = [] 400*4882a593Smuzhiyun if patches: 401*4882a593Smuzhiyun recipeoutdir = os.path.join(tmpoutdir, pn, 'patches') 402*4882a593Smuzhiyun bb.utils.mkdirhier(recipeoutdir) 403*4882a593Smuzhiyun for patch in patches: 404*4882a593Smuzhiyun # Patches may be in other layers too 405*4882a593Smuzhiyun patchlayerdir = oe.recipeutils.find_layerdir(patch) 406*4882a593Smuzhiyun # patchlayerdir will be None for remote patches, which we ignore 407*4882a593Smuzhiyun # (since currently they are considered as part of sources) 408*4882a593Smuzhiyun if patchlayerdir: 409*4882a593Smuzhiyun rvalues[pn]['patches'].append((os.path.basename(patchlayerdir), os.path.relpath(patch, patchlayerdir))) 410*4882a593Smuzhiyun shutil.copy(patch, recipeoutdir) 411*4882a593Smuzhiyun 412*4882a593Smuzhiyun if config['packagedir'] == 'yes': 413*4882a593Smuzhiyun pn_dir = os.path.join(tmpoutdir, pn) 414*4882a593Smuzhiyun bb.utils.mkdirhier(pn_dir) 415*4882a593Smuzhiyun f = open(os.path.join(pn_dir, 'recipe.json'), 'w') 416*4882a593Smuzhiyun json.dump(rvalues[pn], f, indent=2) 417*4882a593Smuzhiyun f.close() 418*4882a593Smuzhiyun 419*4882a593Smuzhiyun with open(os.path.join(tmpoutdir, 'recipes.json'), 'w') as f: 420*4882a593Smuzhiyun json.dump(rvalues, f, indent=2) 421*4882a593Smuzhiyun 422*4882a593Smuzhiyun if args.output: 423*4882a593Smuzhiyun outname = os.path.basename(args.output) 424*4882a593Smuzhiyun else: 425*4882a593Smuzhiyun outname = os.path.splitext(os.path.basename(args.manifest))[0] 426*4882a593Smuzhiyun if outname.endswith('.tar.gz'): 427*4882a593Smuzhiyun outname = outname[:-7] 428*4882a593Smuzhiyun elif outname.endswith('.tgz'): 429*4882a593Smuzhiyun outname = outname[:-4] 430*4882a593Smuzhiyun 431*4882a593Smuzhiyun tarfn = outname 432*4882a593Smuzhiyun if tarfn.endswith(os.sep): 433*4882a593Smuzhiyun tarfn = tarfn[:-1] 434*4882a593Smuzhiyun if not tarfn.endswith(('.tar.gz', '.tgz')): 435*4882a593Smuzhiyun tarfn += '.tar.gz' 436*4882a593Smuzhiyun with open(tarfn, 'wb') as f: 437*4882a593Smuzhiyun with tarfile.open(None, "w:gz", f) as tar: 438*4882a593Smuzhiyun tar.add(tmpoutdir, outname) 439*4882a593Smuzhiyun finally: 440*4882a593Smuzhiyun shutil.rmtree(tmpoutdir) 441*4882a593Smuzhiyun 442*4882a593Smuzhiyun 443*4882a593Smuzhiyundef main(): 444*4882a593Smuzhiyun parser = argparse_oe.ArgumentParser(description="Image manifest utility", 445*4882a593Smuzhiyun epilog="Use %(prog)s <subcommand> --help to get help on a specific command") 446*4882a593Smuzhiyun parser.add_argument('-d', '--debug', help='Enable debug output', action='store_true') 447*4882a593Smuzhiyun parser.add_argument('-q', '--quiet', help='Print only errors', action='store_true') 448*4882a593Smuzhiyun subparsers = parser.add_subparsers(dest="subparser_name", title='subcommands', metavar='<subcommand>') 449*4882a593Smuzhiyun subparsers.required = True 450*4882a593Smuzhiyun 451*4882a593Smuzhiyun # get recipe info 452*4882a593Smuzhiyun parser_get_recipes = subparsers.add_parser('recipe-info', 453*4882a593Smuzhiyun help='Get recipe info', 454*4882a593Smuzhiyun description='Get recipe information for a package') 455*4882a593Smuzhiyun parser_get_recipes.add_argument('package', help='Package name') 456*4882a593Smuzhiyun parser_get_recipes.set_defaults(func=get_recipe) 457*4882a593Smuzhiyun 458*4882a593Smuzhiyun # list runtime dependencies 459*4882a593Smuzhiyun parser_pkg_dep = subparsers.add_parser('list-depends', 460*4882a593Smuzhiyun help='List dependencies', 461*4882a593Smuzhiyun description='List dependencies required to build the package') 462*4882a593Smuzhiyun parser_pkg_dep.add_argument('--native', help='also print native and cross packages', action='store_true') 463*4882a593Smuzhiyun parser_pkg_dep.add_argument('package', help='Package name') 464*4882a593Smuzhiyun parser_pkg_dep.set_defaults(func=pkg_dependencies) 465*4882a593Smuzhiyun 466*4882a593Smuzhiyun # list recipes 467*4882a593Smuzhiyun parser_recipes = subparsers.add_parser('list-recipes', 468*4882a593Smuzhiyun help='List recipes producing packages within an image', 469*4882a593Smuzhiyun description='Lists recipes producing the packages that went into an image, using the manifest and pkgdata') 470*4882a593Smuzhiyun parser_recipes.add_argument('manifest', help='Manifest file') 471*4882a593Smuzhiyun parser_recipes.set_defaults(func=list_recipes) 472*4882a593Smuzhiyun 473*4882a593Smuzhiyun # list packages 474*4882a593Smuzhiyun parser_packages = subparsers.add_parser('list-packages', 475*4882a593Smuzhiyun help='List packages within an image', 476*4882a593Smuzhiyun description='Lists packages that went into an image, using the manifest') 477*4882a593Smuzhiyun parser_packages.add_argument('manifest', help='Manifest file') 478*4882a593Smuzhiyun parser_packages.set_defaults(func=list_packages) 479*4882a593Smuzhiyun 480*4882a593Smuzhiyun # list layers 481*4882a593Smuzhiyun parser_layers = subparsers.add_parser('list-layers', 482*4882a593Smuzhiyun help='List included layers', 483*4882a593Smuzhiyun description='Lists included layers') 484*4882a593Smuzhiyun parser_layers.add_argument('-o', '--output', help='Output file - defaults to stdout if not specified', 485*4882a593Smuzhiyun default=sys.stdout, type=argparse.FileType('w')) 486*4882a593Smuzhiyun parser_layers.set_defaults(func=list_layers) 487*4882a593Smuzhiyun 488*4882a593Smuzhiyun # dump default configuration file 489*4882a593Smuzhiyun parser_dconfig = subparsers.add_parser('dump-config', 490*4882a593Smuzhiyun help='Dump default config', 491*4882a593Smuzhiyun description='Dump default config to default_config.json') 492*4882a593Smuzhiyun parser_dconfig.set_defaults(func=dump_config) 493*4882a593Smuzhiyun 494*4882a593Smuzhiyun # export recipe info for packages in manifest 495*4882a593Smuzhiyun parser_export = subparsers.add_parser('manifest-info', 496*4882a593Smuzhiyun help='Export recipe info for a manifest', 497*4882a593Smuzhiyun description='Export recipe information using the manifest') 498*4882a593Smuzhiyun parser_export.add_argument('-c', '--config', help='load config from json file') 499*4882a593Smuzhiyun parser_export.add_argument('-o', '--output', help='Output file (tarball) - defaults to manifest name if not specified') 500*4882a593Smuzhiyun parser_export.add_argument('manifest', help='Manifest file') 501*4882a593Smuzhiyun parser_export.set_defaults(func=export_manifest_info) 502*4882a593Smuzhiyun 503*4882a593Smuzhiyun args = parser.parse_args() 504*4882a593Smuzhiyun 505*4882a593Smuzhiyun if args.debug: 506*4882a593Smuzhiyun logger.setLevel(logging.DEBUG) 507*4882a593Smuzhiyun logger.debug("Debug Enabled") 508*4882a593Smuzhiyun elif args.quiet: 509*4882a593Smuzhiyun logger.setLevel(logging.ERROR) 510*4882a593Smuzhiyun 511*4882a593Smuzhiyun ret = args.func(args) 512*4882a593Smuzhiyun 513*4882a593Smuzhiyun return ret 514*4882a593Smuzhiyun 515*4882a593Smuzhiyun 516*4882a593Smuzhiyunif __name__ == "__main__": 517*4882a593Smuzhiyun try: 518*4882a593Smuzhiyun ret = main() 519*4882a593Smuzhiyun except Exception: 520*4882a593Smuzhiyun ret = 1 521*4882a593Smuzhiyun import traceback 522*4882a593Smuzhiyun traceback.print_exc() 523*4882a593Smuzhiyun sys.exit(ret) 524