blob: aa7be22dddd1859ad026bcde520f1effd9621594 [file] [log] [blame]
#!/usr/bin/env python
# Copyright (c) 2011 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""A tool to run croc to generate code coverage, executed by buildbot.
When this is run, the current directory (cwd) should be the outer build
directory (e.g., chrome-release/build/).
For a list of command-line options, call this script with '--help'.
"""
import optparse
import os
import re
import shutil
import subprocess
import sys
from common import chromium_utils
from slave import build_directory
COVERAGE_DIR_POSTFIX = '_coverage'
COVERAGE_INFO = 'coverage.info'
def RunCroc(cov_files, cmdline):
"""Common main() routine.
Args:
cov_files: the coverage file we will be working with.
cmdline: the command line to execute.
"""
# Print some details which may help debugging.
for cov_file in cov_files:
try:
print os.stat(cov_file)
except (OSError, IOError), e:
print 'Stat of %s failed. %s' % (cov_file, e)
# Croc.
print 'Running croc:', cmdline
result = subprocess.call(cmdline)
# Move aside the coverage file so a deps check always rebuilds it.
for cov_file in cov_files:
saved_name = cov_file + '.old'
print 'Moving %s to %s (if possible)' % (cov_file, saved_name)
if os.path.exists(saved_name):
os.remove(saved_name)
if os.path.exists(cov_file):
shutil.move(cov_file, saved_name)
return result
def CrocCommand(platform, cov_files, html_dir):
cmdline = [
sys.executable,
'src/tools/code_coverage/croc.py',
'-c', 'src/build/common.croc',
'-c', 'src/build/%s/chrome_%s.croc' % (platform, platform),
'-r', os.getcwd(),
'--tree',
'--html', html_dir]
for cov_file in cov_files:
cmdline += ['-i', cov_file]
return cmdline
def ReplaceCoveragePath(cov_file, build_dir):
"""Replace the coverage path to this system 'src' directory."""
src_dir = chromium_utils.AbsoluteCanonicalPath(
os.path.join(build_dir, '..'))
src_dir = os.path.normpath(src_dir)
input_file = open(cov_file)
cov_lines = input_file.readlines()
input_file.close()
fhandler = open(cov_file, 'w')
for line in cov_lines:
line = re.sub(r'SF:.*[\\/]src[\\/]', 'SF:%s/' % src_dir, line, 1)
line = line.replace('\\', '/')
fhandler.write(line)
fhandler.close()
def FetchCoverageFiles(options):
"""Fetch coverage files from www dir to be processed."""
target_dir = os.path.join(options.build_dir, options.target)
for test in options.tests:
cov_dir = test.replace('_', '') + COVERAGE_DIR_POSTFIX
output_path = os.path.join(target_dir, cov_dir)
chromium_utils.MaybeMakeDirectory(output_path)
cov_src = ('/%s/%s/%s/%s/' % (options.upload_dir, options.platform,
options.build_id, cov_dir))
if options.sharded_tests and test in options.sharded_tests:
# Copy all Lvoc files.
for shard_count in xrange(1, options.browser_total_shards + 1):
src = cov_src + 'coverage_%s.info' % shard_count
chromium_utils.CopyFileToDir(src, output_path)
else:
chromium_utils.CopyFileToDir(cov_src + COVERAGE_INFO, output_path)
def ProcessCoverage(options, args):
"""Print appropriate size information about built Windows targets.
Returns the first non-zero exit status of any command it executes,
or zero on success.
"""
# Fetch coverage from archive if we're running sharded tests.
if options.browser_total_shards:
FetchCoverageFiles(options)
# Process total coverage.
coverage_dirs = [x.replace('_', '') + COVERAGE_DIR_POSTFIX for x in
options.tests]
target_dir = os.path.join(options.build_dir, options.target)
cov_files = []
for cov in coverage_dirs:
cov_dir = os.path.join(target_dir, cov)
if os.path.exists(cov_dir):
print cov + ' directory exists.'
for fname in os.listdir(cov_dir):
if fname.endswith('.info'):
cov_file = os.path.join(cov_dir, fname)
cov_files.append(cov_file)
ReplaceCoveragePath(cov_file, options.build_dir)
if cov_files:
total_coverage = os.path.join(target_dir, 'total_coverage')
chromium_utils.MaybeMakeDirectory(total_coverage)
html_dir = os.path.join(total_coverage, 'coverage_croc_html')
cmdline = CrocCommand(options.platform, cov_files, html_dir)
return RunCroc(cov_files, cmdline)
else:
print 'No coverage files found.'
return 1
def main():
if sys.platform in ('win32', 'cygwin'):
default_platform = 'win'
elif sys.platform.startswith('darwin'):
default_platform = 'mac'
elif sys.platform == 'linux2':
default_platform = 'linux'
else:
default_platform = None
platforms = ['linux', 'mac', 'win']
option_parser = optparse.OptionParser()
option_parser.add_option('--target',
default='Debug',
help='build target (Debug, Release) '
'[default: %default]')
option_parser.add_option('--build-dir', help='ignored')
option_parser.add_option('--platform',
default=default_platform,
help='specify platform (%s) [default: %%default]'
% ', '.join(platforms))
option_parser.add_option('--build-id',
help='The build number of the tested build.')
option_parser.add_option('--upload-dir',
help='Path coverage file was uploaded to.')
chromium_utils.AddPropertiesOptions(option_parser)
options, args = option_parser.parse_args()
options.build_dir = build_directory.GetBuildOutputDirectory()
fp = options.factory_properties
options.tests = fp.get('tests')
options.sharded_tests = fp.get('sharded_tests')
options.browser_total_shards = fp.get('browser_total_shards')
del options.factory_properties
del options.build_properties
if options.platform not in platforms:
sys.stderr.write('Unsupported sys.platform %s.\n' % repr(sys.platform))
msg = 'Use the --platform= option to specify a supported platform:\n'
sys.stderr.write(msg + ' ' + ' '.join(platforms) + '\n')
return 2
return ProcessCoverage(options, args)
if '__main__' == __name__:
sys.exit(main())