forked from OSchip/llvm-project
[scan-build-py] move argument parsing into separate module
Differential Revision: https://reviews.llvm.org/D30601 llvm-svn: 297307
This commit is contained in:
parent
e05469392e
commit
5270bb978f
|
@ -13,5 +13,5 @@ import os.path
|
||||||
this_dir = os.path.dirname(os.path.realpath(__file__))
|
this_dir = os.path.dirname(os.path.realpath(__file__))
|
||||||
sys.path.append(os.path.dirname(this_dir))
|
sys.path.append(os.path.dirname(this_dir))
|
||||||
|
|
||||||
from libscanbuild.analyze import analyze_build_main
|
from libscanbuild.analyze import analyze_build
|
||||||
sys.exit(analyze_build_main(this_dir, False))
|
sys.exit(analyze_build())
|
||||||
|
|
|
@ -13,5 +13,5 @@ import os.path
|
||||||
this_dir = os.path.dirname(os.path.realpath(__file__))
|
this_dir = os.path.dirname(os.path.realpath(__file__))
|
||||||
sys.path.append(os.path.dirname(this_dir))
|
sys.path.append(os.path.dirname(this_dir))
|
||||||
|
|
||||||
from libscanbuild.intercept import intercept_build_main
|
from libscanbuild.intercept import intercept_build
|
||||||
sys.exit(intercept_build_main(this_dir))
|
sys.exit(intercept_build())
|
||||||
|
|
|
@ -13,5 +13,5 @@ import os.path
|
||||||
this_dir = os.path.dirname(os.path.realpath(__file__))
|
this_dir = os.path.dirname(os.path.realpath(__file__))
|
||||||
sys.path.append(os.path.dirname(this_dir))
|
sys.path.append(os.path.dirname(this_dir))
|
||||||
|
|
||||||
from libscanbuild.analyze import analyze_build_main
|
from libscanbuild.analyze import scan_build
|
||||||
sys.exit(analyze_build_main(this_dir, True))
|
sys.exit(scan_build())
|
||||||
|
|
|
@ -11,73 +11,68 @@ To run the static analyzer against a build is done in multiple steps:
|
||||||
-- Analyze: run the analyzer against the captured commands,
|
-- Analyze: run the analyzer against the captured commands,
|
||||||
-- Report: create a cover report from the analyzer outputs. """
|
-- Report: create a cover report from the analyzer outputs. """
|
||||||
|
|
||||||
import sys
|
|
||||||
import re
|
import re
|
||||||
import os
|
import os
|
||||||
import os.path
|
import os.path
|
||||||
import json
|
import json
|
||||||
import argparse
|
|
||||||
import logging
|
import logging
|
||||||
import tempfile
|
import tempfile
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
import contextlib
|
import contextlib
|
||||||
import datetime
|
import datetime
|
||||||
from libscanbuild import command_entry_point, compiler_wrapper, \
|
from libscanbuild import command_entry_point, compiler_wrapper, \
|
||||||
wrapper_environment, reconfigure_logging, run_build, tempdir
|
wrapper_environment, run_build
|
||||||
|
from libscanbuild.arguments import parse_args_for_scan_build, \
|
||||||
|
parse_args_for_analyze_build
|
||||||
from libscanbuild.runner import run
|
from libscanbuild.runner import run
|
||||||
from libscanbuild.intercept import capture
|
from libscanbuild.intercept import capture
|
||||||
from libscanbuild.report import document
|
from libscanbuild.report import document
|
||||||
from libscanbuild.clang import get_checkers
|
|
||||||
from libscanbuild.compilation import split_command
|
from libscanbuild.compilation import split_command
|
||||||
|
|
||||||
__all__ = ['analyze_build_main', 'analyze_compiler_wrapper']
|
__all__ = ['scan_build', 'analyze_build', 'analyze_compiler_wrapper']
|
||||||
|
|
||||||
COMPILER_WRAPPER_CC = 'analyze-cc'
|
COMPILER_WRAPPER_CC = 'analyze-cc'
|
||||||
COMPILER_WRAPPER_CXX = 'analyze-c++'
|
COMPILER_WRAPPER_CXX = 'analyze-c++'
|
||||||
|
|
||||||
|
|
||||||
@command_entry_point
|
@command_entry_point
|
||||||
def analyze_build_main(bin_dir, from_build_command):
|
def scan_build():
|
||||||
""" Entry point for 'analyze-build' and 'scan-build'. """
|
""" Entry point for scan-build command. """
|
||||||
|
|
||||||
parser = create_parser(from_build_command)
|
|
||||||
args = parser.parse_args()
|
|
||||||
validate(parser, args, from_build_command)
|
|
||||||
|
|
||||||
# setup logging
|
|
||||||
reconfigure_logging(args.verbose)
|
|
||||||
logging.debug('Raw arguments %s', sys.argv)
|
|
||||||
|
|
||||||
|
args = parse_args_for_scan_build()
|
||||||
with report_directory(args.output, args.keep_empty) as target_dir:
|
with report_directory(args.output, args.keep_empty) as target_dir:
|
||||||
if not from_build_command:
|
# Run against a build command. there are cases, when analyzer run
|
||||||
# run analyzer only and generate cover report
|
# is not required. But we need to set up everything for the
|
||||||
run_analyzer(args, target_dir)
|
# wrappers, because 'configure' needs to capture the CC/CXX values
|
||||||
number_of_bugs = document(args, target_dir, True)
|
# for the Makefile.
|
||||||
return number_of_bugs if args.status_bugs else 0
|
if args.intercept_first:
|
||||||
elif args.intercept_first:
|
# Run build command with intercept module.
|
||||||
# run build command and capture compiler executions
|
exit_code = capture(args)
|
||||||
exit_code = capture(args, bin_dir)
|
# Run the analyzer against the captured commands.
|
||||||
# next step to run the analyzer against the captured commands
|
|
||||||
if need_analyzer(args.build):
|
if need_analyzer(args.build):
|
||||||
run_analyzer(args, target_dir)
|
run_analyzer(args, target_dir)
|
||||||
# cover report generation and bug counting
|
|
||||||
number_of_bugs = document(args, target_dir, True)
|
|
||||||
# remove the compilation database when it was not requested
|
|
||||||
if os.path.exists(args.cdb):
|
|
||||||
os.unlink(args.cdb)
|
|
||||||
# set exit status as it was requested
|
|
||||||
return number_of_bugs if args.status_bugs else exit_code
|
|
||||||
else:
|
|
||||||
return exit_code
|
|
||||||
else:
|
else:
|
||||||
# run the build command with compiler wrappers which
|
# Run build command and analyzer with compiler wrappers.
|
||||||
# execute the analyzer too. (interposition)
|
environment = setup_environment(args, target_dir)
|
||||||
environment = setup_environment(args, target_dir, bin_dir)
|
|
||||||
exit_code = run_build(args.build, env=environment)
|
exit_code = run_build(args.build, env=environment)
|
||||||
# cover report generation and bug counting
|
# Cover report generation and bug counting.
|
||||||
number_of_bugs = document(args, target_dir, False)
|
number_of_bugs = document(args, target_dir, False)
|
||||||
# set exit status as it was requested
|
# Set exit status as it was requested.
|
||||||
return number_of_bugs if args.status_bugs else exit_code
|
return number_of_bugs if args.status_bugs else exit_code
|
||||||
|
|
||||||
|
|
||||||
|
@command_entry_point
|
||||||
|
def analyze_build():
|
||||||
|
""" Entry point for analyze-build command. """
|
||||||
|
|
||||||
|
args = parse_args_for_analyze_build()
|
||||||
|
with report_directory(args.output, args.keep_empty) as target_dir:
|
||||||
|
# Run the analyzer against a compilation db.
|
||||||
|
run_analyzer(args, target_dir)
|
||||||
|
# Cover report generation and bug counting.
|
||||||
|
number_of_bugs = document(args, target_dir, True)
|
||||||
|
# Set exit status as it was requested.
|
||||||
|
return number_of_bugs if args.status_bugs else 0
|
||||||
|
|
||||||
|
|
||||||
def need_analyzer(args):
|
def need_analyzer(args):
|
||||||
|
@ -125,14 +120,14 @@ def run_analyzer(args, output_dir):
|
||||||
pool.join()
|
pool.join()
|
||||||
|
|
||||||
|
|
||||||
def setup_environment(args, destination, bin_dir):
|
def setup_environment(args, destination):
|
||||||
""" Set up environment for build command to interpose compiler wrapper. """
|
""" Set up environment for build command to interpose compiler wrapper. """
|
||||||
|
|
||||||
environment = dict(os.environ)
|
environment = dict(os.environ)
|
||||||
environment.update(wrapper_environment(args))
|
environment.update(wrapper_environment(args))
|
||||||
environment.update({
|
environment.update({
|
||||||
'CC': os.path.join(bin_dir, COMPILER_WRAPPER_CC),
|
'CC': COMPILER_WRAPPER_CC,
|
||||||
'CXX': os.path.join(bin_dir, COMPILER_WRAPPER_CXX),
|
'CXX': COMPILER_WRAPPER_CXX,
|
||||||
'ANALYZE_BUILD_CLANG': args.clang if need_analyzer(args.build) else '',
|
'ANALYZE_BUILD_CLANG': args.clang if need_analyzer(args.build) else '',
|
||||||
'ANALYZE_BUILD_REPORT_DIR': destination,
|
'ANALYZE_BUILD_REPORT_DIR': destination,
|
||||||
'ANALYZE_BUILD_REPORT_FORMAT': args.output_format,
|
'ANALYZE_BUILD_REPORT_FORMAT': args.output_format,
|
||||||
|
@ -262,281 +257,3 @@ def analyzer_params(args):
|
||||||
result.append('-analyzer-viz-egraph-ubigraph')
|
result.append('-analyzer-viz-egraph-ubigraph')
|
||||||
|
|
||||||
return prefix_with('-Xclang', result)
|
return prefix_with('-Xclang', result)
|
||||||
|
|
||||||
|
|
||||||
def print_active_checkers(checkers):
|
|
||||||
""" Print active checkers to stdout. """
|
|
||||||
|
|
||||||
for name in sorted(name for name, (_, active) in checkers.items()
|
|
||||||
if active):
|
|
||||||
print(name)
|
|
||||||
|
|
||||||
|
|
||||||
def print_checkers(checkers):
|
|
||||||
""" Print verbose checker help to stdout. """
|
|
||||||
|
|
||||||
print('')
|
|
||||||
print('available checkers:')
|
|
||||||
print('')
|
|
||||||
for name in sorted(checkers.keys()):
|
|
||||||
description, active = checkers[name]
|
|
||||||
prefix = '+' if active else ' '
|
|
||||||
if len(name) > 30:
|
|
||||||
print(' {0} {1}'.format(prefix, name))
|
|
||||||
print(' ' * 35 + description)
|
|
||||||
else:
|
|
||||||
print(' {0} {1: <30} {2}'.format(prefix, name, description))
|
|
||||||
print('')
|
|
||||||
print('NOTE: "+" indicates that an analysis is enabled by default.')
|
|
||||||
print('')
|
|
||||||
|
|
||||||
|
|
||||||
def validate(parser, args, from_build_command):
|
|
||||||
""" Validation done by the parser itself, but semantic check still
|
|
||||||
needs to be done. This method is doing that. """
|
|
||||||
|
|
||||||
# Make plugins always a list. (It might be None when not specified.)
|
|
||||||
args.plugins = args.plugins if args.plugins else []
|
|
||||||
|
|
||||||
if args.help_checkers_verbose:
|
|
||||||
print_checkers(get_checkers(args.clang, args.plugins))
|
|
||||||
parser.exit()
|
|
||||||
elif args.help_checkers:
|
|
||||||
print_active_checkers(get_checkers(args.clang, args.plugins))
|
|
||||||
parser.exit()
|
|
||||||
|
|
||||||
if from_build_command and not args.build:
|
|
||||||
parser.error('missing build command')
|
|
||||||
|
|
||||||
|
|
||||||
def create_parser(from_build_command):
|
|
||||||
""" Command line argument parser factory method. """
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(
|
|
||||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
'--verbose', '-v',
|
|
||||||
action='count',
|
|
||||||
default=0,
|
|
||||||
help="""Enable verbose output from '%(prog)s'. A second and third
|
|
||||||
flag increases verbosity.""")
|
|
||||||
parser.add_argument(
|
|
||||||
'--override-compiler',
|
|
||||||
action='store_true',
|
|
||||||
help="""Always resort to the compiler wrapper even when better
|
|
||||||
interposition methods are available.""")
|
|
||||||
parser.add_argument(
|
|
||||||
'--intercept-first',
|
|
||||||
action='store_true',
|
|
||||||
help="""Run the build commands only, build a compilation database,
|
|
||||||
then run the static analyzer afterwards.
|
|
||||||
Generally speaking it has better coverage on build commands.
|
|
||||||
With '--override-compiler' it use compiler wrapper, but does
|
|
||||||
not run the analyzer till the build is finished. """)
|
|
||||||
parser.add_argument(
|
|
||||||
'--cdb',
|
|
||||||
metavar='<file>',
|
|
||||||
default="compile_commands.json",
|
|
||||||
help="""The JSON compilation database.""")
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
'--output', '-o',
|
|
||||||
metavar='<path>',
|
|
||||||
default=tempdir(),
|
|
||||||
help="""Specifies the output directory for analyzer reports.
|
|
||||||
Subdirectory will be created if default directory is targeted.
|
|
||||||
""")
|
|
||||||
parser.add_argument(
|
|
||||||
'--status-bugs',
|
|
||||||
action='store_true',
|
|
||||||
help="""By default, the exit status of '%(prog)s' is the same as the
|
|
||||||
executed build command. Specifying this option causes the exit
|
|
||||||
status of '%(prog)s' to be non zero if it found potential bugs
|
|
||||||
and zero otherwise.""")
|
|
||||||
parser.add_argument(
|
|
||||||
'--html-title',
|
|
||||||
metavar='<title>',
|
|
||||||
help="""Specify the title used on generated HTML pages.
|
|
||||||
If not specified, a default title will be used.""")
|
|
||||||
parser.add_argument(
|
|
||||||
'--analyze-headers',
|
|
||||||
action='store_true',
|
|
||||||
help="""Also analyze functions in #included files. By default, such
|
|
||||||
functions are skipped unless they are called by functions
|
|
||||||
within the main source file.""")
|
|
||||||
format_group = parser.add_mutually_exclusive_group()
|
|
||||||
format_group.add_argument(
|
|
||||||
'--plist', '-plist',
|
|
||||||
dest='output_format',
|
|
||||||
const='plist',
|
|
||||||
default='html',
|
|
||||||
action='store_const',
|
|
||||||
help="""This option outputs the results as a set of .plist files.""")
|
|
||||||
format_group.add_argument(
|
|
||||||
'--plist-html', '-plist-html',
|
|
||||||
dest='output_format',
|
|
||||||
const='plist-html',
|
|
||||||
default='html',
|
|
||||||
action='store_const',
|
|
||||||
help="""This option outputs the results as a set of .html and .plist
|
|
||||||
files.""")
|
|
||||||
# TODO: implement '-view '
|
|
||||||
|
|
||||||
advanced = parser.add_argument_group('advanced options')
|
|
||||||
advanced.add_argument(
|
|
||||||
'--keep-empty',
|
|
||||||
action='store_true',
|
|
||||||
help="""Don't remove the build results directory even if no issues
|
|
||||||
were reported.""")
|
|
||||||
advanced.add_argument(
|
|
||||||
'--no-failure-reports', '-no-failure-reports',
|
|
||||||
dest='output_failures',
|
|
||||||
action='store_false',
|
|
||||||
help="""Do not create a 'failures' subdirectory that includes analyzer
|
|
||||||
crash reports and preprocessed source files.""")
|
|
||||||
advanced.add_argument(
|
|
||||||
'--stats', '-stats',
|
|
||||||
action='store_true',
|
|
||||||
help="""Generates visitation statistics for the project being analyzed.
|
|
||||||
""")
|
|
||||||
advanced.add_argument(
|
|
||||||
'--internal-stats',
|
|
||||||
action='store_true',
|
|
||||||
help="""Generate internal analyzer statistics.""")
|
|
||||||
advanced.add_argument(
|
|
||||||
'--maxloop', '-maxloop',
|
|
||||||
metavar='<loop count>',
|
|
||||||
type=int,
|
|
||||||
help="""Specifiy the number of times a block can be visited before
|
|
||||||
giving up. Increase for more comprehensive coverage at a cost
|
|
||||||
of speed.""")
|
|
||||||
advanced.add_argument(
|
|
||||||
'--store', '-store',
|
|
||||||
metavar='<model>',
|
|
||||||
dest='store_model',
|
|
||||||
choices=['region', 'basic'],
|
|
||||||
help="""Specify the store model used by the analyzer.
|
|
||||||
'region' specifies a field- sensitive store model.
|
|
||||||
'basic' which is far less precise but can more quickly
|
|
||||||
analyze code. 'basic' was the default store model for
|
|
||||||
checker-0.221 and earlier.""")
|
|
||||||
advanced.add_argument(
|
|
||||||
'--constraints', '-constraints',
|
|
||||||
metavar='<model>',
|
|
||||||
dest='constraints_model',
|
|
||||||
choices=['range', 'basic'],
|
|
||||||
help="""Specify the contraint engine used by the analyzer. Specifying
|
|
||||||
'basic' uses a simpler, less powerful constraint model used by
|
|
||||||
checker-0.160 and earlier.""")
|
|
||||||
advanced.add_argument(
|
|
||||||
'--use-analyzer',
|
|
||||||
metavar='<path>',
|
|
||||||
dest='clang',
|
|
||||||
default='clang',
|
|
||||||
help="""'%(prog)s' uses the 'clang' executable relative to itself for
|
|
||||||
static analysis. One can override this behavior with this
|
|
||||||
option by using the 'clang' packaged with Xcode (on OS X) or
|
|
||||||
from the PATH.""")
|
|
||||||
advanced.add_argument(
|
|
||||||
'--use-cc',
|
|
||||||
metavar='<path>',
|
|
||||||
dest='cc',
|
|
||||||
default='cc',
|
|
||||||
help="""When '%(prog)s' analyzes a project by interposing a "fake
|
|
||||||
compiler", which executes a real compiler for compilation and
|
|
||||||
do other tasks (to run the static analyzer or just record the
|
|
||||||
compiler invocation). Because of this interposing, '%(prog)s'
|
|
||||||
does not know what compiler your project normally uses.
|
|
||||||
Instead, it simply overrides the CC environment variable, and
|
|
||||||
guesses your default compiler.
|
|
||||||
|
|
||||||
If you need '%(prog)s' to use a specific compiler for
|
|
||||||
*compilation* then you can use this option to specify a path
|
|
||||||
to that compiler.""")
|
|
||||||
advanced.add_argument(
|
|
||||||
'--use-c++',
|
|
||||||
metavar='<path>',
|
|
||||||
dest='cxx',
|
|
||||||
default='c++',
|
|
||||||
help="""This is the same as "--use-cc" but for C++ code.""")
|
|
||||||
advanced.add_argument(
|
|
||||||
'--analyzer-config', '-analyzer-config',
|
|
||||||
metavar='<options>',
|
|
||||||
help="""Provide options to pass through to the analyzer's
|
|
||||||
-analyzer-config flag. Several options are separated with
|
|
||||||
comma: 'key1=val1,key2=val2'
|
|
||||||
|
|
||||||
Available options:
|
|
||||||
stable-report-filename=true or false (default)
|
|
||||||
|
|
||||||
Switch the page naming to:
|
|
||||||
report-<filename>-<function/method name>-<id>.html
|
|
||||||
instead of report-XXXXXX.html""")
|
|
||||||
advanced.add_argument(
|
|
||||||
'--exclude',
|
|
||||||
metavar='<directory>',
|
|
||||||
dest='excludes',
|
|
||||||
action='append',
|
|
||||||
default=[],
|
|
||||||
help="""Do not run static analyzer against files found in this
|
|
||||||
directory. (You can specify this option multiple times.)
|
|
||||||
Could be usefull when project contains 3rd party libraries.
|
|
||||||
The directory path shall be absolute path as file names in
|
|
||||||
the compilation database.""")
|
|
||||||
advanced.add_argument(
|
|
||||||
'--force-analyze-debug-code',
|
|
||||||
dest='force_debug',
|
|
||||||
action='store_true',
|
|
||||||
help="""Tells analyzer to enable assertions in code even if they were
|
|
||||||
disabled during compilation, enabling more precise results.""")
|
|
||||||
|
|
||||||
plugins = parser.add_argument_group('checker options')
|
|
||||||
plugins.add_argument(
|
|
||||||
'--load-plugin', '-load-plugin',
|
|
||||||
metavar='<plugin library>',
|
|
||||||
dest='plugins',
|
|
||||||
action='append',
|
|
||||||
help="""Loading external checkers using the clang plugin interface.""")
|
|
||||||
plugins.add_argument(
|
|
||||||
'--enable-checker', '-enable-checker',
|
|
||||||
metavar='<checker name>',
|
|
||||||
action=AppendCommaSeparated,
|
|
||||||
help="""Enable specific checker.""")
|
|
||||||
plugins.add_argument(
|
|
||||||
'--disable-checker', '-disable-checker',
|
|
||||||
metavar='<checker name>',
|
|
||||||
action=AppendCommaSeparated,
|
|
||||||
help="""Disable specific checker.""")
|
|
||||||
plugins.add_argument(
|
|
||||||
'--help-checkers',
|
|
||||||
action='store_true',
|
|
||||||
help="""A default group of checkers is run unless explicitly disabled.
|
|
||||||
Exactly which checkers constitute the default group is a
|
|
||||||
function of the operating system in use. These can be printed
|
|
||||||
with this flag.""")
|
|
||||||
plugins.add_argument(
|
|
||||||
'--help-checkers-verbose',
|
|
||||||
action='store_true',
|
|
||||||
help="""Print all available checkers and mark the enabled ones.""")
|
|
||||||
|
|
||||||
if from_build_command:
|
|
||||||
parser.add_argument(
|
|
||||||
dest='build',
|
|
||||||
nargs=argparse.REMAINDER,
|
|
||||||
help="""Command to run.""")
|
|
||||||
|
|
||||||
return parser
|
|
||||||
|
|
||||||
|
|
||||||
class AppendCommaSeparated(argparse.Action):
|
|
||||||
""" argparse Action class to support multiple comma separated lists. """
|
|
||||||
|
|
||||||
def __call__(self, __parser, namespace, values, __option_string):
|
|
||||||
# getattr(obj, attr, default) does not really returns default but none
|
|
||||||
if getattr(namespace, self.dest, None) is None:
|
|
||||||
setattr(namespace, self.dest, [])
|
|
||||||
# once it's fixed we can use as expected
|
|
||||||
actual = getattr(namespace, self.dest)
|
|
||||||
actual.extend(values.split(','))
|
|
||||||
setattr(namespace, self.dest, actual)
|
|
||||||
|
|
|
@ -27,16 +27,16 @@ import re
|
||||||
import itertools
|
import itertools
|
||||||
import json
|
import json
|
||||||
import glob
|
import glob
|
||||||
import argparse
|
|
||||||
import logging
|
import logging
|
||||||
from libear import build_libear, TemporaryDirectory
|
from libear import build_libear, TemporaryDirectory
|
||||||
from libscanbuild import command_entry_point, compiler_wrapper, \
|
from libscanbuild import command_entry_point, compiler_wrapper, \
|
||||||
wrapper_environment, run_command, run_build, reconfigure_logging
|
wrapper_environment, run_command, run_build
|
||||||
from libscanbuild import duplicate_check, tempdir
|
from libscanbuild import duplicate_check, tempdir
|
||||||
from libscanbuild.compilation import split_command
|
from libscanbuild.compilation import split_command
|
||||||
|
from libscanbuild.arguments import parse_args_for_intercept_build
|
||||||
from libscanbuild.shell import encode, decode
|
from libscanbuild.shell import encode, decode
|
||||||
|
|
||||||
__all__ = ['capture', 'intercept_build_main', 'intercept_compiler_wrapper']
|
__all__ = ['capture', 'intercept_build', 'intercept_compiler_wrapper']
|
||||||
|
|
||||||
GS = chr(0x1d)
|
GS = chr(0x1d)
|
||||||
RS = chr(0x1e)
|
RS = chr(0x1e)
|
||||||
|
@ -49,23 +49,14 @@ WRAPPER_ONLY_PLATFORMS = frozenset({'win32', 'cygwin'})
|
||||||
|
|
||||||
|
|
||||||
@command_entry_point
|
@command_entry_point
|
||||||
def intercept_build_main(bin_dir):
|
def intercept_build():
|
||||||
""" Entry point for 'intercept-build' command. """
|
""" Entry point for 'intercept-build' command. """
|
||||||
|
|
||||||
parser = create_parser()
|
args = parse_args_for_intercept_build()
|
||||||
args = parser.parse_args()
|
return capture(args)
|
||||||
|
|
||||||
reconfigure_logging(args.verbose)
|
|
||||||
logging.debug('Raw arguments %s', sys.argv)
|
|
||||||
|
|
||||||
if not args.build:
|
|
||||||
parser.print_help()
|
|
||||||
return 0
|
|
||||||
|
|
||||||
return capture(args, bin_dir)
|
|
||||||
|
|
||||||
|
|
||||||
def capture(args, bin_dir):
|
def capture(args):
|
||||||
""" The entry point of build command interception. """
|
""" The entry point of build command interception. """
|
||||||
|
|
||||||
def post_processing(commands):
|
def post_processing(commands):
|
||||||
|
@ -95,7 +86,7 @@ def capture(args, bin_dir):
|
||||||
|
|
||||||
with TemporaryDirectory(prefix='intercept-', dir=tempdir()) as tmp_dir:
|
with TemporaryDirectory(prefix='intercept-', dir=tempdir()) as tmp_dir:
|
||||||
# run the build command
|
# run the build command
|
||||||
environment = setup_environment(args, tmp_dir, bin_dir)
|
environment = setup_environment(args, tmp_dir)
|
||||||
exit_code = run_build(args.build, env=environment)
|
exit_code = run_build(args.build, env=environment)
|
||||||
# read the intercepted exec calls
|
# read the intercepted exec calls
|
||||||
exec_traces = itertools.chain.from_iterable(
|
exec_traces = itertools.chain.from_iterable(
|
||||||
|
@ -109,7 +100,7 @@ def capture(args, bin_dir):
|
||||||
return exit_code
|
return exit_code
|
||||||
|
|
||||||
|
|
||||||
def setup_environment(args, destination, bin_dir):
|
def setup_environment(args, destination):
|
||||||
""" Sets up the environment for the build command.
|
""" Sets up the environment for the build command.
|
||||||
|
|
||||||
It sets the required environment variables and execute the given command.
|
It sets the required environment variables and execute the given command.
|
||||||
|
@ -129,8 +120,8 @@ def setup_environment(args, destination, bin_dir):
|
||||||
logging.debug('intercept gonna use compiler wrappers')
|
logging.debug('intercept gonna use compiler wrappers')
|
||||||
environment.update(wrapper_environment(args))
|
environment.update(wrapper_environment(args))
|
||||||
environment.update({
|
environment.update({
|
||||||
'CC': os.path.join(bin_dir, COMPILER_WRAPPER_CC),
|
'CC': COMPILER_WRAPPER_CC,
|
||||||
'CXX': os.path.join(bin_dir, COMPILER_WRAPPER_CXX)
|
'CXX': COMPILER_WRAPPER_CXX
|
||||||
})
|
})
|
||||||
elif sys.platform == 'darwin':
|
elif sys.platform == 'darwin':
|
||||||
logging.debug('intercept gonna preload libear on OSX')
|
logging.debug('intercept gonna preload libear on OSX')
|
||||||
|
@ -270,62 +261,3 @@ def entry_hash(entry):
|
||||||
command = ' '.join(decode(entry['command'])[1:])
|
command = ' '.join(decode(entry['command'])[1:])
|
||||||
|
|
||||||
return '<>'.join([filename, directory, command])
|
return '<>'.join([filename, directory, command])
|
||||||
|
|
||||||
|
|
||||||
def create_parser():
|
|
||||||
""" Command line argument parser factory method. """
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(
|
|
||||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
'--verbose', '-v',
|
|
||||||
action='count',
|
|
||||||
default=0,
|
|
||||||
help="""Enable verbose output from '%(prog)s'. A second and third
|
|
||||||
flag increases verbosity.""")
|
|
||||||
parser.add_argument(
|
|
||||||
'--cdb',
|
|
||||||
metavar='<file>',
|
|
||||||
default="compile_commands.json",
|
|
||||||
help="""The JSON compilation database.""")
|
|
||||||
group = parser.add_mutually_exclusive_group()
|
|
||||||
group.add_argument(
|
|
||||||
'--append',
|
|
||||||
action='store_true',
|
|
||||||
help="""Append new entries to existing compilation database.""")
|
|
||||||
|
|
||||||
advanced = parser.add_argument_group('advanced options')
|
|
||||||
advanced.add_argument(
|
|
||||||
'--override-compiler',
|
|
||||||
action='store_true',
|
|
||||||
help="""Always resort to the compiler wrapper even when better
|
|
||||||
intercept methods are available.""")
|
|
||||||
advanced.add_argument(
|
|
||||||
'--use-cc',
|
|
||||||
metavar='<path>',
|
|
||||||
dest='cc',
|
|
||||||
default='cc',
|
|
||||||
help="""When '%(prog)s' analyzes a project by interposing a compiler
|
|
||||||
wrapper, which executes a real compiler for compilation and
|
|
||||||
do other tasks (record the compiler invocation). Because of
|
|
||||||
this interposing, '%(prog)s' does not know what compiler your
|
|
||||||
project normally uses. Instead, it simply overrides the CC
|
|
||||||
environment variable, and guesses your default compiler.
|
|
||||||
|
|
||||||
If you need '%(prog)s' to use a specific compiler for
|
|
||||||
*compilation* then you can use this option to specify a path
|
|
||||||
to that compiler.""")
|
|
||||||
advanced.add_argument(
|
|
||||||
'--use-c++',
|
|
||||||
metavar='<path>',
|
|
||||||
dest='cxx',
|
|
||||||
default='c++',
|
|
||||||
help="""This is the same as "--use-cc" but for C++ code.""")
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
dest='build',
|
|
||||||
nargs=argparse.REMAINDER,
|
|
||||||
help="""Command to run.""")
|
|
||||||
|
|
||||||
return parser
|
|
||||||
|
|
Loading…
Reference in New Issue