xref: /netbsd-src/external/apache2/llvm/dist/clang/tools/scan-build-py/libscanbuild/arguments.py (revision e038c9c4676b0f19b1b7dd08a940c6ed64a6d5ae)
17330f729Sjoerg# -*- coding: utf-8 -*-
27330f729Sjoerg# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
37330f729Sjoerg# See https://llvm.org/LICENSE.txt for license information.
47330f729Sjoerg# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
57330f729Sjoerg""" This module parses and validates arguments for command-line interfaces.
67330f729Sjoerg
77330f729SjoergIt uses argparse module to create the command line parser. (This library is
87330f729Sjoergin the standard python library since 3.2 and backported to 2.7, but not
97330f729Sjoergearlier.)
107330f729Sjoerg
117330f729SjoergIt also implements basic validation methods, related to the command.
127330f729SjoergValidations are mostly calling specific help methods, or mangling values.
137330f729Sjoerg"""
147330f729Sjoergfrom __future__ import absolute_import, division, print_function
157330f729Sjoerg
167330f729Sjoergimport os
177330f729Sjoergimport sys
187330f729Sjoergimport argparse
197330f729Sjoergimport logging
207330f729Sjoergimport tempfile
217330f729Sjoergfrom libscanbuild import reconfigure_logging, CtuConfig
227330f729Sjoergfrom libscanbuild.clang import get_checkers, is_ctu_capable
237330f729Sjoerg
247330f729Sjoerg__all__ = ['parse_args_for_intercept_build', 'parse_args_for_analyze_build',
257330f729Sjoerg           'parse_args_for_scan_build']
267330f729Sjoerg
277330f729Sjoerg
287330f729Sjoergdef parse_args_for_intercept_build():
297330f729Sjoerg    """ Parse and validate command-line arguments for intercept-build. """
307330f729Sjoerg
317330f729Sjoerg    parser = create_intercept_parser()
327330f729Sjoerg    args = parser.parse_args()
337330f729Sjoerg
347330f729Sjoerg    reconfigure_logging(args.verbose)
357330f729Sjoerg    logging.debug('Raw arguments %s', sys.argv)
367330f729Sjoerg
377330f729Sjoerg    # short validation logic
387330f729Sjoerg    if not args.build:
397330f729Sjoerg        parser.error(message='missing build command')
407330f729Sjoerg
417330f729Sjoerg    logging.debug('Parsed arguments: %s', args)
427330f729Sjoerg    return args
437330f729Sjoerg
447330f729Sjoerg
457330f729Sjoergdef parse_args_for_analyze_build():
467330f729Sjoerg    """ Parse and validate command-line arguments for analyze-build. """
477330f729Sjoerg
487330f729Sjoerg    from_build_command = False
497330f729Sjoerg    parser = create_analyze_parser(from_build_command)
507330f729Sjoerg    args = parser.parse_args()
517330f729Sjoerg
527330f729Sjoerg    reconfigure_logging(args.verbose)
537330f729Sjoerg    logging.debug('Raw arguments %s', sys.argv)
547330f729Sjoerg
557330f729Sjoerg    normalize_args_for_analyze(args, from_build_command)
567330f729Sjoerg    validate_args_for_analyze(parser, args, from_build_command)
577330f729Sjoerg    logging.debug('Parsed arguments: %s', args)
587330f729Sjoerg    return args
597330f729Sjoerg
607330f729Sjoerg
617330f729Sjoergdef parse_args_for_scan_build():
627330f729Sjoerg    """ Parse and validate command-line arguments for scan-build. """
637330f729Sjoerg
647330f729Sjoerg    from_build_command = True
657330f729Sjoerg    parser = create_analyze_parser(from_build_command)
667330f729Sjoerg    args = parser.parse_args()
677330f729Sjoerg
687330f729Sjoerg    reconfigure_logging(args.verbose)
697330f729Sjoerg    logging.debug('Raw arguments %s', sys.argv)
707330f729Sjoerg
717330f729Sjoerg    normalize_args_for_analyze(args, from_build_command)
727330f729Sjoerg    validate_args_for_analyze(parser, args, from_build_command)
737330f729Sjoerg    logging.debug('Parsed arguments: %s', args)
747330f729Sjoerg    return args
757330f729Sjoerg
767330f729Sjoerg
777330f729Sjoergdef normalize_args_for_analyze(args, from_build_command):
787330f729Sjoerg    """ Normalize parsed arguments for analyze-build and scan-build.
797330f729Sjoerg
807330f729Sjoerg    :param args: Parsed argument object. (Will be mutated.)
817330f729Sjoerg    :param from_build_command: Boolean value tells is the command suppose
827330f729Sjoerg    to run the analyzer against a build command or a compilation db. """
837330f729Sjoerg
847330f729Sjoerg    # make plugins always a list. (it might be None when not specified.)
857330f729Sjoerg    if args.plugins is None:
867330f729Sjoerg        args.plugins = []
877330f729Sjoerg
887330f729Sjoerg    # make exclude directory list unique and absolute.
897330f729Sjoerg    uniq_excludes = set(os.path.abspath(entry) for entry in args.excludes)
907330f729Sjoerg    args.excludes = list(uniq_excludes)
917330f729Sjoerg
927330f729Sjoerg    # because shared codes for all tools, some common used methods are
937330f729Sjoerg    # expecting some argument to be present. so, instead of query the args
947330f729Sjoerg    # object about the presence of the flag, we fake it here. to make those
957330f729Sjoerg    # methods more readable. (it's an arguable choice, took it only for those
967330f729Sjoerg    # which have good default value.)
977330f729Sjoerg    if from_build_command:
987330f729Sjoerg        # add cdb parameter invisibly to make report module working.
997330f729Sjoerg        args.cdb = 'compile_commands.json'
1007330f729Sjoerg
1017330f729Sjoerg    # Make ctu_dir an abspath as it is needed inside clang
1027330f729Sjoerg    if not from_build_command and hasattr(args, 'ctu_phases') \
1037330f729Sjoerg            and hasattr(args.ctu_phases, 'dir'):
1047330f729Sjoerg        args.ctu_dir = os.path.abspath(args.ctu_dir)
1057330f729Sjoerg
1067330f729Sjoerg
1077330f729Sjoergdef validate_args_for_analyze(parser, args, from_build_command):
1087330f729Sjoerg    """ Command line parsing is done by the argparse module, but semantic
1097330f729Sjoerg    validation still needs to be done. This method is doing it for
1107330f729Sjoerg    analyze-build and scan-build commands.
1117330f729Sjoerg
1127330f729Sjoerg    :param parser: The command line parser object.
1137330f729Sjoerg    :param args: Parsed argument object.
1147330f729Sjoerg    :param from_build_command: Boolean value tells is the command suppose
1157330f729Sjoerg    to run the analyzer against a build command or a compilation db.
1167330f729Sjoerg    :return: No return value, but this call might throw when validation
1177330f729Sjoerg    fails. """
1187330f729Sjoerg
1197330f729Sjoerg    if args.help_checkers_verbose:
1207330f729Sjoerg        print_checkers(get_checkers(args.clang, args.plugins))
1217330f729Sjoerg        parser.exit(status=0)
1227330f729Sjoerg    elif args.help_checkers:
1237330f729Sjoerg        print_active_checkers(get_checkers(args.clang, args.plugins))
1247330f729Sjoerg        parser.exit(status=0)
1257330f729Sjoerg    elif from_build_command and not args.build:
1267330f729Sjoerg        parser.error(message='missing build command')
1277330f729Sjoerg    elif not from_build_command and not os.path.exists(args.cdb):
1287330f729Sjoerg        parser.error(message='compilation database is missing')
1297330f729Sjoerg
1307330f729Sjoerg    # If the user wants CTU mode
1317330f729Sjoerg    if not from_build_command and hasattr(args, 'ctu_phases') \
1327330f729Sjoerg            and hasattr(args.ctu_phases, 'dir'):
1337330f729Sjoerg        # If CTU analyze_only, the input directory should exist
1347330f729Sjoerg        if args.ctu_phases.analyze and not args.ctu_phases.collect \
1357330f729Sjoerg                and not os.path.exists(args.ctu_dir):
1367330f729Sjoerg            parser.error(message='missing CTU directory')
1377330f729Sjoerg        # Check CTU capability via checking clang-extdef-mapping
1387330f729Sjoerg        if not is_ctu_capable(args.extdef_map_cmd):
1397330f729Sjoerg            parser.error(message="""This version of clang does not support CTU
1407330f729Sjoerg            functionality or clang-extdef-mapping command not found.""")
1417330f729Sjoerg
1427330f729Sjoerg
1437330f729Sjoergdef create_intercept_parser():
1447330f729Sjoerg    """ Creates a parser for command-line arguments to 'intercept'. """
1457330f729Sjoerg
1467330f729Sjoerg    parser = create_default_parser()
1477330f729Sjoerg    parser_add_cdb(parser)
1487330f729Sjoerg
1497330f729Sjoerg    parser_add_prefer_wrapper(parser)
1507330f729Sjoerg    parser_add_compilers(parser)
1517330f729Sjoerg
1527330f729Sjoerg    advanced = parser.add_argument_group('advanced options')
1537330f729Sjoerg    group = advanced.add_mutually_exclusive_group()
1547330f729Sjoerg    group.add_argument(
1557330f729Sjoerg        '--append',
1567330f729Sjoerg        action='store_true',
1577330f729Sjoerg        help="""Extend existing compilation database with new entries.
1587330f729Sjoerg        Duplicate entries are detected and not present in the final output.
1597330f729Sjoerg        The output is not continuously updated, it's done when the build
1607330f729Sjoerg        command finished. """)
1617330f729Sjoerg
1627330f729Sjoerg    parser.add_argument(
1637330f729Sjoerg        dest='build', nargs=argparse.REMAINDER, help="""Command to run.""")
1647330f729Sjoerg    return parser
1657330f729Sjoerg
1667330f729Sjoerg
1677330f729Sjoergdef create_analyze_parser(from_build_command):
1687330f729Sjoerg    """ Creates a parser for command-line arguments to 'analyze'. """
1697330f729Sjoerg
1707330f729Sjoerg    parser = create_default_parser()
1717330f729Sjoerg
1727330f729Sjoerg    if from_build_command:
1737330f729Sjoerg        parser_add_prefer_wrapper(parser)
1747330f729Sjoerg        parser_add_compilers(parser)
1757330f729Sjoerg
1767330f729Sjoerg        parser.add_argument(
1777330f729Sjoerg            '--intercept-first',
1787330f729Sjoerg            action='store_true',
1797330f729Sjoerg            help="""Run the build commands first, intercept compiler
1807330f729Sjoerg            calls and then run the static analyzer afterwards.
1817330f729Sjoerg            Generally speaking it has better coverage on build commands.
1827330f729Sjoerg            With '--override-compiler' it use compiler wrapper, but does
1837330f729Sjoerg            not run the analyzer till the build is finished.""")
1847330f729Sjoerg    else:
1857330f729Sjoerg        parser_add_cdb(parser)
1867330f729Sjoerg
1877330f729Sjoerg    parser.add_argument(
1887330f729Sjoerg        '--status-bugs',
1897330f729Sjoerg        action='store_true',
1907330f729Sjoerg        help="""The exit status of '%(prog)s' is the same as the executed
1917330f729Sjoerg        build command. This option ignores the build exit status and sets to
1927330f729Sjoerg        be non zero if it found potential bugs or zero otherwise.""")
1937330f729Sjoerg    parser.add_argument(
1947330f729Sjoerg        '--exclude',
1957330f729Sjoerg        metavar='<directory>',
1967330f729Sjoerg        dest='excludes',
1977330f729Sjoerg        action='append',
1987330f729Sjoerg        default=[],
1997330f729Sjoerg        help="""Do not run static analyzer against files found in this
2007330f729Sjoerg        directory. (You can specify this option multiple times.)
2017330f729Sjoerg        Could be useful when project contains 3rd party libraries.""")
2027330f729Sjoerg
2037330f729Sjoerg    output = parser.add_argument_group('output control options')
2047330f729Sjoerg    output.add_argument(
2057330f729Sjoerg        '--output',
2067330f729Sjoerg        '-o',
2077330f729Sjoerg        metavar='<path>',
2087330f729Sjoerg        default=tempfile.gettempdir(),
2097330f729Sjoerg        help="""Specifies the output directory for analyzer reports.
2107330f729Sjoerg        Subdirectory will be created if default directory is targeted.""")
2117330f729Sjoerg    output.add_argument(
2127330f729Sjoerg        '--keep-empty',
2137330f729Sjoerg        action='store_true',
2147330f729Sjoerg        help="""Don't remove the build results directory even if no issues
2157330f729Sjoerg        were reported.""")
2167330f729Sjoerg    output.add_argument(
2177330f729Sjoerg        '--html-title',
2187330f729Sjoerg        metavar='<title>',
2197330f729Sjoerg        help="""Specify the title used on generated HTML pages.
2207330f729Sjoerg        If not specified, a default title will be used.""")
2217330f729Sjoerg    format_group = output.add_mutually_exclusive_group()
2227330f729Sjoerg    format_group.add_argument(
2237330f729Sjoerg        '--plist',
2247330f729Sjoerg        '-plist',
2257330f729Sjoerg        dest='output_format',
2267330f729Sjoerg        const='plist',
2277330f729Sjoerg        default='html',
2287330f729Sjoerg        action='store_const',
2297330f729Sjoerg        help="""Cause the results as a set of .plist files.""")
2307330f729Sjoerg    format_group.add_argument(
2317330f729Sjoerg        '--plist-html',
2327330f729Sjoerg        '-plist-html',
2337330f729Sjoerg        dest='output_format',
2347330f729Sjoerg        const='plist-html',
2357330f729Sjoerg        default='html',
2367330f729Sjoerg        action='store_const',
2377330f729Sjoerg        help="""Cause the results as a set of .html and .plist files.""")
2387330f729Sjoerg    format_group.add_argument(
2397330f729Sjoerg        '--plist-multi-file',
2407330f729Sjoerg        '-plist-multi-file',
2417330f729Sjoerg        dest='output_format',
2427330f729Sjoerg        const='plist-multi-file',
2437330f729Sjoerg        default='html',
2447330f729Sjoerg        action='store_const',
2457330f729Sjoerg        help="""Cause the results as a set of .plist files with extra
2467330f729Sjoerg        information on related files.""")
247*e038c9c4Sjoerg    format_group.add_argument(
248*e038c9c4Sjoerg        '--sarif',
249*e038c9c4Sjoerg        '-sarif',
250*e038c9c4Sjoerg        dest='output_format',
251*e038c9c4Sjoerg        const='sarif',
252*e038c9c4Sjoerg        default='html',
253*e038c9c4Sjoerg        action='store_const',
254*e038c9c4Sjoerg        help="""Cause the results as a result.sarif file.""")
255*e038c9c4Sjoerg    format_group.add_argument(
256*e038c9c4Sjoerg        '--sarif-html',
257*e038c9c4Sjoerg        '-sarif-html',
258*e038c9c4Sjoerg        dest='output_format',
259*e038c9c4Sjoerg        const='sarif-html',
260*e038c9c4Sjoerg        default='html',
261*e038c9c4Sjoerg        action='store_const',
262*e038c9c4Sjoerg        help="""Cause the results as a result.sarif file and .html files.""")
2637330f729Sjoerg
2647330f729Sjoerg    advanced = parser.add_argument_group('advanced options')
2657330f729Sjoerg    advanced.add_argument(
2667330f729Sjoerg        '--use-analyzer',
2677330f729Sjoerg        metavar='<path>',
2687330f729Sjoerg        dest='clang',
2697330f729Sjoerg        default='clang',
2707330f729Sjoerg        help="""'%(prog)s' uses the 'clang' executable relative to itself for
2717330f729Sjoerg        static analysis. One can override this behavior with this option by
2727330f729Sjoerg        using the 'clang' packaged with Xcode (on OS X) or from the PATH.""")
2737330f729Sjoerg    advanced.add_argument(
2747330f729Sjoerg        '--no-failure-reports',
2757330f729Sjoerg        '-no-failure-reports',
2767330f729Sjoerg        dest='output_failures',
2777330f729Sjoerg        action='store_false',
2787330f729Sjoerg        help="""Do not create a 'failures' subdirectory that includes analyzer
2797330f729Sjoerg        crash reports and preprocessed source files.""")
2807330f729Sjoerg    parser.add_argument(
2817330f729Sjoerg        '--analyze-headers',
2827330f729Sjoerg        action='store_true',
2837330f729Sjoerg        help="""Also analyze functions in #included files. By default, such
2847330f729Sjoerg        functions are skipped unless they are called by functions within the
2857330f729Sjoerg        main source file.""")
2867330f729Sjoerg    advanced.add_argument(
2877330f729Sjoerg        '--stats',
2887330f729Sjoerg        '-stats',
2897330f729Sjoerg        action='store_true',
2907330f729Sjoerg        help="""Generates visitation statistics for the project.""")
2917330f729Sjoerg    advanced.add_argument(
2927330f729Sjoerg        '--internal-stats',
2937330f729Sjoerg        action='store_true',
2947330f729Sjoerg        help="""Generate internal analyzer statistics.""")
2957330f729Sjoerg    advanced.add_argument(
2967330f729Sjoerg        '--maxloop',
2977330f729Sjoerg        '-maxloop',
2987330f729Sjoerg        metavar='<loop count>',
2997330f729Sjoerg        type=int,
3007330f729Sjoerg        help="""Specify the number of times a block can be visited before
3017330f729Sjoerg        giving up. Increase for more comprehensive coverage at a cost of
3027330f729Sjoerg        speed.""")
3037330f729Sjoerg    advanced.add_argument(
3047330f729Sjoerg        '--store',
3057330f729Sjoerg        '-store',
3067330f729Sjoerg        metavar='<model>',
3077330f729Sjoerg        dest='store_model',
3087330f729Sjoerg        choices=['region', 'basic'],
3097330f729Sjoerg        help="""Specify the store model used by the analyzer. 'region'
3107330f729Sjoerg        specifies a field- sensitive store model. 'basic' which is far less
3117330f729Sjoerg        precise but can more quickly analyze code. 'basic' was the default
3127330f729Sjoerg        store model for checker-0.221 and earlier.""")
3137330f729Sjoerg    advanced.add_argument(
3147330f729Sjoerg        '--constraints',
3157330f729Sjoerg        '-constraints',
3167330f729Sjoerg        metavar='<model>',
3177330f729Sjoerg        dest='constraints_model',
3187330f729Sjoerg        choices=['range', 'basic'],
3197330f729Sjoerg        help="""Specify the constraint engine used by the analyzer. Specifying
3207330f729Sjoerg        'basic' uses a simpler, less powerful constraint model used by
3217330f729Sjoerg        checker-0.160 and earlier.""")
3227330f729Sjoerg    advanced.add_argument(
3237330f729Sjoerg        '--analyzer-config',
3247330f729Sjoerg        '-analyzer-config',
3257330f729Sjoerg        metavar='<options>',
3267330f729Sjoerg        help="""Provide options to pass through to the analyzer's
3277330f729Sjoerg        -analyzer-config flag. Several options are separated with comma:
3287330f729Sjoerg        'key1=val1,key2=val2'
3297330f729Sjoerg
3307330f729Sjoerg        Available options:
3317330f729Sjoerg            stable-report-filename=true or false (default)
3327330f729Sjoerg
3337330f729Sjoerg        Switch the page naming to:
3347330f729Sjoerg        report-<filename>-<function/method name>-<id>.html
3357330f729Sjoerg        instead of report-XXXXXX.html""")
3367330f729Sjoerg    advanced.add_argument(
3377330f729Sjoerg        '--force-analyze-debug-code',
3387330f729Sjoerg        dest='force_debug',
3397330f729Sjoerg        action='store_true',
3407330f729Sjoerg        help="""Tells analyzer to enable assertions in code even if they were
3417330f729Sjoerg        disabled during compilation, enabling more precise results.""")
3427330f729Sjoerg
3437330f729Sjoerg    plugins = parser.add_argument_group('checker options')
3447330f729Sjoerg    plugins.add_argument(
3457330f729Sjoerg        '--load-plugin',
3467330f729Sjoerg        '-load-plugin',
3477330f729Sjoerg        metavar='<plugin library>',
3487330f729Sjoerg        dest='plugins',
3497330f729Sjoerg        action='append',
3507330f729Sjoerg        help="""Loading external checkers using the clang plugin interface.""")
3517330f729Sjoerg    plugins.add_argument(
3527330f729Sjoerg        '--enable-checker',
3537330f729Sjoerg        '-enable-checker',
3547330f729Sjoerg        metavar='<checker name>',
3557330f729Sjoerg        action=AppendCommaSeparated,
3567330f729Sjoerg        help="""Enable specific checker.""")
3577330f729Sjoerg    plugins.add_argument(
3587330f729Sjoerg        '--disable-checker',
3597330f729Sjoerg        '-disable-checker',
3607330f729Sjoerg        metavar='<checker name>',
3617330f729Sjoerg        action=AppendCommaSeparated,
3627330f729Sjoerg        help="""Disable specific checker.""")
3637330f729Sjoerg    plugins.add_argument(
3647330f729Sjoerg        '--help-checkers',
3657330f729Sjoerg        action='store_true',
3667330f729Sjoerg        help="""A default group of checkers is run unless explicitly disabled.
3677330f729Sjoerg        Exactly which checkers constitute the default group is a function of
3687330f729Sjoerg        the operating system in use. These can be printed with this flag.""")
3697330f729Sjoerg    plugins.add_argument(
3707330f729Sjoerg        '--help-checkers-verbose',
3717330f729Sjoerg        action='store_true',
3727330f729Sjoerg        help="""Print all available checkers and mark the enabled ones.""")
3737330f729Sjoerg
3747330f729Sjoerg    if from_build_command:
3757330f729Sjoerg        parser.add_argument(
3767330f729Sjoerg            dest='build', nargs=argparse.REMAINDER, help="""Command to run.""")
3777330f729Sjoerg    else:
3787330f729Sjoerg        ctu = parser.add_argument_group('cross translation unit analysis')
3797330f729Sjoerg        ctu_mutex_group = ctu.add_mutually_exclusive_group()
3807330f729Sjoerg        ctu_mutex_group.add_argument(
3817330f729Sjoerg            '--ctu',
3827330f729Sjoerg            action='store_const',
3837330f729Sjoerg            const=CtuConfig(collect=True, analyze=True,
3847330f729Sjoerg                            dir='', extdef_map_cmd=''),
3857330f729Sjoerg            dest='ctu_phases',
3867330f729Sjoerg            help="""Perform cross translation unit (ctu) analysis (both collect
3877330f729Sjoerg            and analyze phases) using default <ctu-dir> for temporary output.
3887330f729Sjoerg            At the end of the analysis, the temporary directory is removed.""")
3897330f729Sjoerg        ctu.add_argument(
3907330f729Sjoerg            '--ctu-dir',
3917330f729Sjoerg            metavar='<ctu-dir>',
3927330f729Sjoerg            dest='ctu_dir',
3937330f729Sjoerg            default='ctu-dir',
3947330f729Sjoerg            help="""Defines the temporary directory used between ctu
3957330f729Sjoerg            phases.""")
3967330f729Sjoerg        ctu_mutex_group.add_argument(
3977330f729Sjoerg            '--ctu-collect-only',
3987330f729Sjoerg            action='store_const',
3997330f729Sjoerg            const=CtuConfig(collect=True, analyze=False,
4007330f729Sjoerg                            dir='', extdef_map_cmd=''),
4017330f729Sjoerg            dest='ctu_phases',
4027330f729Sjoerg            help="""Perform only the collect phase of ctu.
4037330f729Sjoerg            Keep <ctu-dir> for further use.""")
4047330f729Sjoerg        ctu_mutex_group.add_argument(
4057330f729Sjoerg            '--ctu-analyze-only',
4067330f729Sjoerg            action='store_const',
4077330f729Sjoerg            const=CtuConfig(collect=False, analyze=True,
4087330f729Sjoerg                            dir='', extdef_map_cmd=''),
4097330f729Sjoerg            dest='ctu_phases',
4107330f729Sjoerg            help="""Perform only the analyze phase of ctu. <ctu-dir> should be
4117330f729Sjoerg            present and will not be removed after analysis.""")
4127330f729Sjoerg        ctu.add_argument(
4137330f729Sjoerg            '--use-extdef-map-cmd',
4147330f729Sjoerg            metavar='<path>',
4157330f729Sjoerg            dest='extdef_map_cmd',
4167330f729Sjoerg            default='clang-extdef-mapping',
4177330f729Sjoerg            help="""'%(prog)s' uses the 'clang-extdef-mapping' executable
4187330f729Sjoerg            relative to itself for generating external definition maps for
4197330f729Sjoerg            static analysis. One can override this behavior with this option
4207330f729Sjoerg            by using the 'clang-extdef-mapping' packaged with Xcode (on OS X)
4217330f729Sjoerg            or from the PATH.""")
4227330f729Sjoerg    return parser
4237330f729Sjoerg
4247330f729Sjoerg
4257330f729Sjoergdef create_default_parser():
4267330f729Sjoerg    """ Creates command line parser for all build wrapper commands. """
4277330f729Sjoerg
4287330f729Sjoerg    parser = argparse.ArgumentParser(
4297330f729Sjoerg        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
4307330f729Sjoerg
4317330f729Sjoerg    parser.add_argument(
4327330f729Sjoerg        '--verbose',
4337330f729Sjoerg        '-v',
4347330f729Sjoerg        action='count',
4357330f729Sjoerg        default=0,
4367330f729Sjoerg        help="""Enable verbose output from '%(prog)s'. A second, third and
4377330f729Sjoerg        fourth flags increases verbosity.""")
4387330f729Sjoerg    return parser
4397330f729Sjoerg
4407330f729Sjoerg
4417330f729Sjoergdef parser_add_cdb(parser):
4427330f729Sjoerg    parser.add_argument(
4437330f729Sjoerg        '--cdb',
4447330f729Sjoerg        metavar='<file>',
4457330f729Sjoerg        default="compile_commands.json",
4467330f729Sjoerg        help="""The JSON compilation database.""")
4477330f729Sjoerg
4487330f729Sjoerg
4497330f729Sjoergdef parser_add_prefer_wrapper(parser):
4507330f729Sjoerg    parser.add_argument(
4517330f729Sjoerg        '--override-compiler',
4527330f729Sjoerg        action='store_true',
4537330f729Sjoerg        help="""Always resort to the compiler wrapper even when better
4547330f729Sjoerg        intercept methods are available.""")
4557330f729Sjoerg
4567330f729Sjoerg
4577330f729Sjoergdef parser_add_compilers(parser):
4587330f729Sjoerg    parser.add_argument(
4597330f729Sjoerg        '--use-cc',
4607330f729Sjoerg        metavar='<path>',
4617330f729Sjoerg        dest='cc',
4627330f729Sjoerg        default=os.getenv('CC', 'cc'),
4637330f729Sjoerg        help="""When '%(prog)s' analyzes a project by interposing a compiler
4647330f729Sjoerg        wrapper, which executes a real compiler for compilation and do other
4657330f729Sjoerg        tasks (record the compiler invocation). Because of this interposing,
4667330f729Sjoerg        '%(prog)s' does not know what compiler your project normally uses.
4677330f729Sjoerg        Instead, it simply overrides the CC environment variable, and guesses
4687330f729Sjoerg        your default compiler.
4697330f729Sjoerg
4707330f729Sjoerg        If you need '%(prog)s' to use a specific compiler for *compilation*
4717330f729Sjoerg        then you can use this option to specify a path to that compiler.""")
4727330f729Sjoerg    parser.add_argument(
4737330f729Sjoerg        '--use-c++',
4747330f729Sjoerg        metavar='<path>',
4757330f729Sjoerg        dest='cxx',
4767330f729Sjoerg        default=os.getenv('CXX', 'c++'),
4777330f729Sjoerg        help="""This is the same as "--use-cc" but for C++ code.""")
4787330f729Sjoerg
4797330f729Sjoerg
4807330f729Sjoergclass AppendCommaSeparated(argparse.Action):
4817330f729Sjoerg    """ argparse Action class to support multiple comma separated lists. """
4827330f729Sjoerg
4837330f729Sjoerg    def __call__(self, __parser, namespace, values, __option_string):
4847330f729Sjoerg        # getattr(obj, attr, default) does not really returns default but none
4857330f729Sjoerg        if getattr(namespace, self.dest, None) is None:
4867330f729Sjoerg            setattr(namespace, self.dest, [])
4877330f729Sjoerg        # once it's fixed we can use as expected
4887330f729Sjoerg        actual = getattr(namespace, self.dest)
4897330f729Sjoerg        actual.extend(values.split(','))
4907330f729Sjoerg        setattr(namespace, self.dest, actual)
4917330f729Sjoerg
4927330f729Sjoerg
4937330f729Sjoergdef print_active_checkers(checkers):
4947330f729Sjoerg    """ Print active checkers to stdout. """
4957330f729Sjoerg
4967330f729Sjoerg    for name in sorted(name for name, (_, active) in checkers.items()
4977330f729Sjoerg                       if active):
4987330f729Sjoerg        print(name)
4997330f729Sjoerg
5007330f729Sjoerg
5017330f729Sjoergdef print_checkers(checkers):
5027330f729Sjoerg    """ Print verbose checker help to stdout. """
5037330f729Sjoerg
5047330f729Sjoerg    print('')
5057330f729Sjoerg    print('available checkers:')
5067330f729Sjoerg    print('')
5077330f729Sjoerg    for name in sorted(checkers.keys()):
5087330f729Sjoerg        description, active = checkers[name]
5097330f729Sjoerg        prefix = '+' if active else ' '
5107330f729Sjoerg        if len(name) > 30:
5117330f729Sjoerg            print(' {0} {1}'.format(prefix, name))
5127330f729Sjoerg            print(' ' * 35 + description)
5137330f729Sjoerg        else:
5147330f729Sjoerg            print(' {0} {1: <30}  {2}'.format(prefix, name, description))
5157330f729Sjoerg    print('')
5167330f729Sjoerg    print('NOTE: "+" indicates that an analysis is enabled by default.')
5177330f729Sjoerg    print('')
518