def _build_python_coverage(env, single):
    covdir = Dir("#/build/coverage").abspath
    # Return if no coverage to report (i.e. no tests were run)
    coverage_files = glob.glob('%s/.coverage*' % covdir)
    if len(coverage_files) == 0:
        return
    print >> sys.stderr, "Generating Python HTML coverage report... ",

    if single:
        cov = coverage.coverage(branch=True,
                                data_file=os.path.join(covdir, '.coverage'))
        python_coverage.setup_excludes(cov)
        cov.file_locator.relative_dir = Dir("#/build").abspath + '/'
        cov.combine()
        morfs = cov.data.lines.keys()
        morfs.sort()
        cov.html_report(morfs, directory=os.path.join(covdir, 'python'))
    else:
        r = re.compile('\.coverage\.\w+\.(\w+)')
        for f in coverage_files:
            m = r.search(f)
            cov = coverage.coverage(branch=True, data_file=f)
            python_coverage.setup_excludes(cov)
            cov.file_locator.relative_dir = Dir("#/build").abspath + '/'
            cov.load()
            morfs = cov.data.lines.keys()
            morfs.sort()
            try:
                cov.html_report(morfs, directory=os.path.join(covdir, 'python',
                                                              m.group(1)))
            except coverage.CoverageException, detail:
                print >> sys.stderr, "Python coverage of %s failed: %s" \
                                     % (m.group(1), str(detail))
    def start(self):
        cwd = os.path.dirname(sys.argv[0])
        # Don't show full paths in coverage output
        if self.opts.application:
            if not self.opts.pyexe:
                # No Python coverage to report for C++-only apps
                self.cov = None
                return
            self.topdir = os.path.abspath(os.path.join(cwd, '..', 'build',
                                                       'bin'))
            self.mods = [self.topdir + '/' + x for x in self.opts.pyexe]
            # Ensure that applications started as subprocesses are
            # themselves covered
            os.environ['IMP_COVERAGE_APPS'] = os.pathsep.join(self.opts.pyexe)
            self.cov_suffix = 'app.' + self.opts.application
        elif self.opts.module:
            path = self.opts.module.replace('.', '/')
            self.topdir = os.path.abspath(os.path.join(cwd, '..', 'build',
                                                       'lib'))
            self.mods = glob.glob(self.topdir + '/%s/*.py' % path)
            self.mods = [x for x in self.mods \
                         if not x.endswith('_version_check.py')]
            modname = self.opts.module.split('.')[-1]
            if modname == 'IMP':
                modname = 'kernel'
            self.cov_suffix = 'mod.' + modname


        data_file = '.' + self.cov_suffix + '.coverage'
        os.environ['IMP_COVERAGE_DATA_FILE'] = data_file
        for cov in glob.glob(data_file + '*'):
            os.unlink(cov)

        self.cov = coverage.coverage(branch=True, include=self.mods,
                                     data_file=data_file)
        python_coverage.setup_excludes(self.cov)
        self.cov.start()