1*67e74705SXin Li# -*- coding: utf-8 -*- 2*67e74705SXin Li# The LLVM Compiler Infrastructure 3*67e74705SXin Li# 4*67e74705SXin Li# This file is distributed under the University of Illinois Open Source 5*67e74705SXin Li# License. See LICENSE.TXT for details. 6*67e74705SXin Li""" This module implements the 'scan-build' command API. 7*67e74705SXin Li 8*67e74705SXin LiTo run the static analyzer against a build is done in multiple steps: 9*67e74705SXin Li 10*67e74705SXin Li -- Intercept: capture the compilation command during the build, 11*67e74705SXin Li -- Analyze: run the analyzer against the captured commands, 12*67e74705SXin Li -- Report: create a cover report from the analyzer outputs. """ 13*67e74705SXin Li 14*67e74705SXin Liimport sys 15*67e74705SXin Liimport re 16*67e74705SXin Liimport os 17*67e74705SXin Liimport os.path 18*67e74705SXin Liimport json 19*67e74705SXin Liimport argparse 20*67e74705SXin Liimport logging 21*67e74705SXin Liimport subprocess 22*67e74705SXin Liimport multiprocessing 23*67e74705SXin Lifrom libscanbuild import initialize_logging, tempdir, command_entry_point 24*67e74705SXin Lifrom libscanbuild.runner import run 25*67e74705SXin Lifrom libscanbuild.intercept import capture 26*67e74705SXin Lifrom libscanbuild.report import report_directory, document 27*67e74705SXin Lifrom libscanbuild.clang import get_checkers 28*67e74705SXin Lifrom libscanbuild.compilation import split_command 29*67e74705SXin Li 30*67e74705SXin Li__all__ = ['analyze_build_main', 'analyze_build_wrapper'] 31*67e74705SXin Li 32*67e74705SXin LiCOMPILER_WRAPPER_CC = 'analyze-cc' 33*67e74705SXin LiCOMPILER_WRAPPER_CXX = 'analyze-c++' 34*67e74705SXin Li 35*67e74705SXin Li 36*67e74705SXin Li@command_entry_point 37*67e74705SXin Lidef analyze_build_main(bin_dir, from_build_command): 38*67e74705SXin Li """ Entry point for 'analyze-build' and 'scan-build'. """ 39*67e74705SXin Li 40*67e74705SXin Li parser = create_parser(from_build_command) 41*67e74705SXin Li args = parser.parse_args() 42*67e74705SXin Li validate(parser, args, from_build_command) 43*67e74705SXin Li 44*67e74705SXin Li # setup logging 45*67e74705SXin Li initialize_logging(args.verbose) 46*67e74705SXin Li logging.debug('Parsed arguments: %s', args) 47*67e74705SXin Li 48*67e74705SXin Li with report_directory(args.output, args.keep_empty) as target_dir: 49*67e74705SXin Li if not from_build_command: 50*67e74705SXin Li # run analyzer only and generate cover report 51*67e74705SXin Li run_analyzer(args, target_dir) 52*67e74705SXin Li number_of_bugs = document(args, target_dir, True) 53*67e74705SXin Li return number_of_bugs if args.status_bugs else 0 54*67e74705SXin Li elif args.intercept_first: 55*67e74705SXin Li # run build command and capture compiler executions 56*67e74705SXin Li exit_code = capture(args, bin_dir) 57*67e74705SXin Li # next step to run the analyzer against the captured commands 58*67e74705SXin Li if need_analyzer(args.build): 59*67e74705SXin Li run_analyzer(args, target_dir) 60*67e74705SXin Li # cover report generation and bug counting 61*67e74705SXin Li number_of_bugs = document(args, target_dir, True) 62*67e74705SXin Li # remove the compilation database when it was not requested 63*67e74705SXin Li if os.path.exists(args.cdb): 64*67e74705SXin Li os.unlink(args.cdb) 65*67e74705SXin Li # set exit status as it was requested 66*67e74705SXin Li return number_of_bugs if args.status_bugs else exit_code 67*67e74705SXin Li else: 68*67e74705SXin Li return exit_code 69*67e74705SXin Li else: 70*67e74705SXin Li # run the build command with compiler wrappers which 71*67e74705SXin Li # execute the analyzer too. (interposition) 72*67e74705SXin Li environment = setup_environment(args, target_dir, bin_dir) 73*67e74705SXin Li logging.debug('run build in environment: %s', environment) 74*67e74705SXin Li exit_code = subprocess.call(args.build, env=environment) 75*67e74705SXin Li logging.debug('build finished with exit code: %d', exit_code) 76*67e74705SXin Li # cover report generation and bug counting 77*67e74705SXin Li number_of_bugs = document(args, target_dir, False) 78*67e74705SXin Li # set exit status as it was requested 79*67e74705SXin Li return number_of_bugs if args.status_bugs else exit_code 80*67e74705SXin Li 81*67e74705SXin Li 82*67e74705SXin Lidef need_analyzer(args): 83*67e74705SXin Li """ Check the intent of the build command. 84*67e74705SXin Li 85*67e74705SXin Li When static analyzer run against project configure step, it should be 86*67e74705SXin Li silent and no need to run the analyzer or generate report. 87*67e74705SXin Li 88*67e74705SXin Li To run `scan-build` against the configure step might be neccessary, 89*67e74705SXin Li when compiler wrappers are used. That's the moment when build setup 90*67e74705SXin Li check the compiler and capture the location for the build process. """ 91*67e74705SXin Li 92*67e74705SXin Li return len(args) and not re.search('configure|autogen', args[0]) 93*67e74705SXin Li 94*67e74705SXin Li 95*67e74705SXin Lidef run_analyzer(args, output_dir): 96*67e74705SXin Li """ Runs the analyzer against the given compilation database. """ 97*67e74705SXin Li 98*67e74705SXin Li def exclude(filename): 99*67e74705SXin Li """ Return true when any excluded directory prefix the filename. """ 100*67e74705SXin Li return any(re.match(r'^' + directory, filename) 101*67e74705SXin Li for directory in args.excludes) 102*67e74705SXin Li 103*67e74705SXin Li consts = { 104*67e74705SXin Li 'clang': args.clang, 105*67e74705SXin Li 'output_dir': output_dir, 106*67e74705SXin Li 'output_format': args.output_format, 107*67e74705SXin Li 'output_failures': args.output_failures, 108*67e74705SXin Li 'direct_args': analyzer_params(args), 109*67e74705SXin Li 'force_debug': args.force_debug 110*67e74705SXin Li } 111*67e74705SXin Li 112*67e74705SXin Li logging.debug('run analyzer against compilation database') 113*67e74705SXin Li with open(args.cdb, 'r') as handle: 114*67e74705SXin Li generator = (dict(cmd, **consts) 115*67e74705SXin Li for cmd in json.load(handle) if not exclude(cmd['file'])) 116*67e74705SXin Li # when verbose output requested execute sequentially 117*67e74705SXin Li pool = multiprocessing.Pool(1 if args.verbose > 2 else None) 118*67e74705SXin Li for current in pool.imap_unordered(run, generator): 119*67e74705SXin Li if current is not None: 120*67e74705SXin Li # display error message from the static analyzer 121*67e74705SXin Li for line in current['error_output']: 122*67e74705SXin Li logging.info(line.rstrip()) 123*67e74705SXin Li pool.close() 124*67e74705SXin Li pool.join() 125*67e74705SXin Li 126*67e74705SXin Li 127*67e74705SXin Lidef setup_environment(args, destination, bin_dir): 128*67e74705SXin Li """ Set up environment for build command to interpose compiler wrapper. """ 129*67e74705SXin Li 130*67e74705SXin Li environment = dict(os.environ) 131*67e74705SXin Li environment.update({ 132*67e74705SXin Li 'CC': os.path.join(bin_dir, COMPILER_WRAPPER_CC), 133*67e74705SXin Li 'CXX': os.path.join(bin_dir, COMPILER_WRAPPER_CXX), 134*67e74705SXin Li 'ANALYZE_BUILD_CC': args.cc, 135*67e74705SXin Li 'ANALYZE_BUILD_CXX': args.cxx, 136*67e74705SXin Li 'ANALYZE_BUILD_CLANG': args.clang if need_analyzer(args.build) else '', 137*67e74705SXin Li 'ANALYZE_BUILD_VERBOSE': 'DEBUG' if args.verbose > 2 else 'WARNING', 138*67e74705SXin Li 'ANALYZE_BUILD_REPORT_DIR': destination, 139*67e74705SXin Li 'ANALYZE_BUILD_REPORT_FORMAT': args.output_format, 140*67e74705SXin Li 'ANALYZE_BUILD_REPORT_FAILURES': 'yes' if args.output_failures else '', 141*67e74705SXin Li 'ANALYZE_BUILD_PARAMETERS': ' '.join(analyzer_params(args)), 142*67e74705SXin Li 'ANALYZE_BUILD_FORCE_DEBUG': 'yes' if args.force_debug else '' 143*67e74705SXin Li }) 144*67e74705SXin Li return environment 145*67e74705SXin Li 146*67e74705SXin Li 147*67e74705SXin Lidef analyze_build_wrapper(cplusplus): 148*67e74705SXin Li """ Entry point for `analyze-cc` and `analyze-c++` compiler wrappers. """ 149*67e74705SXin Li 150*67e74705SXin Li # initialize wrapper logging 151*67e74705SXin Li logging.basicConfig(format='analyze: %(levelname)s: %(message)s', 152*67e74705SXin Li level=os.getenv('ANALYZE_BUILD_VERBOSE', 'INFO')) 153*67e74705SXin Li # execute with real compiler 154*67e74705SXin Li compiler = os.getenv('ANALYZE_BUILD_CXX', 'c++') if cplusplus \ 155*67e74705SXin Li else os.getenv('ANALYZE_BUILD_CC', 'cc') 156*67e74705SXin Li compilation = [compiler] + sys.argv[1:] 157*67e74705SXin Li logging.info('execute compiler: %s', compilation) 158*67e74705SXin Li result = subprocess.call(compilation) 159*67e74705SXin Li # exit when it fails, ... 160*67e74705SXin Li if result or not os.getenv('ANALYZE_BUILD_CLANG'): 161*67e74705SXin Li return result 162*67e74705SXin Li # ... and run the analyzer if all went well. 163*67e74705SXin Li try: 164*67e74705SXin Li # check is it a compilation 165*67e74705SXin Li compilation = split_command(sys.argv) 166*67e74705SXin Li if compilation is None: 167*67e74705SXin Li return result 168*67e74705SXin Li # collect the needed parameters from environment, crash when missing 169*67e74705SXin Li parameters = { 170*67e74705SXin Li 'clang': os.getenv('ANALYZE_BUILD_CLANG'), 171*67e74705SXin Li 'output_dir': os.getenv('ANALYZE_BUILD_REPORT_DIR'), 172*67e74705SXin Li 'output_format': os.getenv('ANALYZE_BUILD_REPORT_FORMAT'), 173*67e74705SXin Li 'output_failures': os.getenv('ANALYZE_BUILD_REPORT_FAILURES'), 174*67e74705SXin Li 'direct_args': os.getenv('ANALYZE_BUILD_PARAMETERS', 175*67e74705SXin Li '').split(' '), 176*67e74705SXin Li 'force_debug': os.getenv('ANALYZE_BUILD_FORCE_DEBUG'), 177*67e74705SXin Li 'directory': os.getcwd(), 178*67e74705SXin Li 'command': [sys.argv[0], '-c'] + compilation.flags 179*67e74705SXin Li } 180*67e74705SXin Li # call static analyzer against the compilation 181*67e74705SXin Li for source in compilation.files: 182*67e74705SXin Li parameters.update({'file': source}) 183*67e74705SXin Li logging.debug('analyzer parameters %s', parameters) 184*67e74705SXin Li current = run(parameters) 185*67e74705SXin Li # display error message from the static analyzer 186*67e74705SXin Li if current is not None: 187*67e74705SXin Li for line in current['error_output']: 188*67e74705SXin Li logging.info(line.rstrip()) 189*67e74705SXin Li except Exception: 190*67e74705SXin Li logging.exception("run analyzer inside compiler wrapper failed.") 191*67e74705SXin Li return result 192*67e74705SXin Li 193*67e74705SXin Li 194*67e74705SXin Lidef analyzer_params(args): 195*67e74705SXin Li """ A group of command line arguments can mapped to command 196*67e74705SXin Li line arguments of the analyzer. This method generates those. """ 197*67e74705SXin Li 198*67e74705SXin Li def prefix_with(constant, pieces): 199*67e74705SXin Li """ From a sequence create another sequence where every second element 200*67e74705SXin Li is from the original sequence and the odd elements are the prefix. 201*67e74705SXin Li 202*67e74705SXin Li eg.: prefix_with(0, [1,2,3]) creates [0, 1, 0, 2, 0, 3] """ 203*67e74705SXin Li 204*67e74705SXin Li return [elem for piece in pieces for elem in [constant, piece]] 205*67e74705SXin Li 206*67e74705SXin Li result = [] 207*67e74705SXin Li 208*67e74705SXin Li if args.store_model: 209*67e74705SXin Li result.append('-analyzer-store={0}'.format(args.store_model)) 210*67e74705SXin Li if args.constraints_model: 211*67e74705SXin Li result.append('-analyzer-constraints={0}'.format( 212*67e74705SXin Li args.constraints_model)) 213*67e74705SXin Li if args.internal_stats: 214*67e74705SXin Li result.append('-analyzer-stats') 215*67e74705SXin Li if args.analyze_headers: 216*67e74705SXin Li result.append('-analyzer-opt-analyze-headers') 217*67e74705SXin Li if args.stats: 218*67e74705SXin Li result.append('-analyzer-checker=debug.Stats') 219*67e74705SXin Li if args.maxloop: 220*67e74705SXin Li result.extend(['-analyzer-max-loop', str(args.maxloop)]) 221*67e74705SXin Li if args.output_format: 222*67e74705SXin Li result.append('-analyzer-output={0}'.format(args.output_format)) 223*67e74705SXin Li if args.analyzer_config: 224*67e74705SXin Li result.append(args.analyzer_config) 225*67e74705SXin Li if args.verbose >= 4: 226*67e74705SXin Li result.append('-analyzer-display-progress') 227*67e74705SXin Li if args.plugins: 228*67e74705SXin Li result.extend(prefix_with('-load', args.plugins)) 229*67e74705SXin Li if args.enable_checker: 230*67e74705SXin Li checkers = ','.join(args.enable_checker) 231*67e74705SXin Li result.extend(['-analyzer-checker', checkers]) 232*67e74705SXin Li if args.disable_checker: 233*67e74705SXin Li checkers = ','.join(args.disable_checker) 234*67e74705SXin Li result.extend(['-analyzer-disable-checker', checkers]) 235*67e74705SXin Li if os.getenv('UBIVIZ'): 236*67e74705SXin Li result.append('-analyzer-viz-egraph-ubigraph') 237*67e74705SXin Li 238*67e74705SXin Li return prefix_with('-Xclang', result) 239*67e74705SXin Li 240*67e74705SXin Li 241*67e74705SXin Lidef print_active_checkers(checkers): 242*67e74705SXin Li """ Print active checkers to stdout. """ 243*67e74705SXin Li 244*67e74705SXin Li for name in sorted(name for name, (_, active) in checkers.items() 245*67e74705SXin Li if active): 246*67e74705SXin Li print(name) 247*67e74705SXin Li 248*67e74705SXin Li 249*67e74705SXin Lidef print_checkers(checkers): 250*67e74705SXin Li """ Print verbose checker help to stdout. """ 251*67e74705SXin Li 252*67e74705SXin Li print('') 253*67e74705SXin Li print('available checkers:') 254*67e74705SXin Li print('') 255*67e74705SXin Li for name in sorted(checkers.keys()): 256*67e74705SXin Li description, active = checkers[name] 257*67e74705SXin Li prefix = '+' if active else ' ' 258*67e74705SXin Li if len(name) > 30: 259*67e74705SXin Li print(' {0} {1}'.format(prefix, name)) 260*67e74705SXin Li print(' ' * 35 + description) 261*67e74705SXin Li else: 262*67e74705SXin Li print(' {0} {1: <30} {2}'.format(prefix, name, description)) 263*67e74705SXin Li print('') 264*67e74705SXin Li print('NOTE: "+" indicates that an analysis is enabled by default.') 265*67e74705SXin Li print('') 266*67e74705SXin Li 267*67e74705SXin Li 268*67e74705SXin Lidef validate(parser, args, from_build_command): 269*67e74705SXin Li """ Validation done by the parser itself, but semantic check still 270*67e74705SXin Li needs to be done. This method is doing that. """ 271*67e74705SXin Li 272*67e74705SXin Li if args.help_checkers_verbose: 273*67e74705SXin Li print_checkers(get_checkers(args.clang, args.plugins)) 274*67e74705SXin Li parser.exit() 275*67e74705SXin Li elif args.help_checkers: 276*67e74705SXin Li print_active_checkers(get_checkers(args.clang, args.plugins)) 277*67e74705SXin Li parser.exit() 278*67e74705SXin Li 279*67e74705SXin Li if from_build_command and not args.build: 280*67e74705SXin Li parser.error('missing build command') 281*67e74705SXin Li 282*67e74705SXin Li 283*67e74705SXin Lidef create_parser(from_build_command): 284*67e74705SXin Li """ Command line argument parser factory method. """ 285*67e74705SXin Li 286*67e74705SXin Li parser = argparse.ArgumentParser( 287*67e74705SXin Li formatter_class=argparse.ArgumentDefaultsHelpFormatter) 288*67e74705SXin Li 289*67e74705SXin Li parser.add_argument( 290*67e74705SXin Li '--verbose', '-v', 291*67e74705SXin Li action='count', 292*67e74705SXin Li default=0, 293*67e74705SXin Li help="""Enable verbose output from '%(prog)s'. A second and third 294*67e74705SXin Li flag increases verbosity.""") 295*67e74705SXin Li parser.add_argument( 296*67e74705SXin Li '--override-compiler', 297*67e74705SXin Li action='store_true', 298*67e74705SXin Li help="""Always resort to the compiler wrapper even when better 299*67e74705SXin Li interposition methods are available.""") 300*67e74705SXin Li parser.add_argument( 301*67e74705SXin Li '--intercept-first', 302*67e74705SXin Li action='store_true', 303*67e74705SXin Li help="""Run the build commands only, build a compilation database, 304*67e74705SXin Li then run the static analyzer afterwards. 305*67e74705SXin Li Generally speaking it has better coverage on build commands. 306*67e74705SXin Li With '--override-compiler' it use compiler wrapper, but does 307*67e74705SXin Li not run the analyzer till the build is finished. """) 308*67e74705SXin Li parser.add_argument( 309*67e74705SXin Li '--cdb', 310*67e74705SXin Li metavar='<file>', 311*67e74705SXin Li default="compile_commands.json", 312*67e74705SXin Li help="""The JSON compilation database.""") 313*67e74705SXin Li 314*67e74705SXin Li parser.add_argument( 315*67e74705SXin Li '--output', '-o', 316*67e74705SXin Li metavar='<path>', 317*67e74705SXin Li default=tempdir(), 318*67e74705SXin Li help="""Specifies the output directory for analyzer reports. 319*67e74705SXin Li Subdirectory will be created if default directory is targeted. 320*67e74705SXin Li """) 321*67e74705SXin Li parser.add_argument( 322*67e74705SXin Li '--status-bugs', 323*67e74705SXin Li action='store_true', 324*67e74705SXin Li help="""By default, the exit status of '%(prog)s' is the same as the 325*67e74705SXin Li executed build command. Specifying this option causes the exit 326*67e74705SXin Li status of '%(prog)s' to be non zero if it found potential bugs 327*67e74705SXin Li and zero otherwise.""") 328*67e74705SXin Li parser.add_argument( 329*67e74705SXin Li '--html-title', 330*67e74705SXin Li metavar='<title>', 331*67e74705SXin Li help="""Specify the title used on generated HTML pages. 332*67e74705SXin Li If not specified, a default title will be used.""") 333*67e74705SXin Li parser.add_argument( 334*67e74705SXin Li '--analyze-headers', 335*67e74705SXin Li action='store_true', 336*67e74705SXin Li help="""Also analyze functions in #included files. By default, such 337*67e74705SXin Li functions are skipped unless they are called by functions 338*67e74705SXin Li within the main source file.""") 339*67e74705SXin Li format_group = parser.add_mutually_exclusive_group() 340*67e74705SXin Li format_group.add_argument( 341*67e74705SXin Li '--plist', '-plist', 342*67e74705SXin Li dest='output_format', 343*67e74705SXin Li const='plist', 344*67e74705SXin Li default='html', 345*67e74705SXin Li action='store_const', 346*67e74705SXin Li help="""This option outputs the results as a set of .plist files.""") 347*67e74705SXin Li format_group.add_argument( 348*67e74705SXin Li '--plist-html', '-plist-html', 349*67e74705SXin Li dest='output_format', 350*67e74705SXin Li const='plist-html', 351*67e74705SXin Li default='html', 352*67e74705SXin Li action='store_const', 353*67e74705SXin Li help="""This option outputs the results as a set of .html and .plist 354*67e74705SXin Li files.""") 355*67e74705SXin Li # TODO: implement '-view ' 356*67e74705SXin Li 357*67e74705SXin Li advanced = parser.add_argument_group('advanced options') 358*67e74705SXin Li advanced.add_argument( 359*67e74705SXin Li '--keep-empty', 360*67e74705SXin Li action='store_true', 361*67e74705SXin Li help="""Don't remove the build results directory even if no issues 362*67e74705SXin Li were reported.""") 363*67e74705SXin Li advanced.add_argument( 364*67e74705SXin Li '--no-failure-reports', '-no-failure-reports', 365*67e74705SXin Li dest='output_failures', 366*67e74705SXin Li action='store_false', 367*67e74705SXin Li help="""Do not create a 'failures' subdirectory that includes analyzer 368*67e74705SXin Li crash reports and preprocessed source files.""") 369*67e74705SXin Li advanced.add_argument( 370*67e74705SXin Li '--stats', '-stats', 371*67e74705SXin Li action='store_true', 372*67e74705SXin Li help="""Generates visitation statistics for the project being analyzed. 373*67e74705SXin Li """) 374*67e74705SXin Li advanced.add_argument( 375*67e74705SXin Li '--internal-stats', 376*67e74705SXin Li action='store_true', 377*67e74705SXin Li help="""Generate internal analyzer statistics.""") 378*67e74705SXin Li advanced.add_argument( 379*67e74705SXin Li '--maxloop', '-maxloop', 380*67e74705SXin Li metavar='<loop count>', 381*67e74705SXin Li type=int, 382*67e74705SXin Li help="""Specifiy the number of times a block can be visited before 383*67e74705SXin Li giving up. Increase for more comprehensive coverage at a cost 384*67e74705SXin Li of speed.""") 385*67e74705SXin Li advanced.add_argument( 386*67e74705SXin Li '--store', '-store', 387*67e74705SXin Li metavar='<model>', 388*67e74705SXin Li dest='store_model', 389*67e74705SXin Li choices=['region', 'basic'], 390*67e74705SXin Li help="""Specify the store model used by the analyzer. 391*67e74705SXin Li 'region' specifies a field- sensitive store model. 392*67e74705SXin Li 'basic' which is far less precise but can more quickly 393*67e74705SXin Li analyze code. 'basic' was the default store model for 394*67e74705SXin Li checker-0.221 and earlier.""") 395*67e74705SXin Li advanced.add_argument( 396*67e74705SXin Li '--constraints', '-constraints', 397*67e74705SXin Li metavar='<model>', 398*67e74705SXin Li dest='constraints_model', 399*67e74705SXin Li choices=['range', 'basic'], 400*67e74705SXin Li help="""Specify the contraint engine used by the analyzer. Specifying 401*67e74705SXin Li 'basic' uses a simpler, less powerful constraint model used by 402*67e74705SXin Li checker-0.160 and earlier.""") 403*67e74705SXin Li advanced.add_argument( 404*67e74705SXin Li '--use-analyzer', 405*67e74705SXin Li metavar='<path>', 406*67e74705SXin Li dest='clang', 407*67e74705SXin Li default='clang', 408*67e74705SXin Li help="""'%(prog)s' uses the 'clang' executable relative to itself for 409*67e74705SXin Li static analysis. One can override this behavior with this 410*67e74705SXin Li option by using the 'clang' packaged with Xcode (on OS X) or 411*67e74705SXin Li from the PATH.""") 412*67e74705SXin Li advanced.add_argument( 413*67e74705SXin Li '--use-cc', 414*67e74705SXin Li metavar='<path>', 415*67e74705SXin Li dest='cc', 416*67e74705SXin Li default='cc', 417*67e74705SXin Li help="""When '%(prog)s' analyzes a project by interposing a "fake 418*67e74705SXin Li compiler", which executes a real compiler for compilation and 419*67e74705SXin Li do other tasks (to run the static analyzer or just record the 420*67e74705SXin Li compiler invocation). Because of this interposing, '%(prog)s' 421*67e74705SXin Li does not know what compiler your project normally uses. 422*67e74705SXin Li Instead, it simply overrides the CC environment variable, and 423*67e74705SXin Li guesses your default compiler. 424*67e74705SXin Li 425*67e74705SXin Li If you need '%(prog)s' to use a specific compiler for 426*67e74705SXin Li *compilation* then you can use this option to specify a path 427*67e74705SXin Li to that compiler.""") 428*67e74705SXin Li advanced.add_argument( 429*67e74705SXin Li '--use-c++', 430*67e74705SXin Li metavar='<path>', 431*67e74705SXin Li dest='cxx', 432*67e74705SXin Li default='c++', 433*67e74705SXin Li help="""This is the same as "--use-cc" but for C++ code.""") 434*67e74705SXin Li advanced.add_argument( 435*67e74705SXin Li '--analyzer-config', '-analyzer-config', 436*67e74705SXin Li metavar='<options>', 437*67e74705SXin Li help="""Provide options to pass through to the analyzer's 438*67e74705SXin Li -analyzer-config flag. Several options are separated with 439*67e74705SXin Li comma: 'key1=val1,key2=val2' 440*67e74705SXin Li 441*67e74705SXin Li Available options: 442*67e74705SXin Li stable-report-filename=true or false (default) 443*67e74705SXin Li 444*67e74705SXin Li Switch the page naming to: 445*67e74705SXin Li report-<filename>-<function/method name>-<id>.html 446*67e74705SXin Li instead of report-XXXXXX.html""") 447*67e74705SXin Li advanced.add_argument( 448*67e74705SXin Li '--exclude', 449*67e74705SXin Li metavar='<directory>', 450*67e74705SXin Li dest='excludes', 451*67e74705SXin Li action='append', 452*67e74705SXin Li default=[], 453*67e74705SXin Li help="""Do not run static analyzer against files found in this 454*67e74705SXin Li directory. (You can specify this option multiple times.) 455*67e74705SXin Li Could be usefull when project contains 3rd party libraries. 456*67e74705SXin Li The directory path shall be absolute path as file names in 457*67e74705SXin Li the compilation database.""") 458*67e74705SXin Li advanced.add_argument( 459*67e74705SXin Li '--force-analyze-debug-code', 460*67e74705SXin Li dest='force_debug', 461*67e74705SXin Li action='store_true', 462*67e74705SXin Li help="""Tells analyzer to enable assertions in code even if they were 463*67e74705SXin Li disabled during compilation, enabling more precise results.""") 464*67e74705SXin Li 465*67e74705SXin Li plugins = parser.add_argument_group('checker options') 466*67e74705SXin Li plugins.add_argument( 467*67e74705SXin Li '--load-plugin', '-load-plugin', 468*67e74705SXin Li metavar='<plugin library>', 469*67e74705SXin Li dest='plugins', 470*67e74705SXin Li action='append', 471*67e74705SXin Li help="""Loading external checkers using the clang plugin interface.""") 472*67e74705SXin Li plugins.add_argument( 473*67e74705SXin Li '--enable-checker', '-enable-checker', 474*67e74705SXin Li metavar='<checker name>', 475*67e74705SXin Li action=AppendCommaSeparated, 476*67e74705SXin Li help="""Enable specific checker.""") 477*67e74705SXin Li plugins.add_argument( 478*67e74705SXin Li '--disable-checker', '-disable-checker', 479*67e74705SXin Li metavar='<checker name>', 480*67e74705SXin Li action=AppendCommaSeparated, 481*67e74705SXin Li help="""Disable specific checker.""") 482*67e74705SXin Li plugins.add_argument( 483*67e74705SXin Li '--help-checkers', 484*67e74705SXin Li action='store_true', 485*67e74705SXin Li help="""A default group of checkers is run unless explicitly disabled. 486*67e74705SXin Li Exactly which checkers constitute the default group is a 487*67e74705SXin Li function of the operating system in use. These can be printed 488*67e74705SXin Li with this flag.""") 489*67e74705SXin Li plugins.add_argument( 490*67e74705SXin Li '--help-checkers-verbose', 491*67e74705SXin Li action='store_true', 492*67e74705SXin Li help="""Print all available checkers and mark the enabled ones.""") 493*67e74705SXin Li 494*67e74705SXin Li if from_build_command: 495*67e74705SXin Li parser.add_argument( 496*67e74705SXin Li dest='build', 497*67e74705SXin Li nargs=argparse.REMAINDER, 498*67e74705SXin Li help="""Command to run.""") 499*67e74705SXin Li 500*67e74705SXin Li return parser 501*67e74705SXin Li 502*67e74705SXin Li 503*67e74705SXin Liclass AppendCommaSeparated(argparse.Action): 504*67e74705SXin Li """ argparse Action class to support multiple comma separated lists. """ 505*67e74705SXin Li 506*67e74705SXin Li def __call__(self, __parser, namespace, values, __option_string): 507*67e74705SXin Li # getattr(obj, attr, default) does not really returns default but none 508*67e74705SXin Li if getattr(namespace, self.dest, None) is None: 509*67e74705SXin Li setattr(namespace, self.dest, []) 510*67e74705SXin Li # once it's fixed we can use as expected 511*67e74705SXin Li actual = getattr(namespace, self.dest) 512*67e74705SXin Li actual.extend(values.split(',')) 513*67e74705SXin Li setattr(namespace, self.dest, actual) 514