Home | History | Annotate | Download | only in coverity
      1 #!/usr/bin/env python
      2 # Copyright (c) 2011 The Chromium Authors. All rights reserved.
      3 # Use of this source code is governed by a BSD-style license that can be
      4 # found in the LICENSE file.
      5 
      6 """
      7 Runs Coverity Prevent on a build of Chromium.
      8 
      9 This script should be run in a Visual Studio Command Prompt, so that the
     10 INCLUDE, LIB, and PATH environment variables are set properly for Visual
     11 Studio.
     12 
     13 Usage examples:
     14   coverity.py
     15   coverity.py --dry-run
     16   coverity.py --target=debug
     17   %comspec% /c ""C:\Program Files\Microsoft Visual Studio 8\VC\vcvarsall.bat"
     18       x86 && C:\Python24\python.exe C:\coverity.py"
     19 
     20 For a full list of options, pass the '--help' switch.
     21 
     22 See http://support.microsoft.com/kb/308569 for running this script as a
     23 Scheduled Task on Windows XP.
     24 
     25 """
     26 
     27 import optparse
     28 import os
     29 import os.path
     30 import shutil
     31 import subprocess
     32 import sys
     33 import time
     34 
     35 # These constants provide default values, but are exposed as command-line
     36 # flags. See the --help for more info. Note that for historical reasons
     37 # (the script started out as Windows-only and has legacy usages which pre-date
     38 # these switches), the constants are all tuned for Windows.
     39 # Usage of this script on Linux pretty much requires explicit
     40 # --source-dir, --coverity-bin-dir, --coverity-intermediate-dir, and
     41 # --coverity-target command line flags.
     42 
     43 CHROMIUM_SOURCE_DIR = 'C:\\chromium.latest'
     44 
     45 # Relative to CHROMIUM_SOURCE_DIR.
     46 CHROMIUM_SOLUTION_FILE = 'src\\chrome\\chrome.sln'
     47 
     48 # Relative to CHROMIUM_SOURCE_DIR.
     49 CHROMIUM_SOLUTION_DIR = 'src\\chrome'
     50 
     51 COVERITY_BIN_DIR = 'C:\\coverity\\prevent-win32-4.5.1\\bin'
     52 
     53 COVERITY_INTERMEDIATE_DIR = 'C:\\coverity\\cvbuild\\cr_int'
     54 
     55 COVERITY_ANALYZE_OPTIONS = ('--cxx --security --concurrency '
     56                             '--enable ATOMICITY '
     57                             '--enable MISSING_LOCK '
     58                             '--enable DELETE_VOID '
     59                             '--checker-option PASS_BY_VALUE:size_threshold:16 '
     60                             '--checker-option '
     61                             'USE_AFTER_FREE:allow_simple_use:false '
     62                             '--enable-constraint-fpp '
     63                             '--enable-callgraph-metrics')
     64 
     65 # Might need to be changed to FQDN
     66 COVERITY_REMOTE = 'chromecoverity-linux1'
     67 
     68 COVERITY_PORT = '5467'
     69 
     70 COVERITY_PRODUCT = 'Chromium'
     71 
     72 COVERITY_TARGET = 'Windows'
     73 
     74 COVERITY_USER = 'admin'
     75 # looking for a PASSWORD constant? Look at --coverity-password-file instead.
     76 
     77 # Relative to CHROMIUM_SOURCE_DIR.  Contains the pid of this script.
     78 LOCK_FILE = 'coverity.lock'
     79 
     80 
     81 def _ReadPassword(pwfilename):
     82   """Reads the coverity password in from a file where it was stashed"""
     83   pwfile = open(pwfilename, 'r')
     84   password = pwfile.readline()
     85   pwfile.close()
     86   return password.rstrip()
     87 
     88 
     89 def _RunCommand(cmd, dry_run, shell=False, echo_cmd=True):
     90   """Runs the command if dry_run is false, otherwise just prints the command."""
     91   if echo_cmd:
     92     print cmd
     93   if not dry_run:
     94     return subprocess.call(cmd, shell=shell)
     95   else:
     96     return 0
     97 
     98 
     99 def _ReleaseLock(lock_file, lock_filename):
    100   """Removes the lockfile. Function-ized so we can bail from anywhere"""
    101   os.close(lock_file)
    102   os.remove(lock_filename)
    103 
    104 
    105 def run_coverity(options, args):
    106   """Runs all the selected tests for the given build type and target."""
    107   # Create the lock file to prevent another instance of this script from
    108   # running.
    109   lock_filename = os.path.join(options.source_dir, LOCK_FILE)
    110   try:
    111     lock_file = os.open(lock_filename,
    112                         os.O_CREAT | os.O_EXCL | os.O_TRUNC | os.O_RDWR)
    113   except OSError, err:
    114     print 'Failed to open lock file:\n  ' + str(err)
    115     return 1
    116 
    117   # Write the pid of this script (the python.exe process) to the lock file.
    118   os.write(lock_file, str(os.getpid()))
    119 
    120   options.target = options.target.title()
    121 
    122   start_time = time.time()
    123 
    124   print 'Change directory to ' + options.source_dir
    125   os.chdir(options.source_dir)
    126 
    127   # The coverity-password filename may have been a relative path.
    128   # If so, assume it's relative to the source directory, which means
    129   # the time to read the password is after we do the chdir().
    130   coverity_password = _ReadPassword(options.coverity_password_file)
    131 
    132   cmd = 'gclient sync'
    133   gclient_exit = _RunCommand(cmd, options.dry_run, shell=True)
    134   if gclient_exit != 0:
    135     print 'gclient aborted with status %s' % gclient_exit
    136     _ReleaseLock(lock_file, lock_filename)
    137     return 1
    138 
    139   print 'Elapsed time: %ds' % (time.time() - start_time)
    140 
    141   # Do a clean build.  Remove the build output directory first.
    142   if sys.platform.startswith('linux'):
    143     rm_path = os.path.join(options.source_dir,'src','out',options.target)
    144   elif sys.platform == 'win32':
    145     rm_path = os.path.join(options.source_dir,options.solution_dir,
    146                            options.target)
    147   elif sys.platform == 'darwin':
    148     rm_path = os.path.join(options.source_dir,'src','xcodebuild')
    149   else:
    150     print 'Platform "%s" unrecognized, aborting' % sys.platform
    151     _ReleaseLock(lock_file, lock_filename)
    152     return 1
    153 
    154   if options.dry_run:
    155     print 'shutil.rmtree(%s)' % repr(rm_path)
    156   else:
    157     shutil.rmtree(rm_path,True)
    158 
    159   if options.preserve_intermediate_dir:
    160       print 'Preserving intermediate directory.'
    161   else:
    162     if options.dry_run:
    163       print 'shutil.rmtree(%s)' % repr(options.coverity_intermediate_dir)
    164       print 'os.mkdir(%s)' % repr(options.coverity_intermediate_dir)
    165     else:
    166       shutil.rmtree(options.coverity_intermediate_dir,True)
    167       os.mkdir(options.coverity_intermediate_dir)
    168 
    169   print 'Elapsed time: %ds' % (time.time() - start_time)
    170 
    171   use_shell_during_make = False
    172   if sys.platform.startswith('linux'):
    173     use_shell_during_make = True
    174     os.chdir('src')
    175     _RunCommand('pwd', options.dry_run, shell=True)
    176     cmd = '%s/cov-build --dir %s make BUILDTYPE=%s chrome' % (
    177       options.coverity_bin_dir, options.coverity_intermediate_dir,
    178       options.target)
    179   elif sys.platform == 'win32':
    180     cmd = ('%s\\cov-build.exe --dir %s devenv.com %s\\%s /build %s '
    181            '/project chrome.vcproj') % (
    182       options.coverity_bin_dir, options.coverity_intermediate_dir,
    183       options.source_dir, options.solution_file, options.target)
    184   elif sys.platform == 'darwin':
    185     use_shell_during_make = True
    186     os.chdir('src/chrome')
    187     _RunCommand('pwd', options.dry_run, shell=True)
    188     cmd = ('%s/cov-build --dir %s xcodebuild -project chrome.xcodeproj '
    189            '-configuration %s -target chrome') % (
    190       options.coverity_bin_dir, options.coverity_intermediate_dir,
    191       options.target)
    192 
    193 
    194   _RunCommand(cmd, options.dry_run, shell=use_shell_during_make)
    195   print 'Elapsed time: %ds' % (time.time() - start_time)
    196 
    197   cov_analyze_exe = os.path.join(options.coverity_bin_dir,'cov-analyze')
    198   cmd = '%s --dir %s %s' % (cov_analyze_exe,
    199                             options.coverity_intermediate_dir,
    200                             options.coverity_analyze_options)
    201   _RunCommand(cmd, options.dry_run, shell=use_shell_during_make)
    202   print 'Elapsed time: %ds' % (time.time() - start_time)
    203 
    204   cov_commit_exe = os.path.join(options.coverity_bin_dir,'cov-commit-defects')
    205 
    206   # On Linux we have started using a Target with a space in it, so we want
    207   # to quote it. On the other hand, Windows quoting doesn't work quite the
    208   # same way. To be conservative, I'd like to avoid quoting an argument
    209   # that doesn't need quoting and which we haven't historically been quoting
    210   # on that platform. So, only quote the target if we have to.
    211   coverity_target = options.coverity_target
    212   if sys.platform != 'win32':
    213     coverity_target = '"%s"' % coverity_target
    214 
    215   cmd = ('%s --dir %s --remote %s --port %s '
    216          '--product %s '
    217          '--target %s '
    218          '--user %s '
    219          '--password %s') % (cov_commit_exe,
    220                              options.coverity_intermediate_dir,
    221                              options.coverity_dbhost,
    222                              options.coverity_port,
    223                              options.coverity_product,
    224                              coverity_target,
    225                              options.coverity_user,
    226                              coverity_password)
    227   # Avoid echoing the Commit command because it has a password in it
    228   _RunCommand(cmd, options.dry_run, shell=use_shell_during_make, echo_cmd=False)
    229 
    230   print 'Total time: %ds' % (time.time() - start_time)
    231 
    232   _ReleaseLock(lock_file, lock_filename)
    233 
    234   return 0
    235 
    236 
    237 def main():
    238   option_parser = optparse.OptionParser()
    239   option_parser.add_option('', '--dry-run', action='store_true', default=False,
    240                            help='print but don\'t run the commands')
    241 
    242   option_parser.add_option('', '--target', default='Release',
    243                            help='build target (Debug or Release)')
    244 
    245   option_parser.add_option('', '--source-dir', dest='source_dir',
    246                            help='full path to directory ABOVE "src"',
    247                            default=CHROMIUM_SOURCE_DIR)
    248 
    249   option_parser.add_option('', '--solution-file', dest='solution_file',
    250                            default=CHROMIUM_SOLUTION_FILE)
    251 
    252   option_parser.add_option('', '--solution-dir', dest='solution_dir',
    253                            default=CHROMIUM_SOLUTION_DIR)
    254 
    255   option_parser.add_option('', '--coverity-bin-dir', dest='coverity_bin_dir',
    256                            default=COVERITY_BIN_DIR)
    257 
    258   option_parser.add_option('', '--coverity-intermediate-dir',
    259                            dest='coverity_intermediate_dir',
    260                            default=COVERITY_INTERMEDIATE_DIR)
    261 
    262   option_parser.add_option('', '--coverity-analyze-options',
    263                            dest='coverity_analyze_options',
    264                            help=('all cov-analyze options, e.g. "%s"'
    265                                  % COVERITY_ANALYZE_OPTIONS),
    266                            default=COVERITY_ANALYZE_OPTIONS)
    267 
    268   option_parser.add_option('', '--coverity-db-host',
    269                            dest='coverity_dbhost',
    270                            help=('coverity defect db server hostname, e.g. %s'
    271                                  % COVERITY_REMOTE),
    272                            default=COVERITY_REMOTE)
    273 
    274   option_parser.add_option('', '--coverity-db-port', dest='coverity_port',
    275                            help=('port # of coverity web/db server, e.g. %s'
    276                                  % COVERITY_PORT),
    277                            default=COVERITY_PORT)
    278 
    279   option_parser.add_option('', '--coverity-product', dest='coverity_product',
    280                            help=('Product name reported to coverity, e.g. %s'
    281                                  % COVERITY_PRODUCT),
    282                            default=COVERITY_PRODUCT)
    283 
    284   option_parser.add_option('', '--coverity-target', dest='coverity_target',
    285                            help='Platform Target reported to coverity',
    286                            default=COVERITY_TARGET)
    287 
    288   option_parser.add_option('', '--coverity-user', dest='coverity_user',
    289                            help='Username used to log into coverity',
    290                            default=COVERITY_USER)
    291 
    292   option_parser.add_option('', '--coverity-password-file',
    293                            dest='coverity_password_file',
    294                            help='file containing the coverity password',
    295                            default='coverity-password')
    296 
    297   helpmsg = ('By default, the intermediate dir is emptied before analysis. '
    298              'This switch disables that behavior.')
    299   option_parser.add_option('', '--preserve-intermediate-dir',
    300                            action='store_true', help=helpmsg,
    301                            default=False)
    302 
    303   options, args = option_parser.parse_args()
    304   return run_coverity(options, args)
    305 
    306 
    307 if '__main__' == __name__:
    308   sys.exit(main())
    309