xref: /OK3568_Linux_fs/yocto/poky/scripts/lib/devtool/utilcmds.py (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun# Development tool - utility commands plugin
2*4882a593Smuzhiyun#
3*4882a593Smuzhiyun# Copyright (C) 2015-2016 Intel Corporation
4*4882a593Smuzhiyun#
5*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0-only
6*4882a593Smuzhiyun#
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun"""Devtool utility plugins"""
9*4882a593Smuzhiyun
10*4882a593Smuzhiyunimport os
11*4882a593Smuzhiyunimport sys
12*4882a593Smuzhiyunimport shutil
13*4882a593Smuzhiyunimport tempfile
14*4882a593Smuzhiyunimport logging
15*4882a593Smuzhiyunimport argparse
16*4882a593Smuzhiyunimport subprocess
17*4882a593Smuzhiyunimport scriptutils
18*4882a593Smuzhiyunfrom devtool import exec_build_env_command, setup_tinfoil, check_workspace_recipe, DevtoolError
19*4882a593Smuzhiyunfrom devtool import parse_recipe
20*4882a593Smuzhiyun
21*4882a593Smuzhiyunlogger = logging.getLogger('devtool')
22*4882a593Smuzhiyun
23*4882a593Smuzhiyundef _find_recipe_path(args, config, basepath, workspace):
24*4882a593Smuzhiyun    if args.any_recipe:
25*4882a593Smuzhiyun        logger.warning('-a/--any-recipe option is now always active, and thus the option will be removed in a future release')
26*4882a593Smuzhiyun    if args.recipename in workspace:
27*4882a593Smuzhiyun        recipefile = workspace[args.recipename]['recipefile']
28*4882a593Smuzhiyun    else:
29*4882a593Smuzhiyun        recipefile = None
30*4882a593Smuzhiyun    if not recipefile:
31*4882a593Smuzhiyun        tinfoil = setup_tinfoil(config_only=False, basepath=basepath)
32*4882a593Smuzhiyun        try:
33*4882a593Smuzhiyun            rd = parse_recipe(config, tinfoil, args.recipename, True)
34*4882a593Smuzhiyun            if not rd:
35*4882a593Smuzhiyun                raise DevtoolError("Failed to find specified recipe")
36*4882a593Smuzhiyun            recipefile = rd.getVar('FILE')
37*4882a593Smuzhiyun        finally:
38*4882a593Smuzhiyun            tinfoil.shutdown()
39*4882a593Smuzhiyun    return recipefile
40*4882a593Smuzhiyun
41*4882a593Smuzhiyun
42*4882a593Smuzhiyundef find_recipe(args, config, basepath, workspace):
43*4882a593Smuzhiyun    """Entry point for the devtool 'find-recipe' subcommand"""
44*4882a593Smuzhiyun    recipefile = _find_recipe_path(args, config, basepath, workspace)
45*4882a593Smuzhiyun    print(recipefile)
46*4882a593Smuzhiyun    return 0
47*4882a593Smuzhiyun
48*4882a593Smuzhiyun
49*4882a593Smuzhiyundef edit_recipe(args, config, basepath, workspace):
50*4882a593Smuzhiyun    """Entry point for the devtool 'edit-recipe' subcommand"""
51*4882a593Smuzhiyun    return scriptutils.run_editor(_find_recipe_path(args, config, basepath, workspace), logger)
52*4882a593Smuzhiyun
53*4882a593Smuzhiyun
54*4882a593Smuzhiyundef configure_help(args, config, basepath, workspace):
55*4882a593Smuzhiyun    """Entry point for the devtool 'configure-help' subcommand"""
56*4882a593Smuzhiyun    import oe.utils
57*4882a593Smuzhiyun
58*4882a593Smuzhiyun    check_workspace_recipe(workspace, args.recipename)
59*4882a593Smuzhiyun    tinfoil = setup_tinfoil(config_only=False, basepath=basepath)
60*4882a593Smuzhiyun    try:
61*4882a593Smuzhiyun        rd = parse_recipe(config, tinfoil, args.recipename, appends=True, filter_workspace=False)
62*4882a593Smuzhiyun        if not rd:
63*4882a593Smuzhiyun            return 1
64*4882a593Smuzhiyun        b = rd.getVar('B')
65*4882a593Smuzhiyun        s = rd.getVar('S')
66*4882a593Smuzhiyun        configurescript = os.path.join(s, 'configure')
67*4882a593Smuzhiyun        confdisabled = 'noexec' in rd.getVarFlags('do_configure') or 'do_configure' not in (rd.getVar('__BBTASKS', False) or [])
68*4882a593Smuzhiyun        configureopts = oe.utils.squashspaces(rd.getVar('CONFIGUREOPTS') or '')
69*4882a593Smuzhiyun        extra_oeconf = oe.utils.squashspaces(rd.getVar('EXTRA_OECONF') or '')
70*4882a593Smuzhiyun        extra_oecmake = oe.utils.squashspaces(rd.getVar('EXTRA_OECMAKE') or '')
71*4882a593Smuzhiyun        do_configure = rd.getVar('do_configure') or ''
72*4882a593Smuzhiyun        do_configure_noexpand = rd.getVar('do_configure', False) or ''
73*4882a593Smuzhiyun        packageconfig = rd.getVarFlags('PACKAGECONFIG') or []
74*4882a593Smuzhiyun        autotools = bb.data.inherits_class('autotools', rd) and ('oe_runconf' in do_configure or 'autotools_do_configure' in do_configure)
75*4882a593Smuzhiyun        cmake = bb.data.inherits_class('cmake', rd) and ('cmake_do_configure' in do_configure)
76*4882a593Smuzhiyun        cmake_do_configure = rd.getVar('cmake_do_configure')
77*4882a593Smuzhiyun        pn = rd.getVar('PN')
78*4882a593Smuzhiyun    finally:
79*4882a593Smuzhiyun        tinfoil.shutdown()
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun    if 'doc' in packageconfig:
82*4882a593Smuzhiyun        del packageconfig['doc']
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun    if autotools and not os.path.exists(configurescript):
85*4882a593Smuzhiyun        logger.info('Running do_configure to generate configure script')
86*4882a593Smuzhiyun        try:
87*4882a593Smuzhiyun            stdout, _ = exec_build_env_command(config.init_path, basepath,
88*4882a593Smuzhiyun                                               'bitbake -c configure %s' % args.recipename,
89*4882a593Smuzhiyun                                               stderr=subprocess.STDOUT)
90*4882a593Smuzhiyun        except bb.process.ExecutionError:
91*4882a593Smuzhiyun            pass
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun    if confdisabled or do_configure.strip() in ('', ':'):
94*4882a593Smuzhiyun        raise DevtoolError("do_configure task has been disabled for this recipe")
95*4882a593Smuzhiyun    elif args.no_pager and not os.path.exists(configurescript):
96*4882a593Smuzhiyun        raise DevtoolError("No configure script found and no other information to display")
97*4882a593Smuzhiyun    else:
98*4882a593Smuzhiyun        configopttext = ''
99*4882a593Smuzhiyun        if autotools and configureopts:
100*4882a593Smuzhiyun            configopttext = '''
101*4882a593SmuzhiyunArguments currently passed to the configure script:
102*4882a593Smuzhiyun
103*4882a593Smuzhiyun%s
104*4882a593Smuzhiyun
105*4882a593SmuzhiyunSome of those are fixed.''' % (configureopts + ' ' + extra_oeconf)
106*4882a593Smuzhiyun            if extra_oeconf:
107*4882a593Smuzhiyun                configopttext += ''' The ones that are specified through EXTRA_OECONF (which you can change or add to easily):
108*4882a593Smuzhiyun
109*4882a593Smuzhiyun%s''' % extra_oeconf
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun        elif cmake:
112*4882a593Smuzhiyun            in_cmake = False
113*4882a593Smuzhiyun            cmake_cmd = ''
114*4882a593Smuzhiyun            for line in cmake_do_configure.splitlines():
115*4882a593Smuzhiyun                if in_cmake:
116*4882a593Smuzhiyun                    cmake_cmd = cmake_cmd + ' ' + line.strip().rstrip('\\')
117*4882a593Smuzhiyun                    if not line.endswith('\\'):
118*4882a593Smuzhiyun                        break
119*4882a593Smuzhiyun                if line.lstrip().startswith('cmake '):
120*4882a593Smuzhiyun                    cmake_cmd = line.strip().rstrip('\\')
121*4882a593Smuzhiyun                    if line.endswith('\\'):
122*4882a593Smuzhiyun                        in_cmake = True
123*4882a593Smuzhiyun                    else:
124*4882a593Smuzhiyun                        break
125*4882a593Smuzhiyun            if cmake_cmd:
126*4882a593Smuzhiyun                configopttext = '''
127*4882a593SmuzhiyunThe current cmake command line:
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun%s
130*4882a593Smuzhiyun
131*4882a593SmuzhiyunArguments specified through EXTRA_OECMAKE (which you can change or add to easily)
132*4882a593Smuzhiyun
133*4882a593Smuzhiyun%s''' % (oe.utils.squashspaces(cmake_cmd), extra_oecmake)
134*4882a593Smuzhiyun            else:
135*4882a593Smuzhiyun                configopttext = '''
136*4882a593SmuzhiyunThe current implementation of cmake_do_configure:
137*4882a593Smuzhiyun
138*4882a593Smuzhiyuncmake_do_configure() {
139*4882a593Smuzhiyun%s
140*4882a593Smuzhiyun}
141*4882a593Smuzhiyun
142*4882a593SmuzhiyunArguments specified through EXTRA_OECMAKE (which you can change or add to easily)
143*4882a593Smuzhiyun
144*4882a593Smuzhiyun%s''' % (cmake_do_configure.rstrip(), extra_oecmake)
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun        elif do_configure:
147*4882a593Smuzhiyun            configopttext = '''
148*4882a593SmuzhiyunThe current implementation of do_configure:
149*4882a593Smuzhiyun
150*4882a593Smuzhiyundo_configure() {
151*4882a593Smuzhiyun%s
152*4882a593Smuzhiyun}''' % do_configure.rstrip()
153*4882a593Smuzhiyun            if '${EXTRA_OECONF}' in do_configure_noexpand:
154*4882a593Smuzhiyun                configopttext += '''
155*4882a593Smuzhiyun
156*4882a593SmuzhiyunArguments specified through EXTRA_OECONF (which you can change or add to easily):
157*4882a593Smuzhiyun
158*4882a593Smuzhiyun%s''' % extra_oeconf
159*4882a593Smuzhiyun
160*4882a593Smuzhiyun        if packageconfig:
161*4882a593Smuzhiyun            configopttext += '''
162*4882a593Smuzhiyun
163*4882a593SmuzhiyunSome of these options may be controlled through PACKAGECONFIG; for more details please see the recipe.'''
164*4882a593Smuzhiyun
165*4882a593Smuzhiyun        if args.arg:
166*4882a593Smuzhiyun            helpargs = ' '.join(args.arg)
167*4882a593Smuzhiyun        elif cmake:
168*4882a593Smuzhiyun            helpargs = '-LH'
169*4882a593Smuzhiyun        else:
170*4882a593Smuzhiyun            helpargs = '--help'
171*4882a593Smuzhiyun
172*4882a593Smuzhiyun        msg = '''configure information for %s
173*4882a593Smuzhiyun------------------------------------------
174*4882a593Smuzhiyun%s''' % (pn, configopttext)
175*4882a593Smuzhiyun
176*4882a593Smuzhiyun        if cmake:
177*4882a593Smuzhiyun            msg += '''
178*4882a593Smuzhiyun
179*4882a593SmuzhiyunThe cmake %s output for %s follows. After "-- Cache values" you should see a list of variables you can add to EXTRA_OECMAKE (prefixed with -D and suffixed with = followed by the desired value, without any spaces).
180*4882a593Smuzhiyun------------------------------------------''' % (helpargs, pn)
181*4882a593Smuzhiyun        elif os.path.exists(configurescript):
182*4882a593Smuzhiyun            msg += '''
183*4882a593Smuzhiyun
184*4882a593SmuzhiyunThe ./configure %s output for %s follows.
185*4882a593Smuzhiyun------------------------------------------''' % (helpargs, pn)
186*4882a593Smuzhiyun
187*4882a593Smuzhiyun        olddir = os.getcwd()
188*4882a593Smuzhiyun        tmppath = tempfile.mkdtemp()
189*4882a593Smuzhiyun        with tempfile.NamedTemporaryFile('w', delete=False) as tf:
190*4882a593Smuzhiyun            if not args.no_header:
191*4882a593Smuzhiyun                tf.write(msg + '\n')
192*4882a593Smuzhiyun            tf.close()
193*4882a593Smuzhiyun            try:
194*4882a593Smuzhiyun                try:
195*4882a593Smuzhiyun                    cmd = 'cat %s' % tf.name
196*4882a593Smuzhiyun                    if cmake:
197*4882a593Smuzhiyun                        cmd += '; cmake %s %s 2>&1' % (helpargs, s)
198*4882a593Smuzhiyun                        os.chdir(b)
199*4882a593Smuzhiyun                    elif os.path.exists(configurescript):
200*4882a593Smuzhiyun                        cmd += '; %s %s' % (configurescript, helpargs)
201*4882a593Smuzhiyun                    if sys.stdout.isatty() and not args.no_pager:
202*4882a593Smuzhiyun                        pager = os.environ.get('PAGER', 'less')
203*4882a593Smuzhiyun                        cmd = '(%s) | %s' % (cmd, pager)
204*4882a593Smuzhiyun                    subprocess.check_call(cmd, shell=True)
205*4882a593Smuzhiyun                except subprocess.CalledProcessError as e:
206*4882a593Smuzhiyun                    return e.returncode
207*4882a593Smuzhiyun            finally:
208*4882a593Smuzhiyun                os.chdir(olddir)
209*4882a593Smuzhiyun                shutil.rmtree(tmppath)
210*4882a593Smuzhiyun                os.remove(tf.name)
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun
213*4882a593Smuzhiyundef register_commands(subparsers, context):
214*4882a593Smuzhiyun    """Register devtool subcommands from this plugin"""
215*4882a593Smuzhiyun    parser_edit_recipe = subparsers.add_parser('edit-recipe', help='Edit a recipe file',
216*4882a593Smuzhiyun                                         description='Runs the default editor (as specified by the EDITOR variable) on the specified recipe. Note that this will be quicker for recipes in the workspace as the cache does not need to be loaded in that case.',
217*4882a593Smuzhiyun                                         group='working')
218*4882a593Smuzhiyun    parser_edit_recipe.add_argument('recipename', help='Recipe to edit')
219*4882a593Smuzhiyun    # FIXME drop -a at some point in future
220*4882a593Smuzhiyun    parser_edit_recipe.add_argument('--any-recipe', '-a', action="store_true", help='Does nothing (exists for backwards-compatibility)')
221*4882a593Smuzhiyun    parser_edit_recipe.set_defaults(func=edit_recipe)
222*4882a593Smuzhiyun
223*4882a593Smuzhiyun    # Find-recipe
224*4882a593Smuzhiyun    parser_find_recipe = subparsers.add_parser('find-recipe', help='Find a recipe file',
225*4882a593Smuzhiyun                                         description='Finds a recipe file. Note that this will be quicker for recipes in the workspace as the cache does not need to be loaded in that case.',
226*4882a593Smuzhiyun                                         group='working')
227*4882a593Smuzhiyun    parser_find_recipe.add_argument('recipename', help='Recipe to find')
228*4882a593Smuzhiyun    # FIXME drop -a at some point in future
229*4882a593Smuzhiyun    parser_find_recipe.add_argument('--any-recipe', '-a', action="store_true", help='Does nothing (exists for backwards-compatibility)')
230*4882a593Smuzhiyun    parser_find_recipe.set_defaults(func=find_recipe)
231*4882a593Smuzhiyun
232*4882a593Smuzhiyun    # NOTE: Needed to override the usage string here since the default
233*4882a593Smuzhiyun    # gets the order wrong - recipename must come before --arg
234*4882a593Smuzhiyun    parser_configure_help = subparsers.add_parser('configure-help', help='Get help on configure script options',
235*4882a593Smuzhiyun                                         usage='devtool configure-help [options] recipename [--arg ...]',
236*4882a593Smuzhiyun                                         description='Displays the help for the configure script for the specified recipe (i.e. runs ./configure --help) prefaced by a header describing the current options being specified. Output is piped through less (or whatever PAGER is set to, if set) for easy browsing.',
237*4882a593Smuzhiyun                                         group='working')
238*4882a593Smuzhiyun    parser_configure_help.add_argument('recipename', help='Recipe to show configure help for')
239*4882a593Smuzhiyun    parser_configure_help.add_argument('-p', '--no-pager', help='Disable paged output', action="store_true")
240*4882a593Smuzhiyun    parser_configure_help.add_argument('-n', '--no-header', help='Disable explanatory header text', action="store_true")
241*4882a593Smuzhiyun    parser_configure_help.add_argument('--arg', help='Pass remaining arguments to the configure script instead of --help (useful if the script has additional help options)', nargs=argparse.REMAINDER)
242*4882a593Smuzhiyun    parser_configure_help.set_defaults(func=configure_help)
243