From aa289f879bcb55a2ee90e8cc024b93c842753d34 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Thu, 12 May 2016 15:04:15 +0000 Subject: [PATCH] Ship scan-build-py --- debian/changelog | 1 + debian/clang-X.Y.install.in | 1 + debian/patches/install-scan-build-py.diff | 12 + debian/patches/scan-build-py-default.diff | 518 ++++++++++++++++++++++ debian/patches/series | 3 + 5 files changed, 535 insertions(+) create mode 100644 debian/patches/install-scan-build-py.diff create mode 100644 debian/patches/scan-build-py-default.diff diff --git a/debian/changelog b/debian/changelog index c8de7f9c..71858e63 100644 --- a/debian/changelog +++ b/debian/changelog @@ -12,6 +12,7 @@ llvm-toolchain-snapshot (1:3.9~svn260851-2) UNRELEASED; urgency=medium * Do not fail the build if the manpages cannot be built (failing on Ubuntu precise) * Install libfindAllSymbols.a as part of libclang-X.Y-dev + * Ship scan-build-py -- Sylvestre Ledru Wed, 09 Mar 2016 19:30:30 +0100 diff --git a/debian/clang-X.Y.install.in b/debian/clang-X.Y.install.in index 59bb2b4a..137ef986 100644 --- a/debian/clang-X.Y.install.in +++ b/debian/clang-X.Y.install.in @@ -10,6 +10,7 @@ usr/lib/llvm-@LLVM_VERSION@/bin/clang-cl usr/lib/llvm-@LLVM_VERSION@/bin/sancov usr/lib/llvm-@LLVM_VERSION@/share/scan-view/ usr/lib/llvm-@LLVM_VERSION@/share/scan-build/ +usr/lib/llvm-@LLVM_VERSION@/share/scan-build-py/ usr/lib/llvm-@LLVM_VERSION@/share/man/man1/scan-build.1 usr/lib/llvm-@LLVM_VERSION@/libexec/ccc-analyzer usr/lib/llvm-@LLVM_VERSION@/libexec/c++-analyzer diff --git a/debian/patches/install-scan-build-py.diff b/debian/patches/install-scan-build-py.diff new file mode 100644 index 00000000..05023207 --- /dev/null +++ b/debian/patches/install-scan-build-py.diff @@ -0,0 +1,12 @@ +Index: llvm-toolchain-snapshot_3.9~svn269220/clang/tools/CMakeLists.txt +=================================================================== +--- llvm-toolchain-snapshot_3.9~svn269220.orig/clang/tools/CMakeLists.txt ++++ llvm-toolchain-snapshot_3.9~svn269220/clang/tools/CMakeLists.txt +@@ -16,6 +16,7 @@ endif() + if(CLANG_ENABLE_STATIC_ANALYZER) + add_clang_subdirectory(clang-check) + add_clang_subdirectory(scan-build) ++ add_clang_subdirectory(scan-build-py) + add_clang_subdirectory(scan-view) + endif() + diff --git a/debian/patches/scan-build-py-default.diff b/debian/patches/scan-build-py-default.diff new file mode 100644 index 00000000..431bffbd --- /dev/null +++ b/debian/patches/scan-build-py-default.diff @@ -0,0 +1,518 @@ +Index: llvm-toolchain-snapshot_3.9-svn269220/home/sylvestre/dev/debian/pkg-llvm/llvm-toolchain-branches/llvm-toolchain-snapshot_3.9-svn269220/clang/tools/scan-build-py/libscanbuild/analyze.py +=================================================================== +--- llvm-toolchain-snapshot_3.9-svn269220.orig/home/sylvestre/dev/debian/pkg-llvm/llvm-toolchain-branches/llvm-toolchain-snapshot_3.9-svn269220/clang/tools/scan-build-py/libscanbuild/analyze.py ++++ /dev/null +@@ -1,513 +0,0 @@ +-# -*- coding: utf-8 -*- +-# The LLVM Compiler Infrastructure +-# +-# This file is distributed under the University of Illinois Open Source +-# License. See LICENSE.TXT for details. +-""" This module implements the 'scan-build' command API. +- +-To run the static analyzer against a build is done in multiple steps: +- +- -- Intercept: capture the compilation command during the build, +- -- 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 subprocess +-import multiprocessing +-from libscanbuild import initialize_logging, tempdir, command_entry_point +-from libscanbuild.runner import run +-from libscanbuild.intercept import capture +-from libscanbuild.report import report_directory, document +-from libscanbuild.clang import get_checkers +-from libscanbuild.compilation import split_command +- +-__all__ = ['analyze_build_main', 'analyze_build_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 +- initialize_logging(args.verbose) +- logging.debug('Parsed arguments: %s', args) +- +- 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 +- 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) +- logging.debug('run build in environment: %s', environment) +- exit_code = subprocess.call(args.build, env=environment) +- logging.debug('build finished with exit code: %d', 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 +- +- +-def need_analyzer(args): +- """ Check the intent of the build command. +- +- When static analyzer run against project configure step, it should be +- silent and no need to run the analyzer or generate report. +- +- To run `scan-build` against the configure step might be neccessary, +- when compiler wrappers are used. That's the moment when build setup +- check the compiler and capture the location for the build process. """ +- +- return len(args) and not re.search('configure|autogen', args[0]) +- +- +-def run_analyzer(args, output_dir): +- """ Runs the analyzer against the given compilation database. """ +- +- def exclude(filename): +- """ Return true when any excluded directory prefix the filename. """ +- return any(re.match(r'^' + directory, filename) +- for directory in args.excludes) +- +- consts = { +- 'clang': args.clang, +- 'output_dir': output_dir, +- 'output_format': args.output_format, +- 'output_failures': args.output_failures, +- 'direct_args': analyzer_params(args), +- 'force_debug': args.force_debug +- } +- +- logging.debug('run analyzer against compilation database') +- with open(args.cdb, 'r') as handle: +- generator = (dict(cmd, **consts) +- for cmd in json.load(handle) if not exclude(cmd['file'])) +- # when verbose output requested execute sequentially +- pool = multiprocessing.Pool(1 if args.verbose > 2 else None) +- for current in pool.imap_unordered(run, generator): +- if current is not None: +- # display error message from the static analyzer +- for line in current['error_output']: +- logging.info(line.rstrip()) +- pool.close() +- pool.join() +- +- +-def setup_environment(args, destination, bin_dir): +- """ Set up environment for build command to interpose compiler wrapper. """ +- +- environment = dict(os.environ) +- environment.update({ +- 'CC': os.path.join(bin_dir, COMPILER_WRAPPER_CC), +- 'CXX': os.path.join(bin_dir, COMPILER_WRAPPER_CXX), +- 'ANALYZE_BUILD_CC': args.cc, +- 'ANALYZE_BUILD_CXX': args.cxx, +- 'ANALYZE_BUILD_CLANG': args.clang if need_analyzer(args.build) else '', +- 'ANALYZE_BUILD_VERBOSE': 'DEBUG', +- 'ANALYZE_BUILD_REPORT_DIR': destination, +- 'ANALYZE_BUILD_REPORT_FORMAT': args.output_format, +- 'ANALYZE_BUILD_REPORT_FAILURES': 'yes' if args.output_failures else '', +- 'ANALYZE_BUILD_PARAMETERS': ' '.join(analyzer_params(args)), +- 'ANALYZE_BUILD_FORCE_DEBUG': 'yes' if args.force_debug else '' +- }) +- return environment +- +- +-def analyze_build_wrapper(cplusplus): +- """ Entry point for `analyze-cc` and `analyze-c++` compiler wrappers. """ +- +- # initialize wrapper logging +- logging.basicConfig(format='analyze: %(levelname)s: %(message)s', +- level=os.getenv('ANALYZE_BUILD_VERBOSE', 'INFO')) +- # execute with real compiler +- compiler = os.getenv('ANALYZE_BUILD_CXX', 'c++') if cplusplus \ +- else os.getenv('ANALYZE_BUILD_CC', 'cc') +- compilation = [compiler] + sys.argv[1:] +- logging.info('execute compiler: %s', compilation) +- result = subprocess.call(compilation) +- # exit when it fails, ... +- if result or not os.getenv('ANALYZE_BUILD_CLANG'): +- return result +- # ... and run the analyzer if all went well. +- try: +- # check is it a compilation +- compilation = split_command(sys.argv) +- if compilation is None: +- return result +- # collect the needed parameters from environment, crash when missing +- parameters = { +- 'clang': os.getenv('ANALYZE_BUILD_CLANG'), +- 'output_dir': os.getenv('ANALYZE_BUILD_REPORT_DIR'), +- 'output_format': os.getenv('ANALYZE_BUILD_REPORT_FORMAT'), +- 'output_failures': os.getenv('ANALYZE_BUILD_REPORT_FAILURES'), +- 'direct_args': os.getenv('ANALYZE_BUILD_PARAMETERS', +- '').split(' '), +- 'force_debug': os.getenv('ANALYZE_BUILD_FORCE_DEBUG'), +- 'directory': os.getcwd(), +- 'command': [sys.argv[0], '-c'] + compilation.flags +- } +- # call static analyzer against the compilation +- for source in compilation.files: +- parameters.update({'file': source}) +- logging.debug('analyzer parameters %s', parameters) +- current = run(parameters) +- # display error message from the static analyzer +- if current is not None: +- for line in current['error_output']: +- logging.info(line.rstrip()) +- except Exception: +- logging.exception("run analyzer inside compiler wrapper failed.") +- return result +- +- +-def analyzer_params(args): +- """ A group of command line arguments can mapped to command +- line arguments of the analyzer. This method generates those. """ +- +- def prefix_with(constant, pieces): +- """ From a sequence create another sequence where every second element +- is from the original sequence and the odd elements are the prefix. +- +- eg.: prefix_with(0, [1,2,3]) creates [0, 1, 0, 2, 0, 3] """ +- +- return [elem for piece in pieces for elem in [constant, piece]] +- +- result = [] +- +- if args.store_model: +- result.append('-analyzer-store={0}'.format(args.store_model)) +- if args.constraints_model: +- result.append('-analyzer-constraints={0}'.format( +- args.constraints_model)) +- if args.internal_stats: +- result.append('-analyzer-stats') +- if args.analyze_headers: +- result.append('-analyzer-opt-analyze-headers') +- if args.stats: +- result.append('-analyzer-checker=debug.Stats') +- if args.maxloop: +- result.extend(['-analyzer-max-loop', str(args.maxloop)]) +- if args.output_format: +- result.append('-analyzer-output={0}'.format(args.output_format)) +- if args.analyzer_config: +- result.append(args.analyzer_config) +- if args.verbose >= 4: +- result.append('-analyzer-display-progress') +- if args.plugins: +- result.extend(prefix_with('-load', args.plugins)) +- if args.enable_checker: +- checkers = ','.join(args.enable_checker) +- result.extend(['-analyzer-checker', checkers]) +- if args.disable_checker: +- checkers = ','.join(args.disable_checker) +- result.extend(['-analyzer-disable-checker', checkers]) +- if os.getenv('UBIVIZ'): +- 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. """ +- +- 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='', +- default="compile_commands.json", +- help="""The JSON compilation database.""") +- +- parser.add_argument( +- '--output', '-o', +- metavar='', +- 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='', +- 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) diff --git a/debian/patches/series b/debian/patches/series index 561718fc..c1ed9dc6 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -26,3 +26,6 @@ silent-MCJIIT-tests.diff clang-analyzer-force-version.diff llvm25468-lldb-swig-format-security.diff silent-test-transforms.diff +install-scan-build-py.diff +scan-build-py-default.diff +