xref: /OK3568_Linux_fs/yocto/scripts/contrib/image-manifest (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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