r297307 - [scan-build-py] move argument parsing into separate module

Laszlo Nagy via cfe-commits cfe-commits at lists.llvm.org
Wed Mar 8 13:18:52 PST 2017


Author: rizsotto
Date: Wed Mar  8 15:18:51 2017
New Revision: 297307

URL: http://llvm.org/viewvc/llvm-project?rev=297307&view=rev
Log:
[scan-build-py] move argument parsing into separate module

Differential Revision: https://reviews.llvm.org/D30601

Modified:
    cfe/trunk/tools/scan-build-py/bin/analyze-build
    cfe/trunk/tools/scan-build-py/bin/intercept-build
    cfe/trunk/tools/scan-build-py/bin/scan-build
    cfe/trunk/tools/scan-build-py/libscanbuild/analyze.py
    cfe/trunk/tools/scan-build-py/libscanbuild/intercept.py

Modified: cfe/trunk/tools/scan-build-py/bin/analyze-build
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/scan-build-py/bin/analyze-build?rev=297307&r1=297306&r2=297307&view=diff
==============================================================================
--- cfe/trunk/tools/scan-build-py/bin/analyze-build (original)
+++ cfe/trunk/tools/scan-build-py/bin/analyze-build Wed Mar  8 15:18:51 2017
@@ -13,5 +13,5 @@ import os.path
 this_dir = os.path.dirname(os.path.realpath(__file__))
 sys.path.append(os.path.dirname(this_dir))
 
-from libscanbuild.analyze import analyze_build_main
-sys.exit(analyze_build_main(this_dir, False))
+from libscanbuild.analyze import analyze_build
+sys.exit(analyze_build())

Modified: cfe/trunk/tools/scan-build-py/bin/intercept-build
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/scan-build-py/bin/intercept-build?rev=297307&r1=297306&r2=297307&view=diff
==============================================================================
--- cfe/trunk/tools/scan-build-py/bin/intercept-build (original)
+++ cfe/trunk/tools/scan-build-py/bin/intercept-build Wed Mar  8 15:18:51 2017
@@ -13,5 +13,5 @@ import os.path
 this_dir = os.path.dirname(os.path.realpath(__file__))
 sys.path.append(os.path.dirname(this_dir))
 
-from libscanbuild.intercept import intercept_build_main
-sys.exit(intercept_build_main(this_dir))
+from libscanbuild.intercept import intercept_build
+sys.exit(intercept_build())

Modified: cfe/trunk/tools/scan-build-py/bin/scan-build
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/scan-build-py/bin/scan-build?rev=297307&r1=297306&r2=297307&view=diff
==============================================================================
--- cfe/trunk/tools/scan-build-py/bin/scan-build (original)
+++ cfe/trunk/tools/scan-build-py/bin/scan-build Wed Mar  8 15:18:51 2017
@@ -13,5 +13,5 @@ import os.path
 this_dir = os.path.dirname(os.path.realpath(__file__))
 sys.path.append(os.path.dirname(this_dir))
 
-from libscanbuild.analyze import analyze_build_main
-sys.exit(analyze_build_main(this_dir, True))
+from libscanbuild.analyze import scan_build
+sys.exit(scan_build())

Modified: cfe/trunk/tools/scan-build-py/libscanbuild/analyze.py
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/scan-build-py/libscanbuild/analyze.py?rev=297307&r1=297306&r2=297307&view=diff
==============================================================================
--- cfe/trunk/tools/scan-build-py/libscanbuild/analyze.py (original)
+++ cfe/trunk/tools/scan-build-py/libscanbuild/analyze.py Wed Mar  8 15:18:51 2017
@@ -11,73 +11,68 @@ To run the static analyzer against a bui
  -- Analyze:   run the analyzer against the captured commands,
  -- Report:    create a cover report from the analyzer outputs.  """
 
-import sys
 import re
 import os
 import os.path
 import json
-import argparse
 import logging
 import tempfile
 import multiprocessing
 import contextlib
 import datetime
 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.intercept import capture
 from libscanbuild.report import document
-from libscanbuild.clang import get_checkers
 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_CXX = 'analyze-c++'
 
 
 @command_entry_point
-def analyze_build_main(bin_dir, from_build_command):
-    """ Entry point for 'analyze-build' and 'scan-build'. """
-
-    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)
+def scan_build():
+    """ Entry point for scan-build command. """
 
+    args = parse_args_for_scan_build()
     with report_directory(args.output, args.keep_empty) as target_dir:
-        if not from_build_command:
-            # run analyzer only and generate cover report
-            run_analyzer(args, target_dir)
-            number_of_bugs = document(args, target_dir, True)
-            return number_of_bugs if args.status_bugs else 0
-        elif args.intercept_first:
-            # run build command and capture compiler executions
-            exit_code = capture(args, bin_dir)
-            # next step to run the analyzer against the captured commands
+        # Run against a build command. there are cases, when analyzer run
+        # is not required. But we need to set up everything for the
+        # wrappers, because 'configure' needs to capture the CC/CXX values
+        # for the Makefile.
+        if args.intercept_first:
+            # Run build command with intercept module.
+            exit_code = capture(args)
+            # Run the analyzer against the captured commands.
             if need_analyzer(args.build):
                 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:
-            # run the build command with compiler wrappers which
-            # execute the analyzer too. (interposition)
-            environment = setup_environment(args, target_dir, bin_dir)
+            # Run build command and analyzer with compiler wrappers.
+            environment = setup_environment(args, target_dir)
             exit_code = run_build(args.build, env=environment)
-            # cover report generation and bug counting
-            number_of_bugs = document(args, target_dir, False)
-            # set exit status as it was requested
-            return number_of_bugs if args.status_bugs else exit_code
+        # Cover report generation and bug counting.
+        number_of_bugs = document(args, target_dir, False)
+        # Set exit status as it was requested.
+        return number_of_bugs if args.status_bugs else exit_code
+
+
+ at 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):
@@ -125,14 +120,14 @@ def run_analyzer(args, output_dir):
         pool.join()
 
 
-def setup_environment(args, destination, bin_dir):
+def setup_environment(args, destination):
     """ Set up environment for build command to interpose compiler wrapper. """
 
     environment = dict(os.environ)
     environment.update(wrapper_environment(args))
     environment.update({
-        'CC': os.path.join(bin_dir, COMPILER_WRAPPER_CC),
-        'CXX': os.path.join(bin_dir, COMPILER_WRAPPER_CXX),
+        'CC': COMPILER_WRAPPER_CC,
+        'CXX': COMPILER_WRAPPER_CXX,
         'ANALYZE_BUILD_CLANG': args.clang if need_analyzer(args.build) else '',
         'ANALYZE_BUILD_REPORT_DIR': destination,
         'ANALYZE_BUILD_REPORT_FORMAT': args.output_format,
@@ -262,281 +257,3 @@ def analyzer_params(args):
         result.append('-analyzer-viz-egraph-ubigraph')
 
     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)

Modified: cfe/trunk/tools/scan-build-py/libscanbuild/intercept.py
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/scan-build-py/libscanbuild/intercept.py?rev=297307&r1=297306&r2=297307&view=diff
==============================================================================
--- cfe/trunk/tools/scan-build-py/libscanbuild/intercept.py (original)
+++ cfe/trunk/tools/scan-build-py/libscanbuild/intercept.py Wed Mar  8 15:18:51 2017
@@ -27,16 +27,16 @@ import re
 import itertools
 import json
 import glob
-import argparse
 import logging
 from libear import build_libear, TemporaryDirectory
 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.compilation import split_command
+from libscanbuild.arguments import parse_args_for_intercept_build
 from libscanbuild.shell import encode, decode
 
-__all__ = ['capture', 'intercept_build_main', 'intercept_compiler_wrapper']
+__all__ = ['capture', 'intercept_build', 'intercept_compiler_wrapper']
 
 GS = chr(0x1d)
 RS = chr(0x1e)
@@ -49,23 +49,14 @@ WRAPPER_ONLY_PLATFORMS = frozenset({'win
 
 
 @command_entry_point
-def intercept_build_main(bin_dir):
+def intercept_build():
     """ Entry point for 'intercept-build' command. """
 
-    parser = create_parser()
-    args = parser.parse_args()
+    args = parse_args_for_intercept_build()
+    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. """
 
     def post_processing(commands):
@@ -95,7 +86,7 @@ def capture(args, bin_dir):
 
     with TemporaryDirectory(prefix='intercept-', dir=tempdir()) as tmp_dir:
         # 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)
         # read the intercepted exec calls
         exec_traces = itertools.chain.from_iterable(
@@ -109,7 +100,7 @@ def capture(args, bin_dir):
         return exit_code
 
 
-def setup_environment(args, destination, bin_dir):
+def setup_environment(args, destination):
     """ Sets up the environment for the build command.
 
     It sets the required environment variables and execute the given command.
@@ -129,8 +120,8 @@ def setup_environment(args, destination,
         logging.debug('intercept gonna use compiler wrappers')
         environment.update(wrapper_environment(args))
         environment.update({
-            'CC': os.path.join(bin_dir, COMPILER_WRAPPER_CC),
-            'CXX': os.path.join(bin_dir, COMPILER_WRAPPER_CXX)
+            'CC': COMPILER_WRAPPER_CC,
+            'CXX': COMPILER_WRAPPER_CXX
         })
     elif sys.platform == 'darwin':
         logging.debug('intercept gonna preload libear on OSX')
@@ -270,62 +261,3 @@ def entry_hash(entry):
     command = ' '.join(decode(entry['command'])[1:])
 
     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




More information about the cfe-commits mailing list