• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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"""
7Runs Coverity Prevent on a build of Chromium.
8
9This script should be run in a Visual Studio Command Prompt, so that the
10INCLUDE, LIB, and PATH environment variables are set properly for Visual
11Studio.
12
13Usage 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
20For a full list of options, pass the '--help' switch.
21
22See http://support.microsoft.com/kb/308569 for running this script as a
23Scheduled Task on Windows XP.
24
25"""
26
27import optparse
28import os
29import os.path
30import shutil
31import subprocess
32import sys
33import 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
43CHROMIUM_SOURCE_DIR = 'C:\\chromium.latest'
44
45# Relative to CHROMIUM_SOURCE_DIR.
46CHROMIUM_SOLUTION_FILE = 'src\\chrome\\chrome.sln'
47
48# Relative to CHROMIUM_SOURCE_DIR.
49CHROMIUM_SOLUTION_DIR = 'src\\chrome'
50
51COVERITY_BIN_DIR = 'C:\\coverity\\prevent-win32-4.5.1\\bin'
52
53COVERITY_INTERMEDIATE_DIR = 'C:\\coverity\\cvbuild\\cr_int'
54
55COVERITY_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
66COVERITY_REMOTE = 'chromecoverity-linux1'
67
68COVERITY_PORT = '5467'
69
70COVERITY_PRODUCT = 'Chromium'
71
72COVERITY_TARGET = 'Windows'
73
74COVERITY_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.
78LOCK_FILE = 'coverity.lock'
79
80
81def _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
89def _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
99def _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
105def 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
237def 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
307if '__main__' == __name__:
308  sys.exit(main())
309