1#!/usr/bin/env python3
2
3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by
5# the Free Software Foundation; either version 2 of the License, or
6# (at your option) any later version.
7#
8# This program is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11# GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License
14# along with this program; if not, write to the Free Software Foundation.
15#
16# Copyright (C) 2013 Wind River Systems, Inc.
17# Copyright (C) 2014 Intel Corporation
18#
19# - list available recipes which have PACKAGECONFIG flags
20# - list available PACKAGECONFIG flags and all affected recipes
21# - list all recipes and PACKAGECONFIG information
22
23import sys
24import optparse
25import os
26
27
28scripts_path = os.path.abspath(os.path.dirname(os.path.abspath(sys.argv[0])))
29lib_path = os.path.abspath(scripts_path + '/../lib')
30sys.path = sys.path + [lib_path]
31
32import scriptpath
33
34# For importing the following modules
35bitbakepath = scriptpath.add_bitbake_lib_path()
36if not bitbakepath:
37    sys.stderr.write("Unable to find bitbake by searching parent directory of this script or PATH\n")
38    sys.exit(1)
39
40import bb.cooker
41import bb.providers
42import bb.tinfoil
43
44def get_fnlist(bbhandler, pkg_pn, preferred):
45    ''' Get all recipe file names '''
46    if preferred:
47        (latest_versions, preferred_versions) = bb.providers.findProviders(bbhandler.config_data, bbhandler.cooker.recipecaches[''], pkg_pn)
48
49    fn_list = []
50    for pn in sorted(pkg_pn):
51        if preferred:
52            fn_list.append(preferred_versions[pn][1])
53        else:
54            fn_list.extend(pkg_pn[pn])
55
56    return fn_list
57
58def get_recipesdata(bbhandler, preferred):
59    ''' Get data of all available recipes which have PACKAGECONFIG flags '''
60    pkg_pn = bbhandler.cooker.recipecaches[''].pkg_pn
61
62    data_dict = {}
63    for fn in get_fnlist(bbhandler, pkg_pn, preferred):
64        data = bbhandler.parse_recipe_file(fn)
65        flags = data.getVarFlags("PACKAGECONFIG")
66        flags.pop('doc', None)
67        if flags:
68            data_dict[fn] = data
69
70    return data_dict
71
72def collect_pkgs(data_dict):
73    ''' Collect available pkgs in which have PACKAGECONFIG flags '''
74    # pkg_dict = {'pkg1': ['flag1', 'flag2',...]}
75    pkg_dict = {}
76    for fn in data_dict:
77        pkgconfigflags = data_dict[fn].getVarFlags("PACKAGECONFIG")
78        pkgconfigflags.pop('doc', None)
79        pkgname = data_dict[fn].getVar("P")
80        pkg_dict[pkgname] = sorted(pkgconfigflags.keys())
81
82    return pkg_dict
83
84def collect_flags(pkg_dict):
85    ''' Collect available PACKAGECONFIG flags and all affected pkgs '''
86    # flag_dict = {'flag': ['pkg1', 'pkg2',...]}
87    flag_dict = {}
88    for pkgname, flaglist in pkg_dict.items():
89        for flag in flaglist:
90            if flag in flag_dict:
91                flag_dict[flag].append(pkgname)
92            else:
93                flag_dict[flag] = [pkgname]
94
95    return flag_dict
96
97def display_pkgs(pkg_dict):
98    ''' Display available pkgs which have PACKAGECONFIG flags '''
99    pkgname_len = len("RECIPE NAME") + 1
100    for pkgname in pkg_dict:
101        if pkgname_len < len(pkgname):
102            pkgname_len = len(pkgname)
103    pkgname_len += 1
104
105    header = '%-*s%s' % (pkgname_len, str("RECIPE NAME"), str("PACKAGECONFIG FLAGS"))
106    print(header)
107    print(str("").ljust(len(header), '='))
108    for pkgname in sorted(pkg_dict):
109        print('%-*s%s' % (pkgname_len, pkgname, ' '.join(pkg_dict[pkgname])))
110
111
112def display_flags(flag_dict):
113    ''' Display available PACKAGECONFIG flags and all affected pkgs '''
114    flag_len = len("PACKAGECONFIG FLAG") + 5
115
116    header = '%-*s%s' % (flag_len, str("PACKAGECONFIG FLAG"), str("RECIPE NAMES"))
117    print(header)
118    print(str("").ljust(len(header), '='))
119
120    for flag in sorted(flag_dict):
121        print('%-*s%s' % (flag_len, flag, '  '.join(sorted(flag_dict[flag]))))
122
123def display_all(data_dict):
124    ''' Display all pkgs and PACKAGECONFIG information '''
125    print(str("").ljust(50, '='))
126    for fn in data_dict:
127        print('%s' % data_dict[fn].getVar("P"))
128        print(fn)
129        packageconfig = data_dict[fn].getVar("PACKAGECONFIG") or ''
130        if packageconfig.strip() == '':
131            packageconfig = 'None'
132        print('PACKAGECONFIG %s' % packageconfig)
133
134        for flag,flag_val in data_dict[fn].getVarFlags("PACKAGECONFIG").items():
135            if flag == "doc":
136                continue
137            print('PACKAGECONFIG[%s] %s' % (flag, flag_val))
138        print('')
139
140def main():
141    pkg_dict = {}
142    flag_dict = {}
143
144    # Collect and validate input
145    parser = optparse.OptionParser(
146        description = "Lists recipes and PACKAGECONFIG flags. Without -a or -f, recipes and their available PACKAGECONFIG flags are listed.",
147        usage = """
148    %prog [options]""")
149
150    parser.add_option("-f", "--flags",
151            help = "list available PACKAGECONFIG flags and affected recipes",
152            action="store_const", dest="listtype", const="flags", default="recipes")
153    parser.add_option("-a", "--all",
154            help = "list all recipes and PACKAGECONFIG information",
155            action="store_const", dest="listtype", const="all")
156    parser.add_option("-p", "--preferred-only",
157            help = "where multiple recipe versions are available, list only the preferred version",
158            action="store_true", dest="preferred", default=False)
159
160    options, args = parser.parse_args(sys.argv)
161
162    with bb.tinfoil.Tinfoil() as bbhandler:
163        bbhandler.prepare()
164        print("Gathering recipe data...")
165        data_dict = get_recipesdata(bbhandler, options.preferred)
166
167        if options.listtype == 'flags':
168            pkg_dict = collect_pkgs(data_dict)
169            flag_dict = collect_flags(pkg_dict)
170            display_flags(flag_dict)
171        elif options.listtype == 'recipes':
172            pkg_dict = collect_pkgs(data_dict)
173            display_pkgs(pkg_dict)
174        elif options.listtype == 'all':
175            display_all(data_dict)
176
177if __name__ == "__main__":
178    main()
179