1*4882a593Smuzhiyun#!/usr/bin/env python3 2*4882a593Smuzhiyun 3*4882a593Smuzhiyun# OpenEmbedded pkgdata utility 4*4882a593Smuzhiyun# 5*4882a593Smuzhiyun# Written by: Paul Eggleton <paul.eggleton@linux.intel.com> 6*4882a593Smuzhiyun# 7*4882a593Smuzhiyun# Copyright 2012-2015 Intel Corporation 8*4882a593Smuzhiyun# 9*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0-only 10*4882a593Smuzhiyun# 11*4882a593Smuzhiyun 12*4882a593Smuzhiyunimport sys 13*4882a593Smuzhiyunimport os 14*4882a593Smuzhiyunimport os.path 15*4882a593Smuzhiyunimport fnmatch 16*4882a593Smuzhiyunimport re 17*4882a593Smuzhiyunimport argparse 18*4882a593Smuzhiyunimport logging 19*4882a593Smuzhiyunfrom collections import defaultdict, OrderedDict 20*4882a593Smuzhiyun 21*4882a593Smuzhiyunscripts_path = os.path.dirname(os.path.realpath(__file__)) 22*4882a593Smuzhiyunlib_path = scripts_path + '/lib' 23*4882a593Smuzhiyunsys.path = sys.path + [lib_path] 24*4882a593Smuzhiyunimport scriptutils 25*4882a593Smuzhiyunimport argparse_oe 26*4882a593Smuzhiyunlogger = scriptutils.logger_create('pkgdatautil') 27*4882a593Smuzhiyun 28*4882a593Smuzhiyundef tinfoil_init(): 29*4882a593Smuzhiyun import bb.tinfoil 30*4882a593Smuzhiyun import logging 31*4882a593Smuzhiyun tinfoil = bb.tinfoil.Tinfoil() 32*4882a593Smuzhiyun tinfoil.logger.setLevel(logging.WARNING) 33*4882a593Smuzhiyun tinfoil.prepare(True) 34*4882a593Smuzhiyun return tinfoil 35*4882a593Smuzhiyun 36*4882a593Smuzhiyun 37*4882a593Smuzhiyundef glob(args): 38*4882a593Smuzhiyun # Handle both multiple arguments and multiple values within an arg (old syntax) 39*4882a593Smuzhiyun globs = [] 40*4882a593Smuzhiyun for globitem in args.glob: 41*4882a593Smuzhiyun globs.extend(globitem.split()) 42*4882a593Smuzhiyun 43*4882a593Smuzhiyun if not os.path.exists(args.pkglistfile): 44*4882a593Smuzhiyun logger.error('Unable to find package list file %s' % args.pkglistfile) 45*4882a593Smuzhiyun sys.exit(1) 46*4882a593Smuzhiyun 47*4882a593Smuzhiyun skipval = "-locale-|^locale-base-|-dev$|-doc$|-dbg$|-staticdev$|^kernel-module-" 48*4882a593Smuzhiyun if args.exclude: 49*4882a593Smuzhiyun skipval += "|" + args.exclude 50*4882a593Smuzhiyun skipregex = re.compile(skipval) 51*4882a593Smuzhiyun 52*4882a593Smuzhiyun skippedpkgs = set() 53*4882a593Smuzhiyun mappedpkgs = set() 54*4882a593Smuzhiyun with open(args.pkglistfile, 'r') as f: 55*4882a593Smuzhiyun for line in f: 56*4882a593Smuzhiyun fields = line.rstrip().split() 57*4882a593Smuzhiyun if not fields: 58*4882a593Smuzhiyun continue 59*4882a593Smuzhiyun pkg = fields[0] 60*4882a593Smuzhiyun # We don't care about other args (used to need the package architecture but the 61*4882a593Smuzhiyun # new pkgdata structure avoids the need for that) 62*4882a593Smuzhiyun 63*4882a593Smuzhiyun # Skip packages for which there is no point applying globs 64*4882a593Smuzhiyun if skipregex.search(pkg): 65*4882a593Smuzhiyun logger.debug("%s -> !!" % pkg) 66*4882a593Smuzhiyun skippedpkgs.add(pkg) 67*4882a593Smuzhiyun continue 68*4882a593Smuzhiyun 69*4882a593Smuzhiyun # Skip packages that already match the globs, so if e.g. a dev package 70*4882a593Smuzhiyun # is already installed and thus in the list, we don't process it any further 71*4882a593Smuzhiyun # Most of these will be caught by skipregex already, but just in case... 72*4882a593Smuzhiyun already = False 73*4882a593Smuzhiyun for g in globs: 74*4882a593Smuzhiyun if fnmatch.fnmatchcase(pkg, g): 75*4882a593Smuzhiyun already = True 76*4882a593Smuzhiyun break 77*4882a593Smuzhiyun if already: 78*4882a593Smuzhiyun skippedpkgs.add(pkg) 79*4882a593Smuzhiyun logger.debug("%s -> !" % pkg) 80*4882a593Smuzhiyun continue 81*4882a593Smuzhiyun 82*4882a593Smuzhiyun # Define some functions 83*4882a593Smuzhiyun def revpkgdata(pkgn): 84*4882a593Smuzhiyun return os.path.join(args.pkgdata_dir, "runtime-reverse", pkgn) 85*4882a593Smuzhiyun def fwdpkgdata(pkgn): 86*4882a593Smuzhiyun return os.path.join(args.pkgdata_dir, "runtime", pkgn) 87*4882a593Smuzhiyun def readpn(pkgdata_file): 88*4882a593Smuzhiyun pn = "" 89*4882a593Smuzhiyun with open(pkgdata_file, 'r') as f: 90*4882a593Smuzhiyun for line in f: 91*4882a593Smuzhiyun if line.startswith("PN:"): 92*4882a593Smuzhiyun pn = line.split(': ')[1].rstrip() 93*4882a593Smuzhiyun return pn 94*4882a593Smuzhiyun def readrenamed(pkgdata_file): 95*4882a593Smuzhiyun renamed = "" 96*4882a593Smuzhiyun pn = os.path.basename(pkgdata_file) 97*4882a593Smuzhiyun with open(pkgdata_file, 'r') as f: 98*4882a593Smuzhiyun for line in f: 99*4882a593Smuzhiyun if line.startswith("PKG:%s:" % pn): 100*4882a593Smuzhiyun renamed = line.split(': ')[1].rstrip() 101*4882a593Smuzhiyun return renamed 102*4882a593Smuzhiyun 103*4882a593Smuzhiyun # Main processing loop 104*4882a593Smuzhiyun for g in globs: 105*4882a593Smuzhiyun mappedpkg = "" 106*4882a593Smuzhiyun # First just try substitution (i.e. packagename -> packagename-dev) 107*4882a593Smuzhiyun newpkg = g.replace("*", pkg) 108*4882a593Smuzhiyun revlink = revpkgdata(newpkg) 109*4882a593Smuzhiyun if os.path.exists(revlink): 110*4882a593Smuzhiyun mappedpkg = os.path.basename(os.readlink(revlink)) 111*4882a593Smuzhiyun fwdfile = fwdpkgdata(mappedpkg) 112*4882a593Smuzhiyun if os.path.exists(fwdfile): 113*4882a593Smuzhiyun mappedpkg = readrenamed(fwdfile) 114*4882a593Smuzhiyun if not os.path.exists(fwdfile + ".packaged"): 115*4882a593Smuzhiyun mappedpkg = "" 116*4882a593Smuzhiyun else: 117*4882a593Smuzhiyun revlink = revpkgdata(pkg) 118*4882a593Smuzhiyun if os.path.exists(revlink): 119*4882a593Smuzhiyun # Check if we can map after undoing the package renaming (by resolving the symlink) 120*4882a593Smuzhiyun origpkg = os.path.basename(os.readlink(revlink)) 121*4882a593Smuzhiyun newpkg = g.replace("*", origpkg) 122*4882a593Smuzhiyun fwdfile = fwdpkgdata(newpkg) 123*4882a593Smuzhiyun if os.path.exists(fwdfile): 124*4882a593Smuzhiyun mappedpkg = readrenamed(fwdfile) 125*4882a593Smuzhiyun else: 126*4882a593Smuzhiyun # That didn't work, so now get the PN, substitute that, then map in the other direction 127*4882a593Smuzhiyun pn = readpn(revlink) 128*4882a593Smuzhiyun newpkg = g.replace("*", pn) 129*4882a593Smuzhiyun fwdfile = fwdpkgdata(newpkg) 130*4882a593Smuzhiyun if os.path.exists(fwdfile): 131*4882a593Smuzhiyun mappedpkg = readrenamed(fwdfile) 132*4882a593Smuzhiyun if not os.path.exists(fwdfile + ".packaged"): 133*4882a593Smuzhiyun mappedpkg = "" 134*4882a593Smuzhiyun else: 135*4882a593Smuzhiyun # Package doesn't even exist... 136*4882a593Smuzhiyun logger.debug("%s is not a valid package!" % (pkg)) 137*4882a593Smuzhiyun break 138*4882a593Smuzhiyun 139*4882a593Smuzhiyun if mappedpkg: 140*4882a593Smuzhiyun logger.debug("%s (%s) -> %s" % (pkg, g, mappedpkg)) 141*4882a593Smuzhiyun mappedpkgs.add(mappedpkg) 142*4882a593Smuzhiyun else: 143*4882a593Smuzhiyun logger.debug("%s (%s) -> ?" % (pkg, g)) 144*4882a593Smuzhiyun 145*4882a593Smuzhiyun logger.debug("------") 146*4882a593Smuzhiyun 147*4882a593Smuzhiyun print("\n".join(mappedpkgs - skippedpkgs)) 148*4882a593Smuzhiyun 149*4882a593Smuzhiyundef read_value(args): 150*4882a593Smuzhiyun # Handle both multiple arguments and multiple values within an arg (old syntax) 151*4882a593Smuzhiyun packages = [] 152*4882a593Smuzhiyun if args.file: 153*4882a593Smuzhiyun with open(args.file, 'r') as f: 154*4882a593Smuzhiyun for line in f: 155*4882a593Smuzhiyun splitline = line.split() 156*4882a593Smuzhiyun if splitline: 157*4882a593Smuzhiyun packages.append(splitline[0]) 158*4882a593Smuzhiyun else: 159*4882a593Smuzhiyun for pkgitem in args.pkg: 160*4882a593Smuzhiyun packages.extend(pkgitem.split()) 161*4882a593Smuzhiyun if not packages: 162*4882a593Smuzhiyun logger.error("No packages specified") 163*4882a593Smuzhiyun sys.exit(1) 164*4882a593Smuzhiyun 165*4882a593Smuzhiyun def readvar(pkgdata_file, valuename, mappedpkg): 166*4882a593Smuzhiyun val = "" 167*4882a593Smuzhiyun with open(pkgdata_file, 'r') as f: 168*4882a593Smuzhiyun for line in f: 169*4882a593Smuzhiyun if (line.startswith(valuename + ":") or 170*4882a593Smuzhiyun line.startswith(valuename + "_" + mappedpkg + ":")): 171*4882a593Smuzhiyun val = line.split(': ', 1)[1].rstrip() 172*4882a593Smuzhiyun return val 173*4882a593Smuzhiyun 174*4882a593Smuzhiyun logger.debug("read-value('%s', '%s' '%s')" % (args.pkgdata_dir, args.valuenames, packages)) 175*4882a593Smuzhiyun for package in packages: 176*4882a593Smuzhiyun pkg_split = package.split('_') 177*4882a593Smuzhiyun pkg_name = pkg_split[0] 178*4882a593Smuzhiyun logger.debug("package: '%s'" % pkg_name) 179*4882a593Smuzhiyun revlink = os.path.join(args.pkgdata_dir, "runtime-reverse", pkg_name) 180*4882a593Smuzhiyun logger.debug(revlink) 181*4882a593Smuzhiyun if os.path.exists(revlink): 182*4882a593Smuzhiyun mappedpkg = os.path.basename(os.readlink(revlink)) 183*4882a593Smuzhiyun qvars = args.valuenames 184*4882a593Smuzhiyun val_names = qvars.split(',') 185*4882a593Smuzhiyun values = [] 186*4882a593Smuzhiyun for qvar in val_names: 187*4882a593Smuzhiyun if qvar == "PACKAGE": 188*4882a593Smuzhiyun value = mappedpkg 189*4882a593Smuzhiyun else: 190*4882a593Smuzhiyun value = readvar(revlink, qvar, mappedpkg) 191*4882a593Smuzhiyun if qvar == "PKGSIZE": 192*4882a593Smuzhiyun # PKGSIZE is now in bytes, but we we want it in KB 193*4882a593Smuzhiyun pkgsize = (int(value) + 1024 // 2) // 1024 194*4882a593Smuzhiyun value = "%d" % pkgsize 195*4882a593Smuzhiyun if args.unescape: 196*4882a593Smuzhiyun import codecs 197*4882a593Smuzhiyun # escape_decode() unescapes backslash encodings in byte streams 198*4882a593Smuzhiyun value = codecs.escape_decode(bytes(value, "utf-8"))[0].decode("utf-8") 199*4882a593Smuzhiyun values.append(value) 200*4882a593Smuzhiyun 201*4882a593Smuzhiyun values_str = ' '.join(values) 202*4882a593Smuzhiyun if args.prefix_name: 203*4882a593Smuzhiyun print('%s %s' % (pkg_name, values_str)) 204*4882a593Smuzhiyun else: 205*4882a593Smuzhiyun print(values_str) 206*4882a593Smuzhiyun else: 207*4882a593Smuzhiyun logger.debug("revlink %s does not exist", revlink) 208*4882a593Smuzhiyun 209*4882a593Smuzhiyundef lookup_pkglist(pkgs, pkgdata_dir, reverse): 210*4882a593Smuzhiyun if reverse: 211*4882a593Smuzhiyun mappings = OrderedDict() 212*4882a593Smuzhiyun for pkg in pkgs: 213*4882a593Smuzhiyun revlink = os.path.join(pkgdata_dir, "runtime-reverse", pkg) 214*4882a593Smuzhiyun logger.debug(revlink) 215*4882a593Smuzhiyun if os.path.exists(revlink): 216*4882a593Smuzhiyun mappings[pkg] = os.path.basename(os.readlink(revlink)) 217*4882a593Smuzhiyun else: 218*4882a593Smuzhiyun mappings = defaultdict(list) 219*4882a593Smuzhiyun for pkg in pkgs: 220*4882a593Smuzhiyun pkgfile = os.path.join(pkgdata_dir, 'runtime', pkg) 221*4882a593Smuzhiyun if os.path.exists(pkgfile): 222*4882a593Smuzhiyun with open(pkgfile, 'r') as f: 223*4882a593Smuzhiyun for line in f: 224*4882a593Smuzhiyun fields = line.rstrip().split(': ') 225*4882a593Smuzhiyun if fields[0] == 'PKG:%s' % pkg: 226*4882a593Smuzhiyun mappings[pkg].append(fields[1]) 227*4882a593Smuzhiyun break 228*4882a593Smuzhiyun return mappings 229*4882a593Smuzhiyun 230*4882a593Smuzhiyundef lookup_pkg(args): 231*4882a593Smuzhiyun # Handle both multiple arguments and multiple values within an arg (old syntax) 232*4882a593Smuzhiyun pkgs = [] 233*4882a593Smuzhiyun for pkgitem in args.pkg: 234*4882a593Smuzhiyun pkgs.extend(pkgitem.split()) 235*4882a593Smuzhiyun 236*4882a593Smuzhiyun mappings = lookup_pkglist(pkgs, args.pkgdata_dir, args.reverse) 237*4882a593Smuzhiyun 238*4882a593Smuzhiyun if len(mappings) < len(pkgs): 239*4882a593Smuzhiyun missing = list(set(pkgs) - set(mappings.keys())) 240*4882a593Smuzhiyun logger.error("The following packages could not be found: %s" % ', '.join(missing)) 241*4882a593Smuzhiyun sys.exit(1) 242*4882a593Smuzhiyun 243*4882a593Smuzhiyun if args.reverse: 244*4882a593Smuzhiyun items = list(mappings.values()) 245*4882a593Smuzhiyun else: 246*4882a593Smuzhiyun items = [] 247*4882a593Smuzhiyun for pkg in pkgs: 248*4882a593Smuzhiyun items.extend(mappings.get(pkg, [])) 249*4882a593Smuzhiyun 250*4882a593Smuzhiyun print('\n'.join(items)) 251*4882a593Smuzhiyun 252*4882a593Smuzhiyundef lookup_recipe(args): 253*4882a593Smuzhiyun def parse_pkgdatafile(pkgdatafile): 254*4882a593Smuzhiyun with open(pkgdatafile, 'r') as f: 255*4882a593Smuzhiyun found = False 256*4882a593Smuzhiyun for line in f: 257*4882a593Smuzhiyun if line.startswith('PN:'): 258*4882a593Smuzhiyun print("%s" % line.split(':', 1)[1].strip()) 259*4882a593Smuzhiyun found = True 260*4882a593Smuzhiyun break 261*4882a593Smuzhiyun if not found: 262*4882a593Smuzhiyun logger.error("Unable to find PN entry in %s" % pkgdatafile) 263*4882a593Smuzhiyun sys.exit(1) 264*4882a593Smuzhiyun 265*4882a593Smuzhiyun # Handle both multiple arguments and multiple values within an arg (old syntax) 266*4882a593Smuzhiyun pkgs = [] 267*4882a593Smuzhiyun for pkgitem in args.pkg: 268*4882a593Smuzhiyun pkgs.extend(pkgitem.split()) 269*4882a593Smuzhiyun 270*4882a593Smuzhiyun for pkg in pkgs: 271*4882a593Smuzhiyun providepkgpath = os.path.join(args.pkgdata_dir, "runtime-rprovides", pkg) 272*4882a593Smuzhiyun if os.path.exists(providepkgpath): 273*4882a593Smuzhiyun for f in os.listdir(providepkgpath): 274*4882a593Smuzhiyun if f != pkg: 275*4882a593Smuzhiyun print("%s is in the RPROVIDES of %s:" % (pkg, f)) 276*4882a593Smuzhiyun pkgdatafile = os.path.join(args.pkgdata_dir, "runtime", f) 277*4882a593Smuzhiyun parse_pkgdatafile(pkgdatafile) 278*4882a593Smuzhiyun continue 279*4882a593Smuzhiyun pkgdatafile = os.path.join(args.pkgdata_dir, 'runtime-reverse', pkg) 280*4882a593Smuzhiyun if os.path.exists(pkgdatafile): 281*4882a593Smuzhiyun parse_pkgdatafile(pkgdatafile) 282*4882a593Smuzhiyun else: 283*4882a593Smuzhiyun if args.carryon: 284*4882a593Smuzhiyun print("The following packages could not be found: %s" % pkg) 285*4882a593Smuzhiyun else: 286*4882a593Smuzhiyun logger.error("The following packages could not be found: %s" % pkg) 287*4882a593Smuzhiyun sys.exit(1) 288*4882a593Smuzhiyun 289*4882a593Smuzhiyundef package_info(args): 290*4882a593Smuzhiyun def parse_pkgdatafile(pkgdatafile): 291*4882a593Smuzhiyun vars = ['PKGV', 'PKGE', 'PKGR', 'PN', 'PV', 'PE', 'PR', 'PKGSIZE'] 292*4882a593Smuzhiyun if args.extra: 293*4882a593Smuzhiyun vars += args.extra 294*4882a593Smuzhiyun with open(pkgdatafile, 'r') as f: 295*4882a593Smuzhiyun vals = dict() 296*4882a593Smuzhiyun extra = '' 297*4882a593Smuzhiyun for line in f: 298*4882a593Smuzhiyun for var in vars: 299*4882a593Smuzhiyun m = re.match(var + '(?::\S+)?:\s*(.+?)\s*$', line) 300*4882a593Smuzhiyun if m: 301*4882a593Smuzhiyun vals[var] = m.group(1) 302*4882a593Smuzhiyun pkg_version = vals['PKGV'] or '' 303*4882a593Smuzhiyun recipe = vals['PN'] or '' 304*4882a593Smuzhiyun recipe_version = vals['PV'] or '' 305*4882a593Smuzhiyun pkg_size = vals['PKGSIZE'] or '' 306*4882a593Smuzhiyun if 'PKGE' in vals: 307*4882a593Smuzhiyun pkg_version = vals['PKGE'] + ":" + pkg_version 308*4882a593Smuzhiyun if 'PKGR' in vals: 309*4882a593Smuzhiyun pkg_version = pkg_version + "-" + vals['PKGR'] 310*4882a593Smuzhiyun if 'PE' in vals: 311*4882a593Smuzhiyun recipe_version = vals['PE'] + ":" + recipe_version 312*4882a593Smuzhiyun if 'PR' in vals: 313*4882a593Smuzhiyun recipe_version = recipe_version + "-" + vals['PR'] 314*4882a593Smuzhiyun if args.extra: 315*4882a593Smuzhiyun for var in args.extra: 316*4882a593Smuzhiyun if var in vals: 317*4882a593Smuzhiyun val = re.sub(r'\s+', ' ', vals[var]) 318*4882a593Smuzhiyun extra += ' "%s"' % val 319*4882a593Smuzhiyun print("%s %s %s %s %s%s" % (pkg, pkg_version, recipe, recipe_version, pkg_size, extra)) 320*4882a593Smuzhiyun 321*4882a593Smuzhiyun # Handle both multiple arguments and multiple values within an arg (old syntax) 322*4882a593Smuzhiyun packages = [] 323*4882a593Smuzhiyun if args.file: 324*4882a593Smuzhiyun with open(args.file, 'r') as f: 325*4882a593Smuzhiyun for line in f: 326*4882a593Smuzhiyun splitline = line.split() 327*4882a593Smuzhiyun if splitline: 328*4882a593Smuzhiyun packages.append(splitline[0]) 329*4882a593Smuzhiyun else: 330*4882a593Smuzhiyun for pkgitem in args.pkg: 331*4882a593Smuzhiyun packages.extend(pkgitem.split()) 332*4882a593Smuzhiyun if not packages: 333*4882a593Smuzhiyun logger.error("No packages specified") 334*4882a593Smuzhiyun sys.exit(1) 335*4882a593Smuzhiyun 336*4882a593Smuzhiyun for pkg in packages: 337*4882a593Smuzhiyun providepkgpath = os.path.join(args.pkgdata_dir, "runtime-rprovides", pkg) 338*4882a593Smuzhiyun if os.path.exists(providepkgpath): 339*4882a593Smuzhiyun for f in os.listdir(providepkgpath): 340*4882a593Smuzhiyun if f != pkg: 341*4882a593Smuzhiyun print("%s is in the RPROVIDES of %s:" % (pkg, f)) 342*4882a593Smuzhiyun pkgdatafile = os.path.join(args.pkgdata_dir, "runtime", f) 343*4882a593Smuzhiyun parse_pkgdatafile(pkgdatafile) 344*4882a593Smuzhiyun continue 345*4882a593Smuzhiyun pkgdatafile = os.path.join(args.pkgdata_dir, "runtime-reverse", pkg) 346*4882a593Smuzhiyun if not os.path.exists(pkgdatafile): 347*4882a593Smuzhiyun logger.error("Unable to find any built runtime package named %s" % pkg) 348*4882a593Smuzhiyun sys.exit(1) 349*4882a593Smuzhiyun parse_pkgdatafile(pkgdatafile) 350*4882a593Smuzhiyun 351*4882a593Smuzhiyundef get_recipe_pkgs(pkgdata_dir, recipe, unpackaged): 352*4882a593Smuzhiyun recipedatafile = os.path.join(pkgdata_dir, recipe) 353*4882a593Smuzhiyun if not os.path.exists(recipedatafile): 354*4882a593Smuzhiyun logger.error("Unable to find packaged recipe with name %s" % recipe) 355*4882a593Smuzhiyun sys.exit(1) 356*4882a593Smuzhiyun packages = [] 357*4882a593Smuzhiyun with open(recipedatafile, 'r') as f: 358*4882a593Smuzhiyun for line in f: 359*4882a593Smuzhiyun fields = line.rstrip().split(': ') 360*4882a593Smuzhiyun if fields[0] == 'PACKAGES': 361*4882a593Smuzhiyun packages = fields[1].split() 362*4882a593Smuzhiyun break 363*4882a593Smuzhiyun 364*4882a593Smuzhiyun if not unpackaged: 365*4882a593Smuzhiyun pkglist = [] 366*4882a593Smuzhiyun for pkg in packages: 367*4882a593Smuzhiyun if os.path.exists(os.path.join(pkgdata_dir, 'runtime', '%s.packaged' % pkg)): 368*4882a593Smuzhiyun pkglist.append(pkg) 369*4882a593Smuzhiyun return pkglist 370*4882a593Smuzhiyun else: 371*4882a593Smuzhiyun return packages 372*4882a593Smuzhiyun 373*4882a593Smuzhiyundef list_pkgs(args): 374*4882a593Smuzhiyun found = False 375*4882a593Smuzhiyun 376*4882a593Smuzhiyun def matchpkg(pkg): 377*4882a593Smuzhiyun if args.pkgspec: 378*4882a593Smuzhiyun matched = False 379*4882a593Smuzhiyun for pkgspec in args.pkgspec: 380*4882a593Smuzhiyun if fnmatch.fnmatchcase(pkg, pkgspec): 381*4882a593Smuzhiyun matched = True 382*4882a593Smuzhiyun break 383*4882a593Smuzhiyun if not matched: 384*4882a593Smuzhiyun return False 385*4882a593Smuzhiyun if not args.unpackaged: 386*4882a593Smuzhiyun if args.runtime: 387*4882a593Smuzhiyun revlink = os.path.join(args.pkgdata_dir, "runtime-reverse", pkg) 388*4882a593Smuzhiyun if os.path.exists(revlink): 389*4882a593Smuzhiyun # We're unlikely to get here if the package was not packaged, but just in case 390*4882a593Smuzhiyun # we add the symlinks for unpackaged files in the future 391*4882a593Smuzhiyun mappedpkg = os.path.basename(os.readlink(revlink)) 392*4882a593Smuzhiyun if not os.path.exists(os.path.join(args.pkgdata_dir, 'runtime', '%s.packaged' % mappedpkg)): 393*4882a593Smuzhiyun return False 394*4882a593Smuzhiyun else: 395*4882a593Smuzhiyun return False 396*4882a593Smuzhiyun else: 397*4882a593Smuzhiyun if not os.path.exists(os.path.join(args.pkgdata_dir, 'runtime', '%s.packaged' % pkg)): 398*4882a593Smuzhiyun return False 399*4882a593Smuzhiyun return True 400*4882a593Smuzhiyun 401*4882a593Smuzhiyun pkglist = [] 402*4882a593Smuzhiyun if args.recipe: 403*4882a593Smuzhiyun packages = get_recipe_pkgs(args.pkgdata_dir, args.recipe, args.unpackaged) 404*4882a593Smuzhiyun 405*4882a593Smuzhiyun if args.runtime: 406*4882a593Smuzhiyun runtime_pkgs = lookup_pkglist(packages, args.pkgdata_dir, False) 407*4882a593Smuzhiyun for rtpkgs in runtime_pkgs.values(): 408*4882a593Smuzhiyun pkglist.extend(rtpkgs) 409*4882a593Smuzhiyun else: 410*4882a593Smuzhiyun pkglist = packages 411*4882a593Smuzhiyun else: 412*4882a593Smuzhiyun if args.runtime: 413*4882a593Smuzhiyun searchdir = 'runtime-reverse' 414*4882a593Smuzhiyun else: 415*4882a593Smuzhiyun searchdir = 'runtime' 416*4882a593Smuzhiyun 417*4882a593Smuzhiyun for root, dirs, files in os.walk(os.path.join(args.pkgdata_dir, searchdir)): 418*4882a593Smuzhiyun for fn in files: 419*4882a593Smuzhiyun if fn.endswith('.packaged'): 420*4882a593Smuzhiyun continue 421*4882a593Smuzhiyun pkglist.append(fn) 422*4882a593Smuzhiyun 423*4882a593Smuzhiyun for pkg in sorted(pkglist): 424*4882a593Smuzhiyun if matchpkg(pkg): 425*4882a593Smuzhiyun found = True 426*4882a593Smuzhiyun print("%s" % pkg) 427*4882a593Smuzhiyun 428*4882a593Smuzhiyun if not found: 429*4882a593Smuzhiyun if args.pkgspec: 430*4882a593Smuzhiyun logger.error("Unable to find any package matching %s" % args.pkgspec) 431*4882a593Smuzhiyun else: 432*4882a593Smuzhiyun logger.error("No packages found") 433*4882a593Smuzhiyun sys.exit(1) 434*4882a593Smuzhiyun 435*4882a593Smuzhiyundef list_pkg_files(args): 436*4882a593Smuzhiyun import json 437*4882a593Smuzhiyun def parse_pkgdatafile(pkgdatafile, long=False): 438*4882a593Smuzhiyun with open(pkgdatafile, 'r') as f: 439*4882a593Smuzhiyun found = False 440*4882a593Smuzhiyun for line in f: 441*4882a593Smuzhiyun if line.startswith('FILES_INFO:'): 442*4882a593Smuzhiyun found = True 443*4882a593Smuzhiyun val = line.split(': ', 1)[1].strip() 444*4882a593Smuzhiyun dictval = json.loads(val) 445*4882a593Smuzhiyun if long: 446*4882a593Smuzhiyun width = max(map(len, dictval), default=0) 447*4882a593Smuzhiyun for fullpth in sorted(dictval): 448*4882a593Smuzhiyun print("\t{:{width}}\t{}".format(fullpth, dictval[fullpth], width=width)) 449*4882a593Smuzhiyun else: 450*4882a593Smuzhiyun for fullpth in sorted(dictval): 451*4882a593Smuzhiyun print("\t%s" % fullpth) 452*4882a593Smuzhiyun break 453*4882a593Smuzhiyun if not found: 454*4882a593Smuzhiyun logger.error("Unable to find FILES_INFO entry in %s" % pkgdatafile) 455*4882a593Smuzhiyun sys.exit(1) 456*4882a593Smuzhiyun 457*4882a593Smuzhiyun 458*4882a593Smuzhiyun if args.recipe: 459*4882a593Smuzhiyun if args.pkg: 460*4882a593Smuzhiyun logger.error("list-pkg-files: If -p/--recipe is specified then a package name cannot be specified") 461*4882a593Smuzhiyun sys.exit(1) 462*4882a593Smuzhiyun recipepkglist = get_recipe_pkgs(args.pkgdata_dir, args.recipe, args.unpackaged) 463*4882a593Smuzhiyun if args.runtime: 464*4882a593Smuzhiyun pkglist = [] 465*4882a593Smuzhiyun runtime_pkgs = lookup_pkglist(recipepkglist, args.pkgdata_dir, False) 466*4882a593Smuzhiyun for rtpkgs in runtime_pkgs.values(): 467*4882a593Smuzhiyun pkglist.extend(rtpkgs) 468*4882a593Smuzhiyun else: 469*4882a593Smuzhiyun pkglist = recipepkglist 470*4882a593Smuzhiyun else: 471*4882a593Smuzhiyun if not args.pkg: 472*4882a593Smuzhiyun logger.error("list-pkg-files: If -p/--recipe is not specified then at least one package name must be specified") 473*4882a593Smuzhiyun sys.exit(1) 474*4882a593Smuzhiyun pkglist = args.pkg 475*4882a593Smuzhiyun 476*4882a593Smuzhiyun for pkg in sorted(pkglist): 477*4882a593Smuzhiyun print("%s:" % pkg) 478*4882a593Smuzhiyun if args.runtime: 479*4882a593Smuzhiyun pkgdatafile = os.path.join(args.pkgdata_dir, "runtime-reverse", pkg) 480*4882a593Smuzhiyun if not os.path.exists(pkgdatafile): 481*4882a593Smuzhiyun if args.recipe: 482*4882a593Smuzhiyun # This package was empty and thus never packaged, ignore 483*4882a593Smuzhiyun continue 484*4882a593Smuzhiyun logger.error("Unable to find any built runtime package named %s" % pkg) 485*4882a593Smuzhiyun sys.exit(1) 486*4882a593Smuzhiyun parse_pkgdatafile(pkgdatafile, args.long) 487*4882a593Smuzhiyun 488*4882a593Smuzhiyun else: 489*4882a593Smuzhiyun providepkgpath = os.path.join(args.pkgdata_dir, "runtime-rprovides", pkg) 490*4882a593Smuzhiyun if os.path.exists(providepkgpath): 491*4882a593Smuzhiyun for f in os.listdir(providepkgpath): 492*4882a593Smuzhiyun if f != pkg: 493*4882a593Smuzhiyun print("%s is in the RPROVIDES of %s:" % (pkg, f)) 494*4882a593Smuzhiyun pkgdatafile = os.path.join(args.pkgdata_dir, "runtime", f) 495*4882a593Smuzhiyun parse_pkgdatafile(pkgdatafile, args.long) 496*4882a593Smuzhiyun continue 497*4882a593Smuzhiyun pkgdatafile = os.path.join(args.pkgdata_dir, "runtime", pkg) 498*4882a593Smuzhiyun if not os.path.exists(pkgdatafile): 499*4882a593Smuzhiyun logger.error("Unable to find any built recipe-space package named %s" % pkg) 500*4882a593Smuzhiyun sys.exit(1) 501*4882a593Smuzhiyun parse_pkgdatafile(pkgdatafile, args.long) 502*4882a593Smuzhiyun 503*4882a593Smuzhiyundef find_path(args): 504*4882a593Smuzhiyun import json 505*4882a593Smuzhiyun 506*4882a593Smuzhiyun found = False 507*4882a593Smuzhiyun for root, dirs, files in os.walk(os.path.join(args.pkgdata_dir, 'runtime')): 508*4882a593Smuzhiyun for fn in files: 509*4882a593Smuzhiyun with open(os.path.join(root,fn)) as f: 510*4882a593Smuzhiyun for line in f: 511*4882a593Smuzhiyun if line.startswith('FILES_INFO:'): 512*4882a593Smuzhiyun val = line.split(': ', 1)[1].strip() 513*4882a593Smuzhiyun dictval = json.loads(val) 514*4882a593Smuzhiyun for fullpth in dictval.keys(): 515*4882a593Smuzhiyun if fnmatch.fnmatchcase(fullpth, args.targetpath): 516*4882a593Smuzhiyun found = True 517*4882a593Smuzhiyun print("%s: %s" % (fn, fullpth)) 518*4882a593Smuzhiyun break 519*4882a593Smuzhiyun if not found: 520*4882a593Smuzhiyun logger.error("Unable to find any package producing path %s" % args.targetpath) 521*4882a593Smuzhiyun sys.exit(1) 522*4882a593Smuzhiyun 523*4882a593Smuzhiyun 524*4882a593Smuzhiyundef main(): 525*4882a593Smuzhiyun parser = argparse_oe.ArgumentParser(description="OpenEmbedded pkgdata tool - queries the pkgdata files written out during do_package", 526*4882a593Smuzhiyun epilog="Use %(prog)s <subcommand> --help to get help on a specific command") 527*4882a593Smuzhiyun parser.add_argument('-d', '--debug', help='Enable debug output', action='store_true') 528*4882a593Smuzhiyun parser.add_argument('-p', '--pkgdata-dir', help='Path to pkgdata directory (determined automatically if not specified)') 529*4882a593Smuzhiyun subparsers = parser.add_subparsers(title='subcommands', metavar='<subcommand>') 530*4882a593Smuzhiyun subparsers.required = True 531*4882a593Smuzhiyun 532*4882a593Smuzhiyun parser_lookup_pkg = subparsers.add_parser('lookup-pkg', 533*4882a593Smuzhiyun help='Translate between recipe-space package names and runtime package names', 534*4882a593Smuzhiyun description='Looks up the specified recipe-space package name(s) to see what the final runtime package name is (e.g. glibc becomes libc6), or with -r/--reverse looks up the other way.') 535*4882a593Smuzhiyun parser_lookup_pkg.add_argument('pkg', nargs='+', help='Package name to look up') 536*4882a593Smuzhiyun parser_lookup_pkg.add_argument('-r', '--reverse', help='Switch to looking up recipe-space package names from runtime package names', action='store_true') 537*4882a593Smuzhiyun parser_lookup_pkg.set_defaults(func=lookup_pkg) 538*4882a593Smuzhiyun 539*4882a593Smuzhiyun parser_list_pkgs = subparsers.add_parser('list-pkgs', 540*4882a593Smuzhiyun help='List packages', 541*4882a593Smuzhiyun description='Lists packages that have been built') 542*4882a593Smuzhiyun parser_list_pkgs.add_argument('pkgspec', nargs='*', help='Package name to search for (wildcards * ? allowed, use quotes to avoid shell expansion)') 543*4882a593Smuzhiyun parser_list_pkgs.add_argument('-r', '--runtime', help='Show runtime package names instead of recipe-space package names', action='store_true') 544*4882a593Smuzhiyun parser_list_pkgs.add_argument('-p', '--recipe', help='Limit to packages produced by the specified recipe') 545*4882a593Smuzhiyun parser_list_pkgs.add_argument('-u', '--unpackaged', help='Include unpackaged (i.e. empty) packages', action='store_true') 546*4882a593Smuzhiyun parser_list_pkgs.set_defaults(func=list_pkgs) 547*4882a593Smuzhiyun 548*4882a593Smuzhiyun parser_list_pkg_files = subparsers.add_parser('list-pkg-files', 549*4882a593Smuzhiyun help='List files within a package', 550*4882a593Smuzhiyun description='Lists files included in one or more packages') 551*4882a593Smuzhiyun parser_list_pkg_files.add_argument('pkg', nargs='*', help='Package name to report on (if -p/--recipe is not specified)') 552*4882a593Smuzhiyun parser_list_pkg_files.add_argument('-r', '--runtime', help='Specified package(s) are runtime package names instead of recipe-space package names', action='store_true') 553*4882a593Smuzhiyun parser_list_pkg_files.add_argument('-p', '--recipe', help='Report on all packages produced by the specified recipe') 554*4882a593Smuzhiyun parser_list_pkg_files.add_argument('-u', '--unpackaged', help='Include unpackaged (i.e. empty) packages (only useful with -p/--recipe)', action='store_true') 555*4882a593Smuzhiyun parser_list_pkg_files.add_argument('-l', '--long', help='Show more information per file', action='store_true') 556*4882a593Smuzhiyun parser_list_pkg_files.set_defaults(func=list_pkg_files) 557*4882a593Smuzhiyun 558*4882a593Smuzhiyun parser_lookup_recipe = subparsers.add_parser('lookup-recipe', 559*4882a593Smuzhiyun help='Find recipe producing one or more packages', 560*4882a593Smuzhiyun description='Looks up the specified runtime package(s) to see which recipe they were produced by') 561*4882a593Smuzhiyun parser_lookup_recipe.add_argument('pkg', nargs='+', help='Runtime package name to look up') 562*4882a593Smuzhiyun parser_lookup_recipe.add_argument('-c', '--continue', dest="carryon", help='Continue looking up recipes even if we can not find one', action='store_true') 563*4882a593Smuzhiyun parser_lookup_recipe.set_defaults(func=lookup_recipe) 564*4882a593Smuzhiyun 565*4882a593Smuzhiyun parser_package_info = subparsers.add_parser('package-info', 566*4882a593Smuzhiyun help='Show version, recipe and size information for one or more packages', 567*4882a593Smuzhiyun description='Looks up the specified runtime package(s) and display information') 568*4882a593Smuzhiyun parser_package_info.add_argument('pkg', nargs='*', help='Runtime package name to look up') 569*4882a593Smuzhiyun parser_package_info.add_argument('-f', '--file', help='Read package names from the specified file (one per line, first field only)') 570*4882a593Smuzhiyun parser_package_info.add_argument('-e', '--extra', help='Extra variables to display, e.g., LICENSE (can be specified multiple times)', action='append') 571*4882a593Smuzhiyun parser_package_info.set_defaults(func=package_info) 572*4882a593Smuzhiyun 573*4882a593Smuzhiyun parser_find_path = subparsers.add_parser('find-path', 574*4882a593Smuzhiyun help='Find package providing a target path', 575*4882a593Smuzhiyun description='Finds the recipe-space package providing the specified target path') 576*4882a593Smuzhiyun parser_find_path.add_argument('targetpath', help='Path to find (wildcards * ? allowed, use quotes to avoid shell expansion)') 577*4882a593Smuzhiyun parser_find_path.set_defaults(func=find_path) 578*4882a593Smuzhiyun 579*4882a593Smuzhiyun parser_read_value = subparsers.add_parser('read-value', 580*4882a593Smuzhiyun help='Read any pkgdata value for one or more packages', 581*4882a593Smuzhiyun description='Reads the named value from the pkgdata files for the specified packages') 582*4882a593Smuzhiyun parser_read_value.add_argument('valuenames', help='Name of the value/s to look up (separated by commas, no spaces)') 583*4882a593Smuzhiyun parser_read_value.add_argument('pkg', nargs='*', help='Runtime package name to look up') 584*4882a593Smuzhiyun parser_read_value.add_argument('-f', '--file', help='Read package names from the specified file (one per line, first field only)') 585*4882a593Smuzhiyun parser_read_value.add_argument('-n', '--prefix-name', help='Prefix output with package name', action='store_true') 586*4882a593Smuzhiyun parser_read_value.add_argument('-u', '--unescape', help='Expand escapes such as \\n', action='store_true') 587*4882a593Smuzhiyun parser_read_value.set_defaults(func=read_value) 588*4882a593Smuzhiyun 589*4882a593Smuzhiyun parser_glob = subparsers.add_parser('glob', 590*4882a593Smuzhiyun help='Expand package name glob expression', 591*4882a593Smuzhiyun description='Expands one or more glob expressions over the packages listed in pkglistfile') 592*4882a593Smuzhiyun parser_glob.add_argument('pkglistfile', help='File listing packages (one package name per line)') 593*4882a593Smuzhiyun parser_glob.add_argument('glob', nargs="+", help='Glob expression for package names, e.g. *-dev') 594*4882a593Smuzhiyun parser_glob.add_argument('-x', '--exclude', help='Exclude packages matching specified regex from the glob operation') 595*4882a593Smuzhiyun parser_glob.set_defaults(func=glob) 596*4882a593Smuzhiyun 597*4882a593Smuzhiyun 598*4882a593Smuzhiyun args = parser.parse_args() 599*4882a593Smuzhiyun 600*4882a593Smuzhiyun if args.debug: 601*4882a593Smuzhiyun logger.setLevel(logging.DEBUG) 602*4882a593Smuzhiyun 603*4882a593Smuzhiyun if not args.pkgdata_dir: 604*4882a593Smuzhiyun import scriptpath 605*4882a593Smuzhiyun bitbakepath = scriptpath.add_bitbake_lib_path() 606*4882a593Smuzhiyun if not bitbakepath: 607*4882a593Smuzhiyun logger.error("Unable to find bitbake by searching parent directory of this script or PATH") 608*4882a593Smuzhiyun sys.exit(1) 609*4882a593Smuzhiyun logger.debug('Found bitbake path: %s' % bitbakepath) 610*4882a593Smuzhiyun if not os.environ.get('BUILDDIR', ''): 611*4882a593Smuzhiyun logger.error("This script can only be run after initialising the build environment (e.g. by using oe-init-build-env)") 612*4882a593Smuzhiyun sys.exit(1) 613*4882a593Smuzhiyun tinfoil = tinfoil_init() 614*4882a593Smuzhiyun try: 615*4882a593Smuzhiyun args.pkgdata_dir = tinfoil.config_data.getVar('PKGDATA_DIR') 616*4882a593Smuzhiyun finally: 617*4882a593Smuzhiyun tinfoil.shutdown() 618*4882a593Smuzhiyun logger.debug('Value of PKGDATA_DIR is "%s"' % args.pkgdata_dir) 619*4882a593Smuzhiyun if not args.pkgdata_dir: 620*4882a593Smuzhiyun logger.error('Unable to determine pkgdata directory from PKGDATA_DIR') 621*4882a593Smuzhiyun sys.exit(1) 622*4882a593Smuzhiyun 623*4882a593Smuzhiyun if not os.path.exists(args.pkgdata_dir): 624*4882a593Smuzhiyun logger.error('Unable to find pkgdata directory %s' % args.pkgdata_dir) 625*4882a593Smuzhiyun sys.exit(1) 626*4882a593Smuzhiyun 627*4882a593Smuzhiyun ret = args.func(args) 628*4882a593Smuzhiyun 629*4882a593Smuzhiyun return ret 630*4882a593Smuzhiyun 631*4882a593Smuzhiyun 632*4882a593Smuzhiyunif __name__ == "__main__": 633*4882a593Smuzhiyun main() 634