pax_global_header00006660000000000000000000000064123560505710014516gustar00rootroot0000000000000052 comment=34f2b575c87c9d6c656abfeb3c7bec4f2d235aa4 gcovr-3.2/000077500000000000000000000000001235605057100125025ustar00rootroot00000000000000gcovr-3.2/.gitignore000066400000000000000000000004571235605057100145000ustar00rootroot00000000000000*.py[cod] # C extensions *.so # Packages *.egg *.egg-info dist build eggs parts bin var sdist develop-eggs .installed.cfg lib lib64 # Installer logs pip-log.txt # Unit test / coverage reports .coverage .tox nosetests.xml # Translations *.mo # Mr Developer .mr.developer.cfg .project .pydevproject gcovr-3.2/AUTHORS.txt000066400000000000000000000001521235605057100143660ustar00rootroot00000000000000William Hart - lead developer John Siirola - lead developer Each release documents author contributions. gcovr-3.2/CHANGELOG.txt000066400000000000000000000130251235605057100145330ustar00rootroot00000000000000= `gcovr` Release History and Change Log = === 3.2 ''(5 July 2014)'' === - Adding a test for out of source builds - Using the starting directory when processing gcov filenames. (#42) - Making relative paths the default in html output. - Simplify html bar with coverage is zero. - Add option for using existing gcov files (#35) - Fixing --root argument processing (#27) - Adding logic to cover branches that are ignored (#28) === 3.1 ''(6 December 2013)'' === - Change to make the -r/--root options define the root directory for source files. - Fix to apply the -p option when the --html option is used. - Adding new option, '--exclude-unreachable-branches' that will exclude branches in certain lines from coverage report. - Simplifying and standardizing the processing of linked files. - Adding tests for deeply nested code, and symbolic links. - Add support for multiple —filter options in same manner as —exclude option. === 3.0 ''(10 August 2013)'' === - Adding the '--gcov-executable' option to specify the name/location of the gcov executable. The command line option overrides the environment variable, which overrides the default 'gcov'. - Adding an empty "" block to in the XML output: this makes out XML complient with the Cobertura DTD. (#3951) - Allow the GCOV environment variable to override the default 'gcov' executable. The default is to search the PATH for 'gcov' if the GCOV environment variable is not set. (#3950) - Adding support for LCOV-style flags for excluding certain lines from coverage analysis. (#3942) - Setup additional logic to test with Python 2.5. - Added the --html and --html-details options to generate HTML. - Sort output for XML to facilitate baseline tests. - Added error when the --object-directory option specifies a bad directory. - Added more flexible XML testing, which can ignore XML elements that frequently change (e.g. timestamps). - Added the '—xml-pretty' option, which is used to generate pretty XML output for the user manual. - Many documentation updates === 2.4 ''(13 April 2012)'' === - New approach to walking the directory tree that is more robust to symbolic links (#3908) - Normalize all reported path names - Normalize using the full absolute path (#3921) - Attempt to resolve files refeenced through symlinks to a common project-relative path - Process `gcno` files when there is no corresponding `gcda` file to provide coverage information for unexecuted modules (#3887) - Windows compatibility fixes - Fix for how we parse `source:` file names (#3913) - Better handling od EOL indicators (#3920) - Fix so that gcovr cleans up all `.gcov` files, even those filtered by command line arguments - Added compatibility with GCC 4.8 (#3918) - Added a check to warn users who specify an empty `--root` option (see #3917) - Force `gcov` to run with en_US localization, so the gcovr parser runs correctly on systems with non-English locales (#3898, #3902). - Segregate warning/error information onto the stderr stream (#3924) - Miscellaneous (Python 3.x) portability fixes - Added the master svn revision number as part of the verson identifier === 2.3.1 ''(6 January 2012)'' === - Adding support for Python 3.x === 2.3 ''(11 December 2011)'' === - Adding the `--gcov-filter` and `--gcov-exclude` options. === 2.2 ''(10 December 2011)'' === - Added a test driver for gcovr. - Improved estimation of the `` element when using gcovr with filters. - Added revision and date keywords to gcovr so it is easier to identify what version of the script users are using (especially when they are running a snapshot from trunk). - Addressed special case mentioned in [comment:ticket:3884:1]: do not truncate the reported file name if the filter does not start matching at the beginning of the string. - Overhaul of the `--root` / `--filter` logic. This should resolve the issue raised in #3884, along with the more general filter issue raised in [comment:ticket:3884:1] - Overhaul of gcovr's logic for determining gcc/g++'s original working directory. This resolves issues introduced in the original implementation of `--object-directory` (#3872, #3883). - Bugfix: gcovr was only including a `` element in the XML report if the user specified `-r` (#3869) - Adding timestamp and version attributes to the gcovr XML report (see #3877). It looks like the standard Cobertura output reports number of seconds since the epoch for the timestamp and a doted decimal version string. Now, gcovr reports seconds since the epoch and "`gcovr `"+`__version__` (e.g. "gcovr 2.2") to differentiate it from a pure Cobertura report. === 2.1 ''(26 November 2010)'' === - Added the `--object-directory` option, which allows for a flexible specification of the directory that contains the objects generated by gcov. - Adding fix to compare the absolute path of a filename to an exclusion pattern. - Adding error checking when no coverage results are found. The line and branch counts can be zero. - Adding logic to process the `-o`/`--output` option (#3870). - Adding patch to scan for lines that look like: {{{ creating `foo' }}} as well as {{{ creating 'foo' }}} - Changing the semantics for EOL to be portable for MS Windows. - Add attributes to xml format so that it could be used by hudson/bamboo with cobertura plug-in. === 2.0 ''(22 August 2010)'' === - Initial release as a separate package. Earlier versions of gcovr were managed within the 'fast' Python package. gcovr-3.2/LICENSE.txt000066400000000000000000000031331235605057100143250ustar00rootroot00000000000000Copyright 2013 Sandia Corporation. Under the terms of Contract DE-AC04- 94AL85000 with Sandia Corporation, the U.S. Government retains certain rights in this software. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Sandia National Laboratories nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. gcovr-3.2/README.md000066400000000000000000000015301235605057100137600ustar00rootroot00000000000000Overview -------- Gcovr provides a utility for managing the use of the GNU gcov utility and generating summarized code coverage results. This command is inspired by the Python coverage.py package, which provides a similar utility in Python. The gcovr command produces either compact human-readable summary reports, machine readable XML reports (in Cobertura format) or simple HTML reports. Thus, gcovr can be viewed as a command-line alternative to the lcov utility, which runs gcov and generates an HTML-formatted report. Gcovr is available under the BSD License. A user guide can be downloaded from the [gcovr home page](http://gcovr.com). Developer discussions are hosted by [google groups](https://groups.google.com/forum/#!forum/gcovr-developers). Gcovr development moved to this repository in September, 2013 from Sandia National Laboratories. gcovr-3.2/admin/000077500000000000000000000000001235605057100135725ustar00rootroot00000000000000gcovr-3.2/admin/jenkins_gcovr000077500000000000000000000037051235605057100163660ustar00rootroot00000000000000#!/bin/bash if test -z "$WORKSPACE"; then echo "ERROR: \$WORKSPACE not defined" exit 1 fi if test -n "$1"; then PYTHON="$1" else PYTHON=python fi if test -n "$2"; then export CXX="$2" else export CXX="g++" fi export BUILD_SCRIPTS="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" export PATH="$WORKSPACE/vpython/bin:$PATH" echo echo "Building on `hostname`:" echo echo " Workspace: ${WORKSPACE}" echo " Scripts: ${BUILD_SCRIPTS}" echo echo " Environment:" /usr/bin/env 2>&1 | sort | sed 's/^/ /' echo echo " Python:" "$PYTHON" -c 'import sys; sys.stdout.write(sys.version+"\n")' 2>&1 \ | sed 's/^/ /' PYTHON_VER=`"$PYTHON" -c 'import sys; sys.stdout.write(str(sys.version_info[0]))'` echo # The following executables are required (missing app yields build failure) for app in $CXX; do echo " $app:" $app --version 2>&1 | grep -v '^$' | sed 's/^/ /' || exit 1 echo done # Setup virtual Python environment \rm -Rf "$WORKSPACE"/vpython tmp=2.6 if [ "yes" = "$(echo | awk "($PYTHON_VER < $tmp) { print \"yes\"; }")" ]; then "$PYTHON" "$BUILD_SCRIPTS"/virtualenv_1.7.py "$WORKSPACE"/vpython || exit 1 else "$PYTHON" "$BUILD_SCRIPTS"/virtualenv.py "$WORKSPACE"/vpython || exit 1 fi "$WORKSPACE"/vpython/bin/easy_install nose || exit 1 if test "$PYTHON_VER" -gt 2; then "$WORKSPACE"/vpython/bin/easy_install unittest2py3k || exit 1 else "$WORKSPACE"/vpython/bin/easy_install unittest2 || exit 1 fi "$WORKSPACE"/vpython/bin/easy_install ply || exit 1 "$WORKSPACE"/vpython/bin/easy_install ordereddict || exit 1 "$WORKSPACE"/vpython/bin/easy_install pyutilib.th || exit 1 # Install our source # -- not necessary: nosetests will add the source to the sys.path. #"$WORKSPACE"/vpython/bin/python setup.py install || exit 1 # Run tests "$WORKSPACE"/vpython/bin/nosetests -v -v -w "$WORKSPACE"/gcovr \ --with-xunit --xunit-file="$WORKSPACE"/TEST-gcovr.xml \ || echo "(INFO) nosetests returned non-zero return code" echo "DONE" gcovr-3.2/admin/release_checklist000066400000000000000000000002651235605057100171710ustar00rootroot00000000000000Files that contain version information: CHANGELOG.txt setup.py scripts/gcovr doc/examples/example2.txt doc/guide.txt Tagging a release git tag -a 3.2 git push origin --tags gcovr-3.2/admin/virtualenv.py000066400000000000000000003405051235605057100163520ustar00rootroot00000000000000#!/usr/bin/env python """Create a "virtual" Python installation """ # If you change the version here, change it in setup.py # and docs/conf.py as well. __version__ = "1.9.1" # following best practices virtualenv_version = __version__ # legacy, again import base64 import sys import os import codecs import optparse import re import shutil import logging import tempfile import zlib import errno import glob import distutils.sysconfig from distutils.util import strtobool import struct import subprocess if sys.version_info < (2, 5): print('ERROR: %s' % sys.exc_info()[1]) print('ERROR: this script requires Python 2.5 or greater.') sys.exit(101) try: set except NameError: from sets import Set as set try: basestring except NameError: basestring = str try: import ConfigParser except ImportError: import configparser as ConfigParser join = os.path.join py_version = 'python%s.%s' % (sys.version_info[0], sys.version_info[1]) is_jython = sys.platform.startswith('java') is_pypy = hasattr(sys, 'pypy_version_info') is_win = (sys.platform == 'win32') is_cygwin = (sys.platform == 'cygwin') is_darwin = (sys.platform == 'darwin') abiflags = getattr(sys, 'abiflags', '') user_dir = os.path.expanduser('~') if is_win: default_storage_dir = os.path.join(user_dir, 'virtualenv') else: default_storage_dir = os.path.join(user_dir, '.virtualenv') default_config_file = os.path.join(default_storage_dir, 'virtualenv.ini') if is_pypy: expected_exe = 'pypy' elif is_jython: expected_exe = 'jython' else: expected_exe = 'python' REQUIRED_MODULES = ['os', 'posix', 'posixpath', 'nt', 'ntpath', 'genericpath', 'fnmatch', 'locale', 'encodings', 'codecs', 'stat', 'UserDict', 'readline', 'copy_reg', 'types', 're', 'sre', 'sre_parse', 'sre_constants', 'sre_compile', 'zlib'] REQUIRED_FILES = ['lib-dynload', 'config'] majver, minver = sys.version_info[:2] if majver == 2: if minver >= 6: REQUIRED_MODULES.extend(['warnings', 'linecache', '_abcoll', 'abc']) if minver >= 7: REQUIRED_MODULES.extend(['_weakrefset']) if minver <= 3: REQUIRED_MODULES.extend(['sets', '__future__']) elif majver == 3: # Some extra modules are needed for Python 3, but different ones # for different versions. REQUIRED_MODULES.extend(['_abcoll', 'warnings', 'linecache', 'abc', 'io', '_weakrefset', 'copyreg', 'tempfile', 'random', '__future__', 'collections', 'keyword', 'tarfile', 'shutil', 'struct', 'copy', 'tokenize', 'token', 'functools', 'heapq', 'bisect', 'weakref', 'reprlib']) if minver >= 2: REQUIRED_FILES[-1] = 'config-%s' % majver if minver == 3: import sysconfig platdir = sysconfig.get_config_var('PLATDIR') REQUIRED_FILES.append(platdir) # The whole list of 3.3 modules is reproduced below - the current # uncommented ones are required for 3.3 as of now, but more may be # added as 3.3 development continues. REQUIRED_MODULES.extend([ #"aifc", #"antigravity", #"argparse", #"ast", #"asynchat", #"asyncore", "base64", #"bdb", #"binhex", #"bisect", #"calendar", #"cgi", #"cgitb", #"chunk", #"cmd", #"codeop", #"code", #"colorsys", #"_compat_pickle", #"compileall", #"concurrent", #"configparser", #"contextlib", #"cProfile", #"crypt", #"csv", #"ctypes", #"curses", #"datetime", #"dbm", #"decimal", #"difflib", #"dis", #"doctest", #"dummy_threading", "_dummy_thread", #"email", #"filecmp", #"fileinput", #"formatter", #"fractions", #"ftplib", #"functools", #"getopt", #"getpass", #"gettext", #"glob", #"gzip", "hashlib", #"heapq", "hmac", #"html", #"http", #"idlelib", #"imaplib", #"imghdr", "imp", "importlib", #"inspect", #"json", #"lib2to3", #"logging", #"macpath", #"macurl2path", #"mailbox", #"mailcap", #"_markupbase", #"mimetypes", #"modulefinder", #"multiprocessing", #"netrc", #"nntplib", #"nturl2path", #"numbers", #"opcode", #"optparse", #"os2emxpath", #"pdb", #"pickle", #"pickletools", #"pipes", #"pkgutil", #"platform", #"plat-linux2", #"plistlib", #"poplib", #"pprint", #"profile", #"pstats", #"pty", #"pyclbr", #"py_compile", #"pydoc_data", #"pydoc", #"_pyio", #"queue", #"quopri", #"reprlib", "rlcompleter", #"runpy", #"sched", #"shelve", #"shlex", #"smtpd", #"smtplib", #"sndhdr", #"socket", #"socketserver", #"sqlite3", #"ssl", #"stringprep", #"string", #"_strptime", #"subprocess", #"sunau", #"symbol", #"symtable", #"sysconfig", #"tabnanny", #"telnetlib", #"test", #"textwrap", #"this", #"_threading_local", #"threading", #"timeit", #"tkinter", #"tokenize", #"token", #"traceback", #"trace", #"tty", #"turtledemo", #"turtle", #"unittest", #"urllib", #"uuid", #"uu", #"wave", #"weakref", #"webbrowser", #"wsgiref", #"xdrlib", #"xml", #"xmlrpc", #"zipfile", ]) if is_pypy: # these are needed to correctly display the exceptions that may happen # during the bootstrap REQUIRED_MODULES.extend(['traceback', 'linecache']) class Logger(object): """ Logging object for use in command-line script. Allows ranges of levels, to avoid some redundancy of displayed information. """ DEBUG = logging.DEBUG INFO = logging.INFO NOTIFY = (logging.INFO+logging.WARN)/2 WARN = WARNING = logging.WARN ERROR = logging.ERROR FATAL = logging.FATAL LEVELS = [DEBUG, INFO, NOTIFY, WARN, ERROR, FATAL] def __init__(self, consumers): self.consumers = consumers self.indent = 0 self.in_progress = None self.in_progress_hanging = False def debug(self, msg, *args, **kw): self.log(self.DEBUG, msg, *args, **kw) def info(self, msg, *args, **kw): self.log(self.INFO, msg, *args, **kw) def notify(self, msg, *args, **kw): self.log(self.NOTIFY, msg, *args, **kw) def warn(self, msg, *args, **kw): self.log(self.WARN, msg, *args, **kw) def error(self, msg, *args, **kw): self.log(self.ERROR, msg, *args, **kw) def fatal(self, msg, *args, **kw): self.log(self.FATAL, msg, *args, **kw) def log(self, level, msg, *args, **kw): if args: if kw: raise TypeError( "You may give positional or keyword arguments, not both") args = args or kw rendered = None for consumer_level, consumer in self.consumers: if self.level_matches(level, consumer_level): if (self.in_progress_hanging and consumer in (sys.stdout, sys.stderr)): self.in_progress_hanging = False sys.stdout.write('\n') sys.stdout.flush() if rendered is None: if args: rendered = msg % args else: rendered = msg rendered = ' '*self.indent + rendered if hasattr(consumer, 'write'): consumer.write(rendered+'\n') else: consumer(rendered) def start_progress(self, msg): assert not self.in_progress, ( "Tried to start_progress(%r) while in_progress %r" % (msg, self.in_progress)) if self.level_matches(self.NOTIFY, self._stdout_level()): sys.stdout.write(msg) sys.stdout.flush() self.in_progress_hanging = True else: self.in_progress_hanging = False self.in_progress = msg def end_progress(self, msg='done.'): assert self.in_progress, ( "Tried to end_progress without start_progress") if self.stdout_level_matches(self.NOTIFY): if not self.in_progress_hanging: # Some message has been printed out since start_progress sys.stdout.write('...' + self.in_progress + msg + '\n') sys.stdout.flush() else: sys.stdout.write(msg + '\n') sys.stdout.flush() self.in_progress = None self.in_progress_hanging = False def show_progress(self): """If we are in a progress scope, and no log messages have been shown, write out another '.'""" if self.in_progress_hanging: sys.stdout.write('.') sys.stdout.flush() def stdout_level_matches(self, level): """Returns true if a message at this level will go to stdout""" return self.level_matches(level, self._stdout_level()) def _stdout_level(self): """Returns the level that stdout runs at""" for level, consumer in self.consumers: if consumer is sys.stdout: return level return self.FATAL def level_matches(self, level, consumer_level): """ >>> l = Logger([]) >>> l.level_matches(3, 4) False >>> l.level_matches(3, 2) True >>> l.level_matches(slice(None, 3), 3) False >>> l.level_matches(slice(None, 3), 2) True >>> l.level_matches(slice(1, 3), 1) True >>> l.level_matches(slice(2, 3), 1) False """ if isinstance(level, slice): start, stop = level.start, level.stop if start is not None and start > consumer_level: return False if stop is not None and stop <= consumer_level: return False return True else: return level >= consumer_level #@classmethod def level_for_integer(cls, level): levels = cls.LEVELS if level < 0: return levels[0] if level >= len(levels): return levels[-1] return levels[level] level_for_integer = classmethod(level_for_integer) # create a silent logger just to prevent this from being undefined # will be overridden with requested verbosity main() is called. logger = Logger([(Logger.LEVELS[-1], sys.stdout)]) def mkdir(path): if not os.path.exists(path): logger.info('Creating %s', path) os.makedirs(path) else: logger.info('Directory %s already exists', path) def copyfileordir(src, dest): if os.path.isdir(src): shutil.copytree(src, dest, True) else: shutil.copy2(src, dest) def copyfile(src, dest, symlink=True): if not os.path.exists(src): # Some bad symlink in the src logger.warn('Cannot find file %s (bad symlink)', src) return if os.path.exists(dest): logger.debug('File %s already exists', dest) return if not os.path.exists(os.path.dirname(dest)): logger.info('Creating parent directories for %s' % os.path.dirname(dest)) os.makedirs(os.path.dirname(dest)) if not os.path.islink(src): srcpath = os.path.abspath(src) else: srcpath = os.readlink(src) if symlink and hasattr(os, 'symlink') and not is_win: logger.info('Symlinking %s', dest) try: os.symlink(srcpath, dest) except (OSError, NotImplementedError): logger.info('Symlinking failed, copying to %s', dest) copyfileordir(src, dest) else: logger.info('Copying to %s', dest) copyfileordir(src, dest) def writefile(dest, content, overwrite=True): if not os.path.exists(dest): logger.info('Writing %s', dest) f = open(dest, 'wb') f.write(content.encode('utf-8')) f.close() return else: f = open(dest, 'rb') c = f.read() f.close() if c != content.encode("utf-8"): if not overwrite: logger.notify('File %s exists with different content; not overwriting', dest) return logger.notify('Overwriting %s with new content', dest) f = open(dest, 'wb') f.write(content.encode('utf-8')) f.close() else: logger.info('Content %s already in place', dest) def rmtree(dir): if os.path.exists(dir): logger.notify('Deleting tree %s', dir) shutil.rmtree(dir) else: logger.info('Do not need to delete %s; already gone', dir) def make_exe(fn): if hasattr(os, 'chmod'): oldmode = os.stat(fn).st_mode & 0xFFF # 0o7777 newmode = (oldmode | 0x16D) & 0xFFF # 0o555, 0o7777 os.chmod(fn, newmode) logger.info('Changed mode of %s to %s', fn, oct(newmode)) def _find_file(filename, dirs): for dir in reversed(dirs): files = glob.glob(os.path.join(dir, filename)) if files and os.path.isfile(files[0]): return True, files[0] return False, filename def _install_req(py_executable, unzip=False, distribute=False, search_dirs=None, never_download=False): if search_dirs is None: search_dirs = file_search_dirs() if not distribute: egg_path = 'setuptools-*-py%s.egg' % sys.version[:3] found, egg_path = _find_file(egg_path, search_dirs) project_name = 'setuptools' bootstrap_script = EZ_SETUP_PY tgz_path = None else: # Look for a distribute egg (these are not distributed by default, # but can be made available by the user) egg_path = 'distribute-*-py%s.egg' % sys.version[:3] found, egg_path = _find_file(egg_path, search_dirs) project_name = 'distribute' if found: tgz_path = None bootstrap_script = DISTRIBUTE_FROM_EGG_PY else: # Fall back to sdist # NB: egg_path is not None iff tgz_path is None # iff bootstrap_script is a generic setup script accepting # the standard arguments. egg_path = None tgz_path = 'distribute-*.tar.gz' found, tgz_path = _find_file(tgz_path, search_dirs) bootstrap_script = DISTRIBUTE_SETUP_PY if is_jython and os._name == 'nt': # Jython's .bat sys.executable can't handle a command line # argument with newlines fd, ez_setup = tempfile.mkstemp('.py') os.write(fd, bootstrap_script) os.close(fd) cmd = [py_executable, ez_setup] else: cmd = [py_executable, '-c', bootstrap_script] if unzip and egg_path: cmd.append('--always-unzip') env = {} remove_from_env = ['__PYVENV_LAUNCHER__'] if logger.stdout_level_matches(logger.DEBUG) and egg_path: cmd.append('-v') old_chdir = os.getcwd() if egg_path is not None and os.path.exists(egg_path): logger.info('Using existing %s egg: %s' % (project_name, egg_path)) cmd.append(egg_path) if os.environ.get('PYTHONPATH'): env['PYTHONPATH'] = egg_path + os.path.pathsep + os.environ['PYTHONPATH'] else: env['PYTHONPATH'] = egg_path elif tgz_path is not None and os.path.exists(tgz_path): # Found a tgz source dist, let's chdir logger.info('Using existing %s egg: %s' % (project_name, tgz_path)) os.chdir(os.path.dirname(tgz_path)) # in this case, we want to be sure that PYTHONPATH is unset (not # just empty, really unset), else CPython tries to import the # site.py that it's in virtualenv_support remove_from_env.append('PYTHONPATH') elif never_download: logger.fatal("Can't find any local distributions of %s to install " "and --never-download is set. Either re-run virtualenv " "without the --never-download option, or place a %s " "distribution (%s) in one of these " "locations: %r" % (project_name, project_name, egg_path or tgz_path, search_dirs)) sys.exit(1) elif egg_path: logger.info('No %s egg found; downloading' % project_name) cmd.extend(['--always-copy', '-U', project_name]) else: logger.info('No %s tgz found; downloading' % project_name) logger.start_progress('Installing %s...' % project_name) logger.indent += 2 cwd = None if project_name == 'distribute': env['DONT_PATCH_SETUPTOOLS'] = 'true' def _filter_ez_setup(line): return filter_ez_setup(line, project_name) if not os.access(os.getcwd(), os.W_OK): cwd = tempfile.mkdtemp() if tgz_path is not None and os.path.exists(tgz_path): # the current working dir is hostile, let's copy the # tarball to a temp dir target = os.path.join(cwd, os.path.split(tgz_path)[-1]) shutil.copy(tgz_path, target) try: call_subprocess(cmd, show_stdout=False, filter_stdout=_filter_ez_setup, extra_env=env, remove_from_env=remove_from_env, cwd=cwd) finally: logger.indent -= 2 logger.end_progress() if cwd is not None: shutil.rmtree(cwd) if os.getcwd() != old_chdir: os.chdir(old_chdir) if is_jython and os._name == 'nt': os.remove(ez_setup) def file_search_dirs(): here = os.path.dirname(os.path.abspath(__file__)) dirs = ['.', here, join(here, 'virtualenv_support')] if os.path.splitext(os.path.dirname(__file__))[0] != 'virtualenv': # Probably some boot script; just in case virtualenv is installed... try: import virtualenv except ImportError: pass else: dirs.append(os.path.join(os.path.dirname(virtualenv.__file__), 'virtualenv_support')) return [d for d in dirs if os.path.isdir(d)] def install_setuptools(py_executable, unzip=False, search_dirs=None, never_download=False): _install_req(py_executable, unzip, search_dirs=search_dirs, never_download=never_download) def install_distribute(py_executable, unzip=False, search_dirs=None, never_download=False): _install_req(py_executable, unzip, distribute=True, search_dirs=search_dirs, never_download=never_download) _pip_re = re.compile(r'^pip-.*(zip|tar.gz|tar.bz2|tgz|tbz)$', re.I) def install_pip(py_executable, search_dirs=None, never_download=False): if search_dirs is None: search_dirs = file_search_dirs() filenames = [] for dir in search_dirs: filenames.extend([join(dir, fn) for fn in os.listdir(dir) if _pip_re.search(fn)]) filenames = [(os.path.basename(filename).lower(), i, filename) for i, filename in enumerate(filenames)] filenames.sort() filenames = [filename for basename, i, filename in filenames] if not filenames: filename = 'pip' else: filename = filenames[-1] easy_install_script = 'easy_install' if is_win: easy_install_script = 'easy_install-script.py' # There's two subtle issues here when invoking easy_install. # 1. On unix-like systems the easy_install script can *only* be executed # directly if its full filesystem path is no longer than 78 characters. # 2. A work around to [1] is to use the `python path/to/easy_install foo` # pattern, but that breaks if the path contains non-ASCII characters, as # you can't put the file encoding declaration before the shebang line. # The solution is to use Python's -x flag to skip the first line of the # script (and any ASCII decoding errors that may have occurred in that line) cmd = [py_executable, '-x', join(os.path.dirname(py_executable), easy_install_script), filename] # jython and pypy don't yet support -x if is_jython or is_pypy: cmd.remove('-x') if filename == 'pip': if never_download: logger.fatal("Can't find any local distributions of pip to install " "and --never-download is set. Either re-run virtualenv " "without the --never-download option, or place a pip " "source distribution (zip/tar.gz/tar.bz2) in one of these " "locations: %r" % search_dirs) sys.exit(1) logger.info('Installing pip from network...') else: logger.info('Installing existing %s distribution: %s' % ( os.path.basename(filename), filename)) logger.start_progress('Installing pip...') logger.indent += 2 def _filter_setup(line): return filter_ez_setup(line, 'pip') try: call_subprocess(cmd, show_stdout=False, filter_stdout=_filter_setup) finally: logger.indent -= 2 logger.end_progress() def filter_ez_setup(line, project_name='setuptools'): if not line.strip(): return Logger.DEBUG if project_name == 'distribute': for prefix in ('Extracting', 'Now working', 'Installing', 'Before', 'Scanning', 'Setuptools', 'Egg', 'Already', 'running', 'writing', 'reading', 'installing', 'creating', 'copying', 'byte-compiling', 'removing', 'Processing'): if line.startswith(prefix): return Logger.DEBUG return Logger.DEBUG for prefix in ['Reading ', 'Best match', 'Processing setuptools', 'Copying setuptools', 'Adding setuptools', 'Installing ', 'Installed ']: if line.startswith(prefix): return Logger.DEBUG return Logger.INFO class UpdatingDefaultsHelpFormatter(optparse.IndentedHelpFormatter): """ Custom help formatter for use in ConfigOptionParser that updates the defaults before expanding them, allowing them to show up correctly in the help listing """ def expand_default(self, option): if self.parser is not None: self.parser.update_defaults(self.parser.defaults) return optparse.IndentedHelpFormatter.expand_default(self, option) class ConfigOptionParser(optparse.OptionParser): """ Custom option parser which updates its defaults by by checking the configuration files and environmental variables """ def __init__(self, *args, **kwargs): self.config = ConfigParser.RawConfigParser() self.files = self.get_config_files() self.config.read(self.files) optparse.OptionParser.__init__(self, *args, **kwargs) def get_config_files(self): config_file = os.environ.get('VIRTUALENV_CONFIG_FILE', False) if config_file and os.path.exists(config_file): return [config_file] return [default_config_file] def update_defaults(self, defaults): """ Updates the given defaults with values from the config files and the environ. Does a little special handling for certain types of options (lists). """ # Then go and look for the other sources of configuration: config = {} # 1. config files config.update(dict(self.get_config_section('virtualenv'))) # 2. environmental variables config.update(dict(self.get_environ_vars())) # Then set the options with those values for key, val in config.items(): key = key.replace('_', '-') if not key.startswith('--'): key = '--%s' % key # only prefer long opts option = self.get_option(key) if option is not None: # ignore empty values if not val: continue # handle multiline configs if option.action == 'append': val = val.split() else: option.nargs = 1 if option.action == 'store_false': val = not strtobool(val) elif option.action in ('store_true', 'count'): val = strtobool(val) try: val = option.convert_value(key, val) except optparse.OptionValueError: e = sys.exc_info()[1] print("An error occured during configuration: %s" % e) sys.exit(3) defaults[option.dest] = val return defaults def get_config_section(self, name): """ Get a section of a configuration """ if self.config.has_section(name): return self.config.items(name) return [] def get_environ_vars(self, prefix='VIRTUALENV_'): """ Returns a generator with all environmental vars with prefix VIRTUALENV """ for key, val in os.environ.items(): if key.startswith(prefix): yield (key.replace(prefix, '').lower(), val) def get_default_values(self): """ Overridding to make updating the defaults after instantiation of the option parser possible, update_defaults() does the dirty work. """ if not self.process_default_values: # Old, pre-Optik 1.5 behaviour. return optparse.Values(self.defaults) defaults = self.update_defaults(self.defaults.copy()) # ours for option in self._get_all_options(): default = defaults.get(option.dest) if isinstance(default, basestring): opt_str = option.get_opt_string() defaults[option.dest] = option.check_value(opt_str, default) return optparse.Values(defaults) def main(): parser = ConfigOptionParser( version=virtualenv_version, usage="%prog [OPTIONS] DEST_DIR", formatter=UpdatingDefaultsHelpFormatter()) parser.add_option( '-v', '--verbose', action='count', dest='verbose', default=0, help="Increase verbosity") parser.add_option( '-q', '--quiet', action='count', dest='quiet', default=0, help='Decrease verbosity') parser.add_option( '-p', '--python', dest='python', metavar='PYTHON_EXE', help='The Python interpreter to use, e.g., --python=python2.5 will use the python2.5 ' 'interpreter to create the new environment. The default is the interpreter that ' 'virtualenv was installed with (%s)' % sys.executable) parser.add_option( '--clear', dest='clear', action='store_true', help="Clear out the non-root install and start from scratch") parser.set_defaults(system_site_packages=False) parser.add_option( '--no-site-packages', dest='system_site_packages', action='store_false', help="Don't give access to the global site-packages dir to the " "virtual environment (default)") parser.add_option( '--system-site-packages', dest='system_site_packages', action='store_true', help="Give access to the global site-packages dir to the " "virtual environment") parser.add_option( '--unzip-setuptools', dest='unzip_setuptools', action='store_true', help="Unzip Setuptools or Distribute when installing it") parser.add_option( '--relocatable', dest='relocatable', action='store_true', help='Make an EXISTING virtualenv environment relocatable. ' 'This fixes up scripts and makes all .pth files relative') parser.add_option( '--distribute', '--use-distribute', # the second option is for legacy reasons here. Hi Kenneth! dest='use_distribute', action='store_true', help='Use Distribute instead of Setuptools. Set environ variable ' 'VIRTUALENV_DISTRIBUTE to make it the default ') parser.add_option( '--no-setuptools', dest='no_setuptools', action='store_true', help='Do not install distribute/setuptools (or pip) ' 'in the new virtualenv.') parser.add_option( '--no-pip', dest='no_pip', action='store_true', help='Do not install pip in the new virtualenv.') parser.add_option( '--setuptools', dest='use_distribute', action='store_false', help='Use Setuptools instead of Distribute. Set environ variable ' 'VIRTUALENV_SETUPTOOLS to make it the default ') # Set this to True to use distribute by default, even in Python 2. parser.set_defaults(use_distribute=False) default_search_dirs = file_search_dirs() parser.add_option( '--extra-search-dir', dest="search_dirs", action="append", default=default_search_dirs, help="Directory to look for setuptools/distribute/pip distributions in. " "You can add any number of additional --extra-search-dir paths.") parser.add_option( '--never-download', dest="never_download", action="store_true", help="Never download anything from the network. Instead, virtualenv will fail " "if local distributions of setuptools/distribute/pip are not present.") parser.add_option( '--prompt', dest='prompt', help='Provides an alternative prompt prefix for this environment') if 'extend_parser' in globals(): extend_parser(parser) options, args = parser.parse_args() global logger if 'adjust_options' in globals(): adjust_options(options, args) verbosity = options.verbose - options.quiet logger = Logger([(Logger.level_for_integer(2 - verbosity), sys.stdout)]) if options.python and not os.environ.get('VIRTUALENV_INTERPRETER_RUNNING'): env = os.environ.copy() interpreter = resolve_interpreter(options.python) if interpreter == sys.executable: logger.warn('Already using interpreter %s' % interpreter) else: logger.notify('Running virtualenv with interpreter %s' % interpreter) env['VIRTUALENV_INTERPRETER_RUNNING'] = 'true' file = __file__ if file.endswith('.pyc'): file = file[:-1] popen = subprocess.Popen([interpreter, file] + sys.argv[1:], env=env) raise SystemExit(popen.wait()) # Force --distribute on Python 3, since setuptools is not available. if majver > 2: options.use_distribute = True if os.environ.get('PYTHONDONTWRITEBYTECODE') and not options.use_distribute: print( "The PYTHONDONTWRITEBYTECODE environment variable is " "not compatible with setuptools. Either use --distribute " "or unset PYTHONDONTWRITEBYTECODE.") sys.exit(2) if not args: print('You must provide a DEST_DIR') parser.print_help() sys.exit(2) if len(args) > 1: print('There must be only one argument: DEST_DIR (you gave %s)' % ( ' '.join(args))) parser.print_help() sys.exit(2) home_dir = args[0] if os.environ.get('WORKING_ENV'): logger.fatal('ERROR: you cannot run virtualenv while in a workingenv') logger.fatal('Please deactivate your workingenv, then re-run this script') sys.exit(3) if 'PYTHONHOME' in os.environ: logger.warn('PYTHONHOME is set. You *must* activate the virtualenv before using it') del os.environ['PYTHONHOME'] if options.relocatable: make_environment_relocatable(home_dir) return create_environment(home_dir, site_packages=options.system_site_packages, clear=options.clear, unzip_setuptools=options.unzip_setuptools, use_distribute=options.use_distribute, prompt=options.prompt, search_dirs=options.search_dirs, never_download=options.never_download, no_setuptools=options.no_setuptools, no_pip=options.no_pip) if 'after_install' in globals(): after_install(options, home_dir) def call_subprocess(cmd, show_stdout=True, filter_stdout=None, cwd=None, raise_on_returncode=True, extra_env=None, remove_from_env=None): cmd_parts = [] for part in cmd: if len(part) > 45: part = part[:20]+"..."+part[-20:] if ' ' in part or '\n' in part or '"' in part or "'" in part: part = '"%s"' % part.replace('"', '\\"') if hasattr(part, 'decode'): try: part = part.decode(sys.getdefaultencoding()) except UnicodeDecodeError: part = part.decode(sys.getfilesystemencoding()) cmd_parts.append(part) cmd_desc = ' '.join(cmd_parts) if show_stdout: stdout = None else: stdout = subprocess.PIPE logger.debug("Running command %s" % cmd_desc) if extra_env or remove_from_env: env = os.environ.copy() if extra_env: env.update(extra_env) if remove_from_env: for varname in remove_from_env: env.pop(varname, None) else: env = None try: proc = subprocess.Popen( cmd, stderr=subprocess.STDOUT, stdin=None, stdout=stdout, cwd=cwd, env=env) except Exception: e = sys.exc_info()[1] logger.fatal( "Error %s while executing command %s" % (e, cmd_desc)) raise all_output = [] if stdout is not None: stdout = proc.stdout encoding = sys.getdefaultencoding() fs_encoding = sys.getfilesystemencoding() while 1: line = stdout.readline() try: line = line.decode(encoding) except UnicodeDecodeError: line = line.decode(fs_encoding) if not line: break line = line.rstrip() all_output.append(line) if filter_stdout: level = filter_stdout(line) if isinstance(level, tuple): level, line = level logger.log(level, line) if not logger.stdout_level_matches(level): logger.show_progress() else: logger.info(line) else: proc.communicate() proc.wait() if proc.returncode: if raise_on_returncode: if all_output: logger.notify('Complete output from command %s:' % cmd_desc) logger.notify('\n'.join(all_output) + '\n----------------------------------------') raise OSError( "Command %s failed with error code %s" % (cmd_desc, proc.returncode)) else: logger.warn( "Command %s had error code %s" % (cmd_desc, proc.returncode)) def create_environment(home_dir, site_packages=False, clear=False, unzip_setuptools=False, use_distribute=False, prompt=None, search_dirs=None, never_download=False, no_setuptools=False, no_pip=False): """ Creates a new environment in ``home_dir``. If ``site_packages`` is true, then the global ``site-packages/`` directory will be on the path. If ``clear`` is true (default False) then the environment will first be cleared. """ home_dir, lib_dir, inc_dir, bin_dir = path_locations(home_dir) py_executable = os.path.abspath(install_python( home_dir, lib_dir, inc_dir, bin_dir, site_packages=site_packages, clear=clear)) install_distutils(home_dir) if not no_setuptools: if use_distribute: install_distribute(py_executable, unzip=unzip_setuptools, search_dirs=search_dirs, never_download=never_download) else: install_setuptools(py_executable, unzip=unzip_setuptools, search_dirs=search_dirs, never_download=never_download) if not no_pip: install_pip(py_executable, search_dirs=search_dirs, never_download=never_download) install_activate(home_dir, bin_dir, prompt) def is_executable_file(fpath): return os.path.isfile(fpath) and os.access(fpath, os.X_OK) def path_locations(home_dir): """Return the path locations for the environment (where libraries are, where scripts go, etc)""" # XXX: We'd use distutils.sysconfig.get_python_inc/lib but its # prefix arg is broken: http://bugs.python.org/issue3386 if is_win: # Windows has lots of problems with executables with spaces in # the name; this function will remove them (using the ~1 # format): mkdir(home_dir) if ' ' in home_dir: import ctypes GetShortPathName = ctypes.windll.kernel32.GetShortPathNameW size = max(len(home_dir)+1, 256) buf = ctypes.create_unicode_buffer(size) try: u = unicode except NameError: u = str ret = GetShortPathName(u(home_dir), buf, size) if not ret: print('Error: the path "%s" has a space in it' % home_dir) print('We could not determine the short pathname for it.') print('Exiting.') sys.exit(3) home_dir = str(buf.value) lib_dir = join(home_dir, 'Lib') inc_dir = join(home_dir, 'Include') bin_dir = join(home_dir, 'Scripts') if is_jython: lib_dir = join(home_dir, 'Lib') inc_dir = join(home_dir, 'Include') bin_dir = join(home_dir, 'bin') elif is_pypy: lib_dir = home_dir inc_dir = join(home_dir, 'include') bin_dir = join(home_dir, 'bin') elif not is_win: lib_dir = join(home_dir, 'lib', py_version) multiarch_exec = '/usr/bin/multiarch-platform' if is_executable_file(multiarch_exec): # In Mageia (2) and Mandriva distros the include dir must be like: # virtualenv/include/multiarch-x86_64-linux/python2.7 # instead of being virtualenv/include/python2.7 p = subprocess.Popen(multiarch_exec, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = p.communicate() # stdout.strip is needed to remove newline character inc_dir = join(home_dir, 'include', stdout.strip(), py_version + abiflags) else: inc_dir = join(home_dir, 'include', py_version + abiflags) bin_dir = join(home_dir, 'bin') return home_dir, lib_dir, inc_dir, bin_dir def change_prefix(filename, dst_prefix): prefixes = [sys.prefix] if is_darwin: prefixes.extend(( os.path.join("/Library/Python", sys.version[:3], "site-packages"), os.path.join(sys.prefix, "Extras", "lib", "python"), os.path.join("~", "Library", "Python", sys.version[:3], "site-packages"), # Python 2.6 no-frameworks os.path.join("~", ".local", "lib","python", sys.version[:3], "site-packages"), # System Python 2.7 on OSX Mountain Lion os.path.join("~", "Library", "Python", sys.version[:3], "lib", "python", "site-packages"))) if hasattr(sys, 'real_prefix'): prefixes.append(sys.real_prefix) if hasattr(sys, 'base_prefix'): prefixes.append(sys.base_prefix) prefixes = list(map(os.path.expanduser, prefixes)) prefixes = list(map(os.path.abspath, prefixes)) # Check longer prefixes first so we don't split in the middle of a filename prefixes = sorted(prefixes, key=len, reverse=True) filename = os.path.abspath(filename) for src_prefix in prefixes: if filename.startswith(src_prefix): _, relpath = filename.split(src_prefix, 1) if src_prefix != os.sep: # sys.prefix == "/" assert relpath[0] == os.sep relpath = relpath[1:] return join(dst_prefix, relpath) assert False, "Filename %s does not start with any of these prefixes: %s" % \ (filename, prefixes) def copy_required_modules(dst_prefix): import imp # If we are running under -p, we need to remove the current # directory from sys.path temporarily here, so that we # definitely get the modules from the site directory of # the interpreter we are running under, not the one # virtualenv.py is installed under (which might lead to py2/py3 # incompatibility issues) _prev_sys_path = sys.path if os.environ.get('VIRTUALENV_INTERPRETER_RUNNING'): sys.path = sys.path[1:] try: for modname in REQUIRED_MODULES: if modname in sys.builtin_module_names: logger.info("Ignoring built-in bootstrap module: %s" % modname) continue try: f, filename, _ = imp.find_module(modname) except ImportError: logger.info("Cannot import bootstrap module: %s" % modname) else: if f is not None: f.close() # special-case custom readline.so on OS X, but not for pypy: if modname == 'readline' and sys.platform == 'darwin' and not ( is_pypy or filename.endswith(join('lib-dynload', 'readline.so'))): dst_filename = join(dst_prefix, 'lib', 'python%s' % sys.version[:3], 'readline.so') else: dst_filename = change_prefix(filename, dst_prefix) copyfile(filename, dst_filename) if filename.endswith('.pyc'): pyfile = filename[:-1] if os.path.exists(pyfile): copyfile(pyfile, dst_filename[:-1]) finally: sys.path = _prev_sys_path def subst_path(prefix_path, prefix, home_dir): prefix_path = os.path.normpath(prefix_path) prefix = os.path.normpath(prefix) home_dir = os.path.normpath(home_dir) if not prefix_path.startswith(prefix): logger.warn('Path not in prefix %r %r', prefix_path, prefix) return return prefix_path.replace(prefix, home_dir, 1) def install_python(home_dir, lib_dir, inc_dir, bin_dir, site_packages, clear): """Install just the base environment, no distutils patches etc""" if sys.executable.startswith(bin_dir): print('Please use the *system* python to run this script') return if clear: rmtree(lib_dir) ## FIXME: why not delete it? ## Maybe it should delete everything with #!/path/to/venv/python in it logger.notify('Not deleting %s', bin_dir) if hasattr(sys, 'real_prefix'): logger.notify('Using real prefix %r' % sys.real_prefix) prefix = sys.real_prefix elif hasattr(sys, 'base_prefix'): logger.notify('Using base prefix %r' % sys.base_prefix) prefix = sys.base_prefix else: prefix = sys.prefix mkdir(lib_dir) fix_lib64(lib_dir) stdlib_dirs = [os.path.dirname(os.__file__)] if is_win: stdlib_dirs.append(join(os.path.dirname(stdlib_dirs[0]), 'DLLs')) elif is_darwin: stdlib_dirs.append(join(stdlib_dirs[0], 'site-packages')) if hasattr(os, 'symlink'): logger.info('Symlinking Python bootstrap modules') else: logger.info('Copying Python bootstrap modules') logger.indent += 2 try: # copy required files... for stdlib_dir in stdlib_dirs: if not os.path.isdir(stdlib_dir): continue for fn in os.listdir(stdlib_dir): bn = os.path.splitext(fn)[0] if fn != 'site-packages' and bn in REQUIRED_FILES: copyfile(join(stdlib_dir, fn), join(lib_dir, fn)) # ...and modules copy_required_modules(home_dir) finally: logger.indent -= 2 mkdir(join(lib_dir, 'site-packages')) import site site_filename = site.__file__ if site_filename.endswith('.pyc'): site_filename = site_filename[:-1] elif site_filename.endswith('$py.class'): site_filename = site_filename.replace('$py.class', '.py') site_filename_dst = change_prefix(site_filename, home_dir) site_dir = os.path.dirname(site_filename_dst) writefile(site_filename_dst, SITE_PY) writefile(join(site_dir, 'orig-prefix.txt'), prefix) site_packages_filename = join(site_dir, 'no-global-site-packages.txt') if not site_packages: writefile(site_packages_filename, '') if is_pypy or is_win: stdinc_dir = join(prefix, 'include') else: stdinc_dir = join(prefix, 'include', py_version + abiflags) if os.path.exists(stdinc_dir): copyfile(stdinc_dir, inc_dir) else: logger.debug('No include dir %s' % stdinc_dir) platinc_dir = distutils.sysconfig.get_python_inc(plat_specific=1) if platinc_dir != stdinc_dir: platinc_dest = distutils.sysconfig.get_python_inc( plat_specific=1, prefix=home_dir) if platinc_dir == platinc_dest: # Do platinc_dest manually due to a CPython bug; # not http://bugs.python.org/issue3386 but a close cousin platinc_dest = subst_path(platinc_dir, prefix, home_dir) if platinc_dest: # PyPy's stdinc_dir and prefix are relative to the original binary # (traversing virtualenvs), whereas the platinc_dir is relative to # the inner virtualenv and ignores the prefix argument. # This seems more evolved than designed. copyfile(platinc_dir, platinc_dest) # pypy never uses exec_prefix, just ignore it if sys.exec_prefix != prefix and not is_pypy: if is_win: exec_dir = join(sys.exec_prefix, 'lib') elif is_jython: exec_dir = join(sys.exec_prefix, 'Lib') else: exec_dir = join(sys.exec_prefix, 'lib', py_version) for fn in os.listdir(exec_dir): copyfile(join(exec_dir, fn), join(lib_dir, fn)) if is_jython: # Jython has either jython-dev.jar and javalib/ dir, or just # jython.jar for name in 'jython-dev.jar', 'javalib', 'jython.jar': src = join(prefix, name) if os.path.exists(src): copyfile(src, join(home_dir, name)) # XXX: registry should always exist after Jython 2.5rc1 src = join(prefix, 'registry') if os.path.exists(src): copyfile(src, join(home_dir, 'registry'), symlink=False) copyfile(join(prefix, 'cachedir'), join(home_dir, 'cachedir'), symlink=False) mkdir(bin_dir) py_executable = join(bin_dir, os.path.basename(sys.executable)) if 'Python.framework' in prefix: # OS X framework builds cause validation to break # https://github.com/pypa/virtualenv/issues/322 if os.environ.get('__PYVENV_LAUNCHER__'): os.unsetenv('__PYVENV_LAUNCHER__') if re.search(r'/Python(?:-32|-64)*$', py_executable): # The name of the python executable is not quite what # we want, rename it. py_executable = os.path.join( os.path.dirname(py_executable), 'python') logger.notify('New %s executable in %s', expected_exe, py_executable) pcbuild_dir = os.path.dirname(sys.executable) pyd_pth = os.path.join(lib_dir, 'site-packages', 'virtualenv_builddir_pyd.pth') if is_win and os.path.exists(os.path.join(pcbuild_dir, 'build.bat')): logger.notify('Detected python running from build directory %s', pcbuild_dir) logger.notify('Writing .pth file linking to build directory for *.pyd files') writefile(pyd_pth, pcbuild_dir) else: pcbuild_dir = None if os.path.exists(pyd_pth): logger.info('Deleting %s (not Windows env or not build directory python)' % pyd_pth) os.unlink(pyd_pth) if sys.executable != py_executable: ## FIXME: could I just hard link? executable = sys.executable shutil.copyfile(executable, py_executable) make_exe(py_executable) if is_win or is_cygwin: pythonw = os.path.join(os.path.dirname(sys.executable), 'pythonw.exe') if os.path.exists(pythonw): logger.info('Also created pythonw.exe') shutil.copyfile(pythonw, os.path.join(os.path.dirname(py_executable), 'pythonw.exe')) python_d = os.path.join(os.path.dirname(sys.executable), 'python_d.exe') python_d_dest = os.path.join(os.path.dirname(py_executable), 'python_d.exe') if os.path.exists(python_d): logger.info('Also created python_d.exe') shutil.copyfile(python_d, python_d_dest) elif os.path.exists(python_d_dest): logger.info('Removed python_d.exe as it is no longer at the source') os.unlink(python_d_dest) # we need to copy the DLL to enforce that windows will load the correct one. # may not exist if we are cygwin. py_executable_dll = 'python%s%s.dll' % ( sys.version_info[0], sys.version_info[1]) py_executable_dll_d = 'python%s%s_d.dll' % ( sys.version_info[0], sys.version_info[1]) pythondll = os.path.join(os.path.dirname(sys.executable), py_executable_dll) pythondll_d = os.path.join(os.path.dirname(sys.executable), py_executable_dll_d) pythondll_d_dest = os.path.join(os.path.dirname(py_executable), py_executable_dll_d) if os.path.exists(pythondll): logger.info('Also created %s' % py_executable_dll) shutil.copyfile(pythondll, os.path.join(os.path.dirname(py_executable), py_executable_dll)) if os.path.exists(pythondll_d): logger.info('Also created %s' % py_executable_dll_d) shutil.copyfile(pythondll_d, pythondll_d_dest) elif os.path.exists(pythondll_d_dest): logger.info('Removed %s as the source does not exist' % pythondll_d_dest) os.unlink(pythondll_d_dest) if is_pypy: # make a symlink python --> pypy-c python_executable = os.path.join(os.path.dirname(py_executable), 'python') if sys.platform in ('win32', 'cygwin'): python_executable += '.exe' logger.info('Also created executable %s' % python_executable) copyfile(py_executable, python_executable) if is_win: for name in 'libexpat.dll', 'libpypy.dll', 'libpypy-c.dll', 'libeay32.dll', 'ssleay32.dll', 'sqlite.dll': src = join(prefix, name) if os.path.exists(src): copyfile(src, join(bin_dir, name)) if os.path.splitext(os.path.basename(py_executable))[0] != expected_exe: secondary_exe = os.path.join(os.path.dirname(py_executable), expected_exe) py_executable_ext = os.path.splitext(py_executable)[1] if py_executable_ext == '.exe': # python2.4 gives an extension of '.4' :P secondary_exe += py_executable_ext if os.path.exists(secondary_exe): logger.warn('Not overwriting existing %s script %s (you must use %s)' % (expected_exe, secondary_exe, py_executable)) else: logger.notify('Also creating executable in %s' % secondary_exe) shutil.copyfile(sys.executable, secondary_exe) make_exe(secondary_exe) if '.framework' in prefix: if 'Python.framework' in prefix: logger.debug('MacOSX Python framework detected') # Make sure we use the the embedded interpreter inside # the framework, even if sys.executable points to # the stub executable in ${sys.prefix}/bin # See http://groups.google.com/group/python-virtualenv/ # browse_thread/thread/17cab2f85da75951 original_python = os.path.join( prefix, 'Resources/Python.app/Contents/MacOS/Python') if 'EPD' in prefix: logger.debug('EPD framework detected') original_python = os.path.join(prefix, 'bin/python') shutil.copy(original_python, py_executable) # Copy the framework's dylib into the virtual # environment virtual_lib = os.path.join(home_dir, '.Python') if os.path.exists(virtual_lib): os.unlink(virtual_lib) copyfile( os.path.join(prefix, 'Python'), virtual_lib) # And then change the install_name of the copied python executable try: mach_o_change(py_executable, os.path.join(prefix, 'Python'), '@executable_path/../.Python') except: e = sys.exc_info()[1] logger.warn("Could not call mach_o_change: %s. " "Trying to call install_name_tool instead." % e) try: call_subprocess( ["install_name_tool", "-change", os.path.join(prefix, 'Python'), '@executable_path/../.Python', py_executable]) except: logger.fatal("Could not call install_name_tool -- you must " "have Apple's development tools installed") raise if not is_win: # Ensure that 'python', 'pythonX' and 'pythonX.Y' all exist py_exe_version_major = 'python%s' % sys.version_info[0] py_exe_version_major_minor = 'python%s.%s' % ( sys.version_info[0], sys.version_info[1]) py_exe_no_version = 'python' required_symlinks = [ py_exe_no_version, py_exe_version_major, py_exe_version_major_minor ] py_executable_base = os.path.basename(py_executable) if py_executable_base in required_symlinks: # Don't try to symlink to yourself. required_symlinks.remove(py_executable_base) for pth in required_symlinks: full_pth = join(bin_dir, pth) if os.path.exists(full_pth): os.unlink(full_pth) os.symlink(py_executable_base, full_pth) if is_win and ' ' in py_executable: # There's a bug with subprocess on Windows when using a first # argument that has a space in it. Instead we have to quote # the value: py_executable = '"%s"' % py_executable # NOTE: keep this check as one line, cmd.exe doesn't cope with line breaks cmd = [py_executable, '-c', 'import sys;out=sys.stdout;' 'getattr(out, "buffer", out).write(sys.prefix.encode("utf-8"))'] logger.info('Testing executable with %s %s "%s"' % tuple(cmd)) try: proc = subprocess.Popen(cmd, stdout=subprocess.PIPE) proc_stdout, proc_stderr = proc.communicate() except OSError: e = sys.exc_info()[1] if e.errno == errno.EACCES: logger.fatal('ERROR: The executable %s could not be run: %s' % (py_executable, e)) sys.exit(100) else: raise e proc_stdout = proc_stdout.strip().decode("utf-8") proc_stdout = os.path.normcase(os.path.abspath(proc_stdout)) norm_home_dir = os.path.normcase(os.path.abspath(home_dir)) if hasattr(norm_home_dir, 'decode'): norm_home_dir = norm_home_dir.decode(sys.getfilesystemencoding()) if proc_stdout != norm_home_dir: logger.fatal( 'ERROR: The executable %s is not functioning' % py_executable) logger.fatal( 'ERROR: It thinks sys.prefix is %r (should be %r)' % (proc_stdout, norm_home_dir)) logger.fatal( 'ERROR: virtualenv is not compatible with this system or executable') if is_win: logger.fatal( 'Note: some Windows users have reported this error when they ' 'installed Python for "Only this user" or have multiple ' 'versions of Python installed. Copying the appropriate ' 'PythonXX.dll to the virtualenv Scripts/ directory may fix ' 'this problem.') sys.exit(100) else: logger.info('Got sys.prefix result: %r' % proc_stdout) pydistutils = os.path.expanduser('~/.pydistutils.cfg') if os.path.exists(pydistutils): logger.notify('Please make sure you remove any previous custom paths from ' 'your %s file.' % pydistutils) ## FIXME: really this should be calculated earlier fix_local_scheme(home_dir) if site_packages: if os.path.exists(site_packages_filename): logger.info('Deleting %s' % site_packages_filename) os.unlink(site_packages_filename) return py_executable def install_activate(home_dir, bin_dir, prompt=None): home_dir = os.path.abspath(home_dir) if is_win or is_jython and os._name == 'nt': files = { 'activate.bat': ACTIVATE_BAT, 'deactivate.bat': DEACTIVATE_BAT, 'activate.ps1': ACTIVATE_PS, } # MSYS needs paths of the form /c/path/to/file drive, tail = os.path.splitdrive(home_dir.replace(os.sep, '/')) home_dir_msys = (drive and "/%s%s" or "%s%s") % (drive[:1], tail) # Run-time conditional enables (basic) Cygwin compatibility home_dir_sh = ("""$(if [ "$OSTYPE" "==" "cygwin" ]; then cygpath -u '%s'; else echo '%s'; fi;)""" % (home_dir, home_dir_msys)) files['activate'] = ACTIVATE_SH.replace('__VIRTUAL_ENV__', home_dir_sh) else: files = {'activate': ACTIVATE_SH} # suppling activate.fish in addition to, not instead of, the # bash script support. files['activate.fish'] = ACTIVATE_FISH # same for csh/tcsh support... files['activate.csh'] = ACTIVATE_CSH files['activate_this.py'] = ACTIVATE_THIS if hasattr(home_dir, 'decode'): home_dir = home_dir.decode(sys.getfilesystemencoding()) vname = os.path.basename(home_dir) for name, content in files.items(): content = content.replace('__VIRTUAL_PROMPT__', prompt or '') content = content.replace('__VIRTUAL_WINPROMPT__', prompt or '(%s)' % vname) content = content.replace('__VIRTUAL_ENV__', home_dir) content = content.replace('__VIRTUAL_NAME__', vname) content = content.replace('__BIN_NAME__', os.path.basename(bin_dir)) writefile(os.path.join(bin_dir, name), content) def install_distutils(home_dir): distutils_path = change_prefix(distutils.__path__[0], home_dir) mkdir(distutils_path) ## FIXME: maybe this prefix setting should only be put in place if ## there's a local distutils.cfg with a prefix setting? home_dir = os.path.abspath(home_dir) ## FIXME: this is breaking things, removing for now: #distutils_cfg = DISTUTILS_CFG + "\n[install]\nprefix=%s\n" % home_dir writefile(os.path.join(distutils_path, '__init__.py'), DISTUTILS_INIT) writefile(os.path.join(distutils_path, 'distutils.cfg'), DISTUTILS_CFG, overwrite=False) def fix_local_scheme(home_dir): """ Platforms that use the "posix_local" install scheme (like Ubuntu with Python 2.7) need to be given an additional "local" location, sigh. """ try: import sysconfig except ImportError: pass else: if sysconfig._get_default_scheme() == 'posix_local': local_path = os.path.join(home_dir, 'local') if not os.path.exists(local_path): os.mkdir(local_path) for subdir_name in os.listdir(home_dir): if subdir_name == 'local': continue os.symlink(os.path.abspath(os.path.join(home_dir, subdir_name)), \ os.path.join(local_path, subdir_name)) def fix_lib64(lib_dir): """ Some platforms (particularly Gentoo on x64) put things in lib64/pythonX.Y instead of lib/pythonX.Y. If this is such a platform we'll just create a symlink so lib64 points to lib """ if [p for p in distutils.sysconfig.get_config_vars().values() if isinstance(p, basestring) and 'lib64' in p]: logger.debug('This system uses lib64; symlinking lib64 to lib') assert os.path.basename(lib_dir) == 'python%s' % sys.version[:3], ( "Unexpected python lib dir: %r" % lib_dir) lib_parent = os.path.dirname(lib_dir) top_level = os.path.dirname(lib_parent) lib_dir = os.path.join(top_level, 'lib') lib64_link = os.path.join(top_level, 'lib64') assert os.path.basename(lib_parent) == 'lib', ( "Unexpected parent dir: %r" % lib_parent) if os.path.lexists(lib64_link): return os.symlink('lib', lib64_link) def resolve_interpreter(exe): """ If the executable given isn't an absolute path, search $PATH for the interpreter """ if os.path.abspath(exe) != exe: paths = os.environ.get('PATH', '').split(os.pathsep) for path in paths: if os.path.exists(os.path.join(path, exe)): exe = os.path.join(path, exe) break if not os.path.exists(exe): logger.fatal('The executable %s (from --python=%s) does not exist' % (exe, exe)) raise SystemExit(3) if not is_executable(exe): logger.fatal('The executable %s (from --python=%s) is not executable' % (exe, exe)) raise SystemExit(3) return exe def is_executable(exe): """Checks a file is executable""" return os.access(exe, os.X_OK) ############################################################ ## Relocating the environment: def make_environment_relocatable(home_dir): """ Makes the already-existing environment use relative paths, and takes out the #!-based environment selection in scripts. """ home_dir, lib_dir, inc_dir, bin_dir = path_locations(home_dir) activate_this = os.path.join(bin_dir, 'activate_this.py') if not os.path.exists(activate_this): logger.fatal( 'The environment doesn\'t have a file %s -- please re-run virtualenv ' 'on this environment to update it' % activate_this) fixup_scripts(home_dir) fixup_pth_and_egg_link(home_dir) ## FIXME: need to fix up distutils.cfg OK_ABS_SCRIPTS = ['python', 'python%s' % sys.version[:3], 'activate', 'activate.bat', 'activate_this.py'] def fixup_scripts(home_dir): # This is what we expect at the top of scripts: shebang = '#!%s/bin/python' % os.path.normcase(os.path.abspath(home_dir)) # This is what we'll put: new_shebang = '#!/usr/bin/env python%s' % sys.version[:3] if is_win: bin_suffix = 'Scripts' else: bin_suffix = 'bin' bin_dir = os.path.join(home_dir, bin_suffix) home_dir, lib_dir, inc_dir, bin_dir = path_locations(home_dir) for filename in os.listdir(bin_dir): filename = os.path.join(bin_dir, filename) if not os.path.isfile(filename): # ignore subdirs, e.g. .svn ones. continue f = open(filename, 'rb') try: try: lines = f.read().decode('utf-8').splitlines() except UnicodeDecodeError: # This is probably a binary program instead # of a script, so just ignore it. continue finally: f.close() if not lines: logger.warn('Script %s is an empty file' % filename) continue if not lines[0].strip().startswith(shebang): if os.path.basename(filename) in OK_ABS_SCRIPTS: logger.debug('Cannot make script %s relative' % filename) elif lines[0].strip() == new_shebang: logger.info('Script %s has already been made relative' % filename) else: logger.warn('Script %s cannot be made relative (it\'s not a normal script that starts with %s)' % (filename, shebang)) continue logger.notify('Making script %s relative' % filename) script = relative_script([new_shebang] + lines[1:]) f = open(filename, 'wb') f.write('\n'.join(script).encode('utf-8')) f.close() def relative_script(lines): "Return a script that'll work in a relocatable environment." activate = "import os; activate_this=os.path.join(os.path.dirname(os.path.realpath(__file__)), 'activate_this.py'); execfile(activate_this, dict(__file__=activate_this)); del os, activate_this" # Find the last future statement in the script. If we insert the activation # line before a future statement, Python will raise a SyntaxError. activate_at = None for idx, line in reversed(list(enumerate(lines))): if line.split()[:3] == ['from', '__future__', 'import']: activate_at = idx + 1 break if activate_at is None: # Activate after the shebang. activate_at = 1 return lines[:activate_at] + ['', activate, ''] + lines[activate_at:] def fixup_pth_and_egg_link(home_dir, sys_path=None): """Makes .pth and .egg-link files use relative paths""" home_dir = os.path.normcase(os.path.abspath(home_dir)) if sys_path is None: sys_path = sys.path for path in sys_path: if not path: path = '.' if not os.path.isdir(path): continue path = os.path.normcase(os.path.abspath(path)) if not path.startswith(home_dir): logger.debug('Skipping system (non-environment) directory %s' % path) continue for filename in os.listdir(path): filename = os.path.join(path, filename) if filename.endswith('.pth'): if not os.access(filename, os.W_OK): logger.warn('Cannot write .pth file %s, skipping' % filename) else: fixup_pth_file(filename) if filename.endswith('.egg-link'): if not os.access(filename, os.W_OK): logger.warn('Cannot write .egg-link file %s, skipping' % filename) else: fixup_egg_link(filename) def fixup_pth_file(filename): lines = [] prev_lines = [] f = open(filename) prev_lines = f.readlines() f.close() for line in prev_lines: line = line.strip() if (not line or line.startswith('#') or line.startswith('import ') or os.path.abspath(line) != line): lines.append(line) else: new_value = make_relative_path(filename, line) if line != new_value: logger.debug('Rewriting path %s as %s (in %s)' % (line, new_value, filename)) lines.append(new_value) if lines == prev_lines: logger.info('No changes to .pth file %s' % filename) return logger.notify('Making paths in .pth file %s relative' % filename) f = open(filename, 'w') f.write('\n'.join(lines) + '\n') f.close() def fixup_egg_link(filename): f = open(filename) link = f.readline().strip() f.close() if os.path.abspath(link) != link: logger.debug('Link in %s already relative' % filename) return new_link = make_relative_path(filename, link) logger.notify('Rewriting link %s in %s as %s' % (link, filename, new_link)) f = open(filename, 'w') f.write(new_link) f.close() def make_relative_path(source, dest, dest_is_directory=True): """ Make a filename relative, where the filename is dest, and it is being referred to from the filename source. >>> make_relative_path('/usr/share/something/a-file.pth', ... '/usr/share/another-place/src/Directory') '../another-place/src/Directory' >>> make_relative_path('/usr/share/something/a-file.pth', ... '/home/user/src/Directory') '../../../home/user/src/Directory' >>> make_relative_path('/usr/share/a-file.pth', '/usr/share/') './' """ source = os.path.dirname(source) if not dest_is_directory: dest_filename = os.path.basename(dest) dest = os.path.dirname(dest) dest = os.path.normpath(os.path.abspath(dest)) source = os.path.normpath(os.path.abspath(source)) dest_parts = dest.strip(os.path.sep).split(os.path.sep) source_parts = source.strip(os.path.sep).split(os.path.sep) while dest_parts and source_parts and dest_parts[0] == source_parts[0]: dest_parts.pop(0) source_parts.pop(0) full_parts = ['..']*len(source_parts) + dest_parts if not dest_is_directory: full_parts.append(dest_filename) if not full_parts: # Special case for the current directory (otherwise it'd be '') return './' return os.path.sep.join(full_parts) ############################################################ ## Bootstrap script creation: def create_bootstrap_script(extra_text, python_version=''): """ Creates a bootstrap script, which is like this script but with extend_parser, adjust_options, and after_install hooks. This returns a string that (written to disk of course) can be used as a bootstrap script with your own customizations. The script will be the standard virtualenv.py script, with your extra text added (your extra text should be Python code). If you include these functions, they will be called: ``extend_parser(optparse_parser)``: You can add or remove options from the parser here. ``adjust_options(options, args)``: You can change options here, or change the args (if you accept different kinds of arguments, be sure you modify ``args`` so it is only ``[DEST_DIR]``). ``after_install(options, home_dir)``: After everything is installed, this function is called. This is probably the function you are most likely to use. An example would be:: def after_install(options, home_dir): subprocess.call([join(home_dir, 'bin', 'easy_install'), 'MyPackage']) subprocess.call([join(home_dir, 'bin', 'my-package-script'), 'setup', home_dir]) This example immediately installs a package, and runs a setup script from that package. If you provide something like ``python_version='2.5'`` then the script will start with ``#!/usr/bin/env python2.5`` instead of ``#!/usr/bin/env python``. You can use this when the script must be run with a particular Python version. """ filename = __file__ if filename.endswith('.pyc'): filename = filename[:-1] f = codecs.open(filename, 'r', encoding='utf-8') content = f.read() f.close() py_exe = 'python%s' % python_version content = (('#!/usr/bin/env %s\n' % py_exe) + '## WARNING: This file is generated\n' + content) return content.replace('##EXT' 'END##', extra_text) ##EXTEND## def convert(s): b = base64.b64decode(s.encode('ascii')) return zlib.decompress(b).decode('utf-8') ##file site.py SITE_PY = convert(""" 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 UM3ckC/Q8aP8VLy+mjYY5MxOtAdgjULwf2RtvCc= """) ##file ez_setup.py EZ_SETUP_PY = convert(""" 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 qL7+C1AkCcw= """) ##file distribute_from_egg.py DISTRIBUTE_FROM_EGG_PY = convert(""" eJw9j8tqAzEMRfcG/4MgmxQyptkGusonZBmGoGTUGYFfWPKE6dfXTkM3gqt7rh47OKP3NMF3SQFW LlrRU1zhybpAxoKBlIqcrNnBdRjQP3GTocYfzmNrrCPQPN9iwzpxSQfQhWBi0cL3qtRtYIG/4Mv0 KApY5hooqrOGQ05FQTaxptF9Fnx16Rq0XofjaE1XGXVxHIWK7j8P8EY/rHndLqQ1a0pe3COFgHFy hLLdWkDbi/DeEpCjNb3u/zccT2Ob8gtnwVyI """) ##file distribute_setup.py DISTRIBUTE_SETUP_PY = convert(""" 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 """) ##file activate.sh ACTIVATE_SH = convert(""" eJytVVFvokAQfudXTLEPtTlLeo9tvMSmJpq02hSvl7u2wRUG2QR2DSxSe7n/frOACEVNLlceRHa+ nfl25pvZDswCnoDPQ4QoTRQsENIEPci4CsBMZBq7CAsuLOYqvmYKTTj3YxnBgiXBudGBjUzBZUJI BXEqgCvweIyuCjeG4eF2F5x14bcB9KQiQQWrjSddI1/oQIx6SYYeoFjzWIoIhYI1izlbhJjkKO7D M/QEmKfO9O7WeRo/zr4P7pyHwWxkwitcgwpQ5Ej96OX+PmiFwLeVjFUOrNYKaq1Nud3nR2n8nI2m k9H0friPTGVsUdptaxGrTEfpNVFEskxpXtUkkCkl1UNF9cgLBkx48J4EXyALuBtAwNYIjF5kcmUU abMKmMq1ULoiRbgsDEkTSsKSGFCJ6Z8vY/2xYiSacmtyAfCDdCNTVZoVF8vSTQOoEwSnOrngBkws MYGMBMg8/bMBLSYKS7pYEXP0PqT+ZmBT0Xuy+Pplj5yn4aM9nk72JD8/Wi+Gr98sD9eWSMOwkapD BbUv91XSvmyVkICt2tmXR4tWmrcUCsjWOpw87YidEC8i0gdTSOFhouJUNxR+4NYBG0MftoCTD9F7 2rTtxG3oPwY1b2HncYwhrlmj6Wq924xtGDWqfdNxap+OYxplEurnMVo9RWks+rH8qKEtx7kZT5zJ 4H7oOFclrN6uFe+d+nW2aIUsSgs/42EIPuOhXq+jEo3S6tX6w2ilNkDnIpHCWdEQhFgwj9pkk7FN l/y5eQvRSIQ5+TrL05lewxWpt/Lbhes5cJF3mLET1MGhcKCF+40tNWnUulxrpojwDo2sObdje3Bz N3QeHqf3D7OjEXMVV8LN3ZlvuzoWHqiUcNKHtwNd0IbvPGKYYM31nPKCgkUILw3KL+Y8l7aO1ArS Ad37nIU0fCj5NE5gQCuC5sOSu+UdI2NeXg/lFkQIlFpdWVaWZRfvqGiirC9o6liJ9FXGYrSY9mI1 D/Ncozgn13vJvsznr7DnkJWXsyMH7e42ljdJ+aqNDF1bFnKWFLdj31xtaJYK6EXFgqmV/ymD/ROG +n8O9H8f5vsGOWXsL1+1k3g= """) ##file activate.fish ACTIVATE_FISH = convert(""" eJyVVWFv2jAQ/c6vuBoqQVWC9nVSNVGVCaS2VC2rNLWVZZILWAs2s52wVvvxsyEJDrjbmgpK7PP5 3bt3d22YLbmGlGcIq1wbmCPkGhPYcLMEEsGciwGLDS+YwSjlekngLFVyBe73GXSXxqw/DwbuTS8x yyKpFr1WG15lDjETQhpQuQBuIOEKY5O9tlppLqxHKSDByjVAPwEy+mXtCq5MzjIUBTCRgEKTKwFG gpBqxTLYXgN2myspVigMaYF92tZSowGZJf4mFExxNs9Qb614CgZtmH0BpEOn11f0cXI/+za8pnfD 2ZjA1sg9zlV/8QvcMhxbNu0QwgYokn/d+n02nt6Opzcjcnx1vXcIoN74O4ymWQXmHURfJw9jenc/ vbmb0enj6P5+cuVhqlKm3S0u2XRtRbA2QQAhV7VhBF0rsgUX9Ur1rBUXJgVSy8O751k8mzY5OrKH RW3eaQhYGTr8hrXO59ALhxQ83mCsDLAid3T72CCSdJhaFE+fXgicXAARUiR2WeVO37gH3oYHzFKo 9k7CaPZ1UeNwH1tWuXA4uFKYYcEa8vaKqXl7q1UpygMPhFLvlVKyNzsSM3S2km7UBOl4xweUXk5u 6e3wZmQ9leY1XE/Ili670tr9g/5POBBpGIJXCCF79L1siarl/dbESa8mD8PL61GpzqpzuMS7tqeB 1YkALrRBloBMbR9yLcVx7frQAgUqR7NZIuzkEu110gbNit1enNs82Rx5utq7Z3prU78HFRgulqNC OTwbqJa9vkJFclQgZSjbKeBgSsUtCtt9D8OwAbIVJuewQdfvQRaoFE9wd1TmCuRG7OgJ1bVXGHc7 z5WDL/WW36v2oi37CyVBak61+yPBA9C1qqGxzKQqZ0oPuocU9hpud0PIp8sDHkXR1HKkNlzjuUWA a0enFUyzOWZA4yXGP+ZMI3Tdt2OuqU/SO4q64526cPE0A7ZyW2PMbWZiZ5HamIZ2RcCKLXhcDl2b vXL+eccQoRzem80mekPDEiyiWK4GWqZmwxQOmPM0eIfgp1P9cqrBsewR2p/DPMtt+pfcYM+Ls2uh hALufTAdmGl8B1H3VPd2af8fQAc4PgqjlIBL9cGQqNpXaAwe3LrtVn8AkZTUxg== """) ##file activate.csh ACTIVATE_CSH = convert(""" eJx9VG1P2zAQ/u5fcYQKNgTNPtN1WxlIQ4KCUEGaxuQ6yYVYSuzKdhqVX7+zk3bpy5YPUXL3PPfc ne98DLNCWshliVDV1kGCUFvMoJGugMjq2qQIiVSxSJ1cCofD1BYRnOVGV0CfZ0N2DD91DalQSjsw tQLpIJMGU1euvPe7QeJlkKzgWixlhnAt4aoUVsLnLBiy5NtbJWQ5THX1ZciYKKWwkOFaE04dUm6D r/zh7pq/3D7Nnid3/HEy+wFHY/gEJydg0aFaQrBFgz1c5DG1IhTs+UZgsBC2GMFBlaeH+8dZXwcW VPvCjXdlAvCfQsE7al0+07XjZvrSCUevR5dnkVeKlFYZmUztG4BdzL2u9KyLVabTU0bdfg7a0hgs cSmUg6UwUiQl2iHrcbcVGNvPCiLOe7+cRwG13z9qRGgx2z6DHjfm/Op2yqeT+xvOLzs0PTKHDz2V tkckFHoQfQRXoGJAj9el0FyJCmEMhzgMS4sB7KPOE2ExoLcSieYwDvR+cP8cg11gKkVJc2wRcm1g QhYFlXiTaTfO2ki0fQoiFM4tLuO4aZrhOzqR4dIPcWx17hphMBY+Srwh7RTyN83XOWkcSPh1Pg/k TXX/jbJTbMtUmcxZ+/bbqOsy82suFQg/BhdSOTRhMNBHlUarCpU7JzBhmkKmRejKOQzayQe6MWoa n1wqWmuh6LZAaHxcdeqIlVLhIBJdO9/kbl0It2oEXQj+eGjJOuvOIR/YGRqvFhttUB2XTvLXYN2H 37CBdbW2W7j2r2+VsCn0doVWcFG1/4y1VwBjfwAyoZhD """) ##file activate.bat ACTIVATE_BAT = convert(""" eJx9UdEKgjAUfW6wfxjiIH+hEDKUFHSKLCMI7kNOEkIf9P9pTJ3OLJ/03HPPPed4Es9XS9qqwqgT PbGKKOdXL4aAFS7A4gvAwgijuiKlqOpGlATS2NeMLE+TjJM9RkQ+SmqAXLrBo1LLIeLdiWlD6jZt r7VNubWkndkXaxg5GO3UaOOKS6drO3luDDiO5my3iA0YAKGzPRV1ack8cOdhysI0CYzIPzjSiH5X 0QcvC8Lfaj0emsVKYF2rhL5L3fCkVjV76kShi59NHwDniAHzkgDgqBcwOgTMx+gDQQqXCw== """) ##file deactivate.bat DEACTIVATE_BAT = convert(""" eJxzSE3OyFfIT0vj4ipOLVEI8wwKCXX0iXf1C7Pl4spMU0hJTcvMS01RiPf3cYmHyQYE+fsGhCho cCkAAUibEkTEVhWLMlUlLk6QGixStlyaeCyJDPHw9/Pw93VFsQguim4ZXAJoIUw5DhX47XUM8UCx EchHtwsohN1bILUgw61c/Vy4AJYPYm4= """) ##file activate.ps1 ACTIVATE_PS = convert(""" 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 QastYw== """) ##file distutils-init.py DISTUTILS_INIT = convert(""" eJytV1uL4zYUfvevOE0ottuMW9q3gVDa3aUMXXbLMlDKMBiNrSTqOJKRlMxkf33PkXyRbGe7Dw2E UXTu37lpxLFV2oIyifAncxmOL0xLIfcG+gv80x9VW6maw7o/CANSWWBwFtqeWMPlGY6qPjV8A0bB C4eKSTgZ5LRgFeyErMEeOBhbN+Ipgeizhjtnhkn7DdyjuNLPoCS0l/ayQTG0djwZC08cLXozeMss aG5EzQ0IScpnWtHSTXuxByV/QCmxE7y+eS0uxWeoheaVVfqSJHiU7Mhhi6gULbOHorshkrEnKxpT 0n3A8Y8SMpuwZx6aoix3ouFlmW8gHRSkeSJ2g7hU+kiHLDaQw3bmRDaTGfTnty7gPm0FHbIBg9U9 oh1kZzAFLaue2R6htPCtAda2nGlDSUJ4PZBgCJBGVcwKTAMz/vJiLD+Oin5Z5QlvDPdulC6EsiyE NFzb7McNTKJzbJqzphx92VKRFY1idenzmq3K0emRcbWBD0ryqc4NZGmKOOOX9Pz5x+/l27tP797c f/z0d+4NruGNai8uAM0bfsYaw8itFk8ny41jsfpyO+BWlpqfhcG4yxLdi/0tQqoT4a8Vby382mt8 p7XSo7aWGdPBc+b6utaBmCQ7rQKQoWtAuthQCiold2KfJIPTT8xwg9blPumc+YDZC/wYGdAyHpJk vUbHbHWAp5No6pK/WhhLEWrFjUwtPEv1Agf8YmnsuXUQYkeZoHm8ogP16gt2uHoxcEMdf2C6pmbw hUMsWGhanboh4IzzmsIpWs134jVPqD/c74bZHdY69UKKSn/+KfVhxLgUlToemayLMYQOqfEC61bh cbhwaqoGUzIyZRFHPmau5juaWqwRn3mpWmoEA5nhzS5gog/5jbcFQqOZvmBasZtwYlG93k5GEiyw buHhMWLjDarEGpMGB2LFs5nIJkhp/nUmZneFaRth++lieJtHepIvKgx6PJqIlD9X2j6pG1i9x3pZ 5bHuCPFiirGHeO7McvoXkz786GaKVzC9DSpnOxJdc4xm6NSVq7lNEnKdVlnpu9BNYoKX2Iq3wvgh gGEUM66kK6j4NiyoneuPLSwaCWDxczgaolEWpiMyDVDb7dNuLAbriL8ig8mmeju31oNvQdpnvEPC 1vAXbWacGRVrGt/uXN/gU0CDDwgooKRrHfTBb1/s9lYZ8ZqOBU0yLvpuP6+K9hLFsvIjeNhBi0KL MlOuWRn3FRwx5oHXjl0YImUx0+gLzjGchrgzca026ETmYJzPD+IpuKzNi8AFn048Thd63OdD86M6 84zE8yQm0VqXdbbgvub2pKVnS76icBGdeTHHXTKspUmr4NYo/furFLKiMdQzFjHJNcdAnMhltBJK 0/IKX3DVFqvPJ2dLE7bDBkH0l/PJ29074+F0CsGYOxsb7U3myTUncYfXqnLLfa6sJybX4g+hmcjO kMRBfA1JellfRRKJcyRpxdS4rIl6FdmQCWjo/o9Qz7yKffoP4JHjOvABcRn4CZIT2RH4jnxmfpVG qgLaAvQBNfuO6X0/Ux02nb4FKx3vgP+XnkX0QW9pLy/NsXgdN24dD3LxO2Nwil7Zlc1dqtP3d7/h kzp1/+7hGBuY4pk0XD/0Ao/oTe/XGrfyM773aB7iUhgkpy+dwAMalxMP0DrBcsVw/6p25+/hobP9 GBknrWExDhLJ1bwt1NcCNblaFbMKCyvmX0PeRaQ= """) ##file distutils.cfg DISTUTILS_CFG = convert(""" eJxNj00KwkAMhfc9xYNuxe4Ft57AjYiUtDO1wXSmNJnK3N5pdSEEAu8nH6lxHVlRhtDHMPATA4uH xJ4EFmGbvfJiicSHFRzUSISMY6hq3GLCRLnIvSTnEefN0FIjw5tF0Hkk9Q5dRunBsVoyFi24aaLg 9FDOlL0FPGluf4QjcInLlxd6f6rqkgPu/5nHLg0cXCscXoozRrP51DRT3j9QNl99AP53T2Q= """) ##file activate_this.py ACTIVATE_THIS = convert(""" eJyNU01v2zAMvetXEB4K21jmDOstQA4dMGCHbeihlyEIDMWmG62yJEiKE//7kXKdpN2KzYBt8euR fKSyLPs8wiEo8wh4wqZTGou4V6Hm0wJa1cSiTkJdr8+GsoTRHuCotBayiWqQEYGtMCgfD1KjGYBe 5a3p0cRKiAe2NtLADikftnDco0ko/SFEVgEZ8aRC5GLux7i3BpSJ6J1H+i7A2CjiHq9z7JRZuuQq siwTIvpxJYCeuWaBpwZdhB+yxy/eWz+ZvVSU8C4E9FFZkyxFsvCT/ZzL8gcz9aXVE14Yyp2M+2W0 y7n5mp0qN+avKXvbsyyzUqjeWR8hjGE+2iCE1W1tQ82hsCZN9UzlJr+/e/iab8WfqsmPI6pWeUPd FrMsd4H/55poeO9n54COhUs+sZNEzNtg/wanpjpuqHJaxs76HtZryI/K3H7KJ/KDIhqcbJ7kI4ar XL+sMgXnX0D+Te2Iy5xdP8yueSlQB/x/ED2BTAtyE3K4SYUN6AMNfbO63f4lBW3bUJPbTL+mjSxS PyRfJkZRgj+VbFv+EzHFi5pKwUEepa4JslMnwkowSRCXI+m5XvEOvtuBrxHdhLalG0JofYBok6qj YdN2dEngUlbC4PG60M1WEN0piu7Nq7on0mgyyUw3iV1etLo6r/81biWdQ9MWHFaePWZYaq+nmp+t s3az+sj7eA0jfgPfeoN1 """) MH_MAGIC = 0xfeedface MH_CIGAM = 0xcefaedfe MH_MAGIC_64 = 0xfeedfacf MH_CIGAM_64 = 0xcffaedfe FAT_MAGIC = 0xcafebabe BIG_ENDIAN = '>' LITTLE_ENDIAN = '<' LC_LOAD_DYLIB = 0xc maxint = majver == 3 and getattr(sys, 'maxsize') or getattr(sys, 'maxint') class fileview(object): """ A proxy for file-like objects that exposes a given view of a file. Modified from macholib. """ def __init__(self, fileobj, start=0, size=maxint): if isinstance(fileobj, fileview): self._fileobj = fileobj._fileobj else: self._fileobj = fileobj self._start = start self._end = start + size self._pos = 0 def __repr__(self): return '' % ( self._start, self._end, self._fileobj) def tell(self): return self._pos def _checkwindow(self, seekto, op): if not (self._start <= seekto <= self._end): raise IOError("%s to offset %d is outside window [%d, %d]" % ( op, seekto, self._start, self._end)) def seek(self, offset, whence=0): seekto = offset if whence == os.SEEK_SET: seekto += self._start elif whence == os.SEEK_CUR: seekto += self._start + self._pos elif whence == os.SEEK_END: seekto += self._end else: raise IOError("Invalid whence argument to seek: %r" % (whence,)) self._checkwindow(seekto, 'seek') self._fileobj.seek(seekto) self._pos = seekto - self._start def write(self, bytes): here = self._start + self._pos self._checkwindow(here, 'write') self._checkwindow(here + len(bytes), 'write') self._fileobj.seek(here, os.SEEK_SET) self._fileobj.write(bytes) self._pos += len(bytes) def read(self, size=maxint): assert size >= 0 here = self._start + self._pos self._checkwindow(here, 'read') size = min(size, self._end - here) self._fileobj.seek(here, os.SEEK_SET) bytes = self._fileobj.read(size) self._pos += len(bytes) return bytes def read_data(file, endian, num=1): """ Read a given number of 32-bits unsigned integers from the given file with the given endianness. """ res = struct.unpack(endian + 'L' * num, file.read(num * 4)) if len(res) == 1: return res[0] return res def mach_o_change(path, what, value): """ Replace a given name (what) in any LC_LOAD_DYLIB command found in the given binary with a new name (value), provided it's shorter. """ def do_macho(file, bits, endian): # Read Mach-O header (the magic number is assumed read by the caller) cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags = read_data(file, endian, 6) # 64-bits header has one more field. if bits == 64: read_data(file, endian) # The header is followed by ncmds commands for n in range(ncmds): where = file.tell() # Read command header cmd, cmdsize = read_data(file, endian, 2) if cmd == LC_LOAD_DYLIB: # The first data field in LC_LOAD_DYLIB commands is the # offset of the name, starting from the beginning of the # command. name_offset = read_data(file, endian) file.seek(where + name_offset, os.SEEK_SET) # Read the NUL terminated string load = file.read(cmdsize - name_offset).decode() load = load[:load.index('\0')] # If the string is what is being replaced, overwrite it. if load == what: file.seek(where + name_offset, os.SEEK_SET) file.write(value.encode() + '\0'.encode()) # Seek to the next command file.seek(where + cmdsize, os.SEEK_SET) def do_file(file, offset=0, size=maxint): file = fileview(file, offset, size) # Read magic number magic = read_data(file, BIG_ENDIAN) if magic == FAT_MAGIC: # Fat binaries contain nfat_arch Mach-O binaries nfat_arch = read_data(file, BIG_ENDIAN) for n in range(nfat_arch): # Read arch header cputype, cpusubtype, offset, size, align = read_data(file, BIG_ENDIAN, 5) do_file(file, offset, size) elif magic == MH_MAGIC: do_macho(file, 32, BIG_ENDIAN) elif magic == MH_CIGAM: do_macho(file, 32, LITTLE_ENDIAN) elif magic == MH_MAGIC_64: do_macho(file, 64, BIG_ENDIAN) elif magic == MH_CIGAM_64: do_macho(file, 64, LITTLE_ENDIAN) assert(len(what) >= len(value)) do_file(open(path, 'r+b')) if __name__ == '__main__': main() ## TODO: ## Copy python.exe.manifest ## Monkeypatch distutils.sysconfig gcovr-3.2/admin/virtualenv_1.7.py000077500000000000000000002614011235605057100167370ustar00rootroot00000000000000#!/usr/bin/env python """Create a "virtual" Python installation """ # If you change the version here, change it in setup.py # and docs/conf.py as well. virtualenv_version = "1.7" import base64 import sys import os import optparse import re import shutil import logging import tempfile import zlib import errno import distutils.sysconfig from distutils.util import strtobool try: import subprocess except ImportError: if sys.version_info <= (2, 3): print('ERROR: %s' % sys.exc_info()[1]) print('ERROR: this script requires Python 2.4 or greater; or at least the subprocess module.') print('If you copy subprocess.py from a newer version of Python this script will probably work') sys.exit(101) else: raise try: set except NameError: from sets import Set as set try: basestring except NameError: basestring = str try: import ConfigParser except ImportError: import configparser as ConfigParser join = os.path.join py_version = 'python%s.%s' % (sys.version_info[0], sys.version_info[1]) is_jython = sys.platform.startswith('java') is_pypy = hasattr(sys, 'pypy_version_info') is_win = (sys.platform == 'win32') abiflags = getattr(sys, 'abiflags', '') user_dir = os.path.expanduser('~') if sys.platform == 'win32': user_dir = os.environ.get('APPDATA', user_dir) # Use %APPDATA% for roaming default_storage_dir = os.path.join(user_dir, 'virtualenv') else: default_storage_dir = os.path.join(user_dir, '.virtualenv') default_config_file = os.path.join(default_storage_dir, 'virtualenv.ini') if is_pypy: expected_exe = 'pypy' elif is_jython: expected_exe = 'jython' else: expected_exe = 'python' REQUIRED_MODULES = ['os', 'posix', 'posixpath', 'nt', 'ntpath', 'genericpath', 'fnmatch', 'locale', 'encodings', 'codecs', 'stat', 'UserDict', 'readline', 'copy_reg', 'types', 're', 'sre', 'sre_parse', 'sre_constants', 'sre_compile', 'zlib'] REQUIRED_FILES = ['lib-dynload', 'config'] majver, minver = sys.version_info[:2] if majver == 2: if minver >= 6: REQUIRED_MODULES.extend(['warnings', 'linecache', '_abcoll', 'abc']) if minver >= 7: REQUIRED_MODULES.extend(['_weakrefset']) if minver <= 3: REQUIRED_MODULES.extend(['sets', '__future__']) elif majver == 3: # Some extra modules are needed for Python 3, but different ones # for different versions. REQUIRED_MODULES.extend(['_abcoll', 'warnings', 'linecache', 'abc', 'io', '_weakrefset', 'copyreg', 'tempfile', 'random', '__future__', 'collections', 'keyword', 'tarfile', 'shutil', 'struct', 'copy']) if minver >= 2: REQUIRED_FILES[-1] = 'config-%s' % majver if minver == 3: # The whole list of 3.3 modules is reproduced below - the current # uncommented ones are required for 3.3 as of now, but more may be # added as 3.3 development continues. REQUIRED_MODULES.extend([ #"aifc", #"antigravity", #"argparse", #"ast", #"asynchat", #"asyncore", "base64", #"bdb", #"binhex", "bisect", #"calendar", #"cgi", #"cgitb", #"chunk", #"cmd", #"codeop", #"code", #"colorsys", #"_compat_pickle", #"compileall", #"concurrent", #"configparser", #"contextlib", #"cProfile", #"crypt", #"csv", #"ctypes", #"curses", #"datetime", #"dbm", #"decimal", #"difflib", #"dis", #"doctest", #"dummy_threading", "_dummy_thread", #"email", #"filecmp", #"fileinput", #"formatter", #"fractions", #"ftplib", #"functools", #"getopt", #"getpass", #"gettext", #"glob", #"gzip", "hashlib", "heapq", "hmac", #"html", #"http", #"idlelib", #"imaplib", #"imghdr", #"importlib", #"inspect", #"json", #"lib2to3", #"logging", #"macpath", #"macurl2path", #"mailbox", #"mailcap", #"_markupbase", #"mimetypes", #"modulefinder", #"multiprocessing", #"netrc", #"nntplib", #"nturl2path", #"numbers", #"opcode", #"optparse", #"os2emxpath", #"pdb", #"pickle", #"pickletools", #"pipes", #"pkgutil", #"platform", #"plat-linux2", #"plistlib", #"poplib", #"pprint", #"profile", #"pstats", #"pty", #"pyclbr", #"py_compile", #"pydoc_data", #"pydoc", #"_pyio", #"queue", #"quopri", "reprlib", "rlcompleter", #"runpy", #"sched", #"shelve", #"shlex", #"smtpd", #"smtplib", #"sndhdr", #"socket", #"socketserver", #"sqlite3", #"ssl", #"stringprep", #"string", #"_strptime", #"subprocess", #"sunau", #"symbol", #"symtable", #"sysconfig", #"tabnanny", #"telnetlib", #"test", #"textwrap", #"this", #"_threading_local", #"threading", #"timeit", #"tkinter", #"tokenize", #"token", #"traceback", #"trace", #"tty", #"turtledemo", #"turtle", #"unittest", #"urllib", #"uuid", #"uu", #"wave", "weakref", #"webbrowser", #"wsgiref", #"xdrlib", #"xml", #"xmlrpc", #"zipfile", ]) if is_pypy: # these are needed to correctly display the exceptions that may happen # during the bootstrap REQUIRED_MODULES.extend(['traceback', 'linecache']) class Logger(object): """ Logging object for use in command-line script. Allows ranges of levels, to avoid some redundancy of displayed information. """ DEBUG = logging.DEBUG INFO = logging.INFO NOTIFY = (logging.INFO+logging.WARN)/2 WARN = WARNING = logging.WARN ERROR = logging.ERROR FATAL = logging.FATAL LEVELS = [DEBUG, INFO, NOTIFY, WARN, ERROR, FATAL] def __init__(self, consumers): self.consumers = consumers self.indent = 0 self.in_progress = None self.in_progress_hanging = False def debug(self, msg, *args, **kw): self.log(self.DEBUG, msg, *args, **kw) def info(self, msg, *args, **kw): self.log(self.INFO, msg, *args, **kw) def notify(self, msg, *args, **kw): self.log(self.NOTIFY, msg, *args, **kw) def warn(self, msg, *args, **kw): self.log(self.WARN, msg, *args, **kw) def error(self, msg, *args, **kw): self.log(self.WARN, msg, *args, **kw) def fatal(self, msg, *args, **kw): self.log(self.FATAL, msg, *args, **kw) def log(self, level, msg, *args, **kw): if args: if kw: raise TypeError( "You may give positional or keyword arguments, not both") args = args or kw rendered = None for consumer_level, consumer in self.consumers: if self.level_matches(level, consumer_level): if (self.in_progress_hanging and consumer in (sys.stdout, sys.stderr)): self.in_progress_hanging = False sys.stdout.write('\n') sys.stdout.flush() if rendered is None: if args: rendered = msg % args else: rendered = msg rendered = ' '*self.indent + rendered if hasattr(consumer, 'write'): consumer.write(rendered+'\n') else: consumer(rendered) def start_progress(self, msg): assert not self.in_progress, ( "Tried to start_progress(%r) while in_progress %r" % (msg, self.in_progress)) if self.level_matches(self.NOTIFY, self._stdout_level()): sys.stdout.write(msg) sys.stdout.flush() self.in_progress_hanging = True else: self.in_progress_hanging = False self.in_progress = msg def end_progress(self, msg='done.'): assert self.in_progress, ( "Tried to end_progress without start_progress") if self.stdout_level_matches(self.NOTIFY): if not self.in_progress_hanging: # Some message has been printed out since start_progress sys.stdout.write('...' + self.in_progress + msg + '\n') sys.stdout.flush() else: sys.stdout.write(msg + '\n') sys.stdout.flush() self.in_progress = None self.in_progress_hanging = False def show_progress(self): """If we are in a progress scope, and no log messages have been shown, write out another '.'""" if self.in_progress_hanging: sys.stdout.write('.') sys.stdout.flush() def stdout_level_matches(self, level): """Returns true if a message at this level will go to stdout""" return self.level_matches(level, self._stdout_level()) def _stdout_level(self): """Returns the level that stdout runs at""" for level, consumer in self.consumers: if consumer is sys.stdout: return level return self.FATAL def level_matches(self, level, consumer_level): """ >>> l = Logger([]) >>> l.level_matches(3, 4) False >>> l.level_matches(3, 2) True >>> l.level_matches(slice(None, 3), 3) False >>> l.level_matches(slice(None, 3), 2) True >>> l.level_matches(slice(1, 3), 1) True >>> l.level_matches(slice(2, 3), 1) False """ if isinstance(level, slice): start, stop = level.start, level.stop if start is not None and start > consumer_level: return False if stop is not None and stop <= consumer_level: return False return True else: return level >= consumer_level #@classmethod def level_for_integer(cls, level): levels = cls.LEVELS if level < 0: return levels[0] if level >= len(levels): return levels[-1] return levels[level] level_for_integer = classmethod(level_for_integer) # create a silent logger just to prevent this from being undefined # will be overridden with requested verbosity main() is called. logger = Logger([(Logger.LEVELS[-1], sys.stdout)]) def mkdir(path): if not os.path.exists(path): logger.info('Creating %s', path) os.makedirs(path) else: logger.info('Directory %s already exists', path) def copyfileordir(src, dest): if os.path.isdir(src): shutil.copytree(src, dest, True) else: shutil.copy2(src, dest) def copyfile(src, dest, symlink=True): if not os.path.exists(src): # Some bad symlink in the src logger.warn('Cannot find file %s (bad symlink)', src) return if os.path.exists(dest): logger.debug('File %s already exists', dest) return if not os.path.exists(os.path.dirname(dest)): logger.info('Creating parent directories for %s' % os.path.dirname(dest)) os.makedirs(os.path.dirname(dest)) if not os.path.islink(src): srcpath = os.path.abspath(src) else: srcpath = os.readlink(src) if symlink and hasattr(os, 'symlink'): logger.info('Symlinking %s', dest) try: os.symlink(srcpath, dest) except (OSError, NotImplementedError): logger.info('Symlinking failed, copying to %s', dest) copyfileordir(src, dest) else: logger.info('Copying to %s', dest) copyfileordir(src, dest) def writefile(dest, content, overwrite=True): if not os.path.exists(dest): logger.info('Writing %s', dest) f = open(dest, 'wb') f.write(content.encode('utf-8')) f.close() return else: f = open(dest, 'rb') c = f.read() f.close() if c != content: if not overwrite: logger.notify('File %s exists with different content; not overwriting', dest) return logger.notify('Overwriting %s with new content', dest) f = open(dest, 'wb') f.write(content.encode('utf-8')) f.close() else: logger.info('Content %s already in place', dest) def rmtree(dir): if os.path.exists(dir): logger.notify('Deleting tree %s', dir) shutil.rmtree(dir) else: logger.info('Do not need to delete %s; already gone', dir) def make_exe(fn): if hasattr(os, 'chmod'): oldmode = os.stat(fn).st_mode & 0xFFF # 0o7777 newmode = (oldmode | 0x16D) & 0xFFF # 0o555, 0o7777 os.chmod(fn, newmode) logger.info('Changed mode of %s to %s', fn, oct(newmode)) def _find_file(filename, dirs): for dir in dirs: if os.path.exists(join(dir, filename)): return join(dir, filename) return filename def _install_req(py_executable, unzip=False, distribute=False, search_dirs=None, never_download=False): if search_dirs is None: search_dirs = file_search_dirs() if not distribute: setup_fn = 'setuptools-0.6c11-py%s.egg' % sys.version[:3] project_name = 'setuptools' bootstrap_script = EZ_SETUP_PY source = None else: setup_fn = None source = 'distribute-0.6.24.tar.gz' project_name = 'distribute' bootstrap_script = DISTRIBUTE_SETUP_PY if setup_fn is not None: setup_fn = _find_file(setup_fn, search_dirs) if source is not None: source = _find_file(source, search_dirs) if is_jython and os._name == 'nt': # Jython's .bat sys.executable can't handle a command line # argument with newlines fd, ez_setup = tempfile.mkstemp('.py') os.write(fd, bootstrap_script) os.close(fd) cmd = [py_executable, ez_setup] else: cmd = [py_executable, '-c', bootstrap_script] if unzip: cmd.append('--always-unzip') env = {} remove_from_env = [] if logger.stdout_level_matches(logger.DEBUG): cmd.append('-v') old_chdir = os.getcwd() if setup_fn is not None and os.path.exists(setup_fn): logger.info('Using existing %s egg: %s' % (project_name, setup_fn)) cmd.append(setup_fn) if os.environ.get('PYTHONPATH'): env['PYTHONPATH'] = setup_fn + os.path.pathsep + os.environ['PYTHONPATH'] else: env['PYTHONPATH'] = setup_fn else: # the source is found, let's chdir if source is not None and os.path.exists(source): logger.info('Using existing %s egg: %s' % (project_name, source)) os.chdir(os.path.dirname(source)) # in this case, we want to be sure that PYTHONPATH is unset (not # just empty, really unset), else CPython tries to import the # site.py that it's in virtualenv_support remove_from_env.append('PYTHONPATH') else: if never_download: logger.fatal("Can't find any local distributions of %s to install " "and --never-download is set. Either re-run virtualenv " "without the --never-download option, or place a %s " "distribution (%s) in one of these " "locations: %r" % (project_name, project_name, setup_fn or source, search_dirs)) sys.exit(1) logger.info('No %s egg found; downloading' % project_name) cmd.extend(['--always-copy', '-U', project_name]) logger.start_progress('Installing %s...' % project_name) logger.indent += 2 cwd = None if project_name == 'distribute': env['DONT_PATCH_SETUPTOOLS'] = 'true' def _filter_ez_setup(line): return filter_ez_setup(line, project_name) if not os.access(os.getcwd(), os.W_OK): cwd = tempfile.mkdtemp() if source is not None and os.path.exists(source): # the current working dir is hostile, let's copy the # tarball to a temp dir target = os.path.join(cwd, os.path.split(source)[-1]) shutil.copy(source, target) try: call_subprocess(cmd, show_stdout=False, filter_stdout=_filter_ez_setup, extra_env=env, remove_from_env=remove_from_env, cwd=cwd) finally: logger.indent -= 2 logger.end_progress() if os.getcwd() != old_chdir: os.chdir(old_chdir) if is_jython and os._name == 'nt': os.remove(ez_setup) def file_search_dirs(): here = os.path.dirname(os.path.abspath(__file__)) dirs = ['.', here, join(here, 'virtualenv_support')] if os.path.splitext(os.path.dirname(__file__))[0] != 'virtualenv': # Probably some boot script; just in case virtualenv is installed... try: import virtualenv except ImportError: pass else: dirs.append(os.path.join(os.path.dirname(virtualenv.__file__), 'virtualenv_support')) return [d for d in dirs if os.path.isdir(d)] def install_setuptools(py_executable, unzip=False, search_dirs=None, never_download=False): _install_req(py_executable, unzip, search_dirs=search_dirs, never_download=never_download) def install_distribute(py_executable, unzip=False, search_dirs=None, never_download=False): _install_req(py_executable, unzip, distribute=True, search_dirs=search_dirs, never_download=never_download) _pip_re = re.compile(r'^pip-.*(zip|tar.gz|tar.bz2|tgz|tbz)$', re.I) def install_pip(py_executable, search_dirs=None, never_download=False): if search_dirs is None: search_dirs = file_search_dirs() filenames = [] for dir in search_dirs: filenames.extend([join(dir, fn) for fn in os.listdir(dir) if _pip_re.search(fn)]) filenames = [(os.path.basename(filename).lower(), i, filename) for i, filename in enumerate(filenames)] filenames.sort() filenames = [filename for basename, i, filename in filenames] if not filenames: filename = 'pip' else: filename = filenames[-1] easy_install_script = 'easy_install' if sys.platform == 'win32': easy_install_script = 'easy_install-script.py' cmd = [join(os.path.dirname(py_executable), easy_install_script), filename] if sys.platform == 'win32': cmd.insert(0, py_executable) if filename == 'pip': if never_download: logger.fatal("Can't find any local distributions of pip to install " "and --never-download is set. Either re-run virtualenv " "without the --never-download option, or place a pip " "source distribution (zip/tar.gz/tar.bz2) in one of these " "locations: %r" % search_dirs) sys.exit(1) logger.info('Installing pip from network...') else: logger.info('Installing existing %s distribution: %s' % ( os.path.basename(filename), filename)) logger.start_progress('Installing pip...') logger.indent += 2 def _filter_setup(line): return filter_ez_setup(line, 'pip') try: call_subprocess(cmd, show_stdout=False, filter_stdout=_filter_setup) finally: logger.indent -= 2 logger.end_progress() def filter_ez_setup(line, project_name='setuptools'): if not line.strip(): return Logger.DEBUG if project_name == 'distribute': for prefix in ('Extracting', 'Now working', 'Installing', 'Before', 'Scanning', 'Setuptools', 'Egg', 'Already', 'running', 'writing', 'reading', 'installing', 'creating', 'copying', 'byte-compiling', 'removing', 'Processing'): if line.startswith(prefix): return Logger.DEBUG return Logger.DEBUG for prefix in ['Reading ', 'Best match', 'Processing setuptools', 'Copying setuptools', 'Adding setuptools', 'Installing ', 'Installed ']: if line.startswith(prefix): return Logger.DEBUG return Logger.INFO class UpdatingDefaultsHelpFormatter(optparse.IndentedHelpFormatter): """ Custom help formatter for use in ConfigOptionParser that updates the defaults before expanding them, allowing them to show up correctly in the help listing """ def expand_default(self, option): if self.parser is not None: self.parser.update_defaults(self.parser.defaults) return optparse.IndentedHelpFormatter.expand_default(self, option) class ConfigOptionParser(optparse.OptionParser): """ Custom option parser which updates its defaults by by checking the configuration files and environmental variables """ def __init__(self, *args, **kwargs): self.config = ConfigParser.RawConfigParser() self.files = self.get_config_files() self.config.read(self.files) optparse.OptionParser.__init__(self, *args, **kwargs) def get_config_files(self): config_file = os.environ.get('VIRTUALENV_CONFIG_FILE', False) if config_file and os.path.exists(config_file): return [config_file] return [default_config_file] def update_defaults(self, defaults): """ Updates the given defaults with values from the config files and the environ. Does a little special handling for certain types of options (lists). """ # Then go and look for the other sources of configuration: config = {} # 1. config files config.update(dict(self.get_config_section('virtualenv'))) # 2. environmental variables config.update(dict(self.get_environ_vars())) # Then set the options with those values for key, val in config.items(): key = key.replace('_', '-') if not key.startswith('--'): key = '--%s' % key # only prefer long opts option = self.get_option(key) if option is not None: # ignore empty values if not val: continue # handle multiline configs if option.action == 'append': val = val.split() else: option.nargs = 1 if option.action in ('store_true', 'store_false', 'count'): val = strtobool(val) try: val = option.convert_value(key, val) except optparse.OptionValueError: e = sys.exc_info()[1] print("An error occured during configuration: %s" % e) sys.exit(3) defaults[option.dest] = val return defaults def get_config_section(self, name): """ Get a section of a configuration """ if self.config.has_section(name): return self.config.items(name) return [] def get_environ_vars(self, prefix='VIRTUALENV_'): """ Returns a generator with all environmental vars with prefix VIRTUALENV """ for key, val in os.environ.items(): if key.startswith(prefix): yield (key.replace(prefix, '').lower(), val) def get_default_values(self): """ Overridding to make updating the defaults after instantiation of the option parser possible, update_defaults() does the dirty work. """ if not self.process_default_values: # Old, pre-Optik 1.5 behaviour. return optparse.Values(self.defaults) defaults = self.update_defaults(self.defaults.copy()) # ours for option in self._get_all_options(): default = defaults.get(option.dest) if isinstance(default, basestring): opt_str = option.get_opt_string() defaults[option.dest] = option.check_value(opt_str, default) return optparse.Values(defaults) def main(): parser = ConfigOptionParser( version=virtualenv_version, usage="%prog [OPTIONS] DEST_DIR", formatter=UpdatingDefaultsHelpFormatter()) parser.add_option( '-v', '--verbose', action='count', dest='verbose', default=0, help="Increase verbosity") parser.add_option( '-q', '--quiet', action='count', dest='quiet', default=0, help='Decrease verbosity') parser.add_option( '-p', '--python', dest='python', metavar='PYTHON_EXE', help='The Python interpreter to use, e.g., --python=python2.5 will use the python2.5 ' 'interpreter to create the new environment. The default is the interpreter that ' 'virtualenv was installed with (%s)' % sys.executable) parser.add_option( '--clear', dest='clear', action='store_true', help="Clear out the non-root install and start from scratch") parser.add_option( '--no-site-packages', dest='no_site_packages', action='store_true', help="Don't give access to the global site-packages dir to the " "virtual environment") parser.add_option( '--system-site-packages', dest='system_site_packages', action='store_true', help="Give access to the global site-packages dir to the " "virtual environment") parser.add_option( '--unzip-setuptools', dest='unzip_setuptools', action='store_true', help="Unzip Setuptools or Distribute when installing it") parser.add_option( '--relocatable', dest='relocatable', action='store_true', help='Make an EXISTING virtualenv environment relocatable. ' 'This fixes up scripts and makes all .pth files relative') parser.add_option( '--distribute', dest='use_distribute', action='store_true', help='Use Distribute instead of Setuptools. Set environ variable ' 'VIRTUALENV_DISTRIBUTE to make it the default ') default_search_dirs = file_search_dirs() parser.add_option( '--extra-search-dir', dest="search_dirs", action="append", default=default_search_dirs, help="Directory to look for setuptools/distribute/pip distributions in. " "You can add any number of additional --extra-search-dir paths.") parser.add_option( '--never-download', dest="never_download", action="store_true", help="Never download anything from the network. Instead, virtualenv will fail " "if local distributions of setuptools/distribute/pip are not present.") parser.add_option( '--prompt=', dest='prompt', help='Provides an alternative prompt prefix for this environment') if 'extend_parser' in globals(): extend_parser(parser) options, args = parser.parse_args() global logger if 'adjust_options' in globals(): adjust_options(options, args) verbosity = options.verbose - options.quiet logger = Logger([(Logger.level_for_integer(2-verbosity), sys.stdout)]) if options.python and not os.environ.get('VIRTUALENV_INTERPRETER_RUNNING'): env = os.environ.copy() interpreter = resolve_interpreter(options.python) if interpreter == sys.executable: logger.warn('Already using interpreter %s' % interpreter) else: logger.notify('Running virtualenv with interpreter %s' % interpreter) env['VIRTUALENV_INTERPRETER_RUNNING'] = 'true' file = __file__ if file.endswith('.pyc'): file = file[:-1] popen = subprocess.Popen([interpreter, file] + sys.argv[1:], env=env) raise SystemExit(popen.wait()) # Force --use-distribute on Python 3, since setuptools is not available. if majver > 2: options.use_distribute = True if os.environ.get('PYTHONDONTWRITEBYTECODE') and not options.use_distribute: print( "The PYTHONDONTWRITEBYTECODE environment variable is " "not compatible with setuptools. Either use --distribute " "or unset PYTHONDONTWRITEBYTECODE.") sys.exit(2) if not args: print('You must provide a DEST_DIR') parser.print_help() sys.exit(2) if len(args) > 1: print('There must be only one argument: DEST_DIR (you gave %s)' % ( ' '.join(args))) parser.print_help() sys.exit(2) home_dir = args[0] if os.environ.get('WORKING_ENV'): logger.fatal('ERROR: you cannot run virtualenv while in a workingenv') logger.fatal('Please deactivate your workingenv, then re-run this script') sys.exit(3) if 'PYTHONHOME' in os.environ: logger.warn('PYTHONHOME is set. You *must* activate the virtualenv before using it') del os.environ['PYTHONHOME'] if options.relocatable: make_environment_relocatable(home_dir) return if options.no_site_packages: logger.warn('The --no-site-packages flag is deprecated; it is now ' 'the default behavior.') create_environment(home_dir, site_packages=options.system_site_packages, clear=options.clear, unzip_setuptools=options.unzip_setuptools, use_distribute=options.use_distribute, prompt=options.prompt, search_dirs=options.search_dirs, never_download=options.never_download) if 'after_install' in globals(): after_install(options, home_dir) def call_subprocess(cmd, show_stdout=True, filter_stdout=None, cwd=None, raise_on_returncode=True, extra_env=None, remove_from_env=None): cmd_parts = [] for part in cmd: if len(part) > 45: part = part[:20]+"..."+part[-20:] if ' ' in part or '\n' in part or '"' in part or "'" in part: part = '"%s"' % part.replace('"', '\\"') if hasattr(part, 'decode'): try: part = part.decode(sys.getdefaultencoding()) except UnicodeDecodeError: part = part.decode(sys.getfilesystemencoding()) cmd_parts.append(part) cmd_desc = ' '.join(cmd_parts) if show_stdout: stdout = None else: stdout = subprocess.PIPE logger.debug("Running command %s" % cmd_desc) if extra_env or remove_from_env: env = os.environ.copy() if extra_env: env.update(extra_env) if remove_from_env: for varname in remove_from_env: env.pop(varname, None) else: env = None try: proc = subprocess.Popen( cmd, stderr=subprocess.STDOUT, stdin=None, stdout=stdout, cwd=cwd, env=env) except Exception: e = sys.exc_info()[1] logger.fatal( "Error %s while executing command %s" % (e, cmd_desc)) raise all_output = [] if stdout is not None: stdout = proc.stdout encoding = sys.getdefaultencoding() fs_encoding = sys.getfilesystemencoding() while 1: line = stdout.readline() try: line = line.decode(encoding) except UnicodeDecodeError: line = line.decode(fs_encoding) if not line: break line = line.rstrip() all_output.append(line) if filter_stdout: level = filter_stdout(line) if isinstance(level, tuple): level, line = level logger.log(level, line) if not logger.stdout_level_matches(level): logger.show_progress() else: logger.info(line) else: proc.communicate() proc.wait() if proc.returncode: if raise_on_returncode: if all_output: logger.notify('Complete output from command %s:' % cmd_desc) logger.notify('\n'.join(all_output) + '\n----------------------------------------') raise OSError( "Command %s failed with error code %s" % (cmd_desc, proc.returncode)) else: logger.warn( "Command %s had error code %s" % (cmd_desc, proc.returncode)) def create_environment(home_dir, site_packages=False, clear=False, unzip_setuptools=False, use_distribute=False, prompt=None, search_dirs=None, never_download=False): """ Creates a new environment in ``home_dir``. If ``site_packages`` is true, then the global ``site-packages/`` directory will be on the path. If ``clear`` is true (default False) then the environment will first be cleared. """ home_dir, lib_dir, inc_dir, bin_dir = path_locations(home_dir) py_executable = os.path.abspath(install_python( home_dir, lib_dir, inc_dir, bin_dir, site_packages=site_packages, clear=clear)) install_distutils(home_dir) # use_distribute also is True if VIRTUALENV_DISTRIBUTE env var is set # we also check VIRTUALENV_USE_DISTRIBUTE for backwards compatibility if use_distribute or os.environ.get('VIRTUALENV_USE_DISTRIBUTE'): install_distribute(py_executable, unzip=unzip_setuptools, search_dirs=search_dirs, never_download=never_download) else: install_setuptools(py_executable, unzip=unzip_setuptools, search_dirs=search_dirs, never_download=never_download) install_pip(py_executable, search_dirs=search_dirs, never_download=never_download) install_activate(home_dir, bin_dir, prompt) def path_locations(home_dir): """Return the path locations for the environment (where libraries are, where scripts go, etc)""" # XXX: We'd use distutils.sysconfig.get_python_inc/lib but its # prefix arg is broken: http://bugs.python.org/issue3386 if sys.platform == 'win32': # Windows has lots of problems with executables with spaces in # the name; this function will remove them (using the ~1 # format): mkdir(home_dir) if ' ' in home_dir: try: import win32api except ImportError: print('Error: the path "%s" has a space in it' % home_dir) print('To handle these kinds of paths, the win32api module must be installed:') print(' http://sourceforge.net/projects/pywin32/') sys.exit(3) home_dir = win32api.GetShortPathName(home_dir) lib_dir = join(home_dir, 'Lib') inc_dir = join(home_dir, 'Include') bin_dir = join(home_dir, 'Scripts') elif is_jython: lib_dir = join(home_dir, 'Lib') inc_dir = join(home_dir, 'Include') bin_dir = join(home_dir, 'bin') elif is_pypy: lib_dir = home_dir inc_dir = join(home_dir, 'include') bin_dir = join(home_dir, 'bin') else: lib_dir = join(home_dir, 'lib', py_version) inc_dir = join(home_dir, 'include', py_version + abiflags) bin_dir = join(home_dir, 'bin') return home_dir, lib_dir, inc_dir, bin_dir def change_prefix(filename, dst_prefix): prefixes = [sys.prefix] if sys.platform == "darwin": prefixes.extend(( os.path.join("/Library/Python", sys.version[:3], "site-packages"), os.path.join(sys.prefix, "Extras", "lib", "python"), os.path.join("~", "Library", "Python", sys.version[:3], "site-packages"))) if hasattr(sys, 'real_prefix'): prefixes.append(sys.real_prefix) prefixes = list(map(os.path.abspath, prefixes)) filename = os.path.abspath(filename) for src_prefix in prefixes: if filename.startswith(src_prefix): _, relpath = filename.split(src_prefix, 1) assert relpath[0] == os.sep relpath = relpath[1:] return join(dst_prefix, relpath) assert False, "Filename %s does not start with any of these prefixes: %s" % \ (filename, prefixes) def copy_required_modules(dst_prefix): import imp for modname in REQUIRED_MODULES: if modname in sys.builtin_module_names: logger.info("Ignoring built-in bootstrap module: %s" % modname) continue try: f, filename, _ = imp.find_module(modname) except ImportError: logger.info("Cannot import bootstrap module: %s" % modname) else: if f is not None: f.close() dst_filename = change_prefix(filename, dst_prefix) copyfile(filename, dst_filename) if filename.endswith('.pyc'): pyfile = filename[:-1] if os.path.exists(pyfile): copyfile(pyfile, dst_filename[:-1]) def install_python(home_dir, lib_dir, inc_dir, bin_dir, site_packages, clear): """Install just the base environment, no distutils patches etc""" if sys.executable.startswith(bin_dir): print('Please use the *system* python to run this script') return if clear: rmtree(lib_dir) ## FIXME: why not delete it? ## Maybe it should delete everything with #!/path/to/venv/python in it logger.notify('Not deleting %s', bin_dir) if hasattr(sys, 'real_prefix'): logger.notify('Using real prefix %r' % sys.real_prefix) prefix = sys.real_prefix else: prefix = sys.prefix mkdir(lib_dir) fix_lib64(lib_dir) fix_local_scheme(home_dir) stdlib_dirs = [os.path.dirname(os.__file__)] if sys.platform == 'win32': stdlib_dirs.append(join(os.path.dirname(stdlib_dirs[0]), 'DLLs')) elif sys.platform == 'darwin': stdlib_dirs.append(join(stdlib_dirs[0], 'site-packages')) if hasattr(os, 'symlink'): logger.info('Symlinking Python bootstrap modules') else: logger.info('Copying Python bootstrap modules') logger.indent += 2 try: # copy required files... for stdlib_dir in stdlib_dirs: if not os.path.isdir(stdlib_dir): continue for fn in os.listdir(stdlib_dir): bn = os.path.splitext(fn)[0] if fn != 'site-packages' and bn in REQUIRED_FILES: copyfile(join(stdlib_dir, fn), join(lib_dir, fn)) # ...and modules copy_required_modules(home_dir) finally: logger.indent -= 2 mkdir(join(lib_dir, 'site-packages')) import site site_filename = site.__file__ if site_filename.endswith('.pyc'): site_filename = site_filename[:-1] elif site_filename.endswith('$py.class'): site_filename = site_filename.replace('$py.class', '.py') site_filename_dst = change_prefix(site_filename, home_dir) site_dir = os.path.dirname(site_filename_dst) writefile(site_filename_dst, SITE_PY) writefile(join(site_dir, 'orig-prefix.txt'), prefix) site_packages_filename = join(site_dir, 'no-global-site-packages.txt') if not site_packages: writefile(site_packages_filename, '') else: if os.path.exists(site_packages_filename): logger.info('Deleting %s' % site_packages_filename) os.unlink(site_packages_filename) if is_pypy or is_win: stdinc_dir = join(prefix, 'include') else: stdinc_dir = join(prefix, 'include', py_version + abiflags) if os.path.exists(stdinc_dir): copyfile(stdinc_dir, inc_dir) else: logger.debug('No include dir %s' % stdinc_dir) # pypy never uses exec_prefix, just ignore it if sys.exec_prefix != prefix and not is_pypy: if sys.platform == 'win32': exec_dir = join(sys.exec_prefix, 'lib') elif is_jython: exec_dir = join(sys.exec_prefix, 'Lib') else: exec_dir = join(sys.exec_prefix, 'lib', py_version) for fn in os.listdir(exec_dir): copyfile(join(exec_dir, fn), join(lib_dir, fn)) if is_jython: # Jython has either jython-dev.jar and javalib/ dir, or just # jython.jar for name in 'jython-dev.jar', 'javalib', 'jython.jar': src = join(prefix, name) if os.path.exists(src): copyfile(src, join(home_dir, name)) # XXX: registry should always exist after Jython 2.5rc1 src = join(prefix, 'registry') if os.path.exists(src): copyfile(src, join(home_dir, 'registry'), symlink=False) copyfile(join(prefix, 'cachedir'), join(home_dir, 'cachedir'), symlink=False) mkdir(bin_dir) py_executable = join(bin_dir, os.path.basename(sys.executable)) if 'Python.framework' in prefix: if re.search(r'/Python(?:-32|-64)*$', py_executable): # The name of the python executable is not quite what # we want, rename it. py_executable = os.path.join( os.path.dirname(py_executable), 'python') logger.notify('New %s executable in %s', expected_exe, py_executable) if sys.executable != py_executable: ## FIXME: could I just hard link? executable = sys.executable if sys.platform == 'cygwin' and os.path.exists(executable + '.exe'): # Cygwin misreports sys.executable sometimes executable += '.exe' py_executable += '.exe' logger.info('Executable actually exists in %s' % executable) shutil.copyfile(executable, py_executable) make_exe(py_executable) if sys.platform == 'win32' or sys.platform == 'cygwin': pythonw = os.path.join(os.path.dirname(sys.executable), 'pythonw.exe') if os.path.exists(pythonw): logger.info('Also created pythonw.exe') shutil.copyfile(pythonw, os.path.join(os.path.dirname(py_executable), 'pythonw.exe')) if is_pypy: # make a symlink python --> pypy-c python_executable = os.path.join(os.path.dirname(py_executable), 'python') logger.info('Also created executable %s' % python_executable) copyfile(py_executable, python_executable) if os.path.splitext(os.path.basename(py_executable))[0] != expected_exe: secondary_exe = os.path.join(os.path.dirname(py_executable), expected_exe) py_executable_ext = os.path.splitext(py_executable)[1] if py_executable_ext == '.exe': # python2.4 gives an extension of '.4' :P secondary_exe += py_executable_ext if os.path.exists(secondary_exe): logger.warn('Not overwriting existing %s script %s (you must use %s)' % (expected_exe, secondary_exe, py_executable)) else: logger.notify('Also creating executable in %s' % secondary_exe) shutil.copyfile(sys.executable, secondary_exe) make_exe(secondary_exe) if 'Python.framework' in prefix: logger.debug('MacOSX Python framework detected') # Make sure we use the the embedded interpreter inside # the framework, even if sys.executable points to # the stub executable in ${sys.prefix}/bin # See http://groups.google.com/group/python-virtualenv/ # browse_thread/thread/17cab2f85da75951 original_python = os.path.join( prefix, 'Resources/Python.app/Contents/MacOS/Python') shutil.copy(original_python, py_executable) # Copy the framework's dylib into the virtual # environment virtual_lib = os.path.join(home_dir, '.Python') if os.path.exists(virtual_lib): os.unlink(virtual_lib) copyfile( os.path.join(prefix, 'Python'), virtual_lib) # And then change the install_name of the copied python executable try: call_subprocess( ["install_name_tool", "-change", os.path.join(prefix, 'Python'), '@executable_path/../.Python', py_executable]) except: logger.fatal( "Could not call install_name_tool -- you must have Apple's development tools installed") raise # Some tools depend on pythonX.Y being present py_executable_version = '%s.%s' % ( sys.version_info[0], sys.version_info[1]) if not py_executable.endswith(py_executable_version): # symlinking pythonX.Y > python pth = py_executable + '%s.%s' % ( sys.version_info[0], sys.version_info[1]) if os.path.exists(pth): os.unlink(pth) os.symlink('python', pth) else: # reverse symlinking python -> pythonX.Y (with --python) pth = join(bin_dir, 'python') if os.path.exists(pth): os.unlink(pth) os.symlink(os.path.basename(py_executable), pth) if sys.platform == 'win32' and ' ' in py_executable: # There's a bug with subprocess on Windows when using a first # argument that has a space in it. Instead we have to quote # the value: py_executable = '"%s"' % py_executable cmd = [py_executable, '-c', """ import sys prefix = sys.prefix if sys.version_info[0] == 3: prefix = prefix.encode('utf8') if hasattr(sys.stdout, 'detach'): sys.stdout = sys.stdout.detach() elif hasattr(sys.stdout, 'buffer'): sys.stdout = sys.stdout.buffer sys.stdout.write(prefix) """] logger.info('Testing executable with %s %s "%s"' % tuple(cmd)) try: proc = subprocess.Popen(cmd, stdout=subprocess.PIPE) proc_stdout, proc_stderr = proc.communicate() except OSError: e = sys.exc_info()[1] if e.errno == errno.EACCES: logger.fatal('ERROR: The executable %s could not be run: %s' % (py_executable, e)) sys.exit(100) else: raise e proc_stdout = proc_stdout.strip().decode("utf-8") proc_stdout = os.path.normcase(os.path.abspath(proc_stdout)) norm_home_dir = os.path.normcase(os.path.abspath(home_dir)) if hasattr(norm_home_dir, 'decode'): norm_home_dir = norm_home_dir.decode(sys.getfilesystemencoding()) if proc_stdout != norm_home_dir: logger.fatal( 'ERROR: The executable %s is not functioning' % py_executable) logger.fatal( 'ERROR: It thinks sys.prefix is %r (should be %r)' % (proc_stdout, norm_home_dir)) logger.fatal( 'ERROR: virtualenv is not compatible with this system or executable') if sys.platform == 'win32': logger.fatal( 'Note: some Windows users have reported this error when they installed Python for "Only this user". The problem may be resolvable if you install Python "For all users". (See https://bugs.launchpad.net/virtualenv/+bug/352844)') sys.exit(100) else: logger.info('Got sys.prefix result: %r' % proc_stdout) pydistutils = os.path.expanduser('~/.pydistutils.cfg') if os.path.exists(pydistutils): logger.notify('Please make sure you remove any previous custom paths from ' 'your %s file.' % pydistutils) ## FIXME: really this should be calculated earlier return py_executable def install_activate(home_dir, bin_dir, prompt=None): if sys.platform == 'win32' or is_jython and os._name == 'nt': files = {'activate.bat': ACTIVATE_BAT, 'deactivate.bat': DEACTIVATE_BAT} if os.environ.get('OS') == 'Windows_NT' and os.environ.get('OSTYPE') == 'cygwin': files['activate'] = ACTIVATE_SH else: files = {'activate': ACTIVATE_SH} # suppling activate.fish in addition to, not instead of, the # bash script support. files['activate.fish'] = ACTIVATE_FISH # same for csh/tcsh support... files['activate.csh'] = ACTIVATE_CSH files['activate_this.py'] = ACTIVATE_THIS home_dir = os.path.abspath(home_dir) if hasattr(home_dir, 'decode'): home_dir = home_dir.decode(sys.getfilesystemencoding()) vname = os.path.basename(home_dir) for name, content in files.items(): content = content.replace('__VIRTUAL_PROMPT__', prompt or '') content = content.replace('__VIRTUAL_WINPROMPT__', prompt or '(%s)' % vname) content = content.replace('__VIRTUAL_ENV__', home_dir) content = content.replace('__VIRTUAL_NAME__', vname) content = content.replace('__BIN_NAME__', os.path.basename(bin_dir)) writefile(os.path.join(bin_dir, name), content) def install_distutils(home_dir): distutils_path = change_prefix(distutils.__path__[0], home_dir) mkdir(distutils_path) ## FIXME: maybe this prefix setting should only be put in place if ## there's a local distutils.cfg with a prefix setting? home_dir = os.path.abspath(home_dir) ## FIXME: this is breaking things, removing for now: #distutils_cfg = DISTUTILS_CFG + "\n[install]\nprefix=%s\n" % home_dir writefile(os.path.join(distutils_path, '__init__.py'), DISTUTILS_INIT) writefile(os.path.join(distutils_path, 'distutils.cfg'), DISTUTILS_CFG, overwrite=False) def fix_local_scheme(home_dir): """ Platforms that use the "posix_local" install scheme (like Ubuntu with Python 2.7) need to be given an additional "local" location, sigh. """ try: import sysconfig except ImportError: pass else: if sysconfig._get_default_scheme() == 'posix_local': local_path = os.path.join(home_dir, 'local') if not os.path.exists(local_path): os.symlink(os.path.abspath(home_dir), local_path) def fix_lib64(lib_dir): """ Some platforms (particularly Gentoo on x64) put things in lib64/pythonX.Y instead of lib/pythonX.Y. If this is such a platform we'll just create a symlink so lib64 points to lib """ if [p for p in distutils.sysconfig.get_config_vars().values() if isinstance(p, basestring) and 'lib64' in p]: logger.debug('This system uses lib64; symlinking lib64 to lib') assert os.path.basename(lib_dir) == 'python%s' % sys.version[:3], ( "Unexpected python lib dir: %r" % lib_dir) lib_parent = os.path.dirname(lib_dir) assert os.path.basename(lib_parent) == 'lib', ( "Unexpected parent dir: %r" % lib_parent) copyfile(lib_parent, os.path.join(os.path.dirname(lib_parent), 'lib64')) def resolve_interpreter(exe): """ If the executable given isn't an absolute path, search $PATH for the interpreter """ if os.path.abspath(exe) != exe: paths = os.environ.get('PATH', '').split(os.pathsep) for path in paths: if os.path.exists(os.path.join(path, exe)): exe = os.path.join(path, exe) break if not os.path.exists(exe): logger.fatal('The executable %s (from --python=%s) does not exist' % (exe, exe)) raise SystemExit(3) if not is_executable(exe): logger.fatal('The executable %s (from --python=%s) is not executable' % (exe, exe)) raise SystemExit(3) return exe def is_executable(exe): """Checks a file is executable""" return os.access(exe, os.X_OK) ############################################################ ## Relocating the environment: def make_environment_relocatable(home_dir): """ Makes the already-existing environment use relative paths, and takes out the #!-based environment selection in scripts. """ home_dir, lib_dir, inc_dir, bin_dir = path_locations(home_dir) activate_this = os.path.join(bin_dir, 'activate_this.py') if not os.path.exists(activate_this): logger.fatal( 'The environment doesn\'t have a file %s -- please re-run virtualenv ' 'on this environment to update it' % activate_this) fixup_scripts(home_dir) fixup_pth_and_egg_link(home_dir) ## FIXME: need to fix up distutils.cfg OK_ABS_SCRIPTS = ['python', 'python%s' % sys.version[:3], 'activate', 'activate.bat', 'activate_this.py'] def fixup_scripts(home_dir): # This is what we expect at the top of scripts: shebang = '#!%s/bin/python' % os.path.normcase(os.path.abspath(home_dir)) # This is what we'll put: new_shebang = '#!/usr/bin/env python%s' % sys.version[:3] activate = "import os; activate_this=os.path.join(os.path.dirname(__file__), 'activate_this.py'); execfile(activate_this, dict(__file__=activate_this)); del os, activate_this" if sys.platform == 'win32': bin_suffix = 'Scripts' else: bin_suffix = 'bin' bin_dir = os.path.join(home_dir, bin_suffix) home_dir, lib_dir, inc_dir, bin_dir = path_locations(home_dir) for filename in os.listdir(bin_dir): filename = os.path.join(bin_dir, filename) if not os.path.isfile(filename): # ignore subdirs, e.g. .svn ones. continue f = open(filename, 'rb') lines = f.readlines() f.close() if not lines: logger.warn('Script %s is an empty file' % filename) continue if not lines[0].strip().startswith(shebang): if os.path.basename(filename) in OK_ABS_SCRIPTS: logger.debug('Cannot make script %s relative' % filename) elif lines[0].strip() == new_shebang: logger.info('Script %s has already been made relative' % filename) else: logger.warn('Script %s cannot be made relative (it\'s not a normal script that starts with %s)' % (filename, shebang)) continue logger.notify('Making script %s relative' % filename) lines = [new_shebang+'\n', activate+'\n'] + lines[1:] f = open(filename, 'wb') f.writelines(lines) f.close() def fixup_pth_and_egg_link(home_dir, sys_path=None): """Makes .pth and .egg-link files use relative paths""" home_dir = os.path.normcase(os.path.abspath(home_dir)) if sys_path is None: sys_path = sys.path for path in sys_path: if not path: path = '.' if not os.path.isdir(path): continue path = os.path.normcase(os.path.abspath(path)) if not path.startswith(home_dir): logger.debug('Skipping system (non-environment) directory %s' % path) continue for filename in os.listdir(path): filename = os.path.join(path, filename) if filename.endswith('.pth'): if not os.access(filename, os.W_OK): logger.warn('Cannot write .pth file %s, skipping' % filename) else: fixup_pth_file(filename) if filename.endswith('.egg-link'): if not os.access(filename, os.W_OK): logger.warn('Cannot write .egg-link file %s, skipping' % filename) else: fixup_egg_link(filename) def fixup_pth_file(filename): lines = [] prev_lines = [] f = open(filename) prev_lines = f.readlines() f.close() for line in prev_lines: line = line.strip() if (not line or line.startswith('#') or line.startswith('import ') or os.path.abspath(line) != line): lines.append(line) else: new_value = make_relative_path(filename, line) if line != new_value: logger.debug('Rewriting path %s as %s (in %s)' % (line, new_value, filename)) lines.append(new_value) if lines == prev_lines: logger.info('No changes to .pth file %s' % filename) return logger.notify('Making paths in .pth file %s relative' % filename) f = open(filename, 'w') f.write('\n'.join(lines) + '\n') f.close() def fixup_egg_link(filename): f = open(filename) link = f.read().strip() f.close() if os.path.abspath(link) != link: logger.debug('Link in %s already relative' % filename) return new_link = make_relative_path(filename, link) logger.notify('Rewriting link %s in %s as %s' % (link, filename, new_link)) f = open(filename, 'w') f.write(new_link) f.close() def make_relative_path(source, dest, dest_is_directory=True): """ Make a filename relative, where the filename is dest, and it is being referred to from the filename source. >>> make_relative_path('/usr/share/something/a-file.pth', ... '/usr/share/another-place/src/Directory') '../another-place/src/Directory' >>> make_relative_path('/usr/share/something/a-file.pth', ... '/home/user/src/Directory') '../../../home/user/src/Directory' >>> make_relative_path('/usr/share/a-file.pth', '/usr/share/') './' """ source = os.path.dirname(source) if not dest_is_directory: dest_filename = os.path.basename(dest) dest = os.path.dirname(dest) dest = os.path.normpath(os.path.abspath(dest)) source = os.path.normpath(os.path.abspath(source)) dest_parts = dest.strip(os.path.sep).split(os.path.sep) source_parts = source.strip(os.path.sep).split(os.path.sep) while dest_parts and source_parts and dest_parts[0] == source_parts[0]: dest_parts.pop(0) source_parts.pop(0) full_parts = ['..']*len(source_parts) + dest_parts if not dest_is_directory: full_parts.append(dest_filename) if not full_parts: # Special case for the current directory (otherwise it'd be '') return './' return os.path.sep.join(full_parts) ############################################################ ## Bootstrap script creation: def create_bootstrap_script(extra_text, python_version=''): """ Creates a bootstrap script, which is like this script but with extend_parser, adjust_options, and after_install hooks. This returns a string that (written to disk of course) can be used as a bootstrap script with your own customizations. The script will be the standard virtualenv.py script, with your extra text added (your extra text should be Python code). If you include these functions, they will be called: ``extend_parser(optparse_parser)``: You can add or remove options from the parser here. ``adjust_options(options, args)``: You can change options here, or change the args (if you accept different kinds of arguments, be sure you modify ``args`` so it is only ``[DEST_DIR]``). ``after_install(options, home_dir)``: After everything is installed, this function is called. This is probably the function you are most likely to use. An example would be:: def after_install(options, home_dir): subprocess.call([join(home_dir, 'bin', 'easy_install'), 'MyPackage']) subprocess.call([join(home_dir, 'bin', 'my-package-script'), 'setup', home_dir]) This example immediately installs a package, and runs a setup script from that package. If you provide something like ``python_version='2.4'`` then the script will start with ``#!/usr/bin/env python2.4`` instead of ``#!/usr/bin/env python``. You can use this when the script must be run with a particular Python version. """ filename = __file__ if filename.endswith('.pyc'): filename = filename[:-1] f = open(filename, 'rb') content = f.read() f.close() py_exe = 'python%s' % python_version content = (('#!/usr/bin/env %s\n' % py_exe) + '## WARNING: This file is generated\n' + content) return content.replace('##EXT' 'END##', extra_text) ##EXTEND## def convert(s): b = base64.b64decode(s.encode('ascii')) return zlib.decompress(b).decode('utf-8') ##file site.py SITE_PY = convert(""" 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 """) ##file ez_setup.py EZ_SETUP_PY = convert(""" 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 BDaonX65d/fwEjNqlDjLVIvM9X+XVxF7 """) ##file distribute_setup.py DISTRIBUTE_SETUP_PY = convert(""" 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 """) ##file activate.sh ACTIVATE_SH = convert(""" eJytVU1v4jAQPW9+xTT0ANVS1GsrDlRFAqmFqmG72m0rY5IJsRRslDiktNr/vuMQ8tFQpNU2B4I9 H36eeW/SglkgYvBFiLBKYg0LhCRGD1KhA7BjlUQuwkLIHne12HCNNpz5kVrBgsfBmdWCrUrA5VIq DVEiQWjwRISuDreW5eE+CtodeLeAnhZEGKMGFXqAciMiJVcoNWx4JPgixDjzEj48QVeCfcqmtzfs cfww+zG4ZfeD2ciGF7gCHaDMPM1jtvuHXAsPfF2rSGeOxV4iDY5GUGb3xVEYv2aj6WQ0vRseAlMY G5DKsAawwnQUXt2LQOYlzZoYByqhonqoqfxZf4BLD97i4DukgXADCPgGgdOLTK5arYxZB1xnrc9T EQFcHoZEAa1gSQioo/TPV5FZrDlxJA+NzwF+Ek1UonOzFnKZp6k5mgLBqSkuuAGXS4whJb5xz/xs wXCHjiVerAk5eh9Kfz1wqOldtVv9dkbscfjgjKeTA8XPrtaNauX5rInOxaHuOReNtpFjo1/OxdFG 5eY9hJ3L3jqcPJbATggXAemDLZX0MNZRYjSDH7C1wMHQh73DyYfTu8a0F9v+6D8W6XNnF1GEIXW/ JrSKPOtnW1YFat9mrLJkzLbyIlTvYzV0RGXcaTBfVLx7jF2PJ2wyuBsydpm7VSVa4C4Zb6pFO2TR huypCEPwuQjNftUrNl6GsYZzuFrrLdC9iJjQ3omAPBbcI2lsU77tUD43kw1NPZhTrnZWzuQKLomx Rd4OXM1ByExVVkmoTwfBJ7Lt10Iq1Kgo23Bmd8Ib1KrGbsbO4Pp2yO4fpnf3s6MnZiwuiJuls1/L Pu4yUCvhpA+vZaJvWWDTr0yFYYyVnHMqCEq+QniuYX225xmnzRENjbXACF3wkCYNVZ1mBwxoR9Iw WAo3/36oSOTfgjwEEQKt15e9Xpqm52+oaXxszmnE9GLl65RH2OMmS6+u5acKxDmlPgj2eT5/gQOX LLK0j1y0Uwbmn438VZkVpqlfNKa/YET/53j+99G8H8tUhr9ZSXs2 """) ##file activate.fish ACTIVATE_FISH = convert(""" eJydVm1v4jgQ/s6vmA1wBxUE7X2stJVYlVWR2lK13d6d9laRk0yIr8HmbIe0++tvnIQQB9pbXT5A Ys/LM55nZtyHx5RrSHiGsMm1gRAh1xhDwU0Kng8hFzMWGb5jBv2E69SDs0TJDdj3MxilxmzPZzP7 pVPMMl+q9bjXh1eZQ8SEkAZULoAbiLnCyGSvvV6SC7IoBcS4Nw0wjcFbvJDcjiuTswzFDpiIQaHJ lQAjQUi1YRmUboC2uZJig8J4PaCnT5IaDcgsbm/CjinOwgx1KcUTMEhhTgV4g2B1fRk8Le8fv86v g7v545UHpZB9rKnp+gXsMhxLunIIpwVQxP/l9c/Hq9Xt1epm4R27bva6AJqN92G4YhbMG2i+LB+u grv71c3dY7B6WtzfLy9bePbp0taDTXSwJQJszUnnp0y57mvpPcrF7ZODyhswtd59+/jdgw+fwBNS xLSscksUPIDqwwNmCez3PpxGeyBYg6HE0YdcWBxcKczYzuVJi5Wu915vn5oWePCCoPUZBN5B7IgV MCi54ZDLG7TUZ0HweXkb3M5vFmSpFm/gthhBx0UrveoPpv9AJ9unIbQYdUoe21bKg2q48sPFGVwu H+afrxd1qvclaNlRFyh1EQ2sSccEuNAGWQwysfVpz1tPajUqbqJUnEcIJkWo6OXDaodK8ZiLdbmM L1wb+9H0D+pcyPSrX5u5kgWSygRYXCnJUi/KKcuU4cqsAyTKZBiissLc7NFwizvjxtieKBVCIdWz fzilzPaYyljZN0cGN1v7NnaIPNCGmVy3GKuJaQ6iVjE1Qfm+36hglErwmnAD8hu0dDy4uICBA8ZV pQr/q/+O0KFW2kjelu9Dgb9SDBsWV4F4x5CswgS0zBVlk5tDMP5bVtUGpslbm81Lu2sdKq7uNMGh MVQ4fy9xhogC1lS5guhISa0DlBWv0O8odT6/LP+4WZzDV6FzIkEqC0uolGZSZoMnlpxplmD2euaT O4hkTpPnbztDccey0bhjDaBIqaWQa0uwEtQEwtyU56i4fq54F9IE3ORR6mKriODM4XOYZwaVYLYz 7SPbKkz4i7VkB6/Ot1upDE3znNqYKpM8raa0Bx8vfvntJ32UENsM4aI6gJL+jJwhxhh3jVIDOcpi m0r2hmEtS8XXXNBk71QCDXTBNhhPiHX2LtHkrVIlhoEshH/EZgdq53Eirqs5iFKMnkOmqZTtr3Xq djvPTWZT4S3NT5aVLgurMPUWI07BRVYqkQrmtCKohNY8qu9EdACoT6ki0a66XxVF4f9AQ3W38yO5 mWmZmIIpnDFrbXakvKWeZhLwhvrbUH8fahhqD0YUcBDJjEBMQwiznE4y5QbHrbhHBOnUAYzb2tVN jJa65e+eE2Ya30E2GurxUP8ssA6e/wOnvo3V78d3vTcvMB3n7l3iX1JXWqk= """) ##file activate.csh ACTIVATE_CSH = convert(""" eJx9U11vmzAUffevOCVRu+UB9pws29Kl0iq1aVWllaZlcgxciiViItsQdb9+xiQp+dh4QOB7Pu49 XHqY59IgkwVhVRmLmFAZSrGRNkdgykonhFiqSCRW1sJSmJg8wCDT5QrucRCyHn6WFRKhVGmhKwVp kUpNiS3emup3TY6XIn7DVNQyJUwlrgthJD6n/iCNv72uhCzCpFx9CRkThRQGKe08cWXJ9db/yh/u pvzl9mn+PLnjj5P5D1yM8QmXlzBkSdXwZ0H/BBc0mEo5FE5qI2jKhclHOOvy9HD/OO/6YO1mX9vx sY0H/tPIV0dtqel0V7iZvWyNg8XFcBA0ToEqVeqOdNUEQFvN41SumAv32VtJrakQNSmLWmgp4oJM yDoBHgoydtoEAs47r5wHHnUal5vbJ8oOI+9wI86vb2d8Nrm/4Xy4RZ8R85E4uTZPB5EZPnTaaAGu E59J8BE2J8XgrkbLeXMlVoQxznEYFYY8uFFdxsKQRx90Giwx9vSueHP1YNaUSFG4vTaErNSYuBOF lXiVyXa9Sy3JdClEyK1dD6Nos9mEf8iKlOpmqSNTZnYjNEWiUYn2pKNB3ttcLJ3HmYYXy6Un76f7 r8rRsC1TpTJj7f19m5sUf/V3Ir+x/yjtLu8KjLX/CmN/AcVGUUo= """) ##file activate.bat ACTIVATE_BAT = convert(""" eJyFUkEKgzAQvAfyhz0YaL9QEWpRqlSjWGspFPZQTevFHOr/adQaU1GaUzI7Mzu7ZF89XhKkEJS8 qxaKMMsvboQ+LxxE44VICSW1gEa2UFaibqoS0iyJ0xw2lIA6nX5AHCu1jpRsv5KRjknkac9VLVug sX9mtzxIeJDE/mg4OGp47qoLo3NHX2jsMB3AiDht5hryAUOEifoTdCXbSh7V0My2NMq/Xbh5MEjU ZT63gpgNT9lKOJ/CtHsvT99re3pX303kydn4HeyOeAg5cjf2EW1D6HOPkg9NGKhu """) ##file deactivate.bat DEACTIVATE_BAT = convert(""" eJxzSE3OyFfIT0vj4spMU0hJTcvMS01RiPf3cYkP8wwKCXX0iQ8I8vcNCFHQ4FIAguLUEgWIgK0q FlWqXJpcICVYpGzx2BAZ4uHv5+Hv6wq1BWINXBTdKriEKkI1DhW2QAfhttcxxANiFZCBbglQSJUL i2dASrm4rFz9XLgAwJNbyQ== """) ##file distutils-init.py DISTUTILS_INIT = convert(""" eJytV92L4zYQf/dfMU0ottuse7RvC6FQrg8Lxz2Ugz4si9HacqKuIxlJ2ST313dG8odkO9d7aGBB luZLv/nNjFacOqUtKJMIvzK3cXlhWgp5MDBsqK5SNYftsBAGpLLA4F1oe2Ytl+9wUvW55TswCi4c KibhbFDSglXQCFmDPXIwtm7FawLRbwtPzg2T9gf4gupKv4GS0N262w7V0NvpbCy8cvTo3eAus6C5 ETU3ICQZX1hFTw/dzR6V/AW1RCN4/XAtbsVXqIXmlVX6liS4lOzEYY9QFB2zx6LfoSNjz1a0pqT9 QOIfJWQ2E888NEVZNqLlZZnvIB0NpHkimlFdKn2iRRY7yGG/CCJb6Iz280d34SFXBS2yEYPNF0Q7 yM7oCjpWvbEDQmnhRwOs6zjThpKE8HogwRAgraqYFZgGZvzmzVh+mgz9vskT3hruwyjdFcqyENJw bbMPO5jdzonxK68QKT7B57CMRRG5shRSWDTX3dI8LzRndZbnSWL1zfvriUmK4TcGWSnZiEPCrxXv bM+sP7VW2is2WgWXCO3sAu3Rzysz3FiNCA8WPyM4gb1JAAmCiyTZbhFjWx3h9SzauuRXC9MFoVbc yNTCm1QXOOIfIn/g1kGMhDUBN72hI5XCBQtIXQw8UEEdma6Jaz4vJIJ51Orc15hzzmu6TdFp3ogr Aof0c98tsw1SiaiWotHffk3XYCkqdToxWRfTFXqgpg2khcLluOHMVC0zZhLKIomesfSreUNNgbXi Ky9VRzwzkBneNoGQyyvGjbsFQqOZvpWIjqH281lJ/jireFgR3cPzSyTGWzQpDNIU+03Fs4XKLkhp /n0uFnuF6VphB44b3uWRneSbBoMSioqE8oeF0JY+qTvYfEK+bPLYdoR4McfYQ7wMZj39q0kfP8q+ FfsymO0GzNlPh644Jje06ulqHpOEQqdJUfoidI2O4CWx4qOglLye6RrFQirpCRXvhoRqXH3sYdVJ AItvc+VUsLO2v2hVAWrNIfVGtkG351cUMNncbh/WdowtSPtCdkzYFv6mwYc9o2Jt68ud6wectBr8 hYAulPSlgzH44YbV3ikjrulEaNJxt+/H3wZ7bXSXje/YY4tfVVrVmUstaDwwOBLMg6iduDB0lMVC UyzYx7Ab4kjCqdViEJmDcdk/SKbgsjYXgfMznUWcrtS4z4fmJ/XOM1LPk/iIpqass5XwNbdnLb1Y 8h3ERXSWZI6rZJxKs1LBqVH65w0Oy4ra0CBYxEeuOMbDmV5GI6E0Ha/wgVTtkX0+OXvqsD02CKLf XHbeft85D7tTCMYy2Njp4DJP7gWJr6paVWXZ1+/6YXLv/iE0M90FktiI7yFJD9e7SOLhEkkaMTUO azq9i2woBNR0/0eoF1HFMf0H8ChxH/jgcB34GZIz3Qn4/vid+VEamQrOVqAPTrOfmD4MPdVh09tb 8dLLjvh/61lEP4yW5vJaH4vHcevG8agXvzPGoOhhXNncpTr99PTHx6e/UvffFLaxUSjuSeP286Dw gtEMcW1xKr/he4/6IQ6FUXP+0gkioHY5iwC9Eyx3HKO7af0zPPe+XyLn7fAY78k4aiR387bCr5XT 5C4rFgwLGfMvJuAMew== """) ##file distutils.cfg DISTUTILS_CFG = convert(""" eJxNj00KwkAMhfc9xYNuxe4Ft57AjYiUtDO1wXSmNJnK3N5pdSEEAu8nH6lxHVlRhtDHMPATA4uH xJ4EFmGbvfJiicSHFRzUSISMY6hq3GLCRLnIvSTnEefN0FIjw5tF0Hkk9Q5dRunBsVoyFi24aaLg 9FDOlL0FPGluf4QjcInLlxd6f6rqkgPu/5nHLg0cXCscXoozRrP51DRT3j9QNl99AP53T2Q= """) ##file activate_this.py ACTIVATE_THIS = convert(""" eJyNUlGL2zAMfvevEBlHEujSsXsL9GGDvW1jD3sZpQQ3Ua7aJXawnbT595Ocpe0dO5ghseVP+vRJ VpIkn2cYPZknwAvWLXWYhRP5Sk4baKgOWRWNqtpdgTyH2Y5wpq5Tug406YAgKEzkwqg7NBPwR86a Hk0olPopaK0NHJHzYQPnE5rI0o8+yBUwiBfyQcT8mMPJGiAT0A0O+b8BY4MKJ7zPcSSzHaKrSpJE qeDmUgGvVbPCS41DgO+6xy/OWbfAThMn/OQ9ukDWRCSLiKzk1yrLjWapq6NnvHUoHXQ4bYPdrsVX 4lQMc/q6ZW975nmSK+oH6wL42a9H65U6aha342Mh0UVDzrD87C1bH73s16R5zsStkBZDp0NrXQ+7 HaRnMo8f06UBnljKoOtn/YT+LtdvSyaT/BtIv9KR60nF9f3qmuYKO4//T9ItJMsjPfgUHqKwCZ3n xu/Lx8M/UvCLTxW7VULHxB1PRRbrYfvWNY5S8it008jOjcleaMqVBDnUXcWULV2YK9JEQ92OfC96 1Tv4ZicZZZ7GpuEpZbbeQ7DxquVx5hdqoyFSSmXwfC90f1Dc7hjFs/tK99I0fpkI8zSLy4tSy+sI 3vMWehjQNJmE5VePlZbL61nzX3S93ZcfDqznnkb9AZ3GWJU= """) if __name__ == '__main__': main() ## TODO: ## Copy python.exe.manifest ## Monkeypatch distutils.sysconfig gcovr-3.2/doc/000077500000000000000000000000001235605057100132475ustar00rootroot00000000000000gcovr-3.2/doc/Makefile000066400000000000000000000021051235605057100147050ustar00rootroot00000000000000.PHONY: all anchors outputs clean manpages export XML_CATALOG_FILES = catalog.xml PY = python XSLTPROC_OPTS := --stringparam toc.section.depth 2 --stringparam generate.section.toc.level 1 DBLATEX_OPTS := -P latex.output.revhistory=0 -P doc.collab.show=1 -P toc.section.depth=2 A2X_OPTS := -a toc -a icons -L -d article -v --xsltproc-opts "$(XSLTPROC_OPTS)" --dblatex-opts "$(DBLATEX_OPTS)" %.html: %.txt anchors outputs asciidoc -a data-uri -v -b html -d article -n -a toc2 -a icons $< # Ignore errors (dblatex may not be installed) %.pdf: %.txt anchors outputs - a2x -f pdf $(A2X_OPTS) $< # Ignore errors %.epub: %.txt anchors outputs - a2x -f epub $(A2X_OPTS) $< all: guide.html guide.pdf guide.epub html: guide.html pdf: guide.pdf epub: guide.epub anchors: guide.txt $(PY) include_anchors.py $< outputs: ../scripts/gcovr -h > examples/gcovr.out clean: - \rm -f guide.xml - \rm -f examples/.*.py examples/.*.h examples/.*.cpp examples/.*.sh examples/runner examples/TEST*.xml examples/parsetab.py examples/*.orig examples/runner.cpp examples/*.html examples/*/*.html gcovr-3.2/doc/README.txt000066400000000000000000000022631235605057100147500ustar00rootroot00000000000000This directory supports the creation of the Gcovr User Guide using asciidoc and a2x commands. HTML The command make html creates the guide.html file. PDF The command make pdf creates the guide.tex file, which generates the guide.pdf file using dblatex. EPUB The command make epub creates the file make.epub. Note that the `catalog.xml` file is used, which configures asciidoc to use the docbook XML data in the `epub` directory. This is a bit of a hack. It apparently works around a limitation of the MacPorts installation of asciidoc. NOTE: when updating the version of gcovr, the following files need to be manually updated: gcovr/doc/examples/example1.png: cd gcovr/doc/examples lbin ./example4.sh open example1.html convert example1.png -resize 700x700 example1.png examples/example2_example1_cpp.png: cd gcovr/doc/examples lbin ./example5.sh cd example1 open example2.html convert example2_example1_cpp.png -resize 700x900 example2_example1_cpp.png gcovr-3.2/doc/catalog.xml000066400000000000000000000005671235605057100154130ustar00rootroot00000000000000 gcovr-3.2/doc/epub/000077500000000000000000000000001235605057100142025ustar00rootroot00000000000000gcovr-3.2/doc/epub/README000066400000000000000000000070451235605057100150700ustar00rootroot00000000000000---------------------------------------------------------------------- README file for the DocBook XSL Stylesheets ---------------------------------------------------------------------- These are XSL stylesheets for transforming DocBook XML document instances into .epub format. .epub is an open standard of the The International Digital Publishing Forum (IDPF), a the trade and standards association for the digital publishing industry. An alpha-quality reference implementation (dbtoepub) for a DocBook to .epub converter (written in Ruby) is available under bin/. From http://idpf.org What is EPUB, .epub, OPS/OCF & OEB? ".epub" is the file extension of an XML format for reflowable digital books and publications. ".epub" is composed of three open standards, the Open Publication Structure (OPS), Open Packaging Format (OPF) and Open Container Format (OCF), produced by the IDPF. "EPUB" allows publishers to produce and send a single digital publication file through distribution and offers consumers interoperability between software/hardware for unencrypted reflowable digital books and other publications. The Open eBook Publication Structure or "OEB", originally produced in 1999, is the precursor to OPS. ---------------------------------------------------------------------- .epub Constraints ---------------------------------------------------------------------- .epub does not support all of the image formats that DocBook supports. When an image is available in an accepted format, it will be used. The accepted @formats are: 'GIF','GIF87a','GIF89a','JPEG','JPG','PNG','SVG' A mime-type for the image will be guessed from the file extension, which may not work if your file extensions are non-standard. Non-supported elements: * * , , , with text/XML @filerefs * * in lists (generic XHTML rendering inability) * (just make your programlistings siblings, rather than descendents of paras) ---------------------------------------------------------------------- dbtoepub Reference Implementation ---------------------------------------------------------------------- An alpha-quality DocBook to .epub conversion program, dbtoepub, is provided in bin/dbtoepub. This tool requires: - 'xsltproc' in your PATH - 'zip' in your PATH - Ruby 1.8.4+ Windows compatibility has not been extensively tested; bug reports encouraged. [See http://www.zlatkovic.com/libxml.en.html and http://unxutils.sourceforge.net/] $ dbtoepub --help Usage: dbtoepub [OPTIONS] [DocBook Files] dbtoepub converts DocBook and
s into to .epub files. .epub is defined by the IDPF at www.idpf.org and is made up of 3 standards: - Open Publication Structure (OPS) - Open Packaging Format (OPF) - Open Container Format (OCF) Specific options: -d, --debug Show debugging output. -h, --help Display usage info -v, --verbose Make output verbose ---------------------------------------------------------------------- Validation ---------------------------------------------------------------------- The epubcheck project provides limited validation for .epub documents. See http://code.google.com/p/epubcheck/ for details. ---------------------------------------------------------------------- Copyright information ---------------------------------------------------------------------- See the accompanying file named COPYING. gcovr-3.2/doc/epub/bin/000077500000000000000000000000001235605057100147525ustar00rootroot00000000000000gcovr-3.2/doc/epub/bin/dbtoepub000077500000000000000000000051541235605057100165110ustar00rootroot00000000000000#!/usr/bin/env ruby # This program converts DocBook documents into .epub files. # # Usage: dbtoepub [OPTIONS] [DocBook Files] # # .epub is defined by the IDPF at www.idpf.org and is made up of 3 standards: # - Open Publication Structure (OPS) # - Open Packaging Format (OPF) # - Open Container Format (OCF) # # Specific options: # -c, --css [FILE] Use FILE for CSS on generated XHTML. # -d, --debug Show debugging output. # -f, --font [OTF FILE] Embed OTF FILE in .epub. # -h, --help Display usage info. # -s, --stylesheet [XSL FILE] Use XSL FILE as a customization # layer (imports epub/docbook.xsl). # -v, --verbose Make output verbose. lib = File.expand_path(File.join(File.dirname(__FILE__), 'lib')) $LOAD_PATH.unshift(lib) if File.exist?(lib) require 'fileutils' require 'optparse' require 'tmpdir' require 'docbook' verbose = false debug = false css_file = nil otf_files = [] customization_layer = nil output_file = nil #$DEBUG=true # Set up the OptionParser opts = OptionParser.new opts.banner = "Usage: #{File.basename($0)} [OPTIONS] [DocBook Files] #{File.basename($0)} converts DocBook and
s into to .epub files. .epub is defined by the IDPF at www.idpf.org and is made up of 3 standards: - Open Publication Structure (OPS) - Open Packaging Format (OPF) - Open Container Format (OCF) Specific options:" opts.on("-c", "--css [FILE]", "Use FILE for CSS on generated XHTML.") {|f| css_file = f} opts.on("-d", "--debug", "Show debugging output.") {debug = true; verbose = true} opts.on("-f", "--font [OTF FILE]", "Embed OTF FILE in .epub.") {|f| otf_files << f} opts.on("-h", "--help", "Display usage info.") {puts opts.to_s; exit 0} opts.on("-o", "--output [OUTPUT FILE]", "Output ePub file as OUTPUT FILE.") {|f| output_file = f} opts.on("-s", "--stylesheet [XSL FILE]", "Use XSL FILE as a customization layer (imports epub/docbook.xsl).") {|f| customization_layer = f} opts.on("-v", "--verbose", "Make output verbose.") {verbose = true} db_files = opts.parse(ARGV) if db_files.size == 0 puts opts.to_s exit 0 end db_files.each {|docbook_file| dir = File.expand_path(File.join(Dir.tmpdir, ".epubtmp#{Time.now.to_f.to_s}")) FileUtils.mkdir_p(dir) e = DocBook::Epub.new(docbook_file, dir, css_file, customization_layer, otf_files) if output_file epub_file = output_file else epub_file = File.basename(docbook_file, ".xml") + ".epub" end puts "Rendering DocBook file #{docbook_file} to #{epub_file}" if verbose e.render_to_file(epub_file) } gcovr-3.2/doc/epub/bin/lib/000077500000000000000000000000001235605057100155205ustar00rootroot00000000000000gcovr-3.2/doc/epub/bin/lib/docbook.rb000077500000000000000000000200341235605057100174670ustar00rootroot00000000000000require 'fileutils' require 'rexml/parsers/pullparser' module DocBook class Epub CHECKER = "epubcheck" STYLESHEET = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', "docbook.xsl")) CALLOUT_PATH = File.join('images', 'callouts') CALLOUT_FULL_PATH = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', CALLOUT_PATH)) CALLOUT_LIMIT = 15 CALLOUT_EXT = ".png" XSLT_PROCESSOR = "xsltproc" OUTPUT_DIR = ".epubtmp#{Time.now.to_f.to_s}" MIMETYPE = "application/epub+zip" META_DIR = "META-INF" OEBPS_DIR = "OEBPS" ZIPPER = "zip" attr_reader :output_dir def initialize(docbook_file, output_dir=OUTPUT_DIR, css_file=nil, customization_layer=nil, embedded_fonts=[]) @docbook_file = docbook_file @output_dir = output_dir @meta_dir = File.join(@output_dir, META_DIR) @oebps_dir = File.join(@output_dir, OEBPS_DIR) @css_file = css_file ? File.expand_path(css_file) : css_file @embedded_fonts = embedded_fonts @to_delete = [] if customization_layer @stylesheet = File.expand_path(customization_layer) else @stylesheet = STYLESHEET end unless File.exist?(@docbook_file) raise ArgumentError.new("File #{@docbook_file} does not exist") end end def render_to_file(output_file, verbose=false) render_to_epub(output_file, verbose) bundle_epub(output_file, verbose) cleanup_files(@to_delete) end def self.invalid?(file) # Obnoxiously, we can't just check for a non-zero output... cmd = %Q(#{CHECKER} "#{file}") output = `#{cmd} 2>&1` if $?.to_i == 0 return false else STDERR.puts output if $DEBUG return output end end private def render_to_epub(output_file, verbose) @collapsed_docbook_file = collapse_docbook() chunk_quietly = "--stringparam chunk.quietly " + (verbose ? '0' : '1') callout_path = "--stringparam callout.graphics.path #{CALLOUT_PATH}/" callout_limit = "--stringparam callout.graphics.number.limit #{CALLOUT_LIMIT}" callout_ext = "--stringparam callout.graphics.extension #{CALLOUT_EXT}" html_stylesheet = "--stringparam html.stylesheet #{File.basename(@css_file)}" if @css_file base = "--stringparam base.dir #{OEBPS_DIR}/" unless @embedded_fonts.empty? embedded_fonts = @embedded_fonts.map {|f| File.basename(f)}.join(',') font = "--stringparam epub.embedded.fonts \"#{embedded_fonts}\"" end meta = "--stringparam epub.metainf.dir #{META_DIR}/" oebps = "--stringparam epub.oebps.dir #{OEBPS_DIR}/" options = [chunk_quietly, callout_path, callout_limit, callout_ext, base, font, meta, oebps, html_stylesheet, ].join(" ") # Double-quote stylesheet & file to help Windows cmd.exe db2epub_cmd = %Q(cd "#{@output_dir}" && #{XSLT_PROCESSOR} #{options} "#{@stylesheet}" "#{@collapsed_docbook_file}") STDERR.puts db2epub_cmd if $DEBUG success = system(db2epub_cmd) raise "Could not render as .epub to #{output_file} (#{db2epub_cmd})" unless success @to_delete << Dir["#{@meta_dir}/*"] @to_delete << Dir["#{@oebps_dir}/*"] end def bundle_epub(output_file, verbose) quiet = verbose ? "" : "-q" mimetype_filename = write_mimetype() meta = File.basename(@meta_dir) oebps = File.basename(@oebps_dir) images = copy_images() csses = copy_csses() fonts = copy_fonts() callouts = copy_callouts() # zip -X -r ../book.epub mimetype META-INF OEBPS # Double-quote stylesheet & file to help Windows cmd.exe zip_cmd = %Q(cd "#{@output_dir}" && #{ZIPPER} #{quiet} -X -r "#{File.expand_path(output_file)}" "#{mimetype_filename}" "#{meta}" "#{oebps}") puts zip_cmd if $DEBUG success = system(zip_cmd) raise "Could not bundle into .epub file to #{output_file}" unless success end # Input must be collapsed because REXML couldn't find figures in files that # were XIncluded or added by ENTITY # http://sourceforge.net/tracker/?func=detail&aid=2750442&group_id=21935&atid=373747 def collapse_docbook # Double-quote stylesheet & file to help Windows cmd.exe collapsed_file = File.join(File.expand_path(File.dirname(@docbook_file)), '.collapsed.' + File.basename(@docbook_file)) entity_collapse_command = %Q(xmllint --loaddtd --noent -o "#{collapsed_file}" "#{@docbook_file}") entity_success = system(entity_collapse_command) raise "Could not collapse named entites in #{@docbook_file}" unless entity_success xinclude_collapse_command = %Q(xmllint --xinclude -o "#{collapsed_file}" "#{collapsed_file}") xinclude_success = system(xinclude_collapse_command) raise "Could not collapse XIncludes in #{@docbook_file}" unless xinclude_success @to_delete << collapsed_file return collapsed_file end def copy_callouts new_callout_images = [] if has_callouts? calloutglob = "#{CALLOUT_FULL_PATH}/*#{CALLOUT_EXT}" Dir.glob(calloutglob).each {|img| img_new_filename = File.join(@oebps_dir, CALLOUT_PATH, File.basename(img)) # TODO: What to rescue for these two? FileUtils.mkdir_p(File.dirname(img_new_filename)) FileUtils.cp(img, img_new_filename) @to_delete << img_new_filename new_callout_images << img } end return new_callout_images end def copy_fonts new_fonts = [] @embedded_fonts.each {|font_file| font_new_filename = File.join(@oebps_dir, File.basename(font_file)) FileUtils.cp(font_file, font_new_filename) new_fonts << font_file } return new_fonts end def copy_csses if @css_file css_new_filename = File.join(@oebps_dir, File.basename(@css_file)) FileUtils.cp(@css_file, css_new_filename) end end def copy_images image_references = get_image_refs() new_images = [] image_references.each {|img| # TODO: It'd be cooler if we had a filetype lookup rather than just # extension if img =~ /\.(svg|png|gif|jpe?g|xml)/i img_new_filename = File.join(@oebps_dir, img) img_full = File.join(File.expand_path(File.dirname(@docbook_file)), img) # TODO: What to rescue for these two? FileUtils.mkdir_p(File.dirname(img_new_filename)) puts(img_full + ": " + img_new_filename) if $DEBUG FileUtils.cp(img_full, img_new_filename) @to_delete << img_new_filename new_images << img_full end } return new_images end def write_mimetype mimetype_filename = File.join(@output_dir, "mimetype") File.open(mimetype_filename, "w") {|f| f.print MIMETYPE} @to_delete << mimetype_filename return File.basename(mimetype_filename) end def cleanup_files(file_list) file_list.flatten.each {|f| # Yikes FileUtils.rm_r(f, :force => true ) } end # Returns an Array of all of the (image) @filerefs in a document def get_image_refs parser = REXML::Parsers::PullParser.new(File.new(@collapsed_docbook_file)) image_refs = [] while parser.has_next? el = parser.pull if el.start_element? and (el[0] == "imagedata" or el[0] == "graphic") image_refs << el[1]['fileref'] end end return image_refs.uniq end # Returns true if the document has code callouts def has_callouts? parser = REXML::Parsers::PullParser.new(File.new(@collapsed_docbook_file)) while parser.has_next? el = parser.pull if el.start_element? and (el[0] == "calloutlist" or el[0] == "co") return true end end return false end end end gcovr-3.2/doc/epub/bin/xslt/000077500000000000000000000000001235605057100157445ustar00rootroot00000000000000gcovr-3.2/doc/epub/bin/xslt/obfuscate.xsl000066400000000000000000000011301235605057100204420ustar00rootroot00000000000000 gcovr-3.2/doc/epub/docbook.xsl000066400000000000000000002205241235605057100163570ustar00rootroot00000000000000 1 2 book toc,title 4 ncxtoc htmltoc 0 .png 1 1 1 1 0 Note namesp. cut stripped namespace before processing Note namesp. cut processing stripped document ID ' ' not found in document. Formatting from urn: : urn:isbn: urn:issn: _ 2.0 cover 1.0 application/oebps-package+xml 2005-1 cover dtb:uid : : © cover Cover toc Table of Contents yes no yes application/x-dtbncx+xml application/xhtml+xml text/css css application/xhtml+xml image/gif image/gif image/png image/png image/jpeg image/jpeg image/jpeg image/jpeg image/svg+xml image/svg+xml WARNING: mediaobjectco almost certainly will not render as expected in .epub! application/xhtml+xml (missing alt) text-align: middle 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 No insertfile extension available. No insertfile extension available. Use a different processor (with extensions) or turn on $use.extensions and $textinsert.extension (see docs for more). Cover text/css img { max-width: 100%; } -toc font/opentype WARNING: OpenType fonts should be supplied! ( ) 6 clear: both 1 1 1 2 3 4 5 6 5 4 3 2 1 title gcovr-3.2/doc/examples/000077500000000000000000000000001235605057100150655ustar00rootroot00000000000000gcovr-3.2/doc/examples/GetGlobals.sh000066400000000000000000000000721235605057100174430ustar00rootroot00000000000000if [[ "x$CXXTEST" -eq "x" ]] then CXXTEST="../../" fi gcovr-3.2/doc/examples/example1.png000066400000000000000000002132641235605057100173170ustar00rootroot00000000000000PNG  IHDR#à iCCPiccHXS$PNM^t@B !T袂kADlȪm-(@e],Py_=sfNh Q%r _fRr ,v'::cyw +Iۢ@!q9BQvh7U pdU3LTp>q1~]X h,dg84d/\ٓcA&99%Ҿ1cX,EZl֜s8w+' ǭ6+7LiQU sG~p[? 6(Y3&,-G}!q ʍf #eqyQ;͂8"m|fƽ~q=GOl9v;.bǰfNb-츄Wҕ0[T[i_F $/_p+`] '2ll- CW#Klyp-ƌo6!G?wl+bQ̆K.@*p ( 8<UbPT-`;9p\6z 0ށaAH#bX"v  H "#%HRT#ېW(rt"ny|B1: : uA}04fyh]Vu =^Fo] t1+â,a Rapo`]'t[l<_ī]xގA+J&X!$BaPIA8B8 ߝ^;" ộL$%$n"'"v{C$IdI EXR i#i$:ANANON.P.EN W,W)[ugrJnQ9[ɦdr9\EG>K~H~`0EHJnŏ2"줜ܣR&Toj @=C}L@Ӭi!4m!DN{(h8CHRU%y%%?%JwʶQ9+w+_TS!pTlW9Ct?:^O?KU%fUPTSQsPKPVv\1L!ljAmƧ :|&p'o յԽչo`jhdihxkZhNќY怖[T}mTB;F{v+C::A:B:gtt޺'tzz| zϙjLf6o6057(6oȐlbnXaf8hga4Ϩ辱1xy&&&LMLMCLLMQͼn]̳7_@--x5W-QK'K&Ή&ޱXXZ5Zu[3í_N22i8d~g#TZ;...+].\}]sVv/w+,}M's'O0`yldzznbyy=6x~c祯{?7~1 RǁAAsN a4 :mņU= F"FG "@THԺGѦyѿM!NR3imؙ̼cƭ{o/oKPLА>?<+iRɚRJBʎSO8dgO8CsF3gfJ%&N̊bձBj~ oN->KH/OXU~j-ﳢvfd'fϑI9*Pd susgv -%®mo-h[SI]vOKe;aՍhϴ=mgo~_S}0`!C=B?Rڄ4il5w$t =z7v?Vs\KO,:9tJxjt鞶m$>l ϝ9s .]~\/r:}I=wSgz_{>y ၒ?}i_]L}%z5z7;:mz.v}txSgó>>W}15Ñ!KĒ~ ` 5~;\Le2iq`7(a5Lww7@hOŢ H| oRT')3VbEoEj$ cHRMz&u0`:pQ<bKGD pHYs%%IR$IDATxw|E3{Kz &Q){"v" ({-Bzew!<*䣗{wgfgwgΜ9llllllll~`cccccclllllll. [h(l械lllllll. [hO "YڥnͿ"RJsJ8yPJ1fF, .t""0g$ u8/]ϖu5J`4 )PܥV^  8vo?EA)Y0 z`.$RDe[+".s"/,2!(lnM< BWc 6 -}ƴ Nٶ4%iBӯOR韔"0α}Ê3/YT@!Qwܹct(E@"Ϙhcph۩S]:wk'{"Rn>}…,iY뎝:ڥSSHBA8y7wM7e~P~unՒq]wgo;6,)&wht)~|J+{[֯\tA\uw{nlbYhUKEBQ<ŚSrWrk5s4NuFV7Lr$0!J$ΝBD iH`J) 0X /Y44M.TDsr3ƃ+m޾s,^0wÜ C߳۞5)b &I)ibU1DY? JRŒ&c~RT\(H)ձD$BTQRR6$%,3eJGVfVL4ՠn-\qy<zkw=ݭ"+^QJq.teI(ݷDiu]Ku8:ƠLLhMcJZ؜K-\(SqsF 2i H"ػ}X!!!=3>/m`BD,-̯_ ]\`zxX0!z+*.vxTzO$tiRBpM PMMuv1/sNضe 6oٚ~:(d+YWy}H$"{XqMwD$R+:o;>3-݌Ժ| }_xCG~Bw8u=nd-"Uϕ4ܻiㆍ6ܽj@'ܹcǮ_Z\1;޽|*u8~bϞir ݫJg?y]{3 Hg,iyjΝ;v:}殝ﺤIDY;jmhT*'+D$MUFOd\F-sujJطsӆ 7nܓvkqpY~b}^?MI;b+6l` 6ecMi&` 9DD~0 0|>mh޵ 8>ԯ5CDLô$|@Z[p8Tm9负aaIDz;Tu}`4/xkn;}$(XAD3Ƽ@hzhJ"ڵr}z$E!h:gA%D9l Jr\!-\e7_y`Mo>`z5ݜB#4?L;!5+zw Þia3SN`C_7"2=?zCa!Ъito* 3cn\ի}kf VL6msM CY㘷;nvR->)t֣s oxR[F?GD =ܶUӄX\5k=ۉ=s oעaL `I7>S ץGN:<5ru_fX5ghޕ-DTRJ+8} >euvIAP)M":~Vj9DDz}hޕ[];~E :m;\իk~I×׮j6l1q$I ¬ѯ=ݢamúМzZ=ژ^IăC;vѹcG_gxsv}p;&_~ -nvw"[t8llnp:t~5Vȟ:嗟~ΞSh2֍r8!"P XɭˮتC.[z"ꐚ1N\AD~ae_߳}۵iuDI% i ^;TQJ*"ߩt]ZcHђ/?ƃ^18y{""vhI Nx!q渦;M-l @I iY>*Knk߲aS{I/*ލ6 Nuf]ukޤo~KQ\n4Yb|$j{pyKXKtK ;JDF{4\KjBް IDEC[$Z^q38&Ý9?%)"#C9灋i: QYnND~f|D%]7',4k4l)V|W( " i_<]񁷾 "OD?z< 5KVO ^"~"YW9Fwh6?vuV{cLsTY+(Um on ;:Yq >x&3.5nVB<- 6a~r~?uMJs.@[߱6oB.,sׄroJi:) K`J"#4 2p\*MZ.!p-p?t˩ ,a ADmMpUZz МUw=k nzGqd׺٭?a;k_zjr΅%4l;%_(.b㫖o),\FI.CWk}n}b L k@C/(=,&0u̺Q-4 rx)DtbD'jog(xn[3uF=r<&ƙDNRAv,dzlTcʢ> ?o;u/~0Hfk @_n׶M/;`23pDD俣{sNb>:h:{gZBzB\|gȑ#G{#o;#~[ۺ6 VrNtZ\}GD4fVr ]>wm-QzL͍(m( :tR+Wu bn8LDDEK* n4|~]')˶f..pcol 0i@Ru\"2_x%c"666***2222*:ܥmV{w0 (i_N.xius9$Wc_,IDE7 ݕ9]Q\(, [T >e%[k @\-' "ΌH^7=s~>s0';x[*Kh(}AD}AÎ},~nx:7r#/|$GݟOD߾8xg""2 ?'v4uZmzolcbh 'kս{4n">%4;@U""PD4~Ǵ&e_Yi-+EK(|+:9!:J `eIXdspu\*m>qSO u{KW]o}i("Q"1ەDG`=M=tO}FDD`ux^j_>z2 4V8M˥͟v)TBB2_R @ɜiJsR 9΁1+p#`8r#b.B-KFRpٿsGsto! ok'4&lۺeNK*_ېk[AsI:]wMz?.&BB#7/W*u'cۅuxٹ>pbG)cbXmFr-CHCݯ׷wŜÓCYkK|t/ #-h5uRپ&DXέ?~ȯ85"\8x>RÅZLF aZ[ZtLغe7_SMW$27 5%~s8~)0"&RƄ2#^zvm3#jihy߻ OZğ+=3@G7HBጽ?m@Ew0|ːCg1l[<˃;{4ʋc\0 sNDůimc 6e M(a0"DhZTT(k ҈d *NY!xIIa1N' }@q"_!܆F݁\ c"2ș%  T^1fyתT b0yv<dbF1ƅiJ٨Jl\8T3VpbwxV -4,yr1ٞ(Kbi.%F 0@!jD'Ե;b><Nr/~ ;zPE KwItq:ҥ+|\^7pBq@7X钩cL[vyFUhBXy*u$t P;%6 >*_zXD'@TT\1n %o\,`S)ŚEL萹˖myRʲ{/n~H*Gײ`~k5m׎'u˹%R[;S¨FO<4$BЬS8v`j+EV1?+]\y?yGhVUcذPRUD N|'^Mg~S{핇Vyvp*-qS~t.fLY1т1R0 pay %/-a15 XvCRB!AJ1}OsB#"s괺)ΝY+A|35M郻6.ذ`R&{+`7X`ʺDWBy -DUm ~c 'o/KDHTFɓ+JY ~uɀT(ϚA3<"*ĭb``PaH ]pT^rʀ HUY~>H|]{\sqoX'7LSpP.&^1}'6/_̚*hS%f,]?OW~C t:s&|[NSةvҁSCەi@h0 ӹ_~uw8W=}Hؤ`\*w.u+ls~7Cc0 tnZ:ߔ?I<TbɃn $NIΘƭ9~芈oov:AJ &<)E۶T,?NRraRJX"S" m~m Շ#Gt>qۛmn㓁mLrlX"bb { O=1@:*5j-ub&vsMpf ݡTqa5R3.|HOp>K?%b)TsL|}r%&]1cŲ[Lyho@K%pfڗס X1 !7?{rH<]Ӥ)angIi4!ȊȔvCzxgru X>za 42*5MCj@vvaaTDP Lhw2G$O;w9bLdKP!rn 섅6SF`ˁA+2ohۍ\约Yjd89}ߖ ۯ|cSp8 `8{s|0+p% 0vcݤ;btzO{4U 9#I} `*p`n(9N{ߖ@*TXZ( 0cOG ;tC(䉳Hʵk@d sm:4MJE3g--W5I+rj䊕*źgϙ3}ڴΈJ00ΥBD+zun(@w=`ӡ[Nnτg!״о~-@' Hiӝ*6i[!Pd*rWhѷkkwߛr3@wu >~,#?BSR*V*qfϚ1cgaN߿H?lWw7mg&`9DɕbI~' >]`6퉕kARعc_dTL唔+Uh)S̙3}~w . W aR` 1F J*viS*ŗυtdҴ9Yw?0jP*ݡ{~:8) N(ͩޢcյIp3ޢ\jKLo;t+B&m?y6~Layq rlB$YW9^m޶kۖ_QP>"2ߴz4?p6ع}ˏ_jTK@qWk;DP,N5 a>j];NVk\k+TڦZw{ӡ"OO(v^o@02Ajl?bH =[AE\ӭǼD4oq彇ȟ8:r;m^0[ U3a=֬|Ǣ]:J% ") FI"RL`|w-""0|@BnAD; XG|K6"""Wo k Z{O(_Y=i/_n~\i=:'w_!/7ʺbc~7@pt|ȟuuc˱`/~8ِN@p 6YQ/=!t(0$o~7ȗyUX.,⥏X^ {VO]A V3o zO]I~k[wꕇBN3. 0yVCI\@@ u""*'$n^"=1'pO[N6[h9/Ƶ躮[9\!an0pXa"4۟C 8c+V!]ׅƬ)Jc8@Dd7ISZSIC0uMla3᰾xJ:Dsr(N _NAgݫ&jxIl٪YBV-;VpX\DcLh ӴR ΁Q]Z)O8+6|yiV4)i XV&Om:VDD>CO $!*WӴQM`$-"ts/o]f>4|խ8\NΠ9[ڴa 9x Q9,~BID˦Bc_ǟSBX_xKǽ&t4cpDeӎXqh#0]4*5t.ԥ=}ԹCID;W;xC)D7 qxw\y?Ҫ5+լJ{u+NڧZL\BLt$8N`ӣoH"׷t:8V^u* +ߦuC2 skRڹB# J<6.F!/c/l.D0;ls<\wUKtoTZKQC 9)Tb>,dK  9l`"r(|XrߤVQ\hBV( ). 7'KZ_ɊW!%NK.,2>{&38aXQvP3?Δ Vv6 %o1P`R/sƄд`NʍoNZL;uwl9pWm|O]Vh]mI' gn8^|T|U;+s)89 hJaVۼ'68'QU(nCE}N'nM=FCtM A7?yV3jwvDvle;۱49iHmۿ PfcNǁOP/gWZ-G I<0 K_QpFV67-k*KT8U;{O9#{7=rj,5Dl|۴."u1'|a5+w9LD&tzU;)8N̟Գ1VbfDdt\v~8'~mפS!SLuդ5TlXVNҲdqcra,<q =yՎ2 s3飖>.FtWJj;tQrRDҗ3zijukV q_|?X&NVdOTVՆ֮u9gJUD(^hXRp״Mf[dWstRnݚջ]7(G?,;spB*||;RFf Uw-'(8_1;6Sfڭ:٧Nm Fψ4 6Λڷ}0!a~쵏=oQvFkU|OZs`ԦukظQڕkuX' s\ۮeJ+U룲& BPe^w`|d MP'W]dIic?uOC\!34r{|dNu%:WQn]ސ]*Y wQn*իVJt#dkl.R^66eBJ $O>cBUnQ RDg-Jjeعhz6)Tn)<\c1 vzi%_^Ę ֈb1S{Gӥ:u'GlaS8gGw.rEԪV+%9:dJ)9]۷gxcW9Kl'?k}3$J {Q%8mYyRu uvi7ܿ;!'U[FDH`Vj|v)\={v|dzzpW^=*unR~_34̛а >A)"`Y]Ye7])EtnU@Ii,U)*GJVg9hRA d;)K)9Ͷ$Be>dWe)H)UWKuQ,|RBR\?h,"yYMS"%APzr4`vRZ=\$_hv鑳4VZhasɲ8I=~ <׋L64\$śYy;YX k K{h?dYXap𬲛ι(U 8НvbWX Zͅ(<p! ܋3]j;oP@>▉;sCwoYbEgzRHXgϺ 9a߮}<ػu7&oy@s8}>_Hb^:YxLq.E4ETjs`,Rrg=_ptvC<Ċ; `[.xcYAQU?1ZEK_S셞+H)&Gv>C }1~twGA806.\E{A[V;\pI&=nZ=ΡDG9tZ;]nW9w;+1\P <** {`4;(6>?--']RŘȋ$<rΈxtl.Ͷ JI"VylYBͿbq}%F_mc 6666666ͳ(l械BRA mCHB=gn35)^=}fccccccsywLSine R6 o7L>[_چ6666666f;L: pA)yށ$)eg\ꞱTYe/X)O~ᜦD5.c[%sƂ˱{x{Hgv?DΚ3`"լw|{zA@tg.!\U3_xʍۿcn("f) X]`-3Sɖ3g;NaLx[k0Ɗu>g~u6666ZnXE!<h՞ɤ -Κ[玵ϿjV7j}`P 4 5ZEY>W&?P"٧<*K/xs*HIEDޣơ$j6x-UZ%"2"R4eqn{4 |Ҕ2ҔŇ)4 C7oM L,.M)Yp"R T*%)ea%ן?6b?t?~mI"2 ""kTJ Tbx&i<3ڟl""G$~D[jԬȔF!/@J4'"zIwǴj̾M^32P`Q=]'ǜEf˜J^ugJJU\RgJJ*=m*wP :@\߮Ie_'^'ޟË՚]{Qqkگ`B u ! tVpEr""b `\0Fxdti `9*,4 (+!>ޡsRĂB y2sbcy -4 66666ep3QbmvfPg(I*Z3nYA)p!PT܈ Y9DF}U}iB*W Q41]0v0eZ(BxBƇF'!_߆͝^Q> sꂢ3Ksl(h" y0!L?rhZ3bΔo`F 󧳴 EDk&xDD]«ϥUv'_DLlӨV~+O:~NZU8Cc;]yʍLjHׅTZ/"!nq=מ~Rr.u[vn""6o_nSV^>ʾe } h=kGڲIyOVzͤpU;'-+pETIhY/< BG^^r(?}+p45]G@hi9D4[TktuQ"~]s i 6666DAᗱqF|Krwr7o9DDw52›& 8K"ze!QWՀh}rF DjW<@9㣹l"/ѻ#MDU^2DD/?j"J Dzc CU45FaicHjb4eU56HBս_4o60v""Ij>EDs'I=@QY oOUU pwDtj\@bO%W2eTlmTki `ՍtD9U4 rJ GQY T0""uHCT6RZZ+3^Qu1!B?>o!4b"|Dtt {mպ$:8[ey߲*!JDYW6Iޤ} zeDȍWHӟ/|KDGDg]۪IjӸ2Gf]n3a% G?@DYiktiٸ}ŻOi 6666DT,4|.d8S G~D+(";61PɈod\g'U:?J"ڸ@&6PX~e"ڈifۤPmat믙-,M,?Q6T@E㑑NU-2<-L8DBivޛLCQ/0j0$Q R$OW0otffz|t-&bn$EF&-/k$u1FIߌu;w&MêfS)䍔] T2TO 3>M^!yn`ݢ蛏M3: ΝBϿOW O* #[Ռg|$kzm02<>l~ < tWͲUUpd߶CĢ}mEUozо^t ./xױ 0wڽFE('{2hہui|1ZG\V{(4"[8r-=-rC1k)}\\]8^ C~:;p6ǸAtuWmt  6ѥ s[B'\Ö6!C'7fİyX"Woá_;V49k {A:dN'(;7cBC@8 ` ـ c8ڊ1H ,_.Nܚw!QZj( @#&0}6)%/WaQ!a1npIi:upeu:nW;.266660ySR~waJ,RQ/:A6cyU))@t׋R\xE p, iBhaũ,B6rܳzڴaH?DOiyxp;grE'm_!@@d$uH<یlƤjB,]YbL1q]7Ԭ p!.4&kVB$TEFlF* "0ņ D88i+\!ڑ'kiʘb5&\>KUvǼ>_'RI p=a}lшẎ8~zLtyT"PPpવpjٶeŠ &  ;]]3mp]9 8vȱwzp egްL$҄4 q/#^(/T:|QLF]˴m3o>+g~ޣ\.y_6n>:R?OG!‰lڸ Pc=Q#۷Y[ZgdVXl>Z xҫj+jDC0jY\we0w}֘iTL`5%ڸGģ@d'MTB,Oߠx't1h&b ѲmCl2[wy3UT6>|vǜ$o~O'8- @ӄˁUݴzPoc`Ĉl+/MX<%_ՠG.N|oĉ>@A 0ƥ$-V4Ǟ\Q^mצk/~ƒuڡ0%4_'ʡSX2M+rv Yũ}wtjP)w_W *`?L ޶^ޝ@nKhrzM<o#zPAG֬eu݉Rw }r9EvҢ Th: NӪI|BX(-䐄* ]8 umƖW7{x|چ->~$q>Z6xxٍK{OWd6tLYUGpD#"e(sD";JeBZ65_|b:Ƒ3*6666Xz>1D <׫nc`e ^63j0? 1XXID0m06o斈c #4FܲL40a? 7o~TZάoH0Ƕ6WR4i8*) UD龿lӺj%7ÏHCTtRu@B f Hi': վPrՙE ?BL :^DH0Q:+ wSЮ {ZPw8}Sм8FN!&r&Qޓ8欠=x*0a)Ec]HJ PJ1#P2fqƳ,tly64g,0=e;gDŽ؅6*4()cx |(@Cv[xL3` wh[?tlY1+U ^lj\``~^\chܔ,oT0WJ;0Tc#_]2MJYYDh[jgӿz ~*UG6Q1?,ZO! Eݒ5WC1zy5w QxAq`Du*< M + oŕӿOc XEo梭CQRrM kpǿno#0sRJ)NT)U-9/^Yo%^}?RᖙG>yVJMg4@z .#(ҜW|}yЊOs|k͝r .u# ({_N<磐0Ǡ Q`sfic"ڽvPHе1/-TDgWI0sML3v?+O'U}ݶvR 鿺ŤM>[O?t-'~?vTHӧNEȂt0MSQR@DJApLObk,U*Bya5nX><@BEy3Tr4;~lG窵DG## g9VgS YZ8X>YHTCԄyAڿa4#T~8b`q!<a8<$˧-َ-g;GAr jc? ?O(^sŢA"C!xwҡov{ \J3pϞ6kGTBBr}ǿxgHh:#XX00HC|F0XqHiRAa"`vP-}竣0j9C _'B}aUN&McJ)T;t $0Mf!~CLXZ-S}[OQ[ [wGu`e 3A-2wru On!L 3OL6e-ҴY=z$ńn[@MIMa_4{¤Y~n_W>6`$ʻe\1>zlGV_GlmB'IŸk]/|%z 0'w5V IK0Ъ-jհwvm%ta?K[A|fN8ja6eHeJkٍh՞Ռ{?]ݜTQi0p_L(R&܄ӨМun~K;S q/WgY_]~R Ȃ7c'l;'\)zzMwK4.׏i)^{&͸vYh{]z.g a,iy=n\4;7$e"ҝM=-M`C^J'7/]f抨ڰ))S1{@ݯlT=>';O4\:Sh. ܩe28v|Hwl_ oqukEؚMQQRT$x-{L#(/Xs':vr{Z>x;~ު.¾(,[PFH ɗO`á㦭\Gy e b fHq|vu Yd@Dzb kx6?gGQ}wBf)@@w>^jFl8 q >N}2XzNh;8c:M'7 TȦ}4bR660f !滏_ {`‚@Ï>ڇ\j}:×3.=Fۏg.~ 3u^7iqi|"ƙwmn}sYB\ )=tKmO3] ~ wf-]gN??V&4V4|~wIhTS7 i9r]'_-uK"iUw< n p2ǡqMCJy'TC];bˢ,$wdv+zMf̄ NӁtU_uӼ40ws6 ":mlɮA?)Kv^_&!g| p!oT ѱ63Ʋ%-VM/vr'L\0&Lћh6Ӝ0(nθXV3oEFlU iߩ/g3iIуAdPp LlW),2B6݋#H7)ll \~θI*Vprt:q8ʹO; H;p}=]s%V3N&."]W?VNNq֦=)M7r@>U-ڥK qb^_0Jo>;|?bn7{#B*UTU)[*+Ҹqݱ}䰈oYɝ %O837>ȇ FcKW˔Y7ā t 6mŎE8h LYHU7r]Wv@iBIJJ?J, 0ko !y^*%skI$ r3aF}aT)TztL .3{]6PFwO]*)vy˂)ŅFL/ߠgT. 9BBl}ʭ\v<$11wu~oݷG>ٱ+_Ey*]!R->蚃k@~`Nehٹbj!0}g`ITp̗^ҽNzZשdN/Sgʤ$ B+VWdgNj3&13@!crZ߳&mV*VP7/G"7L_|] hFL_l|53} 0N)#s@<(G@P}zFԪ>[ɤ( ƇO`a?ϜqJx_TS[ (;Ua]7|k{|̓ m7^XaT˿j uv]ݸ*e0d{YNQZo{r/#n!`FV<Q ޠQ\"s6T!$;vػi?Uc]o.H᛭;4#eHYTQGwYmf}^ uܝ%`  !a`K& ;ri+Ex_&B x} HsBeLFQGxeIBCCgz7,pjDJh.SeeRDDgDNJK> Awt]"T5cC%Ɛ0d$"1 <$[w$ y=9$%Aq&Yi$.dpVJ`V A:g 6@^,V>8H0"BHh(@R])Y}#"p@$$Y']g>,24atKE`(ۆr('˸ GT%M]Ւ 8- GpjdHՁʲ5^!ۉs".k!k:sOfNzx@JQKwF8桰BPQ0&3ƥ-4 s1H/,] -{_LK>eJ@J>έ4I't (8c$)I,DJ+/4QH3$Y$M&Hs!dq(^"(s_NZaT<.0PJ#۪8eq3J6l͟UɟtJĘΌTg n.20S076 @4)?XY vir8#+1ZK8]TDOޖ1ͼXM@Ž2J^eE.Wݠiy0V)%G磘sBcr/_"OQaa lZ\2=60FJi!)ɸ0}OašHX7Qp:t Cg1G^/AzH(Hq(S :+A C B$b493S0'RKWq^FHh$"?+)z3}J7;fR BƗ4y}oǦV#F+6Hk{FfiGμDJBQ% DY1q`VTL 䱠A(="!u0&TޓY}8>,,+,ǹD @# Ctʝ+T eeH1v/K7kY*~HCaݪ8Xu9˳|<**Qw6jBaЂR/+5#QP! c#G摽SVa ÓX 3 2~p֬n^1ۤE{!1n˹2e|@܁g۵/) k4p& rgN[t1@zB˔=OJHNTJ@RJdpRR1!8c%()%`t IBIV*VI,e 7P͚9{skؔ2 "DPߘ;]0K )pe :)pBp>)tw:Q1f>/y|^.3\{̹$A%Z@8K# %<(J(N`\YR%e9)C)Y">-1Kgp_H-)C\=yEiށ E>2"kHc0~_"]Ny|~8+n)pE8g+ErN/IS1 uxYdߥ<,2y x c%U͝`J@$\#OpG )kfM|R"cU%8Jq. e+dCw&g>*ȳRA >|_XbRWbJB j!|'oL!%kTT؎7}ƀCG\ @qd3$bd uXzFbRJlTgkRBoAǙRR.,&4M:zdLBHKra3%K󈐚ɡZ`'ڒUXVfOw;X1yuz5T+1©~Xڱ~VoiN;q4-b*QGj𭟴zv %`P5/} upOLi}3yW++(9~ WrRRC:Gkgg=Q\bTGs&̓fLBR|t2 `n k׎Dv&8z$#7/*.1)1. `B'32pWX1ԭtkXYMLs?z{w=p>/G hW2\U{2y+'N.]&KcK`,;mV=}+5CZF-\fZU*]y=N0M@=sϝwܿPu*&kw07Of;T֢ӡL)B´P7ZxpHtӭAe<|Dd.W@t,[c̝;Gtxu77L\8e8C5u+Uag};mmwD8J*=̙6q[mo|PZAx\-~;&D|xog荷x-(^ .ܤjFb͔7_f͚5U߰ɛc~u+vuӫW|Jݺ4h:v"0ҷػC5jժYn۞zk&vlZ~5jx|//^WVլ]-1?֨Rnڵ4jˬw ~?jزcF8˂25(3M;oﵗba?t4]5}#zw]ԋwj%Vٮ=sb]fUӠ9He7 BgonԼ khR571M $ɵt[n/׸\˥$'zҢM]KAw-̜[_*RGo ^pbQ7hJ޶eoئKۺ}[F޶Kjo\ydc#_y5ݯjwM7xQr?5aC?}=&ΧUVP\M _:azS Ӿ{ڕ\N.M)˗Ԫ\vrG:|歏߶] n5]5Lq KBcG?pϫNSaU7~355¤"/y<-b+%ExĖy~jOLs}zȷCXg]|vCv?) z2&F s~UGFL|7]_i%]QMߵrCnq}T w)0ԡhq"ehSk O6k_!!Rd]tc#wgnLyn=L`Լ^$@;۴Jxg,jQD!MbP<Cz$w{+{ݳɇ!;hۺ]j29L)EB 5ugځ]E~rC*)nzM5nD˻_^sپAI!2+ujρɓvW7KWhSgmIp]ۖY}HxR48sw:O kr]߷`XF3C+yK:-~'w_tGvpbâGǑ5?~1V{8z6bQf~}sj/Y6n{G[.\m߶'˷gBG&-y·n{޵!GnkȘ+nݲ/>Z8uԫq _=׼[MYLjۮ'Iz>[>߾vψQa1ȺE V~4ԡf¾ sOr:zG|`c>wmXDϏfb~&a $;{e{2,*8(pHߞSS .W|e Qxz. KL.̃g@b_LS|jX!ɬ]~)0+L mݹo5 %я> W5BE'~?#_q(:>qx˖Y8nrHE3fmש AEعah.O છݼv{*Pw? Q*oS[OX П;}٩$J]Gdbϫzv;)\ #,4tɴwbb_1?Lc,ExuIt_! E&4FٶIkz劕Bv3}ݡֳA&vQug^ݥGG +1_rs =Qqkt@vW3KbE N&vǖTM?sffNg{3oﱎ-'i7,ǣ> 8w/8;oEoWL}ӲY3gv^xct<{q?]m@$@;x?t;{$Ņbg?Ns3o}d]tX7<>8(^P ;TkjfုϠdyN=]KƯk#X}'Q1α-x{GC6{~Y~v.MaNf70~`wy> GR2o^{ݤ;u "&8/0]d '޾Y |ٱTӁe mxz~McDL?:LsffZ[)Q@QL@VTVPAcaawaΧ&ι?fwY|...g̙0Wؒqlv»Ui.l?UA(B3&9jSy=fB$7^wmy׃9d(ɘ ұf3 ut{}vɂ?MzmښJEsk*~fŠŋ& W/ M8gsuC{llf7sv~ȗmә}7}e`^cj'ΤSu95kBAAJ {zس$Wa@F-qoni.j5m4?pL-lتȥc~Wa@.} "O\KvǍ.S'^O($0@V QEr*j8#ygN:uOCntٽ+v-bxL!0+.عwu.b?\v.w_6q9taO]u]#ÿص*wii zmXT6qBkG~oN:ꪁ/ۼU'<|K * nj:܇ %Kϗ䝘r. ],i4%&̰`>0q\'%yܑq" nzۤ.zMizG펂JĴ$x4i..&L[{o8aݢ݆S tceA:.Y0C`D3=@\7,0}ۚ<{>shɕwi w̰ YvVsUXRe}?UjP"ML[V|>_קKI|΍Ϲqϛ#$IM[V>x)*V.&qZ0V3&Mq\Qŧ{^p3ӗL]05 .^ΐ{$#6duɆsι ;SE Mգθ o+^ CPן{W=\X8PA٦붎NDK-S*X[XZؗnaZYʲ҈!:8cph[L MS`FtI=i)@DX$"*ݕWzPsv7kvU`e[JzuC$2KadZRJ,>yeޫO:ywmZh#T#Tq}톲Ɲ3R[ m?LOyG~5'S,]Ƙ)Y] 0mH8#=ڮqҹח'fW۩ix=h@'z79.FLW2d3’` D|րn/ݪٟ-Ǐ9[h5Ruvh6aXq-͠,8l/=n4:f3۳Ina]w;,\baϞ~ &x ݗ>2&wٳR :V0EL]ŵ"uFdd  -m;ET]S8#d Ӱ[̛³ږcqkګ_N_|֠ n9N|t85?B?3ꃱH2^ܮy0uA̗g (pK=Н7v m ɥVndvmY5cZ؆[6[asMmT1@8tӲLWtڙ;MOU8=jZF $I3b2qrkJ}&ooH Xdӛw^`ȉ.FEx+.%I\sUWW@fb4$100&!ۥS7Y_?|)}ҋTM hFNm J._|rQb' ;Io%-Ԕ\fuNޮնC ዉY~xO׾kHD}")%s{s6/~#761&,ҤYӄkӦ ? 1pi;g }i~q# ¼ s^ ؚsH(%Sg-N05٥CN!-Sb۾Z{ׂ[dx |6T/d;6( s9{=km?@*wG7˴x,7񣬮C۷J܈NЬѨ0z3CΙIdʅFXv=͐e[7sʌ^gmʻ.A|Ss 9,ڭ#}F p3 = IQm74cMQGNbB32EwƟ~-\ދlvZE৷iB@QXu~GR0Θʁgw 8[0#g5n:5ޣ~ ShC9h`z56TًisfY+WuvZe-)D-M1rBam\+\2S*;y\伜~WD_Y YDaZs23V`κYەs۷d\Ѥ `Yx3cRc~냟~~qӉ0Ͳfv2sҬ&_?Z{@R`鄶mⱅ69w6Yq9Lִge38#eD̨8wlۣZ~8 $}g/ KYք;Ðz) hʀxy9V,׭(jr-@eG jYΊǾ V ճy9W]p^:7yi~4E~Zʳ5mnHK0僧/6_b'~:]). ǽ{Ӣ.nX"bjgĠcڄ!, c$`ͷ. lߵeFjGcƲ_Lv.X-}q{KF-|s =ZǨ9Gq~]\-qVbY켟ȊM? R ܲa}3& _[}RJ|7>]omrk-?SN?¹0p=Oزi }VJ!Ћ  >Lp|W.]?xϢfM23cv,ĥP`L#d>yu=zs* *S@3\hӪwLva.!-x=}gc="`k;Fnd}=ڼx}qtj L;_ls%x{Cw>G_?d{ :3.u+͒>qS4DREœ9_2;7 PQ;,mȩK1A&(?uUgLy|g-ÔY_MOH~]q&,(576MzI^MLφ>(M05d#lyk)ұM9 /~ncΛkWA!u'Sk/><Hu-*}u_۫Qβg{\`YI$$ TȨ %zƨ_^BaDR4$1_zϟ(&~؋jzb ?m-Ma~~3{]-&O<t{BA.=k}iWn]9 ܵs'S3f%*i.o/ŞB-I|_vCn)=HdjP6 b_.v\w^ɊG.d>u>.:.آk< i[7 zpR--.@ܿ c܊{=u׾>SfAgwUT?[|ug>}\_~UFS,#<+hϟO$!^ЧơIJ+[=O|\m') >~ I`忑v%cuf[1[Xzx ͕+WT;wo oҙWs4Y1CnIffŪպԺtׄ~y.m:9knOh$FuEc6m6i-_KvX7W vmoar Lq{E[vmR%!(ʒU۵v,pggކi+Vךl`Ͼ=Z5nQ陝*a^0{Kiu=ԁ"{BxvlZ-߽?ou%5:tMMә\\jy.qQJEaBGdQκ2j&9s5BY-U-0$rʏtBjN[5eUnwqe3gEaCM$*ek ޽5Fka˗UJt)- !$^YR[Rf' CĸZcĨ hEPUYmϤhn3E(yꮢ GcJݛ*wְ^;Gߴ8VcFmZ^{ xa Eն۽xl[eDO0XYI~7uEZXfzĢ %Le^;cn:٦ݚz-X]ұyt, Vj ɩaqU2`@DX1o_F~XoqWǓedGjuwj{ک trԬaZa!MQQ]fJՠҍNwYŷaMOkmt@1'e+%$^c>#w.ǝKHiRX+VEv;c vErC nZֲKKZӣg/K1ÕVIW=cn@^`SPRzqx5׭V\ѼcVil\_XѦcTf.T7Ufb|_g.Ljn6mKک x~ٕU[ng$"EU+ Il֪5x=);UEʘqn[1Ƹ,MZuHI)u!5sYΎ?a=ضnMDttGezE$hnK,_c.Gn Ԗھ.o6QaÊ; AiY{b 4m6Ѱ1ڰnʂؘz1ΠZ5M˿!4kFV a\ן/3y`̽({,y=M"Wv)oL;J9! ] }>iQ$(u"20H8npPdj.I$TTT2O @՘[pA%Ien9gLsAuiK}~pӐog"CF"s)[؆ggxl;HI0Cڶ*@Vg!̺r1 @ So|*` l$Ue_ÞL,;ߐS__P29g #1ιdIiq$c PaH~̠.$1Eq4#3EqrP*BÁƖ 9g!? TF}pvv}z!5 9ϓR9v溍u=I;:l2b$􈢪\Ɉ؏.~{G6#\”UY9Q0y ?[IaPPk~PTBҴ|Ii4¥0(drE+ iRa$!`AnwsSJ]ƸF ."b\$(\mt[ 4q7c A>9!,ܳ7nM*պX1MI) 1xskB5BQ4~\P ̖7qws׷iT8#"Q#n&cL"[uaiU@:ʸ[q7:hg! hYik]wdV 1rXF8u[D^#B}r[o9}f}އ2Ι=gƩsn畨/U},";3#1sc1,=bDBl/!wOޕF= А "7W# e׭T9;1q0&%1 8|'#""Ԑȝ+L_ȹJga13Ph˷)][ջw|2=qAU(\-pF rq7ԅRN!j/*vEUk="FE:Mݶa[V]BT&r6e!4^ؚX44uå3pȈCI)MSp0;CQΦ ž=:r!@ @sIns5V@r(Uq)~{/J+H+z{Oa *k_*{y5 /Pg~px3 ѥsjjJT9r.ᅪrK|^ղraL,8pV0:tJC !~=7 R8ippppppp/Lj!_8B~ #48888888_8B~ #48888888_8B~ #48888888_Dq+8pB` p&9Fvpppppp88KH1G*9y]!Nwppp8<`$r\ 3Lҳ=Rl#͆$/?/RGc͒-#m|Y=+!waD)Մ9812={Q\k*oA7;jӿqmzzmOƠ[8oSf '),O\iЧՏ{ oto5{XXGunzM_|hcn撠NO 6 ZWhJ<.uIAm;*$Y )08ḁr::^5ncV.Yf3I$AdX5j9cDdXpMaTgLGj8` <'\HjlThwWrf iIpaII` FB_=c ڶ*/p_4UGT 6+WouIa%P:P5խn;.8 25Q:,Ӹpl3~`(U^6$WRc}*G0-[%"XH7UM1HD/5MX0UMK3¹[ol U~*D,MI.-Tʰvʠ-b,5Ϋ2PUM:LQRHEfoqnm, eK EB°n/)rNhpXդ$[Y9D̖Yie/iveYzqh&Q..,k]L߸TGnp8 !]^qJZƾӥc-@TU];~o7j0lvNv]b9P[l󶇙jVӬ;w$&媍y/M^_hH#78Oa y=+ڣ12XTmh >l5yeC:׽666a{68QaQŤTntڠWI?+[ZQuUGS|QuÊyJ )6l-|cC@fV}iKZ3ycYW -\u|nY.Siee1Sޔu#V1"TB2RL!>wz7+ jHJh{/wVόHm`呮=yQ԰%#I#IHOMNs+oTT]pSq ..SOrUnY1[rg+郻{r[ظөI;'/Zֱ\Bc f,ڥ5SYVl`29OMJ.Ɋ]XE;st˄%YW$qzHFGG_wJ% cou⢐vѼ1o45w3y`02l-K C)` ~g Fimx[Rb=zm`MW iOWn[;35)2zd#߬J𗕔PfEE YVC; hZƨ7yGJils)?pMQWҭw|!()Q tGoGt:?2`zT.i^Ohv5;8eE'v6tӮom 1q_"p3 vƢٻ UݕzWU D"Mo:SQQd+gehJf"(_sβuS[/@7(2*BF%ƣyMx>˶fmm1UC] -ݴŹy#S)k(n儥Cq4ҪX`ip8g{i™SD|my:\[;cs)c gnIڴ3Ȩg utˤ-'GxT:zޚ ӧ16G0US]nR#;5:uwfA A<['9y% kj:vhyBƳԹJt)MI.TRTu@dFNH 0cS 1bXRn.rK_w엝ܥyR!G9s][Yn?'4p45AHYKM_5i1ڠwl$bUձip8!I҉ݶoA^1M! wyT[ iWR) L[ڂ?fvȈYYTMs:DX\$x}1.d{k~|9u UjY3* ܓZRq)%ƍguADŽ~3M%,@׿[^4*F߮-+) bDOY)1nklw_C6 (hg{VT ~ 00Vl)pܺ%c8Zuˈ1Md"up8pખ$MZL&mĶj.=6Jcc}P-hjCcLBƜkٱ 22wi~EjFnQuI1ſ?$ PJUjL)Q-38[TOjuq)B۞k6EؔaG.nZS͟Qe1ӈ,\ m\*ܩu]ҙ" f)iRݟv|Gc5Vq Ko7K/UA~m_{ ˆ8ppO]epN^6fKiij\oYQɻ 8W$< @y߻,1"4pٺ]FBnz.gԣS뜕Y8w,<=]ZtuInrDu8B`~n~amN pE##Zv  ݩ}ẅMGQPxo7{ΘWc?-:cfco+6;d%U<;g}>У-p0f_jsyb*L)+!vaQ0.עoc35Ih$l#'"h WrkŠvMEՊ2z(8yibCUޟywDz4~$|b( @`ަbDC$!+'|b,(^_[Q8c[q|VEbeks#Wnשy ,)ffE~/_;uM֪ yOߐ^8Pm-(YC3 5QՔbj?U bǥZ).Ӵ{`0FDac|8d.$rCϭ5-5dBgfQ>-1j¤ps "3z]{5{{hhD v X"/KOKIM}q0Qh`)+YeƘW( Zn LMe8c ` gi~ o۞|c̫/SݏQq8p ǫe\ߏ|yǥ*u Q7o5W+{7{e ;;DDe MƠ:i#R4S. &AXM$IHÍPx]Hb)I %W{ėP?;`EO%S8guxpgz[)m>A35L0smc-Θ$}mH)m7c!)Iq")$)ʟ,c-GHB+b a!$?94?thp.lo_l4g]\캪BƠ[kZ\ %b+1.7KLOTTq@߃4Y^еէ-$tKDPTUUi$ {Uۓk d܄qEU~ogHS4UmM=2C o~  "accܑSUE(; 4w/"!M"5jt ]Cp@EӠe`{{ w{ӂB2됔ռ0"2fKz(XK6$0dr{NRZi5:UE8$ {j\_2iHu\.`4H/9RJUs=n#,0ͷ뭲 :ԅrp,//dmHI)0d퉶썱^ \Y6e7sʢ\$s:O,l{*fO{]ҾWhiIAj[`J(^> 0{ì>+|~)>GhT`]+/^m).e9m/># l\-RHyWO]kĂ!QQ]ԯ7Y^ي)Iݱ&EDf%AQ?X^^vA`9Cj,c_'N\2lQtt}Tf"7J:Eٶq+?N-z0ҁ}T=Bv}ǟ "cڧoT%|>e#%)?HBQO&/{V0ƷR(?nz!AW9|̈́)kZҮ=2#~Wu4:~Sh[է,?TM+߶b|)Dlfv.2ح3-޹~I)qrCC1~ () =L7?P!:8H ͭ*2RIL*4yv9اnqv7 A~3e4*,ٕtbd)V?;fUsVf@1=+I@k6;tf9ch[>v%'"oXPޥUM!RWu;Rcu'>zg0=dÜI. /nHK2l/Oo)ꇻnmnPT~rVn3!2߮m2|s1nR?>gƫh't3F$ۿ{Gޞk3@MqΉ$wvo^8;ޏ?}Of<+3#Yo%2V")+LGF#."D)~֯:p57ubnW9s[)=9nHL+lq `j., )$as*iqeY E²TEB2$%L!$^IϽ4G\76 HJR4UU)eq0Eo_wk}+Ԭ`GoDR+) ao$䊪*\[N1HJR5 e8; + *8 LP%kDغrHk~U+bڧvbojdZ0"ak@ 8c?XIzRɒQsrH@`kT5?zoɴdFG31KNgO?v+n).c I J>(M)A2@=WL ~ƾnp~:"*k ^f YQ-RP8K;/>1}2kHWs d7L'NDeB\c_8qn=|C3zaw:4w'*ܪLHR}IzS+M phVU7*v{T/CAu2mλMG̢򧾘4} "C?v2<1*KHqŧ(dF"iAUUaĹa=~FfEii$D''[A$ "T[^L?66JXJ=P^ZKH `5/vͣ`Dĸ VUUQqq FVIRX\f6/5䎊zxGEy+* gi>3XUYINT=s"U]^j ؤ a&7<@)#M]j/Jl ;+Y”'pK %ř AJng"E]VzzM<±ut jFr-,zC/tG4r/@Y^2@}ƌ|3h&RB.j)Ҵt6RHx~ج8 ! ꚊRاMg2I$an_pca%Ը={7m\j떽sWW7kݏ;pmMlt)B Lq)Lѓޛ6.;orA͹Pɩ a~d ǎ%zv([$Ҧ5֫YhgV"iY˱g_Г_::#6ͻ!{zn1,qXၭW,sFg8)ԅ zYӖ+!"^CJrzG+751Q f߽leTd^Wk9*[;u L@h„@,j)njGe/=;n4I(ݺ)B&Dgs W \UI<(/OIjwM(.QP8H&гi"J1-QRY^P,0A}~ =o?$`EuMu>_DJ)@Rjި  tygYŻw嗖G223#~!+ٲ雯Zel߸|~]յ]ʟǿ#ct#888Ȏ{4`Q_xwE+MY[bvU?5zcolwYm20lu DTÏT`6i*Ewy5;+oyԘG޳g7y3w̜m*K&2qB~uH}E_l0)s5oڝ=s4hL Šb7N~`[9&PUQj}n[wQEa hryX>U&1jkpPu wE]xص*{w1;U~YpWuXzKZ"aY Tݞmm{xlD" 1ԁs8c|%84BAs|}/KP6/[1/<7iRx6ձ0OԶqtݷޗ\JJj٧'_ҥb/3oxS/ƼyWrBUIa)Vmܹ77Wϛ _TE!0捊GIrfTA{|{w^lݹWrj!]!)c>wƟZQƵڜOcdWsF=f c *r5yk^D"Xg=wx*`FZaʤhyT&@n3Nzm^1 v}nڒ5I `VDL䓆w]*^MqQvMpu `.A?B?k[{ k |O;=6Ge_)eΘD]2ŘR<'=ruaKL bk8$07Eg]: xhռ9˳u{?}mNk.n\4fΘk; v>n=D\9_Y/RRvV4v,,)Ei%t{ۓ&t#R7w4(ߑ;oqޮEK ᔫoplߝ+m\^Y8_l]>&UVG8ڱ{.:ˆOq/oEF.0g'֕s_K6̣).|m]?};q٭5C:O>fTv&MSѐ3^Js$)ʲN ݈o3>;W*AL @5JPEEd* pQ.Q*g,)o>KSLSv8zPx g5A`n}#N퉮tԖlر@f|L&i*̊mʐlJRy7^2mUKn`ʍHL-ܺ1 x3C4S87 ./+d?p -m|- wo( PiV}F`)[0 ;zC "wSz퉋δUa>n@ vq5[}4;g8#I-]g6oܡdPgeM}Y]Q]ͫ;@|5_jlޤԔPyYBۣKDuf$դMݸbYuMPqyZuNo.۝dYۗ2]ׅ`V~,(mtD"ě^re;r $xB+WjfǞnh6uRU'z-k2ZQaG@L2jTه/DClP8֯|Cq8#~2W$Fq ^|}k:%.H6 Ua:;x"ɗ#w]I<90Oʿ|w)?vg8W#:z٪?H<z!EK ,ǒÎIYWTWe#yz=漕/$;j B$DDsN' ɭZTq|Wxwg ¥>7W :1p+B쟙W}H(bD7?OWD KؘWT pE T^X1N$]VAu`xH3rAMxrN')kr\."Mn1gda^iUQiH0MӌHXFSUΨ'lUjuM:}JU5D~n&S>-#1zوes{} k.ad[sٻk#iQ>ܿ{ ş<䃯+2Ox/kM0! S֗讽3)ze$&Z:b egp\B",evH2E>\JX o!]m.] KɌ9IHiOwARIaJmKLpC3 }Q6^i jzmx`:''k$V*n@}D1/+.~W){ə2v| 4> ɘ1򃖢4m'N8qyX gRDj5(V5WfQ8Hw{*vɩ5^:pB|bZt4)7no6⢤l1i4gnZݛ ]W\n ټʠ6[e&Hd_98"::bv#/. CJIIƸyw+f$EA笶jWenۂ$sy&%#Ac0-$Ʊ?d+ zN82ka1  V3pt[Ԉ%jNmn5(H2 4wb#a޷dܞD(eä=FGpE1#!#LSu{U\'U\P&-=+ѭD8L~fDܪqkMDID*) >flUcSգ59c a&7kdvfq` a$dƦd:$q~Y{H䜑ej>WYHZҲшIԣ6{y\:$$\T6 H?h2XN( XaQFBɃeKMH , `ӳ=†.">kɧBץapH`գk0p@TW"lF$mR IQ1R HIyWh`vK×1gl/v=-`uDdo*t]W'5}f{sAZzTjQoB1#4L "+ JJdܰ:O{yTUC7e1ٿ8@6R{ݙ89v1F$M^a Y D}z2$,]sa3{KN`?spGZC^o&)*L$-G$H!`?Z3nx}~($,{2DHӨ_H2#)3ƕ?^E1ΈD8D{?t_X6 =RW?{,E$GiIX]PC5/e|nO0J(ƹ=0qqIz`JW< 0X u `@W 0x 2Q}D pkHIu߂B &x3T1k#lч7vtTz?җu [#;Hp #48888888_8B~ #48888888_8B~ #48888888_8B~ #48888888tp89cl#488888885lu/WT}g !PgPBUeՖ>@Þs pxJyy\.DnKs1tppppppTGhpH)$1yc#""`r3"""{""!9gH)awGƸqF'FUMoJD 0@ "077c$q~}?¿RHV0oZip8p$8 IrȂ az OH({{ l/oh B0Ѹ$k|[ e~%=a< b\.} d )$"R?gH^zަVnқ9^K#HP^T]Zԣ!)瑚պҷ_rJd7 `_kvn}vnH8cu'ڼYskK+ݩsfv#0fE啞s#ڪ`lJU\Up9]Z"XMeevm51I.Y[^jXKN=pٌDuyYeKjDEE{sddۯ˓{vm @W Kb Y]BDR3eYe#BJ)eOES"eG6Vw$"3| %l!^,K7,"uJ6_b$L"Z7elr|¼" jx!*m]~Qw$>鄸ہќܣ&T0LD$,AD{Uӑ݌wVV 'lWZe] w}kf낧c+HX$RNq_\J*~#q cDǍ{Us6R>l?{?t"e*Xb7D$-aR i4!Hs_0-da U+e}FXy7\|&4i7vm-j4"|Oϧ_o{S̆& VDa9Z}ZgZaY S~L䕴ƼJD2Vum%R:>Bʺo0d}y!c뫭Nϸs~sy{5%s{(plM}@On$^+㜓}/ΉHOS8gRJAػO03hx:HJ"RJ+oa$"5A]xBc~mnOν]l{w޶n'v_=.E~pipG?WX8:wЛ/] F>"ɟ7¢6w޶n([9anP7Sm|W!})+$z& v3lkw]7w̜x%> aO䘃o*gh`MȟՎ2.@+ WpfocϦ WIɀ6>7Բ{(\Ug f_8jQ"wOVmQA|iԮ (R{דftô+,ؗL뫧͍D5[ P`K&oSBoe+7^dC#WսTɕ{YBNifu/* QY\V9qѤJ |z aS\7ݜD$J98)*GjVG}O>@n%7 @Z8pG"r`k[noHQuA]<%%ᗕu"*ؼG4DSӳZ49||RN[xg_0]q\kĵ+I>}VP ljSιNb/OnJز|;V< yD$t] ivEo}P]C&E"zD !-KXDOӻۗ7z+[hXMwes]yҢm۷TD.kID9=z0UD$̺ u7~;m%'o0DD[< )3> l;K7)Xp _>dU5'}h3)?32['n~| ͺ[N6}Ow-=NYŴ@ݕԹ,BD+^yF)ˉ*2Dq;}M-FD۾Q{mU5Ӟr.Hmfd{̸':zKٕU3^j.{!t=)HD$]Ѵ|t㓁,8լ{v'8Il>mEye|9xѕ5@ac:DJɢגR[ھrSMn'(5"38V`δD|ֽz6e٦y7y Qo|;K"2kK?@#D?4"YL4dȅDko~|qNr_Ot9=,*0u‚ [fPAt=6[4[N4;LZ," l#Nh?Ʃ}2hzq Yz&\4cոmeFu½dJ0mb -;Fzt{4-&*k Fu_MR%, GlMM٤E_~mL)@uuMZ$H!¼b&=9r#- ESn>kΚU @SS}={]kҲDݶ]"-M;Kߌ~_p]|PLXm>dڰ:\:ھb+9a1@uQx5D`,\k󔟛w;#P]S@2Ң(YCn0rݤO 㿉n֯C Le\o~پoHMXu{+,\KOp"0VYnK+toL8mQ7յO$ڞK&Ti\QdAӢ&BP>}埝q\'&m{TDj0d{@$F{A\,Y7-,STiiືW߬' ۙ"PQx߾%%̐.ĀPiEc\t%/}w/ WuTX\g$̭,n~c[wne{ RIr!igh$E$vo|u:w9 lÀz3EDR\fUΦis۞oZ l_{]T#ҵGZfRHe^:sw7J"M= i K vH?e)뢚WC9fD3|^VU +;M!)ѤGܴ>7ܜLٹ'<风%H@H;ʯd\բAB2 u݊cX!_;U>\aK 9Tϧ9x0Lť zꃮeA!) v @ZaB  P2:^-i~?qܹ(ݨJIp|xiXMn2P_ۊ1VE%Pnk>]a{{V'?͖?ʛ&? +~ix^]ø Ӽe]UҖ29NKr0e_+^)\pOmgqݹ)7kwy# O^8\jC ==ї9TF,|;I3tܥ١U9Uǯ1x43tig}9g?q|ռ4e2%jxLu#skصh@מ:xDQq]^|),&g.ya̮5MDLQm3~Z2Aߚ?~>TT;sGj|@M};jᣧL_47+ܿRV  =Y~I=sq0 $I 6+Cc.ulĶFEHRCSbtk+n'^o7q2]ѪW!C{sVs϶6͙y]GLyV'zҋco]]FKNin`(n`^hdXڜlĂ9xnLa@dgAͮj5!  E9P՗2O'?9) p4YGe'a\b~a &i[t 奙}w?^睛c%5`Þ1Z0b{t=̞iPU/=" Hp_rB(b>qhsy9Qz_ g$wqķ̴s*x|lxֻ/6ǯY =mFUa9gs|*$z[d '?6  +t{ =>Ϯ;/yUf}uGo}{|Tn6YI;("EQ" ReTjQ(EiUHBUR X  QH!d3s Z*tmv3;7w̜9u۲>/ʮ "rȏM/߲li;nφiٝ 8AsCUe YY}siwHTݸ`ۉ7~Y~ >~䠂ode'ޔ? MSoC 9\*G*<5?>pH4i;N~ Y#]|l'gLI[Xw+)}rsu=},l4{3J|Ȥ.֣o1-*J5VژI;s{Uf&F$qۿM9]3vcFLe)~D`vw<| Mqbޭݷ|jIWBP2@p9{yՕ*ݹ=A`ZM e6~=&SVVqi @0 S[9 QSdȸeMvU^aW^ۯԻ#4߬j>yī(_NZ=l1?];b_Ϊs`& 0ińnкyʐB0p3l4@_K{=tE= N! Fd8W}ş~s-{`!lD IDAT4L+/]2G3)%;s^=dy>3__:=e2~:Ļ*Ԗoۜ\pMULAs4.Zmz*XYрv{L[W?>r 4("N8鈈,1 1fV("=9.kv.8pWk-ʈQ?7x.izDX=e]1r$xaTW9aH]蟍qS[ixd;n˛6}r;Wn윻P"'wzHkKڰ b{V8^t2'#b#6~l {ϼ̧R^7'Vԁ .Jq?y*̵]ê0bsZC릇/Zбo}VF㭿w3Eij,ةt񟛯$|,,mYQџ ({{=y‡ԗ| y&#}Ǣ̒Y BaDď_|vqau8PQNvNgbMZ)Pi󋫬hO_?""5q߼GOnBl"7psӲ椤>-7?/^?b_-wq ;P|C]۷ަtnE]n-8>דb"O[ҳ*0zv?tTSxиʳuF^/ "7 Dl5fe<s;"" /\]}m'}_|&"6UVc;(#k3>ް8#EC/]jZA5sS ?H顥MK/jMaZiERD1scI]F '! jkj\۶MReҲB{WDMJJTHt/_ BM~o}+a]BW9\X0aIQHI.IP"_̈́Ķ6Up#x)QA1JLTeȹ$7.:ӫc]@H8'ʳq%%19W1ԟ bs $X1NIE߹*(z|jjlXc-K J2FVBUᦉ<>@U%'"/G51oY9( !NMF9!Veh7]Iv4geU%̍0 #+d)8(1ÔlvUj>5w|R̠23 2 DwƆk?f('dd8p}#I4I",F"ɪBDd9B 0Zrԝ1;̹{a!$y4ÔȲHM8@Z מ='b] 7eYӐ1NM WFgg8*&"](@lmx'A#h3U-.1##mz9>K-);[X8"9U >[|+% x孟y"75HKA8 D^CȘ5`GD99-/[k+_ZT9$VrjvcA|D82"GZ9G5ǖ%eK}OiY3yGws`4vuc%WvN)dES !Qj-G,rjȁ-UMVd@d(%|30h@ %{vl&%cu,lf- n~YdY4Jh*tB2j\g#D"4I/. f\RTYU5V/.օn-s%J-\M`ifb"y.)95 Fk^d$rFh0ӸmȸpΘuG H!g08,˚n=4RDֺ/QmڙۛuyjۓFRol[p_B\z|QYk hU+sh1 XΈ$W}o#妷Q#ȞӰg ]sS6e`ItE, S:ViWi nOykI+")25 $K꛶C@[ EJ# `boBN@irUb7|VDW ^ |9.s_Cpa]M=+_7i@pY\@ ^ N@ B8 @ .lġ-S%tEXtdate:create2014-07-05T08:57:55-06:006c%tEXtdate:modify2014-07-05T08:57:55-06:00Gz(tEXticc:copyrightCopyright Apple Inc., 2014&IjtEXticc:descriptionDisplaytEXticc:manufacturerDisplaytEXticc:modelDisplay IENDB`gcovr-3.2/doc/examples/example1.sh000077500000000000000000000004501235605057100171370ustar00rootroot00000000000000#!/bin/bash . GetGlobals.sh export PATH=$CXXTEST/bin:$PATH:`pwd`/../../scripts cd example1 ROOT=`pwd` # @compile: g++ -fprofile-arcs -ftest-coverage -fPIC -O0 example1.cpp -o program # @:compile # @run: ./program # @:run # @gcovr: ../../../scripts/gcovr -r . # @:gcovr \rm -f program *.gc* gcovr-3.2/doc/examples/example1.txt000066400000000000000000000012321235605057100173400ustar00rootroot00000000000000------------------------------------------------------------------------------ GCC Code Coverage Report Directory: . ------------------------------------------------------------------------------ File Lines Exec Cover Missing ------------------------------------------------------------------------------ example1.cpp 7 6 85% 7 ------------------------------------------------------------------------------ TOTAL 7 6 85% ------------------------------------------------------------------------------ gcovr-3.2/doc/examples/example1/000077500000000000000000000000001235605057100166015ustar00rootroot00000000000000gcovr-3.2/doc/examples/example1/example1.cpp000066400000000000000000000002761235605057100210260ustar00rootroot00000000000000// example1.cpp int foo(int param) { if (param) { return 1; } else { return 0; } } int main(int argc, char* argv[]) { foo(0); return 0; } gcovr-3.2/doc/examples/example2.sh000077500000000000000000000004651235605057100171460ustar00rootroot00000000000000#!/bin/bash . GetGlobals.sh export PATH=$CXXTEST/bin:$PATH:`pwd`/../../scripts cd example1 ROOT=`pwd` # @compile: g++ -fprofile-arcs -ftest-coverage -fPIC -O0 example1.cpp -o program # @:compile # @run: ./program # @:run # @gcovr: ../../../scripts/gcovr -r . --xml-pretty # @:gcovr \rm -f program *.gc* gcovr-3.2/doc/examples/example2.txt000066400000000000000000000020731235605057100173450ustar00rootroot00000000000000 . gcovr-3.2/doc/examples/example2/000077500000000000000000000000001235605057100166025ustar00rootroot00000000000000gcovr-3.2/doc/examples/example2/example2.cpp000066400000000000000000000002761235605057100210300ustar00rootroot00000000000000// example2.cpp int foo(int param) { if (param) { return 1; } else { return 0; } } int main(int argc, char* argv[]) { foo(0); return 0; } gcovr-3.2/doc/examples/example2_example1_cpp.png000066400000000000000000002640761235605057100217650ustar00rootroot00000000000000PNG  IHDR SN; iCCPiccHXS$PNM^t@B !T袂kADlȪm-(@e],Py_=sfNh Q%r _fRr ,v'::cyw +Iۢ@!q9BQvh7U pdU3LTp>q1~]X h,dg84d/\ٓcA&99%Ҿ1cX,EZl֜s8w+' ǭ6+7LiQU sG~p[? 6(Y3&,-G}!q ʍf #eqyQ;͂8"m|fƽ~q=GOl9v;.bǰfNb-츄Wҕ0[T[i_F $/_p+`] '2ll- CW#Klyp-ƌo6!G?wl+bQ̆K.@*p ( 8<UbPT-`;9p\6z 0ށaAH#bX"v  H "#%HRT#ېW(rt"ny|B1: : uA}04fyh]Vu =^Fo] t1+â,a Rapo`]'t[l<_ī]xގA+J&X!$BaPIA8B8 ߝ^;" ộL$%$n"'"v{C$IdI EXR i#i$:ANANON.P.EN W,W)[ugrJnQ9[ɦdr9\EG>K~H~`0EHJnŏ2"줜ܣR&Toj @=C}L@Ӭi!4m!DN{(h8CHRU%y%%?%JwʶQ9+w+_TS!pTlW9Ct?:^O?KU%fUPTSQsPKPVv\1L!ljAmƧ :|&p'o յԽչo`jhdihxkZhNќY怖[T}mTB;F{v+C::A:B:gtt޺'tzz| zϙjLf6o6057(6oȐlbnXaf8hga4Ϩ辱1xy&&&LMLMCLLMQͼn]̳7_@--x5W-QK'K&Ή&ޱXXZ5Zu[3í_N22i8d~g#TZ;...+].\}]sVv/w+,}M's'O0`yldzznbyy=6x~c祯{?7~1 RǁAAsN a4 :mņU= F"FG "@THԺGѦyѿM!NR3imؙ̼cƭ{o/oKPLА>?<+iRɚRJBʎSO8dgO8CsF3gfJ%&N̊bձBj~ oN->KH/OXU~j-ﳢvfd'fϑI9*Pd susgv -%®mo-h[SI]vOKe;aՍhϴ=mgo~_S}0`!C=B?Rڄ4il5w$t =z7v?Vs\KO,:9tJxjt鞶m$>l ϝ9s .]~\/r:}I=wSgz_{>y ၒ?}i_]L}%z5z7;:mz.v}txSgó>>W}15Ñ!KĒ~ ` 5~;\Le2iq`7(a5Lww7@hOŢ H| oRT')3VbEoEj$ cHRMz&u0`:pQ<bKGD pHYs%%IR$IDATxw|_3{wK! !{ bETl`(lHA@:{ݙ;S@~}>| w3s3yϻ0"׺Kh",⊰ +,,,,,,,Kh",Pu,%k] %4X !pE9'+ )(rq9g@Rs9]$8|/*䜛'RJ]aw _A$s8WA`AD1Kr߁Y!-"IOEE^]63aNRJ0˚>2B0_]\ĕ &%]bX$*g!YyEQx5,.Y.XRn l׹Ǩa#{wo)邵J(J~.\K$D tuI!g%@.oa~c I#r]ƎSY) ?d Ե 7ѥ )(7 ,7zDfwǘK_/_e]$JUkw~6g 0x1TǦfItoҲs6NK7! K" "#2>&%2pa"Z=ofe؝cx#S>aDoVuʼ \3Yy3;i¢ckIyD$ĥd惪/<0 "wkĖ9LLJRǐ;*b5u\;׺W6"rg÷zӦٶ?s b œ=CWݦ7MTu;ND$$"CH" fJ3n͓- ߚjAD0+DMljI쪪Sx܅ŇPUͦ2 ]"PnRa2kbagԉ5mB߿|avfJvcͮ* fJs72Ts;R t)p5qrOW(^`>:TiZY/fo>M@o_+>+iqy0QV'4~%XX(SN?aX3 5 Ѻ _z'vA} ii-ƎkUe!7e-Û5r:-+qnS5nlP^UUoh LwcFoٰ(7LJ45CA $T]r΍S7'5_4) CeǍ֡%yO:KM>Dք[|BHLMB؅{!33f|] J ss*s/ )H ΕV|kT4 C'onӬ~~fٴ,0n3'7lCR0*,T= BHs-%[Zכ5c̴Ƚ @ 0 c9HJ!%cuj'2ƘS֒K)$mm0o~xK$%S[m]f*UU_|!aټ9gsCn%0R眱}-oH#}R !ӈq2@3}MTI ӡy3\kܔlpUSu‹zLs_k6V_$oh@4~v YxAH$DC4 \-l%HTݦU2ȯ(Ɵ;!B;[|IDwvj?y)J sk׮]vmRZdNڶmIDŧԨ`ʪv<5D妧ڶ-[KEwfWsXx/usܗ@;4%?Hs/Souk^v{ ݾ*rscLDȾ[mތB/24ۘttǎ]E^S5kO8cǎ]wȒ(ۆ}`ⶭۓN_pM1oYvꙝ[utQ꫋6/dǨ` 4;%w&$7+*OLݽm?[;rsWl* ocGܗ[Hw{7[ﵛ6oIȹTq;lIN$+L_[+,.4$`iMNDB׽n{nkvy C8Ea¿^HD^釛Ņ(} Ol]@S>YNDtDސV*OD(]w!([l`/(U=43`mo I$}J& \cXlCoy`4*q-$KKfaNQ=޺eߎ K iqlq#'E{pX^|\,~RDwx]0U9w}DN ݮR"=^I秽jWhV`C)&iwԷuݻtlѩݩDdi|Mٳ{fm,BDAFQ֌iO5oTi(ZjnsϖSDdw{-ulݲٽ7oScAaHe|;7U\]Ǿom[':nvG/i\'$ 4&܆;B#y:]eJ"iD0dFU0lMG Z)ܼ\ ~O4tSݺwnܪҍ'6YC]A Ș#'>rL;}NMB *L\9,M;_ֺE.v{GrgW0w{m[@Mzṏ#"aX ,4;r0Z鯽iDf)07(ǎ%˯˖o&&%%y!i:Խ)pj ң[~LDk}lHu.-իgÆًo߃(*%b*0( գdz&S0 (bx$"20K:ǹ^V+ȜMA^"1!S&3|Y;'\/Xefg(6;%es{=z6lhf[ADțqrTz0C^p:$#)f!/n<Dd m 3KH0=5p~!~^/.pt6=#DdҼUv}n IDl%~M+W4ja"V||DPٽgEDDe8!44=%=^Dzjf gMU֥G08% <͠qn"C'-gr) jp+8V}>kIsL ID;n9dk|:<uYzWKvy*XBE٘ke(SmvO~% IDLOW T,/nw[rt}<$ue;P5dڶhjg @FNqųSTUU͕\59lAPBR  7WWm6|86&`1u]hVK𞺣)ްyb>ðn""ڳbPN /!}US:y|أ#zPΦ[V 0[iS)׹SUCl q}$j,˥pa;vkX EUm*O2W5UU_3Zs͋o4ԮvMpﳳ׍TZhp.lY12ln tBdSKt;_6},;mvb\l6 }/6USD&j׺KaVIbf)4M \aiti#oP+DGG2_ŅXBE٘*5_<@9U"y8tC@Pl: ?@shf0 3կrt.~(ol*gP͝wsσLڤwUxC}]Kf9;76r=}NMM0iyşsV឴wK ;z_x_xᅩ/8qHM1U&u=8*z2ruO?}Yei+L$-?|PU3[}ߙ0MS`-ɂET(c&^&լn2/jx\QM)?b r :yi M/|`H˻sE,<h;>> "0mm4p8. }CD$t`}G"%4ĔF^5CG50 a0Yt9k/4Հ~۸d[Z[=D6pPTjT!y:ΓЉ0y] =u AD\ח} ntsYK'~r-Ygқͯ=j}]<=E\C*ߟjь`8|"2"R[DsG囃ty? "{jumnDdx_hhpFO'2e+oj&>C M${(-?{ jD*@\~ixDƤ[ۨiDvkv#9h\ݤB"h6k6r4|7|/Ӱ`垰(@H i~($^4ꨔ"F&YɬP ;I¯$֩y@aːPP`P&!*LhܳoUܑāSV·cn7t1pئUECGG)_Zޙ#Yi AtO!ःTEk{ tS0toSU_~+) e9pCOy<+s@ #3q}#P灣WTN @ -ճlt0^ :3S P- U}]^9S8硕_p.wA>'N<|:t3l6bȰѵT`'1LarS4nw{. 9x" 8 EjXLM(<{<;%ϤNN;>GRvx+£c1 +Zn=7~#}O6LD8&NzUhkZ(T!ba+a0 (8s ,]-"<\*AYq@dA@*c N+4o* Ql%pi)e0.@D\dd%w{ޅ,J$= ZB|%s\qIP"D$ <.36h)pIP4r 9Ag&gfı+&ݻ( E%"E% *M+HCׁ1kd @uBR(#&NHhRNbOfp;޻uNص/Nܰ>l!Wn p: Ws_.(;4J5+V1"cŹɯ>셋R+۞|b'M>_A pUT%4LnFOgz߳j( 3+?"niRUU+W$%g0Qpټgg32|ɽkoE^qQj:2z {NP!JUqAx`8ST7S~j֮+y~ kʰMnoSH`$@Hg_Ԧ8«5T@\aIBy_s *ܕvWYfәseǟ}<uxDXvv~~c %Mlg{GqĶo~ (4 /Vˁ5RE l "r޼gb) Cb"91& Yn\0I@\1\Xn ٥6GDfe1閾5u| asmkİ 6*8ӱ_FՆW|(jLlC_D+ts-7Z$` O Fep@5jWۼr~z~(AJa aC{ (JnNv~n1p \OI` XaƉunW=gAkk2l1Q(u`ZYC=>>gcaYR%wQE /_,tVeMg45Gm^nڴGW0` VҫQ׫BR (6_e.: M3 4nٺʑc/+wjJD _/6{D$I>u6 #1ɸ"S`FT+:|f>@qiذH * N|HW5Maw`z /\3:h?x5uSm(|wk7ḳVWDT <)G ⃃%ISC=e2$S<wkV1t 5!)$:t/qIP""50:qPJj\Qm/Tz*:iZ5Óߚ_ou5-+ٌL\|Bt0 (1?|gVFiB+4,i^/ !t0!3!]=5f ` AN~tUӤZPpxX0d E0_¡{32H!9) T@dlU02O:$H)81qPP?6.}haQ +#E0 !YH-|6SS51em,ЦVUB𪊒zǸqOd¦1U_\s$J6ؿkmUٮPU;otf3d}Η+w]F5yc;% B (]b}=vtmԭK5g1I/úܴ!A\*I8l$6 Tn>JkG +~t4&$)_l?4ٛT-"6 `M[|f޼ٟ~vivA8gjwߥYtC` {f(s٫Yj] y+5>H+fかjEk0 ;6[ب '~o͛={3iAp\Q(Vh .cDUyٱӫjq⢢bbc_}2y>GO=$ 6:_~fSTk@ l.|4$NnY_{UCBurBÌWʵĴ~1U$o ccć#C\*W9cfCDnf~Mlݦvɞ=77-7ǥk;U3Z睏صgך@xևM׎4 pߔ7 R;ܠJM09Ԇ/[J-,9 \tEAx;{zFN9wl6oRY'z҄Z>=V-_2鎑*Gkܫl%r7?_PK/^Sϋ/軕0||Sl MQh?Œd^g7=T.+7&"pjм8(= AT+uׯ0cLx9I}} G~KDDc:5?vdfbo?>7+riۯ5|#Įe4ʜwЅ~= #{K0[_w#M0`Ԭ"J=6lk.%L UU8eS%|NTh_IDdaec8eyz{^ܬr 9!.1ONDnZ7Khza·8i6\mۂW _뒈y Qf!%YTlw!߬*Q =t_*6_D2滬V6[0oaN$0?jGi{oOʽTL8a d;iSݙt[Z3>﹕<^x;s-as̷wvn q^g_Bx H#[E9"{HLCHbf]z&D̿%-ǹpP 9>&|D?ׯ_L -*-$х6>Mrsqݳk ~c\^A?xy'V׍G2ͧl7C n\yBa}tJDCt֟ #4 ߮MMD$sOQ|q0w[a-Kh<*D{5Q尙U3^ˎf/0RԾUÆzY)D:BD` j޶[_R(d~r͒ok@t+~ߚ^le$_a\cqCo^9V9luNƉ"[$_۾UP63Z['>TFª߿zqvUQT-`֭hޥY--,8^+'4e"0{M m3լ)o幩,+Jw۴mѴ簱1^wr^mj׹cu[~dC{c5U`wTYoSO%Р8To|tٷկPя&&NX%H,aV*F Us'Tv۝; V{Y:tlֲ=GKX)Ȏ5P~FUV}ҫߒiJD[X\ )$1cI˔W_™26<$rѣjU^zJAJ x{iR>R)FU5(r(/Č"0^1.nB$Jot)gL*Px:5k8mDDTroMR[Qd1͟օ}"`La 9G34zNz*99*Zj͚TTK%KC?tdYTbjU)$%|}T1z) ?r<3/_lFJ^QɧS4JjA6":H"CPeF@B0EIgy}o?*c'NBW^j`WREYG%y #($zj.f䊲c6#!&ܳ)i:c1!A珴':vxAXFGo߫RajX W%4X\)P)FD㜹DҝW !/ h3\lB0ΟUV^*#$ !Y 1|%(3sᆥ@0ED3fR5D\P_hg_^Dž $y΁r$x;ZTTUOILf#G r}V +zd̐iww丹迋s[\Ϋ ^1.Q΁Ռ(SDD_o^5HG:\yu$ "ݲnC=q yE ]'":[F騪o?*`"(^G΢u<\G~Scԟ-"oHJ>K Bz%2GK5M{ch'Nl0yI^yBcyTasE+ue0* ") 23ҋ=!!.) B:I(E`ZtLLpMJMc\޳)YvM|8犪*WChA T4PA~?)m%BQP<F 4aa*;rsjoۋOÐ|u\t$`!HME Q"S?(*B‘j RBH".jbSuhߴS٢9{ffHh*S@̦` 3A2AQ)@Z.lvDG Α N( E)!2Dg3DdHE0HK[ *.E3X"țҪ~,?=:}h4{vn7D6/Դvߡw< wO~^z5֝/[}$Qqg%, RON7s;:+""? qJ?_j3=swqúFE7[6q զA !*~&s-H\sz)NmS>D\ѱfr$1QImH<+69Qg訪f<;K "J+8џߋV=ur;$:(Zmtg/%F:*!M˷K"J#*hwrrQz5:ꨤ7N46DrѤ~ [ᯒ^bk#^nwe2}b]F~_%%G2BjH0ޚ#֣Z!>-f}&Nf=z X_Fæw O0ҵ*z~z:zAF>7[vl#Th7kq:"<"ۅ8}ZJ#JhG*՟B>"ZzP=+ bTo#T#/+_EA "/ #\v "$2t ѓpZjQ@'nJK"ZS5Г iqzF|c.='h\ U[6Whke6mtDAmtO;$ʆ-tzZE AB3V!h?m9{d6PDtt'9日+O91Gc&}K&tCDw>孙oYҿy%_`=/y`-Y K`aa p@nEN/"X_W;Eo#Aj+NƑٴقn5.|'?DthpjM<}dOhiOh&V7sBկ2,:pm{F<) `4Oڙ(kpq(aOD:PXhvDH(C쮱@\m67g7>JFbM[C+w0?C:>cw gr7*U*JH J?c{ʠN$hҚ}j8c)O>!HCiw᜿I [CGR8o[x^ElѱW%`W]uj0!M)Dн8.9$ #IU`H j]G ]uo@b),,,#^T¦Rj$8Vb 8"xA gPYH-Y^ ^e$"cP!溘 EB@a0 ϊSDfϵdk 10@J0 $$8cbϝԥQC!"Jc#?lPG{<Pp,gB4d5S8fpox#YD`xp!T"4PP`s^֢[J6sRJӐ 0'&=^{yګG^=Nfpն叹Z75"al60 EWolر=B]!"Bh&ݻo€✐B0ɧH)^:M'ϐGڣ?AS ҡ{}IG l<4E0 wg:!JVpv<2ʼ: $qxSJ9B~ Wرc> XT(6D߂o_X.?s{naJr(i Xq6oL)'^( c p.´ZapV>Cm[N/}sņb(mۯy=5!Zƌq`8V⟔ ƒXXXX\︐~9 FS4YuŽٷ?M4=W5߄'_EkoŊj 3NzpS>vS|N*3@QHaKvodChLHEhӅ%T/>V^$,r"Ve,;W`Z܂=|+Nmjc1D]CP1W0;]Oǒ Y4 ҧT+ C6t`|Kz(ɱC639QݚD'}cN:عvΘyeC%>Gf)aљ]+c9ջ!|5Ou`ڼ5Tic@u9@W#DGu|s#H /'3ɶte.UI&4tn7 r?œJENǵ~7DwhՎ ϋtVg(% n o*rkk(iw5ZTAd:"b Ғ ^}ֳ=S|ܲNfM4",_%b=[3Ц]@V@d>{ " ]`lzL^S1y`&"!cLQ !WGB_J c)D1f=Ѽd8W R 0*o?>?~O&f\Q.s8% f3W<7R\I5 ŸKDB_.eK.㥅3Y*y{\|PR094bX7XAW^WL協oŋ˧ƾ0,YvgtF ]buƖmQ*:gkkg΃|9-,,,={>- EJ)΋Q$%]`4)By`0E`aaaaaqy|7yֿj X^ K XW@ gy]j3k]r*PR eA 0pH{? ʼ"'$ZXCRUZ<L;Y!Srm +7,i_B zר}20vݷbc>_x9Җ$Nsk)a Z@y^ܯ9X[@v] p,q%eTʍKlWb pTTήMA9gR3-\~Ɣ{B՗wcsW+^ÿƸ/B y(^I-V֟7JB9@ X^h0ae-YI8?Ԗ}ؾs56+p]w{JϺeg7gKuplf_YX\oF]:uOƧ?sX)($gJơm}; imϖUnp8Ri)wϥ 2ƹ?/e\ŒO=պۣ0qMbY֯:]fHGO޽iސP^?DnAU>yŖ<]"&>ZO]`I[޲VI]r juGMΘ{?v{Ã/Poax=?ؖ4i_;n;)IK`:ki`L^w3<4nDdgdB4gdMܡU* @W ij >N^у*Dbg^䩁@L-g-2$΅{\Xa `)%MYt6ɹkȣ-a !_tsg_2:/Hu ;վE T$TJɕq7$y;)@wڌoՐZS'l;0'|߀Ψ 3%#9); !2W9!o4qU99#oZ;*YYc?f=:tbJO8V`;d  @s{Os 3Ιn}!I\'@ZVUIFmV {M>T"- bF0 X\E`X_dڰ-T>;Gsѹ=ۿ5712t]EW)n-":X,ZhI ܡŒZ:M;>zT|t鞾{kjn6#FT=6r޷KNw:ov@fZz^~~phxtt$^*&DRug^lz>iQú|o)>]Wpq3 z=}6IfcƆ)ާ2"0d=E~pʄ #gԉ}ԇ=[=/ǣ{W0M t~T/1Go۔ŵ{V35$@yxNn^Sz=y]vS(HK 6]U8IS& nF}͓^+I)_gGz_].Lm.L] f9{]iB;eW8vӍXsW b ;;T O#7 R8h:RCجaT~dzpUc:N&b3?O&sS$i3 kous1bLY\VHTs$i̞ňjzc݄?~_瘯}שΕrbjԯę,I3fg0:Cr3s<ݛpx)"<Ş,H=^ʣU~G oc1)k[Ar㏷Ҙ #!+xkV]N}-oy[mvj +ugݸpNʜ Aa}oιYyE #؜UeUE: rn ȝ]DhUvɒiHBe-n-p`D!!484  J><|9i3(NwP ؓ#!5CGTu @8+udAHOv yT5/)W$T@x<>4`,85uHW=H}_k0 !baqQ ! ЁJ4 9 N٢ Q;7/qǤq߬{M=qGT^y6HlزfCʫ4MGj]cZ!OO|漽wf溣(*F)3Թc/g8b[S #vKAb-_La,5r+ziu|lW5,:"OgkOn9PXm+4) }u@٪c u"#a8+0@>I[OZZROG4thAOf\lMn_/u[V,TozNwۭ2Dp?0bJ.{xrW4@.ˡ@D͙=vb#]^ ܕ6*ʂ篧D&;ֶO7_%h8d,֞9gmVh}F"R4c4OwmJӦа0-@aVq(@XRdx0M gH€R@B:n띯WloSN'{d?lÑZoߺm`L˨Qk#҅c@Gt+"j7#7X3e t7]:. @crk0Bnr dᆡL,E=zUql/\BP4H<@ZM##[Ø9a $"q$ԚlX/Nlj0!'vm Ca1ܬsY!|/! Cwnk5H =R@RJam4M?Z+v$4ގ՛&TtxzmZ=T(Ԉm5_:l^wa\N7˒32/ʕz48|tx0 "NZVea(1%θOىEnQz݊zGY99z{ӱk񔤜 Ć֨ 2{Myik]xqJ_ٸgWm 0XY B[uj\-&N {H[*٬uODvL3Ź gT\w{^z =y93 pMP[Px:Z||]TiqQ ]/"S>-pժtZ0;[f"" 4ck3;@r=6a"/'Iۈ9EEAԕ%9ݏ7⭔Al8ح~ [we:C|Mނnlll>:N@nهyڐr@cH'1*ɻiY eM⣻2"sE0h׾}ik\Qr3TdwkR/9+9nU f-yBr+x.[_P{zX0)?Pm:+jݴC׸ֽu!Ƽ1lܴ5*h}u-+g TU֣FDD C/?2=ܼE7<=n/kXqғ-[ 3KzƳMOVo>e UEJ-+( YfH EQ~(QFWDPQP?|{O{u~jQ42 tq6MӋ^\Trd9R Xw*3m{b"gez pE(H %^1¡-9R ~ODLUHznY&oJ6%=A qhB4WW܅^AfL[ is0 HROpF$apƘ/!y`\q^V[)fGJi1 !=fHH+$"eWr! //g2Mp)hBv}?8/c8B+B*,`nAH?1n" Q"4J_g*` WByB=+ Pt 9$/Ęr24#0K#Z3,i" %fc^Ղ\vƤ;0c$|۔Ȍ1{Yh R M$.Na{8g` i }*$)HVpIb `L:S4nFح׃`Z*]kod6 $وRN%K2}5ws^p c}?gڷg1|%8.Dl k_( ָ)˭HQa|3siـῑRIf'7Ej4rnlg(`$}#UQJb_a&Ad[}N8sxe6 ds! 3yh?0 ԝJ 4v.Zh וOlle%B / (!0*BUba_bȿ7xtBrw{~} IfOnC(\b&4 \b_| eutRrcXT:z-,揨Ndp|H}ZCD.Mдb!%+6 nFCpui`0~}KH4, _`iaaaaaq!`aaa_4ޛUU{K!ylxM'9+dyTAeUH,EtծC>8?D#$ [B_@%y.ÍRBE/D[p!@E9s3PW'K>1pyYI9J`caqm)+0W'ίҡ|FLۦrr<@R% !.eP)"CŒEK'fF 4$f^#MHY*hg<!e;8`J^>!$pC|0w_mBӵ."_Ee~= >/A+͌yz^̗9⊪L/+,(á.k?rҒֹm̘ZQDhoA^na q 5w0%/RI B:lč̩WZNv[ cB(˫W$D"q CKG2Eq}PF*N?! $A@~dn >>([ 8c8)SfxM!.ΟF rYYka(A.Kl56R&_F ]i6H2ƍUsq/3iGG f$Ip2Ar,C$82Q1"1KRcW{CԲ8^^DAnTgwB}2ŵ$8z&s&!iN 3[^J)/TH!΅p"2PUUOǎB HɦXKM# c "))pzRR3r|EcvKcƯ=9+w?;\V"BAs/c:?;p02ַNM5 $ͥ=6woRnUC{SWv\g9 fpVxSusΈM#Y\8)YC??Go嚶nW_l֪hxt:kqȦ)͏3k>{G3>M9y_ܼ)N>U rS $* S' #+FG=KB\rop{ΐG=Axu6jcrp [8O~ܽ*s.Jrq7pUGV2i_ETO(ѹ̇dNޤ=wB f;6ӟYUb} Oe>^zH{/[rl/%'R n\,qv k Sm6 ;д O|W`V)̜3!)DP4~fҮ}pV~% =|?v1-xध>ſ~]ޢg0~yc'',ts0 ~G%eSxA.adڭ;ƍ7a'p9I3͞O~<Xg$tenyl ; `/֮Q}LkL5m; F_vڵԪ۩{%XpFZunR)fu0h-ھyZk׮ӬE_-z hiVnԨAzyqԀnujשWn yb0znթS~uosML/?}O9g91[3CBaNw (*;y#Zk Og`gNՍ}"pSp"=+=]^|鷟zZ䑍{o[nvOmߟ U%" oU3щww=spIl}?M g-fk~+# /.'=^N 7ik7 7FVMCGݛ/P~0um>a]t w6⛵C*k{/y1çQ6Ϋkl.Lmj: _eժ3p2rկ#IN9T< ݧmܤ&;yTҋcH.ܷ  Bwl#͜zyh6om}͢㳶n]ֿɮw\^1;uK^~e3YA{p:U›Q`*/ \វSE*ܛ9]W0{w/**LK/WJ@ʙظ0'Rn^9Tz 8t=+/ @ MJ9qԝ^zޝ?زUnZgwG>_/!\ql f0Ei* tƕ6o9nٱfݟ}Vp")>yzP7>O;߯=_q{K@!8}>m]"nƌZ3L{Y| p_GVH8찙^ vC0'_iR#Ц"q|sy<ОeeOHO'I:DEyoT4k~flC@q!8N'OR :NH&@^/v"d f;Dgjaq17䋿gww=$@H;Pli=mۻVwQνfΙzO>0'P55n۶c~Ξ&fR+6$߶KʀgPl qӤvos7;-߽g鼿9*5iMw|A mQ [1ӥK~0fDV33 kޡMPuy WfV{[cWڦS1Zl9/>vn@i٪ +Մ\<\{ҷ=v29yNU; 'I@iΘң 0cFqvN ơ(#= \k=wyڸ^ IX+Sj:nHɣ{L-R\K;^0wt9:냈%+6Nm-yndH @ѝt{  b%?\ep`U.". $zjb 0A> l4݌XU6sHt<=]ʊG|>%l@0fy Xz`*`ï$y1V .~OJWt\Q}!_3&X[xlDWr4yp;-ZC0)-oLkigy~;c9 _*7m'+'?-uoNБHҠ}ж>>_K:*)U/p63Bl-LaCGްec9P޵cҏI$TbkϨۼՀރ0F|0\afB1w~{h/=z?ZL] ¯F2E֡艵=D77 Ʉ|yuTdD8@*ʘpΤ[]]MZ>Q_|~5 x\awEzEEe+_+0m>7ydwQ 70Ԯ?9Y6I@[7HM;-3tppPTCƋÇaQDVH T)î Jn*?p! q&PY4` |Y:Ca岭`Up@|o` +`:^`*n q \yZX\S{&vm؁"8 B@Ankڶ됾/Z]r]Sauk~FBotBB`yYEp.)KCy  F#*s_d¬Y_ٽaծu+?u66lDrs+_@J~zǃGvPr6|{GK.}|]|ѺKBO?3Ljip9&m&sJEQ1#3W0a H9vaNSߏ]Cإ>ޙaN U`峞xԲ[I?sŀ[s3w~?&R 676bTec򕫦~;TMwؼv݆m1V h9v@8w`@ŊnXe,JPzTPML C@"ߒ{ȵ={O4/P´d7+~O@n-jbBe\;YkVRa*m)s?b~rg<@H7BF )lR1A4V @Jsݔ9Km?J,*>Rx!.5Xqcw\]v_ q soP`Tkw>Uw=;*9twIDAT="ise7Jvi )Kmڜ3)i2lܼw/i`ʓv}YP|iB'$3Vzmgn;v_{G|e gC^ЌR?φ_߶B˗..9{/_5'H߶`o~8I.6J~a_uC/~2nfvy֚}3둫8뱟Tpx 4l݀Ê]~i&b;@y Qρδe0G9 *+  ^ڰ{EUΦҠ_£Z@B.ZéP,|m@yqBslIT~>4n[ hT@)=#ueKNB >s/9{X ZPŀyQ dјRYnB{-<@͆,G Y8g@w6۪T[@z\eKۖtݠ[h ,XVkVMyˑߘܭ`5eh7?owCJ(mXrC q@H*DÕZ_ra 3zZ)F2lٴd$k8y.Ө^Z~O_琉qnߨz^zy5vYm菿^]UMܽt~?z*\к.#Gvng#65c݋VS<=}1@b%o*/;9A9 ڱuMP=wsKdy:KNTҎ{NCz0iSx4a+noί}R*|>| O&X w1k>AXK릁0@Ѯ&}LRtoPK.@!A4 (.AN!4  %E@?eA:3.bo-9ѝw#[ ^|DOtu$Ȳer!WV c;eM +x]O@T!8lWO3_UUkd[>cEF)q e؅KpN\5b.{ջ/nCyۣpI8$Vu֭(bC*x^gZ!Xa le@;23ѪnmQƞfòUM>,cۉ #4e&e6yL",&c լQ2*Bd޲tƔ ۴h LyҶ7գŋBI.XYRV͛ @ξKVno۶`M} BYa];h`GΛ–mڕOtYpajԪmسeʹYs }UJ !vo]7mJA=ґqۆUZaՏZ2B=n+Sv&Hv{de$MȒ5]Z:5HD3:#dB]2oν( 嫞ۦ]p˖.wv֞gC+c*eK߹rrkՒBEf-6=P4հ[& 1OI1{/CH.=g +znV!:zF346T,moRf/;#[*x2OH +_(%8p;ӑRtԢ\g~jXщC8#Sύ4NY3\.V8,n]JVD`=~'ʊCa@$8HNCJT0+BJn,lH"!'j `XOa*ģRT <^Q Uo9iJQRE%gr`ƌDB!buB (DqPT WŬ` WqpԜ'cdADZX@"pESrEq uB*GƎ=&;Ec/Q7 |ł5  xXt}Й "!8@Gt8X}4;v AN1c w'UD,]*CX>.cXE!Ic1v&BHĮP1!3Od1q,K0cB"M+̗= 1;.p1cDžcc1v\8h`1q᠁1cDžcc1v\8h`1q᠁1cDžcc1v\8h`1q᠁1cDžc4pmc쬅Gſ">8Oyz2; pHpN"p8R qc1v ik/\v)8Rqq֘RC'r GDc QMD G^r Pˎ;/n i@ x1ԨW/٥ՙ2wَCDD7 hjҹo}dž7y?w_4[Xnujjʩ;^ݚڻ;/\apom[Ҷ"|#?~k@D&@0ݥwEȾwsҏ}TM6v ٘OG<t]PnD;_Isխm * "R_xcԑAþgA)gw^!nu[ޥZBroнKnfcKY!v#"DZ7ۧ}=oJTh؅ܯ}1iu_F[w@k==S ȶ" @ըUO,!_Ъy'HCWW-޼t%uv[wxMS};׃HzWCRתVmZ@w߭ee ް/^YHlٷcn^sιs~rZ.&;1zʍ[ `++Xv&:kB0`p.sowE᪁Ck_|یۂ&Xkd}7Æuz,b9 W1Xit /#9aҷo^lفeв ?QvV>Tu yڌs+:u. BoаGW-}'ÆnKgmzG\r')TxyΌ}{֬ՠJ9c@mlֲy@ჯ}m}z,g'WbZe.˜֨F@JBP?E.w`sy?$'LúYNzwz*{nּY}Z֯ Ucxu8ؖMD>V[SҸ?{PӰ *dS׷n;-uD![@B II>SD3d-a@$4XPݿe {ɀ'o&ݱh\$ +ܵWK:cDdo_ l-}o"飞O(<\-oS3vM ]ozi4E_Zx$qIh 2R%S%T4Pc{!8k՘qS@Q}鵕ec& #8V!7 ڰ`E܉e:kf\V"ƎYz3f=/<-CDmw~arR7|i?~rFMTJ ͜R;V};z\F|%7W Bߌ}ݝp:5 Dy O*#vu`V.;~J^ԮRٕ/Nhngߎo]̊U+ӿS/^ )r~ώ "!hIZ(Ӭݥ{|w /ذu+1J#HKPuJ)S"uDncO$P8/T,8tHJC\wc%aArlЦ!(5-J9D b9 QK)OcElۉFD"B&ǎ%B$Q(TqvH!Dm&⬋([+ r"m(UVƢFDPJ )%رDQC RX@SFd1ƎG{c1VZi{)6LLk`lICEDiΙ3SN5jcg͎#7Q"ĉu8p ?ߠ)M)1NC4XbYy aJ !HR4!t] G)tu lQmQ."ؼ2cn?JKzLISR1}"V<^)ذ|S Dطn؉6DRJ1G@_.EGkV>xgG~a%[ Cc۶)l۶'(DZK8QDDʶm8m;J)٣zvnձK E/h/D]#}g.=jN؁Lyp[;۶c駊_mci4( }Ɇ'o=/PʑfY{hP@)&ed̂?fd!bK]PJM4!Pi& \ )ǒ7؟,@Dj&usVmvGjɬ V:)EԤP6!~E~CWt /\>ۃ&pĎGVH,ZSI) Dc쌢]!"HMeoD +ovƦ儲mioFwz@*EN8ߙr+s[ŨvrsգfYޫj&N+ۢ=w$3|XYrF`Y>k7S9y=6" .4qyv! \?w_ k= N5{݃?irSBnb[jz䟧Ʋw,?w\i-1RkMmҤZɿ'UhpUWK6~M9ZgOկ\v ;sĹ]!jC!)G X v=pP!HH =o~2C[U$"Dso8CܮK>];]r}Oڐb= ~ C;u~V̞tnO":h86qD7=:}^;A4 TSh·(D;`]f$oGY+/w W}v}[WF}?zӕ*izfMW\īo{+izeUg1q(RDlk5-lw'} $B)r^吻>i{gos@]б3C~ʼiR?ʛW4B~IQDp̠.={I/J~sh*! ,K:Y=䛆Q=%nׄ_;׿M-G>X ;c@hYet pa @~NV:u"#ڹZ Tݥ#`^|A_ݩO0ؙ!= w!5:zu) |Ke^<.ѯ]{,Ĥ t0͝} 400{+#FH'gYn#'c1ӠI,y]Mے^ W޸wY8RF.z?v1(>bY/؎Q!"hg7ӮzΝ v-['lV]g[Vh;y;?غ낕K/xs7j1/ZdeھľE}5|bC}VwV=~jC]T^{Yj^i_Vh}w҈ν.VoJIkW,R2bosۺz=eݞ^sPL~w\r]*&s:@8b%=^~xu@NoZj6}{im'?5#qc/nA "V&2.T?u*x: glذU!W[+>Sjp&(}0A,ݸӕֶSz 2vT[-E$0Uy-XRmMڦٳ FrB5>Bֵ)oKjNJfT+=]T/%E#' \APmu7>uz^Ȫ]t4%*ggPT^3bV_`fEHںvBi+WoCߩgZQv-}ִԩ}+RarL߰hGKN ZcgbED'x (@ (;FZXRPr믽zWRoWQo=1":)b `ǎ}-{o5mtf*~dtt01?=[ġ{PJ H^݃|%KWmUE@0;g.Zc!gDdguOc1V:}%;)8B< ɱBOuz(bcQ8h8!~?JMV`1vs@DJՋxH1+54n^EuS-;N 2c,>64]ܹ?l\>gaf'cT[РLx. Z7?,t 60c@܆'B0\sKjTjش| ~qpc_im;-UUj^(c@hٲM/Y8a1cA<*iI3{`:c1(+F.|Ԇye Od1J8 OѼ1?)?zvYZZ%qgb1ؙ,NA"5FV)F0JgB2c%_\3BK*E6+@u (,uM@~F1N6RrVԎa@ДJ+5!ac8 xJ""ABf8(hli:ȃ/1ƎM()w'46i)dĢ~(03 fW!i(cGxnJ~%NJ@D5TRjQl h:'DJb)Zϸ-c$HJC码iʱ6 ŗICѡkGfAs:sm;Rt5h.] :Q(""3ѣ  E"QgflqI1邀%~ODLh9($̒t;|nc˔}4M鰋C$ ݛ9j;ݰ/P=8})^MI70k+!;*Ǒg"(R@j )o?H;_y=3t="ׂͩy|)GQѩ)G(<Tد!@)B)DRp (b9PH x{<2t丁1+~A㞝9[46eUwA hنUP0"2 ) ޕ*BD0\sySv(a5RЖߜ%%xcr_2OO{γmgzQ(BxR}RPA=f7KB4)X(Ir-y_^NC 4<+ob Gua\$0LMَS52dױ`Pm^/g opPxR`^XK"ԅ3 N\Ҏ8K"ad GB+уMi ۮdXBӥrsm4{&KHcۜ!0Pҿ}+ 9]'싪ZFZwns}c9`x4rG^thzY**+e=06.ݟRɵge|CJ]ݴ_.ߓ5M tYc#&"טO|\#"O;˕r@{d`Ԇo~_»)HFtϯ8|hZ^FXs*}/D /QIES=b*Jq T4 Ok&r][vX)fκmV쬭k%֬2Kj5*EM]<9}nZԩm@Nݳ9=^T V|~۷~[Co};:"ܼUMT.{O/v]4m ڂ2XsNE-'oa0ǫnܠ~i#Q-Q@吙pKk6Wt]l}a5[M_ KoNG ї :ТNh`֬Հe å/|QL|׏}QWzY^} f#_Nxw35s沌lq~}+A)qt|j֚߶CGRJMڿhS/%9yY;щDWiѩ⏧fi]A_ל<ܼ}#+BN7rI$77ow2t<|UB5 @ hگC֜*""jԫ\E h]ޛ;L=;lY/Rq>A `4So:+k+z~Sf~Yݙ,^޻+P]$]\]f8jmف gްlZHQ`lzժ$ֹ]*y&t}dz( UKߖ81? ɞrI/ 3 4tWKBw@Rm[ФrC @$^0p$ʋ&h4bIiz/@plێ:dh(j ]JY@R'"H74l "@TKz.)[fZ6 PR3L]Qȟ)D]^(GH,G(u>3zvwW0]: VkQ`0HϬ֏W.](G@H +c"@Mhh&*iAcljBF=漍kw¤-YRYNI&(ؙq정ve;,u|gC 4ٚq9l.8[-ܿ%7cW92vrpUS7^K_eudE Y{ۊ=ց l^ݟB5fͯW' b=;g~W~h?֒47qTӍ߶u%mweo^qҽvTen enʉBYG!|4l}kw}?/ Q)r'y2fywo;3\M;WޕMIH ',ؕ؛eHz7ߖ?D~;^C-ruwؕQr b);=7{Oԝ-鑰U [e17ˎ4Ek}k<]yU wz$5Q8+'~zOi]n/k,+zy]+}{Yȕ{Qpx阹+nsEYq.K_=]bԤ}I-z@rU}\ Es=E.M^2w UXE]-;_|=oݲ )5*VfL}9_.X:yCǻ.v(RC' \оZuO89l "KF](Eg=݆ aZ ynJNvQj6ѣ/M7nyN>5߼Zd>@li'69{&LY)eUfލI++otDݣmrWKbWQJ*s;zeßgdkYE`"s !P9*JřDeD Dm(OP Q)i!-l< YG럏}T=hc0/حɍߤJ/p4ET?Z<8Z*}шApJôp-UT]hQBpx]H2*ei`VsKD8HrBAu"%ox47ۛ˹[`DԲy+ǶNNw K v4بEBY(%}a&@CuitRJc mT U,¶X{i;ӼVOy%Nz{}8U';%/cg4([FG[D$Tj/p@J)Ymc%I5D@ wFzjK|РHA#&6ɶB_BR4)ڌ1vBdVBic<A-Z* #ZxCxÖV DAT4H wy/ccsHyD2@J),y" @)B (BKHq5'㤑(pr5n;*p8 &u]" 8DvB"T%2jv;U@HH7SD"Ρ uIByA"BWitV`-(E ļDJHVtOl+~rUv"o>w楑\? DBj73J7R]>%U?V@DRɎ8(H " "xp"@Q b칱 EXr CHY1Jr/ќ:jF|ח]o.J?rsl-|[7gF0\Š50!5?smVOX/ uqD$M#o[ߗ: kJMH]3}aiD7ūEhxMí6<ԀuIz]R)L$tS${LF@j9-]-1x: 6Ñ v лHE+.M gD~!)8{WkuÅG޸{M_1۝GCG .7Nx`}V8l{Wg'(/9i5+U "B]+mjWbElvtW&rر.Bj%m_gp\/)E;m(`07+Z~}kvykTѸ|ʗwۣi7!Z<1|qӠpӖzΰ<5S}oOZ|΀1)RJUp뗅$_e]yFvtcVUMtԟ"mf<}[sBغꃋF]+b Twn;G^f+B뿻mjy}7M{Ts*^`_~jvQ?s7]ywo^IK@+ *o{~F=bk5kשLo1cccilplXdWYvs_>?Kp3"(  "w:.{5 B( E| >4Ѧ+gpn`몵K3.xr`Fiw]kޱ9t@L)eo\:O c- {yM}|]Ӥ~7'}:%{ĚT0P HَrH BCKLH6LL&UH":V,"eo05!pTG Ⱦ+ؾ];RRsT!@D k"-7+Z\bUٚɆ)4eQy${|9/ե{AfF8LUhg;%<c%eM>+1Ð(rJP5>OrbaԄDwi^6JT_L2=q% i^BMݭ|n y(!$ m9PZK6g 3ޜrιoyi5\Më4 ˓vt ہЎX|٨o-zg7ק$ WRBcp 0$pxDQ Od1īnP*ZhZfߎ#(̱jRdxM?n ^1V[INo/Y]W7I r3Lt}Lkk޶0,޼ұg3E&"UI: Ycx4 (A/*ti ┼$) +Nʛ! 3Ě|$7=9ZU^AvZ$+8tXMVBk6}&9J F[^w5w~W>[w[S]TyK 86& ꖊepǚt[h$[_ױLyPZoS!Ո3u`׶O c-0gܺ.V(1#4[sT%>e8 $Kq5qM _xx}&8(A(0= 熎ULK9;S0t7M ׿90/!+\ n)rE >wh$,2]>E(tM:#u'|+$ig=I7^Zد~<1V )Pnt_2DGtKs%@e ,F$ӗ7YxS=5BXhD!%DXBL gm1s͒Q{3u͕BJ`fA冄vn \ZB!ءhA0{$(V@XEM II?+^QeIX0A@hw<{4C߷uSXguJ <|n46N}OC΃Hh ۱,΅ycE!NlO"JfZ =Ro➆む6Uhcvt#&XrՉ]xhnyHa`D'=5NA k{˄vKx/RʍnheA9+Qrc8)AR"@YX%*KX#A&Vvl"tocLV "T/F@$,X4OND]3N`1v}"HN0W^}M_3lS/ɵ'P |&cg(Jio>]KU)qlf}Ơ1cC@|ctqPJy<+jĒ{uT Z%9#@E^}!i 1N4(BqVqfj5_:`pMicH܂DX6n]>kE\?jO stMdZ6wiJ˔\cgxi@yۏs/o{2=q'D "+{vJ/D1N7Dk$JDxz^Rce:kan:/c4$rby^fvӢAy&d\9 Wkx9Mf<c,9 BDM2N.6{!8u%@)HcdgFH!0WiTQ 8.Ss,mbACǦ-kT|@D\14B`4 B@cσ]G}|'”ΨI}l}/E"Eg^^}эEkWlڡo\O?z%ry >+"%4#m{mܱ1NxNDHfsW_OC;)~fg,`[pm23cw>iX1.h"|7VkuH +lլ}]шCVoyw |7E/mVAg?l٨$e+ߜ8󦛟.:V9B"R+yJCцˀmsm;Hasdr2/4. MAu@)MH]aZp Uv{3[we麮D## @KK6cqjM.("W^E P<1NxwoǭyaTa\e4 $rT|/O#^vCLMIx 惩/3t~3=.FM$uCG'1 CĖ$q Zǘ2 4% D܀W_kqݍ.Q<1Nx 6֯%7C "JwOL_q 9ҟ`__x^.5PV(_WӚ7}S׭[^M~uY^/%ȷvs}W6畋hr-b[~b%[6ݧ=OM ?6;oߺ{˸ɳϨv^^"h^_ZT%c'|1n;#ޖۺEUHy߾]mR7\uʍUrhih/ٰ!*t){}I-kF'w`QZ*m7* '\zEmTb9 ܲmmkBy[jlھ>'*:BaJjS[۶f^obVmfTLԟ& +Rd&[. yhu9ʷ=o?yq';7kp0DD`ؽ] נbqt24ۨRQJ-_aJm<̀ M. cP098)R=NԊlB "a B( . ʁptFtXQl@<,GW!P@!vQ`|4}GT4"JzKؙ'F0{4Ļ`JC" ’_ H(SZ`W\#`DFH8btl'K8 P@UwhE{SQcr#;("jw%hT)yc )Pg~Irp?8R&JtiR!i׍QYZc1xrℶ@1y< ;s_A*ڟ';%6>8^/ZYϟ[ڶ)p7RP(.#+|{_ߦ>~{}Bpрʽ[0֯c3&=dpXc1'q /?싖_fRF "(|MF%ضb>*-("J0S+mTԶybJX3-顦Nܭ{MkL#~cS V!+FIi׿ }km)VIvq@]JvR>MB7L0L;xav)ؽj)uNsz=Rȋ5cxiR۹#oy7wwߨ Yۢ@PQ&N=q.5Y4(`X8oyKΜ>{y^~/⽷ NpVVn}*ִңuړ읯'I 1Fzi}"$^?fya4O9he7ơuhʐVo;o ˈ34?鏓ϭQqywGn|鵋Z0aM%U4>1 Ÿ !Ԅ-// al߾=72|W jDr~^zիWO)uԌ'hԤ+BD#4=ࢄ HD<cĊoFHp{>ضcKJ) *WIy5$(b=b{,ܨЁ1ߊs*`aBv260dcL8EDXJn{@Bk2c'%85u>b1p4c1Ǝ˿ ӼQ N|J)41N[`:iq6OݢEy-gsd1v H -˩Rƛ/^ڪNb0A9ɫe_?_|aLǭ#SWzNcsZ5`1vHЀӰi/lmG"*:yޗ^{QJNhlݭG qYa@9c i@p8lGz됞) >w ;(()c! d`1vJN@Q[GlG$TqFэ1"(Tz`q cĉPZYۥ%x!#@jievdoJ0'{OU-@Ӯ|sjR2cƉ4("K.f[2fRH%/WnK~:}YF'{tc$Jˌ8J3%b(O!H2c! GL$bs4|2x䈁1IvH#ϝ;uF%}h-hWWV:=H)E(%cP2$4d-:AfNvvo +"ZdKdN , ɝN5a1v4"![SۈxTpL r@@8"KHwr婜}IV((i YMr >WAnhnS#`0i6 G:AۭۡU@eI¬gSrD \" 8չc%ϗ?iNo] d;~}!Sp^6kJ`p$D g.v~5)"ED@DQXWG,;rȇ۷ekV(k\R5oד`")BMx}i>׈e{~x{4@i\ .Mh\DD"\+ѥix=I.D]3uM}{P|G,Ƹtp.J)1vF9 !0ڝf^6:u@ aEr j}/E$-Q;ڧC-Jx@J_%7Ǫܪ ̛(S^D3uy9 koW)p^e{󳣔;6.=t {欔:s8¨РקX+s_| Ft؅")a!1U6Zņke'dqW}hkjƢY=v/ȷ4S %W9a/a3߉ `;\$=,bˈlǗR1FPU.:@I4D(Z/>_Us{o^A84_߲[^TF#~+r&}jAAC%(YЏl_xƫnj0Qf#.9?=LZwGw|=u rIe$[+ M(7T4 /\U +Rn˴] vQ\:b݊s*23BmJه .tlBVaZ mIt#R$??W.r=ίw 5/Od>:! 4PhQ]IJN8+ ѼАsi3v'@94Nm8)U)  @v],g_fƕe87k[oG VCVbupԮGpB&m+t!U蜻/\0Zm+GШ{"yN4JRl'?% ,L;yUVE`1w'4"ϭ%hT#EDGwZR,ѓԈHygkށu{i¤  hW[c.i N;LzqLٺ@^fqj^=%d\aMN#&bykO+gJWfN9GۆrRW}wHM808N#M.]3ܾC QJCCEd il/hvЅ͒<ݧyt]n>*c/K,AH)@6ѐ}P~ِ>+3zYPݡrH0yS|׌fUWoٷa$'ٰń4Wǫsvl&BZs6`N@M9~|&=8.!҃He wϯU㶇' t=֭oPiB ;0g'4"r']^, C&vHg m{yuC3GDd<ဃ ?O.7:m66)T fmd"۟ Rd&MN@@ 2p\l;8(nMJe]b /JiT2,܀'ѫұP I v"6^!+"35a_UMOeBr!:pWOYRB`4  hKTVA0'7P;QJֹ Es "yc:":! )z=fp- &A "($ϋ}V8zU(Fb? $ FʴmvDŽ:ժUSh0J4,i _;h_ims*T drl%zQP#H8t-g5xȘZUl"\% cC@ OzAS椟 hW m%}>sndnu}FqOX 3 &n8A($N :%|[sdJSTg1J4"J!99eQk -lG晀&$NAmۀБ cgR3_iЄmUB&'ҥe, KPc*\E[{y:f qc `ԕO4qǔJ.x랧~ jC:l<.tcʼn "GuKZ%yċ|qCS>yo::" jՐj wU Nic r"A[vߺ>,e97_xq\4c]!)!n樋_j;p_cSDH.ȎDеC{D*(5kKb_ͲfR`Ù0z4d1vV:ѵ J)*L[G!7;N.]{ k&`Nd_DU+igkc|'( l'.5 .{ mD8Q! WvϻQ@`1v`@D.!])nH0țh3co|QJ׸=/Ѳ͋O)@iXc w'}NiEv|GrK8M?e9Bެ`+*sYz1)q"!b$49:oZ'כlPG|+ARUPD /M"((EcN$h@HiҶ^sPR ?d޳osM @Cs?ioj_W`1VbKD ~?R  C Y G @4hۡ/ hS$Wb1vJx c\ނq28C->@܍c)%28c:4D(%q1No{H)@!<y@,o81c%Ӊמ0\keQ&D "M|c;BH8 ۧO3c'Ҥ=ˇ}ǚ<[:IM+8_l+>[-mJuvָM]رweBcp'4()/ϫ~yWFTqǫ/jsmmvsl,BKa+&|ǿ8u#cD'+{\ss Z )nt7gL[v|_ۣG`>˛@nԱ`9tv_ c%ۉ!EoV$QMxv]l嫕Һ(8{[JP:Yc8lU)SVyGdPrR ڵש:F<;J"B*wإ}?'gf1V`iDV4bo) 2Sp]u?ިszE)ch'^ wB4E׹w30+R㨳=c{pK B}1X|xO)m""!8!c1V`@D.LDCulG"ХPrWc1VzPilE.rо;W&z4G8&MU˳{]c1Vҝ`O~@zvFAf-$;9Y{mR0cÉ B` h׿ӇjQ;)l+@Dǡ×'69kܼ1Xiq:"B{_~02(E.{52!4PYމ1+eN+ cW_x:oۖgi 1XipyHj0u Rw٨];/x^2Eqc|'4 /gڹmxܶ~ݙp('z幝&BY^1+NhɥC>>/33K]VTh+.߶~# 6yņac' %+sn*` Xk(cR4f.L8>+6CvRIO.; "د Կ+cR J0"EVVqkG @ԕ!T$"CDc1vUcMp,$g/Y^3*Va+%c$@@d5Zl8YFrwovQ +f`"h`1v,GxnN~%mӀm)鐭R CRĠ06&@8At oz@\c+B<)%rHhn)"B!J2N%\(ăLS#-eE"*LEKRuFR 6~:>c077,DJ8 o9E#xb6(k"ezxn{ $BֿR/ؿI<,(z>x1JQ/DR]Y! "ql۱,\%"2|2F-!E\3fNF눐n IoW@ѓ0,݂}Wܲ0?A1;٫5H6Y߅鐦h{ @wG?t!kkϧ/h-[UG;#7c5Q nӭ:*nuGn_oY#"e$M&{Do c %wR3&=G"/#QK0}7MϹ6]_߅Jo#Rdx-f'?zYIP4SK_vlk1' ] E@$L#gLN 54Câ!!R]:v{ -9{lͭ"9@D(nh6XJ'YxqYfy(! _p?mZ":5C+~RG{LXZ-=)EK@T3#`ztqȁ"R7u)"(ڏŮ0to⛹g_^)ܭ먻%,,P=ǾcX-0識?+'גI)v^S?"1uB= (#9ګVI6 NR3MY[4 궫%QZuxeYt"ju{?N#y{ݱ7!1+}WKEBBbY'o +KO@8d&z {rtSݒ;޶/+#iׁ "-PHp./ײPbALåd)GPBQdX2ƄBjV~uAVI9a'nAt"sb(lҤGZi9x^bۺ=p9# AI/Ugfq'&"ݠ#ܾ-x`@ѐ+ӕ3v&WSd\mϭ4oϳ;w-JjҨ6`EA.5U c[$̭jv'WyEkG,w YA kԬp?g /}֝,2y;[7vDJ L~}RHg䡮vKeWwxȒ ;O_ w23[v O^z܈;s|<GornRGL%!u;^(Ϡnj޵iGLUs?=|axOze}{6UONrAJ*C9$[Rd\f,֯24ѐ7<3Fd?;y5oLw?lNڍݯ[yǗ;9m]Q_G&ܺi7Y&iŤ'_ HEHC$}S_`YZ䜽}Zt[ө[.\vo-v;7!_X7r!߲r7c 莱A }}OjdG) E+oxY߭-l# jؗ&vGCJŲ0qsP`(3Ra/ >b C:xlD鄈V \_?f@&O־wKw ]@[{sWkG֒S>J0}If`BĤ I$h3:Er}_CyF;p޽a.*iָ +WnGЎ#!@L;ؕpOf[ez5ijvYoqaSFH rے򇪞ۡaݴ5̹K^}疠 S7}I |e@^~sWЊI>LWcЈ+떏c~ِFm{7F꺛flk4sNyCoS[߱bW'XCfn߿Ϲ:,?;A`/|b?<-?dRe#|{;yθ>]*4/khum]M{׫ޣy)Pj:Pp 3S'aםӷ%w.ؼhօ)@~i#fj5np t¯f"m熅VtXI~77UY?8(ݧyaTOiwg<-+Wu}-/+| g kr7uluӾs_ؠQ\پCd{^_QܟVŶrdd$I'מ ˩xT~79Y,jܮXެK9PX}n4s{\h@?4Cq{1!j qa*8}NGBр Uh-2!Y2,dh'_7M"ׇ?^n:c:QwH F@b:P{\ TdNxP 1d ` 8qna`P&pP*Ŭ#pr';f=fS:x֓=bh U-6?K`w˯;n4bڸBJRBö;>9˩d_DhqϹK{ټǻE(8cUUOq$IB`߿!Ӧ[0) 6Ww^T7?k_/M3(\ڣizRG1JC:nք3vnYXxY45UroF\>u;֬P:^n٧3 vUב7kRf7鐦%%:yڰhWB]. Сo_Tijinܻ;ohK~xAAzM+)yFf͜3]ET;ҳ[Y7K3U9k~ޠ]AkƳc>Aa篪H+>۳j׋,.}S{gViY05q}wp%.m[e{vm.t]w}Ұ AM _Z+.ݙ a M2yr ڻwgm^E; Dlj8dwVFUO[hvV:fB6΢pvhE$.;ؑW~|T44?Z'Qn0"/kvuti 2wooTHCr0Ű|Su:.D$jrxW4 F,ǩ@p8uݬei i R#U.wv;[9)ǚnMU@`D թ1 KCnXncNS@:͈GvȲ8'թZ484b R518/eåEedF TyzU:('TjU.:AQ4fd( HQSNipǩTvC4fDMT%iE}Y5,K庁.(b7: ZKtoی,80L=+.g#Ft{뇝f%Ap(Piӫed$ [aNfDtԜ 79qnqҜ=eQ/ܥ pUnr"aBsk ,ˈY+n %{Gt{堛ycp_ U$vG"T~[X<!^5?=~JpD?=<᱗GK(SCJF7Ե'AX<1DcqkfT0JH k] ydE B c,;>VI:q "'a;8{ᏰHԩhFfj{%H 憦Y,a `*"qKKEmFϢ>TT܈!:4$BdI |#܁ë0$g2b5$j'ƀC! G^'Z'9B=h "5s5eM[0Vr;m8o$X{xya7"k_ NMQuۉE9z%$Ij Dy0Cq.Tw2`E-WB$Dtƀ"P}:$IaCNƄl=eV]W`p~$U央KDg걣( X$Ij4pA~6e춧=ߣ37}nE|~Itzi[{$tÜ^w<'I$ID KcդCQg ~rJ$Ɓ r{\6l1W=qkwh4c:#_@?W#u 4$Iɩ&sD0LѨY=] '!a_9_iv ƭ_aG%2ſ%$I$u5z:TBt2{AODu{yNصm=@V$Ijczr@D .FM؜q/jrW'a@D /)O~m7֧Ô$INj5=!u~[KAGA) f5JLo:''A u`dgG{i cђe ;$IZ&' KY0s;l-ůo :uԵy&9afӲi"Yx=ig{<3i>0$$ITkդ/XQ^rBTH0Ҿoo7o%nd'.d=$I҉&= Pݙq6XNZg_{vkc4Catc{ICb ;$IZ'"HҞ:A( !\D>9 j{b(ПPf݋ nO˜$IG֞#SUsRaZ_J9'"}침^)*I$l2J e /e%zu(HagxEdnsۀH" P>$I::qb1A0`G̳WQ^0/[+R너S I aޞ A A@DVsMPz!Cdg.Lm(>nBdjZރ8%N{hm{$I sVCЀtjn)NPaDU6 UT3@{8' FDÈH8}^'pӌTFkЅCDLa@v@Kc a.oGQЎ*Hvo{˅"Ә>f]uF޻-;nRT<#I$&jIp~~Oo !b%7u۰[a[Ԯyr,fy+ 3z=~[Ŏ{nK0%ф~COtoZ"qeTŒd8&L͍YLi٧ϧ13;%ivC^p&2c-7=5 Mn>fGg cTr1˪}U$I8dou70ݢ[7Iw~ENm&*|9!.u}<D c__h7^&}_>r_esԜ7ggr&iI 萒aS$sPݳnXVjBw';=cuИn$I: <#eq!K P *(iNɃoYx# Ӳ /"2z8[\<}>CwQ_3L;KW~ QvcZ435hJZ)zĬ(FN'JBWDD4"FZi y:w R@YX@mH+I$$!n urG]3á(: ٳ 60dƘqH* ̜0~]mSiN͓s8w8_[4Oϗ4řZr]#lZ+^B\NҶYǨF*ޑ*$5CT2 sza7mF\")qNfXdJVKH$IG&= \PW4f׭Zw3.cfIu;n=[ank97~*Է[4?RrhG;ay冊/|z_Ǿ{=q >zKYS(ݴsFϗ>qX[_}k_zBL.OR$C,0΅?[yR3Ml|9P C쭨joϞnGUO9kxT@ג$IѨIЀ\PrQ.ZD!s( Ku=y};&"dž'i٠[ +,^UDJף]ܱ@T7O[D3u|z\(;{iۻB`9OlvP>+3 =:nfzi9'W+k[$H9=͋7[uobEudHEleƥEuiJJI$#j2"r.EDLS-q RZan"q]E7kT "r<GdH{"*@* Vi( nZ8`D \`` 9b2=$*!n!Lc q @H==u!2a 2`GxOab}kZdpWnkc%I?T.MgO0A.DTx D"m#CGTk 8qA8|aX{9 pUFj!\`k5L@ WjDN^cEt;qjlE C7k:V$8G~Kc#VN(e xƿ9ݮyݚGWǸϦ5?啥4㞗,r]fM:UNzĐc$I$P0fS1t{xXE@=swz6udX cI$I:$h@Mo۹2BC1A 7ԥ Ƣp9l$FK$I'i@XTc~Wcp c1;rmƈɼђ$IɩQQȀo9383k{KRB@&Ψv$Iws7o@v ]3F$I#@}]µ*?t ,B 2B҅.sC$IFwC7j4 H` +oL :PZq4 $?$I:VHHX(<[ą \ U qag\TYQ-.7JU{S_7KB^79܉sn3o^t,pjx9I$$hN_pE%{ 8׮plΉšְHu+m[+o`0<%Nǥ7wݪ3K՗wUnrgBWO4u$uť5c8WUIT$< nw[U+K|.o^Z%vUn߶_G|^AEgР;K~IZ&'qg\y[z'br@LXV\wW_ӳw}lyk/PK;1#kv9]FMwX<\nwk7之R[&-僅 %%/l2puc75rs5!YͶNۨMI]ms]6sUvm͛9f3o <-A;=\tdb#dzW]oՔ&:y6&V~3+?RєFi]{?_kؽ>mWtf8\$~@z 2V2 l|{<$IRmu S.C " b@ ̉7|SyTԪ2DLμF?]qtW5'a:{+vnxnkq'#(7*bY%⑷l^W(;ogu&""2iLTP;#Qz{uwNd˰ IZA2 2( =.|k +פe%51"S"`eIWX S5%^h`q9KMYKsYz$ n bLaH1W8uz)ъ1@*R85Eu*N LS!h ąiZy$I: "eFcF,d2DV ;-wx1nF1]1kdDb4`(bPuun\)~Δ_R=';~^eʽ{ʈLp0  2n #l!1L4 `–E3ɟ"oh6M1k-iܠéп`$IT5i߼6&_ȧji>`_ݾY?yX0 vw=jt+tGZyB+?VMԡӽ^}O5T*pϮׯ S'}vpA/ziw|Hq ~xF.o,U\yON[ײѝܙ£|+seڧ2\Ӄц:cwxU~<\fIָڜVYZ$IQ1Ⲙ˭YVqǶw3sticEV[l\WڕFʃ#[%HKNbtIQGB"e嗃 [ߛ.CSQY:u[eeQi*#pcaIpEq̤h4ݥ) [ۙP*W|n( @w"TUfoxJ]a蜓+/]M?<܌P$Iǝ]{4Ї)N|jf@aQZSL UF 2IɝOO##5CBL?UGF󬪟12,n) ,.Pۡ!"X13\3ӗX?Ìw34 < lVSXbvƐHmCar# {%Fd X РksbøIP `i م%0,B7,:d"` z$vXfFHn%@UHCbU"@0t#aTB,b ʊFW*J;$II9p Qa%.< z":ԮwH)QIVxnr"IB@Aɜ'Lg ɬPNҾ. A-OD{jljΦBO$I؂}5ٕ"@s&j{%@pfҚWtп#v-XST?8ɠ+e9@dI$٘"NP l}=vOu<ɉiɉIZAQ(I~F=N!I$r5)8Leާ&=% ,"QK;Sk/[6y  -  I$INMp9Ee./Tc7 5#bLNac<|IܱC7M)$IZ!)gmap[F@O[wT$unG,p U4;~C, I$Z9h@D0[ 2DEQH(ڰo%Ipúy;Jfvǻ5$ItLdߋHؒ$I҉zψG^䅲 $ID/i]$"IsN$I$Tà%Sgl) FDD9U] $ItI <8tt7N]_9k׭NGފMgr./~Ź,-I$I' GCȽr t׳/\q{nwu"a-I$I' DP=cvn1~k@ +iUg0iq!d I$I'Dt-];U_'-B t (I$I' ϳή>p~oWYcą I$I'  6m?kDSCR=^9R$IN9h "E"E0ߣ]Ƴ}1QМe_nOg˫\|Ǩ/-5e I$I'40`,qNc7aN^zN e{^{㷘/^y{KMMc'o*I$I: #>O"d [{wZڿ a'Pb(%I$B # ( 2Ɛ[1"8 22"2N$ID%+@hT=Ui,nYfmIDa˲w[$I$oD(2T. H]vկ_?11d$It@9JQ$I|7A =ͨ I$TdO$I$IGvK$I$#I$I $I$I:*2h$I$ȠA$I"I$I $I$I:*2h$I$ȠA$I"I$I $I$I:*$I$IcUe*I$I vn$I$IF_vYu uǻI$I$1B@ HMJ8M$I$߈1 աbEex7I$I)"@$I$(2h  !9bSK A9 쐅DŽ {32{G ׅiЁ)qki!;,q ;(Gq @nQ#|hPW"!>99 A ' n;AUGgǻ҉@ R !cflKBnݛ  n&I `@c=ꈡbyKIԬݻǹIf,9cV\G՝FW)-9cn,QSNn֐dFҟq|8'e֥X,}IDO#ADXyUE24V34z*Veq)+ؿ_{c<pXL%DHB^bI.h2]S]}v$\©OO} }/'oADB5-ہ%Ds.;O$rYȹ악ޮD"|hš[#"ιi,^2~c"Yuo޻i1"aiV5^HWy8 _""D|헯+<7y8^|! Nvn#D$,>~6}X3DG6TўO|7Ioܓn\n!DDՙ[}i0񋈈[-w.l7eqcBwup5t>#"aZ4!aO%MS/$yU~IlէNTD{[~>s:!o -jnMاƟňHXM֠?-v; ږF.I:Tsp>#fm^:>-쫅`Lc@ 2@࣢(HBToWQ Vɖ;k`{C.f_dn0#2p]*c([~9"cU;H L ~\wuނ~?|0#oD٧4s?yu=O|m+zew=&[\ ٶW͙4+ EF:|u0!8[1}ڢ#(7O|ӏ/ꋞVvK G ZТ`3(s/ V=aq{"TΉ) S0mTVf!ժ΍;}CMOoDLQ2EA{"CTZd%BD*'\y/J2ݶ}^o "rǽ7cNq2䦱wHwW[cUDaըH:tkTuECۛe  [~D V=jH}âumX8 "z3k ߸lWY{FrѨmQ#GNmAe0foE??Q߭ܰWm[G*K&9oJMǟN>4!"KNHC>,{;7c&l- "A AD[6Lݴ9Ckc𞆗o>ЄQ}!"=PzNCV ń0yvV"ϓeQтI|t#& #R`h~1" |3E{aU;ZuJy@k]%k!H޽;XWX>'Hr_Noqg~[%"""Mq6),ԉ]g, G"} 65?LݜODђƎ'g/ꆽ}3\v·ƊgDFv'/f̎5Vzzu?O }0!"nYU&Ofu) kžA5eU};wF`qvƼ}E ֯ V,45j+^5Eߎۻ5_Ti%O_$"3Ry z۲e,иm_;FDio^2vRQ^mk~/ޱraK5 ?xo*r,5j7M"Z#@=:ѫ$I 8w< >b1]{m/K*D[.:i.ҚO_,} ^-)9 :)`m6U:Ip/TDDOp7US?.kD89Y*@Bˋy w~N/6Ck./r6m僻_M ounN~L:L"lɭʈMz{"̪{"yߕe>" ;r "ڻhڱ>0Ddt!]ͼ_7|7| ϧBnJUO'\_Fy9gDD|f0$ PU`Ѣ7<iwӗ ܴ>WGڿs>C,ۧ Ӳ o~V"}O:2?>m?_rQ-wS"2}CDg:!ty6rwѼGFfC]qüAj ѿgƨtݳn^يUT>9%_'XE%-~~C -ˊňhcW?ٺS|[o뙾{]ŷ{7ϸC %%eSoDDSۿؠ[2nte}M26<-aQZ"h魇?ӎ6Sxܟl|C2b}a_6!O'}!-u0G?DDk?{qaIS0b}k?u(q)/V? 0ՉDMxH ̲L"s0~F"Dm[m߾=o޲Ғ}0o,5[;QlokɊ{^6z;"*0;Y7TAן?...4y潥e{\У ߩ4HD: \%"*7QύW峃t̢,ܻlO}nZDī i73dED-#<kڜ3,㇫0ۈhw>ZL G:J "ZL(HDz쪻nv"O#"Z>Ѳ^KV^8߁q֮S~M2n٧`""+ *J* 4&i/X}/8݋++7r $oH;YvW\9 vER3~,~/uHlT_X?jģ_>!=MS}eXpսeT;kooj74H4Ly>a^UEsh 4ꕉBDOo<}e૩ Aduim"zlvYW&P$}W=1Un|4>t1@{޵IG36p舞;:.ID+Fڼs޷yD4`]DdYG ӫ}F3}¢4V\36H޵+'Xa74 Qu_8 :;U=\%+[?>x&"3\[(xp\4q U8LߴMyL")ם>2 M"8v w'ݶܢ˲ QG`/. "?y-'pt"*ٲfܹ;.:c'ZՃ!nG^UKL~`3J_GxaSU"%BDߝxX.:Qɪwn˧! ]Z=ۺt`V= Z4'u&#ace}`2n)^:6Y]D;ȝT1وv @q Z¡#*>E[nt&z4#aڃ+XJ7&溉SOd{^Ѹi"Y ӲCs W̚rٯ1:W'61cM[g{wf'0f*ՙS`#dMn67lÛܤ㩏?vq`8@nOth|ZYΆ*1\Nsav>ꑱ Q3գƼ;';> XŎ5޷dʼv=oZ\/BL VH33@4>`^1^#--wy=Yੈ07;S>!ncIC%"֖,La+}ؾˋ-n}ީ Yg*@M-@D!Ҫt%t,d e܁zǩ:Y zgcL]o( *.w;^@Ez=D)-C- 8Uo:k \p.2a @~gv^V}䱡=:wjtr#0gWMW"Q \n7T@F+)G~2Ĩn q^Uu[nuK 9)C#U]5iLҝ]Xڡsř。cj^fu9 X&'xĕ7w0fY\M!8ٓyU~ ȴu۵<?mQApu蘖'!0}ph%xMh/TM3Oe/϶<%R tȺoP/)GUӼk>i ,H==v^pӶM:&\8;@xw9HPGJV/؉6}<}NUs$u}Vl"BuPiGgY]nЩ 8uk(QUP7JL 1B1*7Iz4Mܠ;)Ӳch~MŠP\Nt&:U'd|M9'wZ8ϻ.snÇzz7Tý/43w'eֻ]M{S9  r? ?F_#լη [1t ?7IP$4vJ*K.KdӓF( h@.첋6F`E[xY]d]Nh!}vѥ_vivu_ -^PSVXydy׬^#8=0gXOk;něz2Ep5m՛Ϯ_9:ԢS1Źe  =K_xl|D #e:}3S~m¤zm.& ""9! !;SQI)A#Y?,! .@ lkvhnU]vJdvzg]Te͚J5,ˈB :}{K@X\pnIwZ Z^~5*w7f1_vo0/o|%foz4bQ=8%ѬGMmԞU +?٥@4 V8P%|Y?ѷW/顇\hX A\aQS&p67mS@HND?{x |}՝1ƌ9 ]'E\"&6ɇ>Vz Fk2嵑E.|V/ŴrO!ή(13^J|.ioc3mUᜈxvW/aN̟ Wr'}Vvϧ'1V,)`~ZIHzUyQ(ٹ"?j&Ѻ^*q"K'];`x7}!ϼgyGDVe75aZM{ga'Oq,'\y@H@v0ꃶ^Ȯ?<+/n~S m{Ovyetxm[| ۲gO,}2|+9_E*#"pX3{÷gzn ͫ; |SU^yQW7jכּo' )%|siAKnUwDpct{9%Nmg6QM.qk}vZ5Q\^^|PP7TjB֞jHأeP?pYeq?JAv$(+9&'' /t # INBB9h$`@RI  ""7Ͳr7.z  XYaKJL(́lKn?.b@DXsW{PyE>KNF\R „a !ǐQCu2ЈQ) NpdwW)exxdTn8gOueZi{tIp:La[0)ȪFXTԜNDܲtUI T1M^s>ipWC/#>QqiIQ0[/CA0V~9@M ?'GFT˲L* !Tա 0S""D$0LUMuhM6؎ ٱsfs33TZ~tB"QH5!۪:ojp77T]puW:4 9<"/;ߎs(O_t]λQ5JbǵV߂ yO[^:]+)(?P:.*)$") dirre = re.compile("^([^%s]*/)*" % re.escape(os.sep)) xmlre = re.compile("\"(?P[^\"]*/[^\"]*)\"") datere = re.compile("date=\"[^\"]*\"") versionre = re.compile("version=\"[^\"]*\"") timestampre = re.compile("timestamp=\"[^\"]*\"") failure = re.compile("^(?P.+)file=\"(?P[^\"]+)\"(?P.*)$") def filter(line): # for xml, remove prefixes from everything that looks like a # file path inside "" line = xmlre.sub( lambda match: '"'+re.sub("^[^/]+/", "", match.group(1))+'"', line ) # Remove date info line = datere.sub( lambda match: 'date=""', line) # Remove version info line = versionre.sub( lambda match: 'version=""', line) # Remove timestamp info line = timestampre.sub( lambda match: 'timestamp=""', line) if 'Running' in line: return False if "IGNORE" in line: return True pathmatch = compilerre.match(line) # see if we can remove the basedir failmatch = failure.match(line) # see if we can remove the basedir #print "HERE", pathmatch, failmatch if failmatch: parts = failmatch.groupdict() #print "X", parts line = "%s file=\"%s\" %s" % (parts['prefix'], dirre.sub("", parts['path']), parts['suffix']) elif pathmatch: parts = pathmatch.groupdict() #print "Y", parts line = dirre.sub("", parts['path']) + parts['rest'] return line # Declare an empty TestCase class class Test(unittest.TestCase): pass if not sys.platform.startswith('win'): # Find all *.sh files, and use them to define baseline tests for file in glob.glob(datadir+'*.sh'): bname = basename(file) name=bname.split('.')[0] if os.path.exists(datadir+name+'.txt'): Test.add_baseline_test(cwd=datadir, cmd=file, baseline=datadir+name+'.txt', name=name, filter=filter) # Execute the tests if __name__ == '__main__': unittest.main() gcovr-3.2/doc/guide.txt000066400000000000000000000265441235605057100151200ustar00rootroot00000000000000Gcovr User Guide ================== :doctype: article :cpp: {basebackend@docbook:c++:cpp} :makefile: {basebackend@docbook:make:makefile} :numbered!: [abstract] Abstract -------- Gcovr provides a utility for managing the use of the GNU http://gcc.gnu.org/onlinedocs/gcc/Gcov.html[gcov] utility and generating summarized code coverage results. This command is inspired by the Python http://nedbatchelder.com/code/coverage/[coverage.py] package, which provides a similar utility in Python. The +gcovr+ command produces either compact human-readable summary reports, machine readable XML reports (in http://cobertura.sourceforge.net/[Cobertura] format) or simple HTML reports. Thus, +gcovr+ can be viewed as a command-line alternative to the http://ltp.sourceforge.net/coverage/lcov.php[lcov] utility, which runs gcov and generates an HTML-formatted report. This documentation describes Gcovr 3.2. :numbered: Overview -------- Gcovr is a Python package that includes a self-contained +gcovr+ command. Gcovr is an extension of +gcov+, a GNU utility that summarizes the lines of code that are executed - or "covered" - while running an executable. The +gcovr+ command interprets +gcov+ data files to summarize code coverage in sevearl formats: * Text output with coverage statistics indicated with summary statistics and lists of uncovered line, and * XML output that is compatible with the Cobertura code coverage utility. //// * HTML output with coverage rates indicated using colored bar graphs. //// The http://gcovr.com[Gcovr Home Page] is http://gcovr.com[http://gcovr.com]. This webpage contains links for documentation in http://gcovr.com/guide.html[HTML], http://gcovr.com/guide.pdf[PDF], and http://gcovr.com/guide.epub[EPUB] formats. The http://gcovr.com[Gcovr Home Page] also includes developer resources (e.g. https://software-login.sandia.gov/hudson/view/gcovr/[automated test results]). Gcovr is available under the http://www.gnu.org/licenses/bsd.html[BSD] license. The Gcovr User Guide provides the following documentation: - <>: Some simple examples that illustrate how to use Gcovr - <>: Description of command-line options for +gcovr+ - <>: How to install Gcovr - <>: Comments on the past, present and future of Gcovr [[gettingStarted]] Getting Started --------------- The +gcovr+ command provides a summary of the lines that have been executed in a program. Code coverage statistics help you discover untested parts of a program, which is particularly important when assessing code quality. Well-tested code is a characteristic of high quality code, and software developers often assess code coverage statistics when deciding if software is ready for a release. The +gcovr+ command can be used to analyze programs compiled with GCC. The following sections illustrate the application of +gcovr+ to test coverage of the following program: [source,{cpp},numbered] ---- include::examples/example1/example1.cpp[] ---- This code executes several subroutines in this program, but some lines in the program are not executed. Tabular Output of Code Coverage ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ We compile +example1.cpp+ with the GCC compiler as follows: [source,bash] ---- include::examples/.example1_compile.sh[] ---- Note that we compile this program without optimization, because optimization may combine lines of code and otherwise change the flow of execution in the program. Additionally, we compile with the +-fprofile-arcs -ftest-coverage -fPIC+ compiler options, which add logic to generate output files that can be processed by the +gcov+ command. The compiler generates the +program+ executable. When we execute this command: [source,bash] ---- include::examples/.example1_run.sh[] ---- the files +example1.gcno+ and +example1.gcda+ are generated. These files are processed with by +gcov+ to generate code coverage statistics. The +gcovr+ command calls +gcov+ and summarizes these code coverage statistics in various formats. For example: [source,bash] ---- include::examples/.example1_gcovr.sh[] ---- generates a text summary of the lines executed: ---- include::examples/example1.txt[] ---- Each line of this output includes a summary for a given source file, including the number of lines instrumented, the number of lines executed, the percentage of lines executed, and a summary of the line numbers that were not executed. To improve clarity, gcovr uses an aggressive approach to grouping uncovered lines and will combine uncovered lines separated by "non-code" lines (blank, freestanding braces, and single-line comments) into a single region. As a result, the number of lines listed in the "Missing" list may be greater than the difference of the "Lines" and "Exec" columns. The +-r+ option specifies the root directory for the files that are being analyzed. This allows +gcovr+ to generate a simpler report (without absolute path names), and it allows system header files to be excluded from the analysis. Note that +gcov+ accumulates statistics by line. Consequently, it works best with a programming style that places only one statement on each line. In +example1.cpp+, the +MACRO+ macro executes a branch, but +gcov+ cannot discern which branch is executed. Tabular Output of Branch Coverage ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The +gcovr+ command can also summarize branch coverage using the +--branches+ option: [source,bash] ---- include::examples/.example3_gcovr.sh[] ---- This generates a tabular output that summarizes the number of branches, the number of branches taken and the branches that were not completely covered: ---- include::examples/example3.txt[] ---- XML Output ~~~~~~~~~~ The default output format for +gcovr+ is to generate a tabular summary in plain text. The +gcovr+ command can also generate an XML output using the +--xml+ and +--xml-pretty+ options: [source,bash] ---- include::examples/.example2_gcovr.sh[] ---- This generates an XML summary of the lines executed: ---- include::examples/example2.txt[] ---- This XML format is in the http://cobertura.sourceforge.net/xml/coverage-03.dtd[Cobertura XML] format suitable for import and display within the http://www.jenkins-ci.org/[Jenkins] and http://www.hudson-ci.org/[Hudson] continuous integration servers using the https://wiki.jenkins-ci.org/display/JENKINS/Cobertura+Plugin[Cobertura Plugin]. The +--xml+ option generates a denser XML output, and the +--xml-pretty+ option generates an indented XML output that is easier to read. Note that the XML output contains more information than the tabular summary. The tabular summary shows the percentage of covered lines, while the XML output includes branch statistics and the number of times that each line was covered. Consequently, XML output can be used to support performance optimization in the same manner that +gcov+ does. HTML Output ~~~~~~~~~~~ The +gcovr+ command can also generate a simple HTML output using the +--html+ option: [source,bash] ---- include::examples/.example4_gcovr.sh[] ---- This generates a HTML summary of the lines executed. In this example, the file +example1.html+ is generated, which has the following output: image::examples/example1.png[align="center"] The default behavior of the +--html+ option is to generate HTML for a single webpage that summarizes the coverage for all files. The HTML is printed to standard output, but the +-o+ (+--output+) option is used to specify a file that stores the HTML output. The +--html-details+ option is used to create a separate web page for each file. Each of these web pages includes the contents of file with annotations that summarize code coverage. Consider the following command: [source,bash] ---- include::examples/.example5_gcovr.sh[] ---- This generates the following HTML page for the file +example1.cpp+: image::examples/example2_example1_cpp.png[align="center"] Note that the +--html-details+ option can only be used with the +-o+ (+--output+) option. For example, if the +--output+ option specifies the output file +coverage.html+, then the web pages generated for each file will have names of the form +coverage..html+. [[gcovrCommand]] The +gcovr+ Command ----------------- The +gcovr+ command recursively searches a directory tree to find +gcov+ coverage files, and generates a text summary of the code coverage. The +--help+ option generates the following summary of the +gcovr+ command line options: ---- include::examples/gcovr.out[] ---- The following sections illustrate the use of these command line options. //// WEH: This section needs to be added to explain the options that specify where files are located. Controlling Coverage ~~~~~~~~~~~~~~~~~~~~ TODO: document options that control where data files and gcov files are found and filtering options for them. //// [[installation]] Installation ------------ Gcovr requires virtually no installation. The +gcovr+ command can be downloaded and used directly without installing additional files. If you have https://pip.pypa.io/en/latest/[pip] installed, then you can install +Gcovr+ from the PyPI network servers by executing [source,bash] ---- pip install gcovr ---- This places the +gcovr+ executable in the +bin+ or +Scripts+ directory for you python installation. The +gcovr+ script has been tested with many different versions of Python: 2.5 - 3.3. Note that this script has only been tested with the CPython implementation. [[discussion]] Status and Future Plans ----------------------- The Gcovr 3.0 release is the first release that is hosted a GitHub. Previous Gcovr development was hosted at Sandia National Laboratories as part of the FAST project. However, Gcovr is now widely used outside of Sandia, and GitHub will facilitate the integration of contributions from a wider set of developers. :numbered!: [[acknowledgements]] Acknowledgements ---------------- The following developers contributed to the Gcovr 3.2 release: * Sylvestre Ledru * Kevin Cai * Luke Woydziak * Nikolaj Schumacher * William Hart The Gcovr documentation is generated using http://www.methods.co.nz/asciidoc/[AsciiDoc]. We would like to thank the following organizations for providing web hosting and computing resources: GitHub and Sandia National Laboratories. The development of Gcovr has been partially supported by Sandia National Laboratories. Sandia National Laboratories is a multi-program laboratory managed and operated by Sandia Corporation, a wholly owned subsidiary of Lockheed Martin Corporation, for the U.S. Department of Energy's National Nuclear Security Administration under contract DE-AC04-94AL85000. [appendix] [[appendix_C]] Testing Gcovr --------------- In the +gcovr/tests+ directory, you can execute [source,bash] ---- python test_gcovr.py ---- to launch all tests. By default, this test script executes test suites on a variety of code configurations that reflect different use-cases for +gcovr+. You can execute a specific test suite by giving its name as an argument to this test script. For example, the command [source,bash] ---- python test_gcovr.py GcovrXml ---- executes the +GcovrXml+ test suite, which tests +gcovr+ with XML output. To run the +test_gcovr.py+ script, you will need to install the https://pypi.python.org/pypi/pyutilib.th[pyutilib.th] package. If you have https://pip.pypa.io/en/latest/[pip] installed, then you can install this package from PyPI by executing [source,bash] ---- pip install pyutilib.th ---- // vim: ft=asciidoc gcovr-3.2/doc/images/000077500000000000000000000000001235605057100145145ustar00rootroot00000000000000gcovr-3.2/doc/images/icons/000077500000000000000000000000001235605057100156275ustar00rootroot00000000000000gcovr-3.2/doc/images/icons/README000066400000000000000000000003421235605057100165060ustar00rootroot00000000000000Replaced the plain DocBook XSL admonition icons with Jimmac's DocBook icons (http://jimmac.musichall.cz/ikony.php3). I dropped transparency from the Jimmac icons to get round MS IE and FOP PNG incompatibilies. Stuart Rackham gcovr-3.2/doc/images/icons/callouts/000077500000000000000000000000001235605057100174555ustar00rootroot00000000000000gcovr-3.2/doc/images/icons/callouts/1.png000066400000000000000000000005111235605057100203200ustar00rootroot00000000000000PNG  IHDR s;bKGD#2cIDATxU 0.)Bft6#dH('XW 9cAM-!d>0(*?/c}֮5uƌ:x,TCtEXtSoftware@(#)ImageMagick 4.2.8 99/08/01 cristy@mystic.es.dupont.com!*tEXtSignature58a072e070da22f6135cbd3e414546f9hj!tEXtPage12x12+0+0m}IENDB`gcovr-3.2/doc/images/icons/callouts/10.png000066400000000000000000000005511235605057100204040ustar00rootroot00000000000000PNG  IHDR s;bKGD#2IDATx%!C#H,N^ [¶p\%${;/yI@l\\ySM}i㎋suȌaX̠ eڭvGj!=dR;?ݢCb kCtEXtSoftware@(#)ImageMagick 4.2.8 99/08/01 cristy@mystic.es.dupont.com!*tEXtSignature386e83bb9bdfba3227f58bb897e2c8a5+ tEXtPage12x12+0+0m}IENDB`gcovr-3.2/doc/images/icons/callouts/11.png000066400000000000000000000010651235605057100204060ustar00rootroot00000000000000PNG  IHDR ˰ pHYsttfxtIME-'kM8okǖejYVǗ˅F C3IENDB`gcovr-3.2/doc/images/icons/callouts/14.png000066400000000000000000000006331235605057100204110ustar00rootroot00000000000000PNG  IHDR ˰bKGD pHYsss"tIME x8(IDATx}=@O2\ق۰X"Y;@)lT!H!}=CDZ;9V DDDqf3qӉ~qXkTQp8|.)mUUz~9EQh 0hQE|jǣ,b)ntnwx<.|q~IENDB`gcovr-3.2/doc/images/icons/callouts/15.png000066400000000000000000000012001235605057100204010ustar00rootroot00000000000000PNG  IHDR ˰ pHYsttfxtIME0 JtEXtAuthorH tEXtDescription !# tEXtCopyright:tEXtCreation time5 tEXtSoftware]p: tEXtDisclaimertEXtWarningtEXtSourcetEXtComment̖tEXtTitle'IDATxu1˂`E`59-AZ[֜šhr /h1A-"6B||g":16BTDDD5dN\8纮.v,K墪eYO`8FQ\.kZNjb2H.1"l6{a0FQ\~^{<u6A|>OVefT eX,vrq?j x8mȔL?IDATZ9皦lN|9Nn+bbW*z)r]z=- !HIENDB`gcovr-3.2/doc/images/icons/callouts/2.png000066400000000000000000000005411235605057100203240ustar00rootroot00000000000000PNG  IHDR s;bKGD#2{IDATx0 D?44,5, ]+fK UG{ukS@cBSChS{2y4Cms^% D+OJ)}:T5`/CtEXtSoftware@(#)ImageMagick 4.2.8 99/08/01 cristy@mystic.es.dupont.com!*tEXtSignature80eae529c94a7f47deccfada28acb9dfo tEXtPage12x12+0+0m}IENDB`gcovr-3.2/doc/images/icons/callouts/3.png000066400000000000000000000005361235605057100203310ustar00rootroot00000000000000PNG  IHDR s;bKGD#2xIDATx%N@ 4^0+ F``a+&U qXҠq K ]pq˟3&=ۿ-#S:bmR&jQ5cLCtEXtSoftware@(#)ImageMagick 4.2.8 99/08/01 cristy@mystic.es.dupont.com!*tEXtSignature80bbda2726ddace8ab8a01f59de2ebdbutEXtPage12x12+0+0m}IENDB`gcovr-3.2/doc/images/icons/callouts/4.png000066400000000000000000000005311235605057100203250ustar00rootroot00000000000000PNG  IHDR s;bKGD#2sIDATx!0C#XdeeP"\o+{%leʰ!b$ci1 q dCwCmJV$6huTj~<_²|㣴 KF6[CtEXtSoftware@(#)ImageMagick 4.2.8 99/08/01 cristy@mystic.es.dupont.com!*tEXtSignature9f82fcac9e039cbdb72380a4591324f5vtEXtPage12x12+0+0m}IENDB`gcovr-3.2/doc/images/icons/callouts/5.png000066400000000000000000000005341235605057100203310ustar00rootroot00000000000000PNG  IHDR s;bKGD#2vIDATx0  ~+_BhIlgvMZmmwb$|Sq$^%)%YP3]2Qj%|#[7/B_CtEXtSoftware@(#)ImageMagick 4.2.8 99/08/01 cristy@mystic.es.dupont.com!*tEXtSignaturefe690463379eb25e562fcc8cc9b3c7e0߲9tEXtPage12x12+0+0m}IENDB`gcovr-3.2/doc/images/icons/callouts/6.png000066400000000000000000000005431235605057100203320ustar00rootroot00000000000000PNG  IHDR s;bKGD#2}IDATx!0    FaPXXj' nn󩺵 oPHl\BuNح!i`d'נ,˖eԸgNLL< V?s8 YCtEXtSoftware@(#)ImageMagick 4.2.8 99/08/01 cristy@mystic.es.dupont.com!*tEXtSignatured25d7176d67a038afc1c56558e3dfb1atEXtPage12x12+0+0m}IENDB`gcovr-3.2/doc/images/icons/callouts/7.png000066400000000000000000000005301235605057100203270ustar00rootroot00000000000000PNG  IHDR s;bKGD#2rIDATx%0 OV"Y!LO Hd+H퇓e _pDlC0T+ʫ+ VAjݓ{O9lsLGIz>61GVSCtEXtSoftware@(#)ImageMagick 4.2.8 99/08/01 cristy@mystic.es.dupont.com!*tEXtSignature298368142ac43cebd2586d8d1137c8df&9tEXtPage12x12+0+0m}IENDB`gcovr-3.2/doc/images/icons/callouts/8.png000066400000000000000000000005451235605057100203360ustar00rootroot00000000000000PNG  IHDR s;bKGD#2IDATx0  v¬a` 544T ?ݻ/TܗW[Б!Dغ[`T3(fpgc31ؿ.0>_ +U99FbCtEXtSoftware@(#)ImageMagick 4.2.8 99/08/01 cristy@mystic.es.dupont.com!*tEXtSignature57be19505c03f92f3847f535e9b114e94kCtEXtPage12x12+0+0m}IENDB`gcovr-3.2/doc/images/icons/callouts/9.png000066400000000000000000000005451235605057100203370ustar00rootroot00000000000000PNG  IHDR s;bKGD#2IDATx!0 GFVbJ,WX ^YkTb++#{?/Yٗy/j!Rj+~ E#y@!s.gEOr /P8bCtEXtSoftware@(#)ImageMagick 4.2.8 99/08/01 cristy@mystic.es.dupont.com!*tEXtSignature34623e5e4d48310e409b280afe24760214$tEXtPage12x12+0+0m}IENDB`gcovr-3.2/doc/images/icons/caution.png000066400000000000000000000052561235605057100200070ustar00rootroot00000000000000PNG  IHDR00W uIDAThY{Pݻ" `4Uښ&Gmq|T0WHmPLN@ DjfqdET$D({_={w" Lfzfvv{eDors|ݾ_Ţ~Z[[T|>U54ƍL?Г !|Mr\kƍ4ؿ?ɲĐ,R__O1бcRrhϿEbӦMCF#i8QR?GjۻogDزeːɵ$I د@GIXJʅRڼeO"u;&1艻w5! ^9FʅRRJH=_Lʹͤ-"R5yQ$mvG$7x/^ ιٷhDԾRuk ~'Oi~߶^II V^=;L\SS~ɼG"8:%Q@B*Ic@ 4YYPPAJdUUUֲf=];7@8XqnLKSؘ< > ({4݌p8(//o`语UTTDʬYS1R/5Q^闤4%T>)'(tv.X'eBeeekW_͆`H4P0j b\4&̼렸 <ɶc2~Hc"<9F"77;w> SnneQWq >e3&-q^'躄Ln}6+WDUUmI$p Zn/gOY0aɐ> 0"em%T] ]p!Vm#?OT]]L-CHNKܥiК(7FH>|~?&^ 466b޽뒿uV VI&D*ȴdsw=<1.q ^x֏~l%[sa`0́ -PM|i{EV-čg3996xeUݎ&69xg+~8~sToXҤ !n rtt)wFĉ=fc\ش.9M7+R;^sDeffWn@ @DzգwįM{cXC7/ab ȈL;gOpj &bY~@[[w ݒi !Ԩ:E8dd@vMF >w*?ƻu#FfV+ǎ?3^ցEػo,ًnm<=>b/[oy>!!BRR8Ej"fL4 7n0صa6Nc8wHspJdjѩ 7Py=G˲#G`ʔ)p8ehLG.nvC?z}K>Ђ1&"gipJdJ\?}K{ DMM &MUUi84MUG3gN>msR8fgIR*9T:wg.ܟ܍6ލckes=6oތKvttB$HԻ&nG@4 ׯ_GVV>O _ F n0Tj7??zpݐe. $Aec$eBV >>ԩSJX=ypF8Hnz{~ҥXp! BP YY>3 irӧOr^5J{8e+8 ʛa̙3d@@QSQG*#`%!9p:(//ٳmcו5oZ'nm8ydXDdP_5wgIPTT >]و^+mzD*wbK7ohBw ,#''CPXBu*cdp8 I)yι$IBaa!^/5J}^~/ڿVnd\Ȳjx,kH[WJàF5Y$u$х(;={._GTB)ESS˗/Xn& pqJ3qʟ񙫯-[xmv"l:"\jzzzG\.siL677sNǵ^wƍZS,YnZ@{{;֮4ýɓ'9u,\W_B4* Ď<5I|)dz1fϞMss33f`ѢE!PJSO[޽{Yf B ~ٺu+L6 DlhP2IPTHt et?e]ܹsYz5Ʋ8tQ7}h3gΤs<GRJP $ UZtbH>O(Bذaǎp!B؈R %'N 8vZkPop`hp!D"0 SQB\AC˸hlhD\qxH˃<1kGZM-ŋٽ{J)nz{X*A1FCa4n+!eӼŋ[oVV}eرcB03ϰk.֜;w~J1\.GC}Y?R l0]w2gΧ'OcJ FB)AkmGWp)VxqRS([9sUV+Bk?\u"D5M+$3?qYlaW{/.M60?xB 9z(ì]5t~TC8<۲zhF```!eFq2Fꬎp55---?~ӧO5kD8H!V$BIE2,Ωg̙#+f Fu1#~#Rk7"0u]Nzs.!bFP(&Tq $6 0V މee֠N{*mz8P*FT˲-sS#;`[]]X BŊhLa Ho1QI%A8:,)(Jd RJBQ V`x%KE*pHicq$v\'-F׻x('JGJB ΋N=Ro ƒ=rV, ˲F$P,QJr4۶oËO/ ?ZbT{<*tIo&iII PTf$P.!ϧ >0owR Ēᕨcֵ0NZژ7o0 Xw,YP_~q&)7o&8hoo̽Id[)J)!e2H)9{,Xsl[s˂Wp>x̿1X8OCCy|O=0E)%j (Ajzu7}qšx|=qaHZM[t(|H_T"_V Ը;1{'q=z?i"0a S,)JA@hyݭ;L& 5RXĆQE϶td2hjj3fLĉcf֚mGNO|"<;OOGJs]'(8IENDB`gcovr-3.2/doc/images/icons/home.png000066400000000000000000000024741235605057100172740ustar00rootroot00000000000000PNG  IHDRĴl;bKGD pHYs ,tIME E`IDATxڅk\E3s\&1"mܨX ꃂ>}'_EQ[c[-զiK&{sΌ۫ 72Co*01& 0!F/)ݬo~0J@w61"0+t|l>gA^fre@ O%\\2_լF8^wO~}?ɱNCy﵃[Glj.^g%D,\\clPٛT*.HϿ xa\g' < J[]6{FC[fXДwR[:;zxaJ(I76z1=y Ye ;|wI>mca["V+Ik6N^V{fnc !pl`AQk9JJ4KVzv2#dj}f1¶n/%)fl/hHX]斵Ȋ 0dэi22’R;<:,hiZd>4]F0:b=wέ).+A k)I Le*h&Fw?`.#eB<9QXnBFIγ8/M=Hv.U ^-m0!x˥: %&uzƟH>7M-oFKƦKw+l7/m6*u$nVY:kZY*MG];!\;Dmuy+t|"4*Yg05%fG 5@6ccI 5Tl^%z&' p,\c0Ti&D_0gWmpuCVEdmAϊE`=ⳏPT̎N#Z'#-95(iH)Cp{]qakkEqк.Z͔f=܌GsiE_QRej#\`|xIV~V}On+94^ӍJ4fv,2-%R1 Bk^o~џ{~tϳ@ /e%R <hI['jӾ&'w/?=vzzZN5-6EZ w}}'o˴%v\IENDB`gcovr-3.2/doc/images/icons/important.png000066400000000000000000000056441235605057100203630ustar00rootroot00000000000000PNG  IHDR00W kIDAThՙkl3;;^?׵qҘGcvID)B4ԖJ*Ej@ 54LJDBDQKԸil ]ةv^wwvw;]k{MԪW:ys9+slVGF:3-+(" Wk/M#G{ ?$a3]ef\-2R@6,^Q6n~?$aپ%0 4GYw@|X{-8vQ0]׍ /åӧs$ 23 D]O?7ۍn_5 "1~tSCC9  t~}>Quog﫯p8X4| o {RZZG%^mt"IҢI,@>I` Xc7Oee%N3o?QN>CO ]HmJ/S\\\ >^ZJ7)++C;s:;  R('ܲEb14/0 UUy{7s'VGk6oWڅ'8ӟ(E_߻"Nȫ23TW[`պu\.3$+mc޽JJpf׿fψFRY}Q 0t]_NW@þ}Z%7eS{;M{ e,ۙ *<,@UU~֖s?4|lٳ>Ͼ}8x`DQdȲfD[ĥ^:}p8(h,O83i@q]Z[)// gattI{lps-䫬d/ ;>t==DQ$aIb. ̺u[ N3/^줷x<Çgϖʦ&ֶ.- ?xUU2@.sc}7]7A~&''sx<0G 0GɓD"ĜC@u෿%W ֟yA!ȼl -AI:h|)&''&-`j漠M{vuA"f F5klhSV@ڕBNfY!S,Y,,3~BUQ^yNeYXthO/A8BDSsrMw[6}כ 7ܰhf\@uu1==M2̱IT*h~93Q[[kYP oy9>|啜`Y~w7փ8rV|>,/ܔFn7V_wuWA׭c͖g%i@4րj7lhGf`0ƍ " w㫨 =j%6=S没Q@ XHʕ|Y~Dޱc. Yy /n &\䄬\ə+m&BZ)455sNۗD@e \sD"qk*@ww@Tl/’,K^};D{e;F~@2L 1@joLK7څ(V <4-B٫N46~!ǣ>wA[[===ϖoG#Y$MӬm@}=EEE) i?<055(ǏUk@rctEQ%U*ںg$g} -zD RPh㏗y-c?Au 0tftm\|Tx#˗/ƚ(B!Ctv.J6Yrawг X 5y(AzŠPQA7Sz5,_AuCUUbP?"ՅWZVR5޼5PTTDyy9TWWZa뺑JLLL0>>a&zz>w d%6=K preDg}=ΆN'Oii)@ p#Qٮ*0==%pihzۤT(zӡIMEF0󺭶6+8VB$ի$ Yq8n^/~"JJJ(..vev90$ňFD""hX,F<#Jn.s>rff!I$aۭrp\x<^/>׋rp8̓L؛uajF*"LH$Px<((B"H$ITUdz nw88NN'.˺e:& IDu4Mh3g(w6S{;7,&LZZe-3Lw2bc3ew _S2 Jg' ]IENDB`gcovr-3.2/doc/images/icons/next.png000066400000000000000000000024261235605057100173170ustar00rootroot00000000000000PNG  IHDRĴl;bKGD pHYs ?@"tIME )(IDATxڕُU{kު{vbpƱ'1.T_ .o@#Yh2сapY{ꮪ{}IK:ʯ'2X@uu 4]T1z=k}끷@ H#8VEׁ}1 p5ۉx֤\amkri:ռbrbBM\W1K?ߜЇANnY>FC6scpy]z3/]bHz Xx^D2+})%c!#O1se/poL;~ubٴ1b)( b6tG82ҕ4}FfZS-Ɯ3ѰȦPAƉJ72X#f+@S4 In2:wN+1||t;=qє|Mm>P" Xbr|QrN/pڀd2%#$TK2U ,l̤h)-}qVį5RtcHH캢H%ЁVG̀"welpmY{R4afсaM l,يbcCϪO4;|f@!*΢+EILF}zV/ZaɱL&w͇/EPg) RJ4JF6eKF{Ao$V@X!wG _|>oNQ?  UT\U i[qcex1[0~hY3-jk$95,3ԔN.b5tb͎mIc5.lS`0Y\`|x-83-I 4TvMOγ׈MR5#f QSJCf* ]sl`Kח<߽Am& olduNJD(@ =7 sǁ0d@~ɢH1(lAMxv3eLN%{_9ExIENDB`gcovr-3.2/doc/images/icons/note.png000066400000000000000000000046761235605057100173170ustar00rootroot00000000000000PNG  IHDR00W IDAThYlW{'^4i I IMKB[, PX$P RA< E<"AE J )%bgqIܱ=fqc(h͌=sgw_} \y-FFFlxxzzzdžmdd?ZV͛7}y&8Ʊ)>^aՇ>Ɩ-[dm}QyIa[D: {&`_Z2z=H (U1hp8W|ࡇ*6E%E`ǎte"Y !KZƎ;1` YPe1T*'u ":8xMDsg}$ i^S8u7_88:y%Y( ׽ %~hQ'%,EUiIQD RB=xWlkmp,'rooI?Ў1 8' 0WhhH(`iJ:SEx~-7峻+Lo#25h\NH3 4;@R4!\Ц!qOG‡]O3ͦr=dzL=_##G)'D74 R @xOp޸5Cl>A=ܶaZdf8!Mp +NFZW4\+*sUySHG4.Ҍ^|NNM;LGGFU:g u"sKl0. shBBQ*EŸsj^fzX$ f\Q;+4Eɿ`s3S\Xd1zDD)d(TIpN\h 7#B I2ipDEQHǩf":OJPgI0ЅPVQUT'X9 FI@&@[; ) H7!- -(#ΑG!$')1QU:b4P-F{WL*?}W,]Mww{yQwcN5-yQcVrݺyz?.p L~Po7=;+yj?y;n-6YNMUY|kV/ Ћg^* W\чHbs5W3* t&&&xբF 8h4fEq$izSV/dYoX(!ᛊE*Ry~y2[ߘsjRqM #hDv8M-"AJhTǙ5^<ΐ$j\ ӔK.> W !F8(fJ<*y.=8).M:F)ZJdalݺնm5v; כшH1iCdk 4+YliAC4EAC(411sR\(&3|5*($R|֜TQ-ITcY貎;;w Sp,DÈ4=+R$dDDKEQ f9j4ff{/_]Ԧ`9xp^㯻0y8=HG{]]m\5kV!EX?k9̌d1*ʂ(NU>9r;VN[bV\>9y7ԉÔm q!]{}}>O8ɣ1V4'631288H^GDذaގ7vJ<ϙEUVl߾6<|[8gլZkKe Q*Λ^VיfffZFf5k6s̘9̨Cs$IHRFUkq6C~w!"[CS$< / [^r˿dvIENDB`gcovr-3.2/doc/images/icons/prev.png000066400000000000000000000025041235605057100173120ustar00rootroot00000000000000PNG  IHDRĴl;bKGD pHYs ,tIME 4ٸIDATxڍۋ\U{Oթ[Wv3t0J 3 C-,B<"/ DP$q=L NtWwUr.{IO4`}ǷYk)~JG $")@]6]wa'A(L;#M`^Ò{R? .y9NMx|szͲjFSFoT*;A:OdٷCx$opTN吓!__'M{ÓwΏ|z>sqt}JhDeNõLVj*˾o깲3 >6yw[1榓|tζ-Q$[ ٔQa fsp{rەK* |O=R$i݈\DU8{KԈ9=H!-=mU,@Y =WuA9)t"2!͞el?hvő= K}(@'|eɺwa%*~ieԝ 5;|rv_~1x_\+PI4r ll|.!lo8p}X\ 1 $i[ƶk<59R]xΆs߃7#cmQd6FILҍԭr^nvnUߏK 򗱴g,2\Zxl[XeK2`,B^Hڄծ8Yw-?$*']r!i?I&i/$mh&U!7o?٧>~9jU~Uz(;;*P$0 bk#v|MF' #$\Arym֙‰\BϔL.3ݏf)֬]PoGxsƁV-Wg&sNj`Rěj*ʩF]fεNjtt#]\ Db=Nl2_8qZT*/ȑ#G4>_GkUCۚr2^t44zA+3*qj%Hr^ns@T*{/{hRhIENDB`gcovr-3.2/doc/images/icons/tip.png000066400000000000000000000052361235605057100171370ustar00rootroot00000000000000PNG  IHDR00W eIDAThkpTs۳&lDbHU,P-5j*"SG i(Sxa0_A23t:8mtq,åb$"ܳ=Hκn2yn>|~<įۀ7@9q[ A^s^ WW 8c6XeYYqF $IH(舢xU m54Mرc222iJ$ J[[dE$gA+'n&}}}ܹݻwSUwM7œyCx=*$8H$|1|$Wfݺu466*YR\mێeYAGG۶mՏ0TĒibTn`8(z|^E9)x_-[|GFm;irqf~7~QFdt#/B~>9~sٺu+---TUu1#DYe5~߾},[떬{#yT/tQLt85h~֬YÞ={FiLĶIk/z? g{2#ό+%GtBYfzzmj*|>,;.\ ^m; Cn]SvE"@Ӵl0ؽ{7^KYV⥿U=p)^*k"HN1 <7|^uf]{ q? Hٷ(On}NM&RU`pDZk׮%  =(xlݺ;D4Ǹh`opyx0$eL*ųā=;ٰapp8L Mieu%b{_mbCP+"eUrDc"]S`̙3DƖ́@UVa67o'T]Q˚U Nldd| ۶yƍD$dؘ7W4hAżݰ`h;1udl0,3iCa/^L$a֬Ya3WGZVs7"3>>jv7@QȊ(J88ؖi4Z:N!ayv֮]K8J***2l\MH&b1zzzصk]]]/`N|| QımAst|8K.eʕ ÄB+Nt:M2$HH$ѣ>}~(a( PZXh---|>~?``0#Fu4M#NNLnd4M+Yx:Yc 7+r_LFžQ7sTdEXݑOo+)^ #vnQ;!owG|R{6d5Y?;ΉVi!xzSV @72GVW\9bp+=== ;v,u]'ىa߸\.RUAGc<?7Vlv;KXÑD bhUiĉ;`@` (v* w+cl(  B moK#`)BGI^Ea~,zqr:/9aṕ:PUͅbbm|}\ϴ4O"0x]Kh? )_bW9e_#+;ghB;Qm 6U1ń; z0Ao'y)  oZ)8 Ψq,\;MͣkLWnRXD8QUI186@}}@(TNcAMבHvL-0cʥymq._p(8pH'򀥠(TEEP];k%mJ4Ӂ7q`CH@&άCľD1ƾSnɓڴ_:X TdXnj(,BuP-ࣳMo7|FruIفǏS^^D@JIQQ3RkpG,1Nx;}tAǍO}^G?Fi-i766h",X@AA.+.eSS;wɓ)snKK ~/L@;R B cb"8L3(;})e~Xd ӦMc֬YF!粒^u I_ύJYzK)c躞GiӦ1sLJJJe.Duu5NJZLJIkk+6n&I*g,YO>@ӴF֯_fSYUU%//x-D;wx`?TH4Uٺu++VI2aAe int foo(int param) { if (param) { return 1; //std::cout << "param not null." << std::endl; } else { return 0; //std::cout << "param is null." << std::endl; } } int main(int argc, char* argv[]) { foo(0); return 0; } gcovr-3.2/gcovr/tests/bad++char/reference/000077500000000000000000000000001235605057100204345ustar00rootroot00000000000000gcovr-3.2/gcovr/tests/bad++char/reference/coverage.html000066400000000000000000000154011235605057100231160ustar00rootroot00000000000000 Head
GCC Code Coverage Report
Directory: . Exec Total Coverage
Date: 2013-07-02 Lines: 7 8 87.5 %
Legend: low: < 75.0 % medium: >= 75.0 % high: >= 90.0 % Branches: 3 6 50.0 %

File Lines Branches
main.cpp
87.5 % 7 / 8 50.0 % 3 / 6


gcovr-3.2/gcovr/tests/bad++char/reference/coverage.txt000066400000000000000000000012321235605057100227660ustar00rootroot00000000000000------------------------------------------------------------------------------ GCC Code Coverage Report Directory: . ------------------------------------------------------------------------------ File Lines Exec Cover Missing ------------------------------------------------------------------------------ main.cpp 8 7 87% 6 ------------------------------------------------------------------------------ TOTAL 8 7 87% ------------------------------------------------------------------------------ gcovr-3.2/gcovr/tests/bad++char/reference/coverage.xml000066400000000000000000000020111235605057100227430ustar00rootroot00000000000000 . gcovr-3.2/gcovr/tests/cmake_oos/000077500000000000000000000000001235605057100167245ustar00rootroot00000000000000gcovr-3.2/gcovr/tests/cmake_oos/CMakeLists.txt000066400000000000000000000010711235605057100214630ustar00rootroot00000000000000 cmake_minimum_required(VERSION 2.6.4 FATAL_ERROR) project(gcovrtest) if (UNIX) set(CMAKE_BUILD_TYPE DEBUG) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0 -Wall -fprofile-arcs -ftest-coverage ") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -Wall -fprofile-arcs -ftest-coverage ") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage ") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage ") else() message(STATUS "coverage flags not supported for this plaform.") endif() add_executable(gcovrtest main.cpp) gcovr-3.2/gcovr/tests/cmake_oos/Makefile000066400000000000000000000010201235605057100203550ustar00rootroot00000000000000# Original build.sh script from ticket: # rm -fr build # mkdir build # cd build # cmake .. # make # gcovrtest all: mkdir -p build cd build && cmake .. && make run: txt xml html txt: cd build && ./gcovrtest ../../../scripts/gcovr -r . -d -o coverage.txt xml: cd build && ./gcovrtest ../../../scripts/gcovr -r . -d -x -o coverage.xml html: cd build && ./gcovrtest ../../../scripts/gcovr -r . -d --html --html-details -o coverage.html clean: rm -rf build rm -f coverage.txt coverage.xml coverage*.html gcovr-3.2/gcovr/tests/cmake_oos/README000066400000000000000000000001611235605057100176020ustar00rootroot00000000000000This test case was originally submitted for ticket #3867: https://software.sandia.gov/trac/fast/ticket/3867 gcovr-3.2/gcovr/tests/cmake_oos/build.sh000077500000000000000000000000751235605057100203640ustar00rootroot00000000000000rm -fr build mkdir build cd build cmake .. make gcovrtest gcovr-3.2/gcovr/tests/cmake_oos/main.cpp000066400000000000000000000004041235605057100203520ustar00rootroot00000000000000#include int foo(int param) { if (param) { return 1; //std::cout << "param not null." << std::endl; } else { return 0; //std::cout << "param is null." << std::endl; } } int main(int argc, char* argv[]) { foo(0); return 0; } gcovr-3.2/gcovr/tests/cmake_oos/reference/000077500000000000000000000000001235605057100206625ustar00rootroot00000000000000gcovr-3.2/gcovr/tests/cmake_oos/reference/coverage.html000066400000000000000000000166441235605057100233560ustar00rootroot00000000000000 Head
GCC Code Coverage Report
Directory: . Exec Total Coverage
Date: 2014-07-03 Lines: 7 8 87.5 %
Legend: low: < 75.0 % medium: >= 75.0 % high: >= 90.0 % Branches: 3 6 50.0 %

File Lines Branches
main.cpp
87.5 % 7 / 8 50.0 % 3 / 6


gcovr-3.2/gcovr/tests/cmake_oos/reference/coverage.txt000066400000000000000000000012321235605057100232140ustar00rootroot00000000000000------------------------------------------------------------------------------ GCC Code Coverage Report Directory: . ------------------------------------------------------------------------------ File Lines Exec Cover Missing ------------------------------------------------------------------------------ main.cpp 8 7 87% 6 ------------------------------------------------------------------------------ TOTAL 8 7 87% ------------------------------------------------------------------------------ gcovr-3.2/gcovr/tests/cmake_oos/reference/coverage.xml000066400000000000000000000020521235605057100231760ustar00rootroot00000000000000 . gcovr-3.2/gcovr/tests/dot/000077500000000000000000000000001235605057100155525ustar00rootroot00000000000000gcovr-3.2/gcovr/tests/dot/.subdir/000077500000000000000000000000001235605057100171205ustar00rootroot00000000000000gcovr-3.2/gcovr/tests/dot/.subdir/A/000077500000000000000000000000001235605057100173005ustar00rootroot00000000000000gcovr-3.2/gcovr/tests/dot/.subdir/A/C/000077500000000000000000000000001235605057100174625ustar00rootroot00000000000000gcovr-3.2/gcovr/tests/dot/.subdir/A/C/D/000077500000000000000000000000001235605057100176455ustar00rootroot00000000000000gcovr-3.2/gcovr/tests/dot/.subdir/A/C/D/file6.cpp000066400000000000000000000001241235605057100213530ustar00rootroot00000000000000int foo6(int param) { if (param) { return 1; } else { return 0; } } gcovr-3.2/gcovr/tests/dot/.subdir/A/C/file5.cpp000066400000000000000000000001241235605057100211670ustar00rootroot00000000000000int foo5(int param) { if (param) { return 1; } else { return 0; } } gcovr-3.2/gcovr/tests/dot/.subdir/A/file1.cpp000066400000000000000000000001231235605057100210000ustar00rootroot00000000000000int foo(int param) { if (param) { return 1; } else { return 0; } } gcovr-3.2/gcovr/tests/dot/.subdir/A/file2.cpp000066400000000000000000000001211235605057100207770ustar00rootroot00000000000000int bar() { int x=1; int y=2; return x+y; } int bar_() { int x=1; return 2*x; } gcovr-3.2/gcovr/tests/dot/.subdir/A/file3.cpp000066400000000000000000000001311235605057100210010ustar00rootroot00000000000000int fourbar() { int x=1; int y=2; return x+y; } int fourbar_() { int x=1; return 2*x; } gcovr-3.2/gcovr/tests/dot/.subdir/A/file4.cpp000066400000000000000000000001261235605057100210060ustar00rootroot00000000000000int foobar(int param) { if (param) { return 1; } else { return 0; } } gcovr-3.2/gcovr/tests/dot/.subdir/B/000077500000000000000000000000001235605057100173015ustar00rootroot00000000000000gcovr-3.2/gcovr/tests/dot/.subdir/B/main.cpp000066400000000000000000000004451235605057100207340ustar00rootroot00000000000000#include extern int foo(int param); extern int foobar(int param); extern int bar(); extern int fourbar(); extern int foo5(int param); extern int foo6(int param); int main(int argc, char* argv[]) { foo(0); foobar(1); bar(); fourbar(); foo5(0); foo6(0); return 0; } gcovr-3.2/gcovr/tests/dot/Makefile000066400000000000000000000021471235605057100172160ustar00rootroot00000000000000CFLAGS= -fprofile-arcs -ftest-coverage -fPIC all: $(CXX) $(CFLAGS) -c .subdir/A/file1.cpp -o .subdir/A/file1.o $(CXX) $(CFLAGS) -c .subdir/A/file2.cpp -o .subdir/A/file2.o $(CXX) $(CFLAGS) -c .subdir/A/file3.cpp -o .subdir/A/file3.o $(CXX) $(CFLAGS) -c .subdir/A/file4.cpp -o .subdir/A/file4.o $(CXX) $(CFLAGS) -c .subdir/A/C/file5.cpp -o .subdir/A/C/file5.o $(CXX) $(CFLAGS) -c .subdir/A/C/D/file6.cpp -o .subdir/A/C/D/file6.o $(CXX) $(CFLAGS) -c .subdir/B/main.cpp -o .subdir/B/main.o $(CXX) $(CFLAGS) .subdir/A/file1.o .subdir/A/file2.o .subdir/A/file3.o .subdir/A/file4.o .subdir/A/C/file5.o .subdir/A/C/D/file6.o .subdir/B/main.o -o .subdir/testcase run: txt xml html txt: ./.subdir/testcase ../../../scripts/gcovr -r . -d -o coverage.txt xml: ./.subdir/testcase ../../../scripts/gcovr -r . -d -x -o coverage.xml html: ./.subdir/testcase ../../../scripts/gcovr -r . -d --html --html-details -o coverage.html clean: rm -f ./.subdir/testcase rm -f .*.gc* */*.gc* .*/*/*.gc* .*/*/*/*.gc* .*/*/*/*/*.gc* rm -f .*.o .*/*.o .*/*/*.o .*/*/*/*.o .*/*/*/*/*.o rm -f coverage.txt coverage.xml coverage*.html gcovr-3.2/gcovr/tests/dot/README000066400000000000000000000001441235605057100164310ustar00rootroot00000000000000This test case was inspired by ticket #3884: https://software.sandia.gov/trac/fast/ticket/3884 gcovr-3.2/gcovr/tests/dot/reference/000077500000000000000000000000001235605057100175105ustar00rootroot00000000000000gcovr-3.2/gcovr/tests/dot/reference/coverage.html000066400000000000000000000272521235605057100222010ustar00rootroot00000000000000 Head
GCC Code Coverage Report
Directory: .subdir/ Exec Total Coverage
Date: 2013-10-17 Lines: 29 39 74.4 %
Legend: low: < 75.0 % medium: >= 75.0 % high: >= 90.0 % Branches: 6 12 50.0 %

File Lines Branches
A/C/D/file6.cpp
75.0 % 3 / 4 50.0 % 1 / 2
A/C/file5.cpp
75.0 % 3 / 4 50.0 % 1 / 2
A/file1.cpp
75.0 % 3 / 4 50.0 % 1 / 2
A/file2.cpp
57.1 % 4 / 7 100.0 % 0 / 0
A/file3.cpp
57.1 % 4 / 7 100.0 % 0 / 0
A/file4.cpp
75.0 % 3 / 4 50.0 % 1 / 2
B/main.cpp
100.0 % 9 / 9 50.0 % 2 / 4


gcovr-3.2/gcovr/tests/dot/reference/coverage.txt000066400000000000000000000020751235605057100220500ustar00rootroot00000000000000------------------------------------------------------------------------------ GCC Code Coverage Report Directory: . ------------------------------------------------------------------------------ File Lines Exec Cover Missing ------------------------------------------------------------------------------ .subdir/A/C/D/file6.cpp 4 3 75% 4 .subdir/A/C/file5.cpp 4 3 75% 4 .subdir/A/file1.cpp 4 3 75% 4 .subdir/A/file2.cpp 7 4 57% 8,10-11 .subdir/A/file3.cpp 7 4 57% 8,10-11 .subdir/A/file4.cpp 4 3 75% 6 .subdir/B/main.cpp 9 9 100% ------------------------------------------------------------------------------ TOTAL 39 29 74% ------------------------------------------------------------------------------ gcovr-3.2/gcovr/tests/dot/reference/coverage.xml000066400000000000000000000076651235605057100220430ustar00rootroot00000000000000 . gcovr-3.2/gcovr/tests/excl-branch/000077500000000000000000000000001235605057100171525ustar00rootroot00000000000000gcovr-3.2/gcovr/tests/excl-branch/Makefile000066400000000000000000000007661235605057100206230ustar00rootroot00000000000000all: $(CXX) -fprofile-arcs -ftest-coverage -fPIC main.cpp -o testcase run: txt xml html txt: ./testcase ../../../scripts/gcovr --exclude-unreachable-branches -b -r . -d -o coverage.txt xml: ./testcase ../../../scripts/gcovr --exclude-unreachable-branches -b -r . -d -x -o coverage.xml html: ./testcase ../../../scripts/gcovr --exclude-unreachable-branches -b -r . -d --html --html-details -o coverage.html clean: rm -f testcase rm -f *.gc* rm -f coverage.txt coverage.xml coverage*.html gcovr-3.2/gcovr/tests/excl-branch/README000066400000000000000000000002321235605057100200270ustar00rootroot00000000000000Test for --exclude-unreachable-branches option The test attempts to test both GCOV/LCOV exclusion markers and auto-detection of compiler-generated code. gcovr-3.2/gcovr/tests/excl-branch/main.cpp000066400000000000000000000024341235605057100206050ustar00rootroot00000000000000#include #include class Bar { public: Bar() {} virtual ~Bar() {} // possible compiler-generated destruction code - auto-detected and excluded private: int m_param; }; int foo(int param) { if (param == 0 || param == 1) { // 4/4 branches return 1; } else if (param == 2 || param == 5) { // 3/4 branches, excluded, GCOV_EXCL_LINE return 0; } else if (param == 10) { // 1/2 branches return 2; } else if (param == 11) { // 1/2 branches return 3; } // GCOV_EXCL_START if (param == 4) { // 2/2 branches, excluded return 1; } else if (param == 5) { // 1/2 branches, excluded return 0; } // GCOV_EXCL_STOP return 0; } int bar(int param) { // never called, GCOV_EXCL_START if (param) { return 1; } return 0; } // GCOV_EXCL_STOP int main(int argc, char* argv[]) { for (int i = 0; i < 5; i++) { // 2/2 branches foo(i); } try { Bar bar; // LCOV_EXCL_LINE } catch (const std::exception &e) { // LCOV_EXCL_START std::cout << "caught exception"; if (std::strlen(e.what()) > 0) { std::cout << ": " << e.what(); } std::cout << std::endl; } // LCOV_EXCL_STOP return 0; } // compiler-generated destruction code - auto-detected and excluded // total: 8/10 branches reported gcovr-3.2/gcovr/tests/excl-branch/reference/000077500000000000000000000000001235605057100211105ustar00rootroot00000000000000gcovr-3.2/gcovr/tests/excl-branch/reference/coverage.html000066400000000000000000000157331235605057100236020ustar00rootroot00000000000000 Head
GCC Code Coverage Report
Directory: . Exec Total Coverage
Date: 2013-11-03 Lines: 16 18 88.9 %
Legend: low: < 75.0 % medium: >= 75.0 % high: >= 90.0 % Branches: 8 10 80.0 %

File Lines Branches
main.cpp
88.9 % 16 / 18 80.0 % 8 / 10


gcovr-3.2/gcovr/tests/excl-branch/reference/coverage.txt000066400000000000000000000012361235605057100234460ustar00rootroot00000000000000------------------------------------------------------------------------------ GCC Code Coverage Report Directory: . ------------------------------------------------------------------------------ File Branches Taken Cover Missing ------------------------------------------------------------------------------ main.cpp 10 8 80% 21,23 ------------------------------------------------------------------------------ TOTAL 10 8 80% ------------------------------------------------------------------------------ gcovr-3.2/gcovr/tests/excl-branch/reference/coverage.xml000066400000000000000000000033641235605057100234330ustar00rootroot00000000000000 . gcovr-3.2/gcovr/tests/excl-line/000077500000000000000000000000001235605057100166445ustar00rootroot00000000000000gcovr-3.2/gcovr/tests/excl-line/Makefile000066400000000000000000000006201235605057100203020ustar00rootroot00000000000000all: $(CXX) -fprofile-arcs -ftest-coverage -fPIC main.cpp -o testcase run: txt xml html txt: ./testcase ../../../scripts/gcovr -r . -d -o coverage.txt xml: ./testcase ../../../scripts/gcovr -r . -d -x -o coverage.xml html: ./testcase ../../../scripts/gcovr -r . -d --html --html-details -o coverage.html clean: rm -f testcase rm -f *.gc* rm -f coverage.txt coverage.xml coverage*.html gcovr-3.2/gcovr/tests/excl-line/README000066400000000000000000000001441235605057100175230ustar00rootroot00000000000000This test case was inspired by ticket #3942: https://software.sandia.gov/trac/fast/ticket/3942 gcovr-3.2/gcovr/tests/excl-line/main.cpp000066400000000000000000000007271235605057100203020ustar00rootroot00000000000000#include int foo(int param) { if (param) { return 1; //std::cout << "param not null." << std::endl; } else { return 0; //std::cout << "param is null." << std::endl; GCOVR_EXCL_LINE } // LCOV_EXCL_START if (param) { return 1; //std::cout << "param not null." << std::endl; } else { return 0; //std::cout << "param is null." << std::endl; } // LCOV_EXCL_STOP } int main(int argc, char* argv[]) { foo(0); return 0; } gcovr-3.2/gcovr/tests/excl-line/reference/000077500000000000000000000000001235605057100206025ustar00rootroot00000000000000gcovr-3.2/gcovr/tests/excl-line/reference/coverage.html000066400000000000000000000154011235605057100232640ustar00rootroot00000000000000 Head
GCC Code Coverage Report
Directory: . Exec Total Coverage
Date: 2013-07-02 Lines: 6 7 85.7 %
Legend: low: < 75.0 % medium: >= 75.0 % high: >= 90.0 % Branches: 3 6 50.0 %

File Lines Branches
main.cpp
85.7 % 7 / 8 50.0 % 3 / 6


gcovr-3.2/gcovr/tests/excl-line/reference/coverage.txt000066400000000000000000000012321235605057100231340ustar00rootroot00000000000000------------------------------------------------------------------------------ GCC Code Coverage Report Directory: . ------------------------------------------------------------------------------ File Lines Exec Cover Missing ------------------------------------------------------------------------------ main.cpp 7 6 85% 6 ------------------------------------------------------------------------------ TOTAL 7 6 85% ------------------------------------------------------------------------------ gcovr-3.2/gcovr/tests/excl-line/reference/coverage.xml000066400000000000000000000017621235605057100231250ustar00rootroot00000000000000 . gcovr-3.2/gcovr/tests/filter-test/000077500000000000000000000000001235605057100172265ustar00rootroot00000000000000gcovr-3.2/gcovr/tests/filter-test/Makefile000066400000000000000000000011371235605057100206700ustar00rootroot00000000000000CFLAGS= -fprofile-arcs -ftest-coverage -fPIC all: $(CXX) $(CFLAGS) -c file1.cpp -o file1.o $(CXX) $(CFLAGS) -c main.cpp -o main.o $(CXX) $(CFLAGS) main.o file1.o -o testcase run: txt xml html GCOVR_TEST_OPTIONS = -f '.*main.cpp' -r '.' txt: ./testcase ../../../scripts/gcovr $(GCOVR_TEST_OPTIONS) -d -o coverage.txt xml: ./testcase ../../../scripts/gcovr $(GCOVR_TEST_OPTIONS) -d -x -o coverage.xml html: ./testcase ../../../scripts/gcovr $(GCOVR_TEST_OPTIONS) -d --html --html-details -o coverage.html clean: rm -f testcase rm -f *.gc* *.o rm -f coverage.txt coverage.xml coverage*.html gcovr-3.2/gcovr/tests/filter-test/README000066400000000000000000000001441235605057100201050ustar00rootroot00000000000000This test case was inspired by ticket #3884: https://software.sandia.gov/trac/fast/ticket/3884 gcovr-3.2/gcovr/tests/filter-test/file1.cpp000066400000000000000000000000311235605057100207240ustar00rootroot00000000000000 int bar() { return 0; } gcovr-3.2/gcovr/tests/filter-test/main.cpp000066400000000000000000000004231235605057100206550ustar00rootroot00000000000000#include int bar(); int foo(int param) { if (param) { return 1; //std::cout << "param not null." << std::endl; } else { return 0; //std::cout << "param is null." << std::endl; } } int main(int argc, char* argv[]) { foo(bar()); return 0; } gcovr-3.2/gcovr/tests/filter-test/reference/000077500000000000000000000000001235605057100211645ustar00rootroot00000000000000gcovr-3.2/gcovr/tests/filter-test/reference/coverage.html000066400000000000000000000154141235605057100236520ustar00rootroot00000000000000 Head
GCC Code Coverage Report
Directory: filter-test/ Exec Total Coverage
Date: 2013-07-02 Lines: 7 8 87.5 %
Legend: low: < 75.0 % medium: >= 75.0 % high: >= 90.0 % Branches: 3 6 50.0 %

File Lines Branches
main.cpp
87.5 % 7 / 8 50.0 % 3 / 6


gcovr-3.2/gcovr/tests/filter-test/reference/coverage.txt000066400000000000000000000012321235605057100235160ustar00rootroot00000000000000------------------------------------------------------------------------------ GCC Code Coverage Report Directory: . ------------------------------------------------------------------------------ File Lines Exec Cover Missing ------------------------------------------------------------------------------ main.cpp 8 7 87% 7 ------------------------------------------------------------------------------ TOTAL 8 7 87% ------------------------------------------------------------------------------ gcovr-3.2/gcovr/tests/filter-test/reference/coverage.xml000066400000000000000000000020521235605057100235000ustar00rootroot00000000000000 . gcovr-3.2/gcovr/tests/linked/000077500000000000000000000000001235605057100162325ustar00rootroot00000000000000gcovr-3.2/gcovr/tests/linked/Makefile000066400000000000000000000024101235605057100176670ustar00rootroot00000000000000CFLAGS= -fprofile-arcs -ftest-coverage -fPIC all: links $(CXX) $(CFLAGS) -c subdir/A/file1.cpp -o subdir/A/file1.o $(CXX) $(CFLAGS) -c subdir/A/file2.cpp -o subdir/A/file2.o $(CXX) $(CFLAGS) -c subdir/A/file3.cpp -o subdir/A/file3.o $(CXX) $(CFLAGS) -c subdir/A/file4.cpp -o subdir/A/file4.o $(CXX) $(CFLAGS) -c subdir/A/file7.cpp -o subdir/A/file7.o $(CXX) $(CFLAGS) -c subdir/A/C/file5.cpp -o subdir/A/C/file5.o $(CXX) $(CFLAGS) -c subdir/m/n/C/D/file6.cpp -o subdir/m/n/C/D/file6.o $(CXX) $(CFLAGS) -c subdir/B/main.cpp -o subdir/B/main.o $(CXX) $(CFLAGS) subdir/A/file1.o subdir/A/file2.o subdir/A/file3.o subdir/A/file4.o subdir/A/C/file5.o subdir/m/n/C/D/file6.o subdir/A/file7.o subdir/B/main.o -o subdir/testcase run: txt xml html txt: ./subdir/testcase ../../../scripts/gcovr -r . -d -o coverage.txt xml: ./subdir/testcase ../../../scripts/gcovr -r . -d -x -o coverage.xml html: ./subdir/testcase ../../../scripts/gcovr -r . -d --html --html-details -o coverage.html clean: rm -Rf subdir rm -f coverage.txt coverage.xml coverage*.html links: if ! [ -d "subdir" ]; then\ mkdir subdir;\ cd subdir;\ ln -s ../../nested/subdir/B .;\ mkdir -p m;\ cd m;\ ln -s ../../../nested/subdir/A n;\ cd ..;\ ln -s m/n A;\ cd ..;\ fi gcovr-3.2/gcovr/tests/linked/README000066400000000000000000000012001235605057100171030ustar00rootroot00000000000000This test case was inspired by the logic in gcovr that traverses symbolic links: # UNIX resolves symbolic links by walking the # entire directory structure. What that means is that relative links # are always relative to the actual directory inode, and not the # "virtual" path that the user might have traversed (over symlinks) on # the way to that directory. Here's the canonical example: # # a / b / c / testfile # a / d / e --> ../../a/b # m / n --> /a # x / y / z --> /m/n/d # # If we start in "y", we will see the following directory structure: # y # |-- z # |-- e # |-- c # |-- testfile gcovr-3.2/gcovr/tests/linked/reference/000077500000000000000000000000001235605057100201705ustar00rootroot00000000000000gcovr-3.2/gcovr/tests/linked/reference/coverage.html000066400000000000000000000315441235605057100226600ustar00rootroot00000000000000 Head
GCC Code Coverage Report
Directory: subdir/ Exec Total Coverage
Date: 2014-07-03 Lines: 29 43 67.4 %
Legend: low: < 75.0 % medium: >= 75.0 % high: >= 90.0 % Branches: 6 14 42.9 %

File Lines Branches
A/C/file5.cpp
75.0 % 3 / 4 50.0 % 1 / 2
A/file1.cpp
75.0 % 3 / 4 50.0 % 1 / 2
A/file2.cpp
57.1 % 4 / 7 100.0 % 0 / 0
A/file3.cpp
44.4 % 4 / 9 0.0 % 0 / 2
A/file4.cpp
75.0 % 3 / 4 50.0 % 1 / 2
A/file7.cpp
0.0 % 0 / 2 100.0 % 0 / 0
B/main.cpp
100.0 % 9 / 9 50.0 % 2 / 4
m/n/C/D/file6.cpp
75.0 % 3 / 4 50.0 % 1 / 2


gcovr-3.2/gcovr/tests/linked/reference/coverage.txt000066400000000000000000000022061235605057100225240ustar00rootroot00000000000000------------------------------------------------------------------------------ GCC Code Coverage Report Directory: . ------------------------------------------------------------------------------ File Lines Exec Cover Missing ------------------------------------------------------------------------------ subdir/A/C/file5.cpp 4 3 75% 4 subdir/A/file1.cpp 4 3 75% 4 subdir/A/file2.cpp 7 4 57% 8,10-11 subdir/A/file3.cpp 9 4 44% 8,10-12,14 subdir/A/file4.cpp 4 3 75% 6 subdir/A/file7.cpp 2 0 0% 1,3 subdir/B/main.cpp 9 9 100% subdir/m/n/C/D/file6.cpp 4 3 75% 4 ------------------------------------------------------------------------------ TOTAL 43 29 67% ------------------------------------------------------------------------------ gcovr-3.2/gcovr/tests/linked/reference/coverage.xml000066400000000000000000000105631235605057100225120ustar00rootroot00000000000000 . gcovr-3.2/gcovr/tests/nested/000077500000000000000000000000001235605057100162465ustar00rootroot00000000000000gcovr-3.2/gcovr/tests/nested/Makefile000066400000000000000000000022401235605057100177040ustar00rootroot00000000000000CFLAGS= -fprofile-arcs -ftest-coverage -fPIC all: $(CXX) $(CFLAGS) -c subdir/A/file1.cpp -o subdir/A/file1.o $(CXX) $(CFLAGS) -c subdir/A/file2.cpp -o subdir/A/file2.o $(CXX) $(CFLAGS) -c subdir/A/file3.cpp -o subdir/A/file3.o $(CXX) $(CFLAGS) -c subdir/A/file4.cpp -o subdir/A/file4.o $(CXX) $(CFLAGS) -c subdir/A/file7.cpp -o subdir/A/file7.o $(CXX) $(CFLAGS) -c subdir/A/C/file5.cpp -o subdir/A/C/file5.o $(CXX) $(CFLAGS) -c subdir/A/C/D/file6.cpp -o subdir/A/C/D/file6.o $(CXX) $(CFLAGS) -c subdir/B/main.cpp -o subdir/B/main.o $(CXX) $(CFLAGS) subdir/A/file1.o subdir/A/file2.o subdir/A/file3.o subdir/A/file4.o subdir/A/C/file5.o subdir/A/C/D/file6.o subdir/A/file7.o subdir/B/main.o -o subdir/testcase run: txt xml html txt: ./subdir/testcase ../../../scripts/gcovr -r subdir -d -o coverage.txt xml: ./subdir/testcase ../../../scripts/gcovr -r subdir -d -x -o coverage.xml html: ./subdir/testcase ../../../scripts/gcovr -r subdir -d --html --html-details -o coverage.html clean: rm -f ./subdir/testcase rm -f *.gc* */*.gc* */*/*.gc* */*/*/*.gc* */*/*/*/*.gc* rm -f *.o */*.o */*/*.o */*/*/*.o */*/*/*/*.o rm -f coverage.txt coverage.xml coverage*.html gcovr-3.2/gcovr/tests/nested/README000066400000000000000000000001441235605057100171250ustar00rootroot00000000000000This test case was inspired by ticket #3884: https://software.sandia.gov/trac/fast/ticket/3884 gcovr-3.2/gcovr/tests/nested/reference/000077500000000000000000000000001235605057100202045ustar00rootroot00000000000000gcovr-3.2/gcovr/tests/nested/reference/coverage.html000066400000000000000000000315711235605057100226740ustar00rootroot00000000000000 Head
GCC Code Coverage Report
Directory: . Exec Total Coverage
Date: 2014-07-03 Lines: 29 43 67.4 %
Legend: low: < 75.0 % medium: >= 75.0 % high: >= 90.0 % Branches: 6 14 42.9 %

File Lines Branches
A/C/D/file6.cpp
75.0 % 3 / 4 50.0 % 1 / 2
A/C/file5.cpp
75.0 % 3 / 4 50.0 % 1 / 2
A/file1.cpp
75.0 % 3 / 4 50.0 % 1 / 2
A/file2.cpp
57.1 % 4 / 7 100.0 % 0 / 0
A/file3.cpp
44.4 % 4 / 9 0.0 % 0 / 2
A/file4.cpp
75.0 % 3 / 4 50.0 % 1 / 2
A/file7.cpp
0.0 % 0 / 2 100.0 % 0 / 0
B/main.cpp
100.0 % 9 / 9 50.0 % 2 / 4


gcovr-3.2/gcovr/tests/nested/reference/coverage.txt000066400000000000000000000022131235605057100225360ustar00rootroot00000000000000------------------------------------------------------------------------------ GCC Code Coverage Report Directory: subdir ------------------------------------------------------------------------------ File Lines Exec Cover Missing ------------------------------------------------------------------------------ A/C/D/file6.cpp 4 3 75% 4 A/C/file5.cpp 4 3 75% 4 A/file1.cpp 4 3 75% 4 A/file2.cpp 7 4 57% 8,10-11 A/file3.cpp 9 4 44% 8,10-12,14 A/file4.cpp 4 3 75% 6 A/file7.cpp 2 0 0% 1,3 B/main.cpp 9 9 100% ------------------------------------------------------------------------------ TOTAL 43 29 67% ------------------------------------------------------------------------------ gcovr-3.2/gcovr/tests/nested/reference/coverage.xml000066400000000000000000000104401235605057100225200ustar00rootroot00000000000000 subdir gcovr-3.2/gcovr/tests/nested/subdir/000077500000000000000000000000001235605057100175365ustar00rootroot00000000000000gcovr-3.2/gcovr/tests/nested/subdir/A/000077500000000000000000000000001235605057100177165ustar00rootroot00000000000000gcovr-3.2/gcovr/tests/nested/subdir/A/C/000077500000000000000000000000001235605057100201005ustar00rootroot00000000000000gcovr-3.2/gcovr/tests/nested/subdir/A/C/D/000077500000000000000000000000001235605057100202635ustar00rootroot00000000000000gcovr-3.2/gcovr/tests/nested/subdir/A/C/D/file6.cpp000066400000000000000000000001241235605057100217710ustar00rootroot00000000000000int foo6(int param) { if (param) { return 1; } else { return 0; } } gcovr-3.2/gcovr/tests/nested/subdir/A/C/file5.cpp000066400000000000000000000001241235605057100216050ustar00rootroot00000000000000int foo5(int param) { if (param) { return 1; } else { return 0; } } gcovr-3.2/gcovr/tests/nested/subdir/A/file1.cpp000066400000000000000000000001231235605057100214160ustar00rootroot00000000000000int foo(int param) { if (param) { return 1; } else { return 0; } } gcovr-3.2/gcovr/tests/nested/subdir/A/file2.cpp000066400000000000000000000001211235605057100214150ustar00rootroot00000000000000int bar() { int x=1; int y=2; return x+y; } int bar_() { int x=1; return 2*x; } gcovr-3.2/gcovr/tests/nested/subdir/A/file3.cpp000066400000000000000000000003511235605057100214230ustar00rootroot00000000000000int fourbar() { int x=1; int y=2; return x+y; } int fourbar_() { int x=1; if (x) return 2*x; /* This is a really long comment that confirms whether gcovr colors lines that exceed normal expectations. */ else return x; } gcovr-3.2/gcovr/tests/nested/subdir/A/file4.cpp000066400000000000000000000001261235605057100214240ustar00rootroot00000000000000int foobar(int param) { if (param) { return 1; } else { return 0; } } gcovr-3.2/gcovr/tests/nested/subdir/A/file7.cpp000066400000000000000000000000361235605057100214270ustar00rootroot00000000000000int uncovered() { return 0; } gcovr-3.2/gcovr/tests/nested/subdir/B/000077500000000000000000000000001235605057100177175ustar00rootroot00000000000000gcovr-3.2/gcovr/tests/nested/subdir/B/main.cpp000066400000000000000000000004751235605057100213550ustar00rootroot00000000000000#include extern int foo(int param); extern int foobar(int param); extern int bar(); extern int fourbar(); extern int foo5(int param); extern int foo6(int param); extern int uncovered(); int main(int argc, char* argv[]) { foo(0); foobar(1); bar(); fourbar(); foo5(0); foo6(0); return 0; } gcovr-3.2/gcovr/tests/oos/000077500000000000000000000000001235605057100155645ustar00rootroot00000000000000gcovr-3.2/gcovr/tests/oos/Makefile000066400000000000000000000011771235605057100172320ustar00rootroot00000000000000CFLAGS= -fprofile-arcs -ftest-coverage -fPIC all: mkdir -p build $(CXX) $(CFLAGS) -c src/file1.cpp -o build/file1.o $(CXX) $(CFLAGS) -c src/main.cpp -o build/main.o $(CXX) $(CFLAGS) build/main.o build/file1.o -o build/testcase run: txt xml html GCOVR_TEST_OPTIONS = -r '.' txt: build/testcase ../../../scripts/gcovr $(GCOVR_TEST_OPTIONS) -d -o coverage.txt xml: build/testcase ../../../scripts/gcovr $(GCOVR_TEST_OPTIONS) -d -x -o coverage.xml html: build/testcase ../../../scripts/gcovr $(GCOVR_TEST_OPTIONS) -d --html --html-details -o coverage.html clean: rm -f build/* rm -f coverage.txt coverage.xml coverage*.html gcovr-3.2/gcovr/tests/oos/README000066400000000000000000000002021235605057100164360ustar00rootroot00000000000000This test case was inspired by github ticket #31: https://github.com/gcovr/gcovr/issues/31 This tests out of source builds. gcovr-3.2/gcovr/tests/oos/reference/000077500000000000000000000000001235605057100175225ustar00rootroot00000000000000gcovr-3.2/gcovr/tests/oos/reference/coverage.html000066400000000000000000000203621235605057100222060ustar00rootroot00000000000000 Head
GCC Code Coverage Report
Directory: src/ Exec Total Coverage
Date: 2014-07-04 Lines: 9 10 90.0 %
Legend: low: < 75.0 % medium: >= 75.0 % high: >= 90.0 % Branches: 3 6 50.0 %

File Lines Branches
file1.cpp
100.0 % 2 / 2 100.0 % 0 / 0
main.cpp
87.5 % 7 / 8 50.0 % 3 / 6


gcovr-3.2/gcovr/tests/oos/reference/coverage.txt000066400000000000000000000013351235605057100220600ustar00rootroot00000000000000------------------------------------------------------------------------------ GCC Code Coverage Report Directory: . ------------------------------------------------------------------------------ File Lines Exec Cover Missing ------------------------------------------------------------------------------ src/file1.cpp 2 2 100% src/main.cpp 8 7 87% 7 ------------------------------------------------------------------------------ TOTAL 10 9 90% ------------------------------------------------------------------------------ gcovr-3.2/gcovr/tests/oos/reference/coverage.xml000066400000000000000000000024561235605057100220460ustar00rootroot00000000000000 . gcovr-3.2/gcovr/tests/oos/src/000077500000000000000000000000001235605057100163535ustar00rootroot00000000000000gcovr-3.2/gcovr/tests/oos/src/file1.cpp000066400000000000000000000000311235605057100200510ustar00rootroot00000000000000 int bar() { return 0; } gcovr-3.2/gcovr/tests/oos/src/main.cpp000066400000000000000000000004231235605057100200020ustar00rootroot00000000000000#include int bar(); int foo(int param) { if (param) { return 1; //std::cout << "param not null." << std::endl; } else { return 0; //std::cout << "param is null." << std::endl; } } int main(int argc, char* argv[]) { foo(bar()); return 0; } gcovr-3.2/gcovr/tests/shared_lib/000077500000000000000000000000001235605057100170605ustar00rootroot00000000000000gcovr-3.2/gcovr/tests/shared_lib/Makefile000066400000000000000000000022221235605057100205160ustar00rootroot00000000000000CFLAGS=-fprofile-arcs -ftest-coverage INSTALL ?= install -p BASE_OS:=$(shell uname | cut -d'-' -f1) ifeq ($(BASE_OS),Darwin) DYNLIB_EXT = dylib CFLAGS += -fPIC SOFLAGS += -dynamiclib -undefined dynamic_lookup endif ifeq ($(BASE_OS),CYGWIN_NT) DYNLIB_EXT = dll #DEFINES += -mno-cygwin #SOFLAGS += -shared -wl,--kill-at SOFLAGS += -shared endif ifeq ($(BASE_OS),Linux) DYNLIB_EXT = so CFLAGS += -fPIC SOFLAGS += -shared endif ifndef DYNLIB_EXT $(error ERROR: platform $(BASE_OS) not supported) endif all: $(INSTALL) -d obj $(CXX) $(CFLAGS) -c lib/lib.cpp -o obj/libs.o $(CXX) $(CFLAGS) $(SOFLAGS) -shared obj/libs.o -o lib/libs.$(DYNLIB_EXT) $(MAKE) -C testApp run: txt xml html txt: LD_LIBRARY_PATH=`pwd`/lib testApp/test/a.out ../../../scripts/gcovr -r . -d -o coverage.txt xml: LD_LIBRARY_PATH=`pwd`/lib testApp/test/a.out ../../../scripts/gcovr -r . -d -x -o coverage.xml html: LD_LIBRARY_PATH=`pwd`/lib testApp/test/a.out ../../../scripts/gcovr -r . -d --html --html-details -o coverage.html clean: rm -rf obj rm -f lib/*.so rm -f coverage.xml coverage.txt coverage*.html $(MAKE) -C testApp clean gcovr-3.2/gcovr/tests/shared_lib/README000066400000000000000000000002521235605057100177370ustar00rootroot00000000000000This test case was originally submitted for ticket #3860: https://software.sandia.gov/trac/fast/ticket/3860 and has been modified to be used as a standard test case.gcovr-3.2/gcovr/tests/shared_lib/lib/000077500000000000000000000000001235605057100176265ustar00rootroot00000000000000gcovr-3.2/gcovr/tests/shared_lib/lib/lib.cpp000066400000000000000000000001461235605057100211010ustar00rootroot00000000000000#include int print(int test) { if ( ! test ) return 1; else return 2; } gcovr-3.2/gcovr/tests/shared_lib/lib/lib.h000066400000000000000000000000201235605057100205350ustar00rootroot00000000000000int print(int); gcovr-3.2/gcovr/tests/shared_lib/reference/000077500000000000000000000000001235605057100210165ustar00rootroot00000000000000gcovr-3.2/gcovr/tests/shared_lib/reference/coverage.html000066400000000000000000000170231235605057100235020ustar00rootroot00000000000000 Head
GCC Code Coverage Report
Directory: . Exec Total Coverage
Date: 2013-07-02 Lines: 8 10 80.0 %
Legend: low: < 75.0 % medium: >= 75.0 % high: >= 90.0 % Branches: 4 8 50.0 %

File Lines Branches
lib/lib.cpp
80.0 % 4 / 5 50.0 % 3 / 6
tmp.cpp
80.0 % 4 / 5 50.0 % 1 / 2


gcovr-3.2/gcovr/tests/shared_lib/reference/coverage.txt000066400000000000000000000013361235605057100233550ustar00rootroot00000000000000------------------------------------------------------------------------------ GCC Code Coverage Report Directory: . ------------------------------------------------------------------------------ File Lines Exec Cover Missing ------------------------------------------------------------------------------ lib/lib.cpp 5 4 80% 8 tmp.cpp 5 4 80% 6 ------------------------------------------------------------------------------ TOTAL 10 8 80% ------------------------------------------------------------------------------ gcovr-3.2/gcovr/tests/shared_lib/reference/coverage.xml000066400000000000000000000031651235605057100233400ustar00rootroot00000000000000 . gcovr-3.2/gcovr/tests/shared_lib/testApp/000077500000000000000000000000001235605057100205005ustar00rootroot00000000000000gcovr-3.2/gcovr/tests/shared_lib/testApp/Makefile000066400000000000000000000003171235605057100221410ustar00rootroot00000000000000CFLAGS=-fprofile-arcs -ftest-coverage INSTALL ?= install -p all: test/a.out test/a.out: tmp.cpp $(INSTALL) -d $(dir $@) $(CXX) $(CFLAGS) -I../lib/ $^ -o $@ -L../lib -ls clean: rm -rf test rm -f *.gc* gcovr-3.2/gcovr/tests/shared_lib/testApp/tmp.cpp000066400000000000000000000001611235605057100220020ustar00rootroot00000000000000#include "lib.h" int main(int argc, char** argv) { if ( argc > 1 ) print(1); else print(0); } gcovr-3.2/gcovr/tests/test_gcovr.py000066400000000000000000000102221235605057100175120ustar00rootroot00000000000000#!/usr/bin/env python import os import os.path import re import sys import subprocess import traceback import pyutilib.th as unittest basedir = os.path.split(os.path.abspath(__file__))[0] starting_dir = os.getcwd() class GcovrTxt(unittest.TestCase): def __init__(self, *args, **kwds): unittest.TestCase.__init__(self, *args, **kwds) GcovrTxt = unittest.category('smoke')(GcovrTxt) class GcovrXml(unittest.TestCase): def __init__(self, *args, **kwds): unittest.TestCase.__init__(self, *args, **kwds) self.xml_re = re.compile('((timestamp)|(version))="[^"]*"') def compare_xml(self): F = open("coverage.xml") testData = self.xml_re.sub('\\1=""',F.read()).replace("\r","") F.close() F = open('coverage.xml', 'w') F.write(testData) F.close() F = open("reference/coverage.xml") refData = self.xml_re.sub('\\1=""',F.read()).replace("\r","") F.close() F = open('reference/coverage.xml', 'w') F.write(refData) F.close() #self.assertSequenceEqual(testData.split('\n'), refData.split('\n')) self.assertMatchesXmlBaseline('coverage.xml', os.path.join('reference','coverage.xml'), tolerance=1e-4) GcovrXml = unittest.category('smoke')(GcovrXml) class GcovrHtml(unittest.TestCase): def __init__(self, *args, **kwds): unittest.TestCase.__init__(self, *args, **kwds) self.xml_re = re.compile('((timestamp)|(version))="[^"]*"') def compare_html(self): F = open("coverage.html") testData = self.xml_re.sub('\\1=""',F.read()).replace("\r","") F.close() F = open('coverage.html', 'w') F.write(testData) F.close() F = open("reference/coverage.html") refData = self.xml_re.sub('\\1=""',F.read()).replace("\r","") F.close() F = open('reference/coverage.html', 'w') F.write(refData) F.close() #self.assertSequenceEqual(testData.split('\n'), refData.split('\n')) self.assertMatchesXmlBaseline('coverage.html', os.path.join('reference','coverage.html'), tolerance=1e-4) GcovrHtml = unittest.category('smoke')(GcovrHtml) def run(cmd): try: proc = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=os.environ ) print("STDOUT - START") sys.stdout.write("%s"%proc.communicate()[0]) print("STDOUT - END") return not proc.returncode except Exception: e = sys.exc_info()[1] sys.stdout.write("Caught unexpected exception in test driver: %s\n%s" % ( str(e), traceback.format_exc() )) raise @unittest.nottest def gcovr_test_txt(self, name): os.chdir(os.path.join(basedir,name)) run(["make","clean"]) or self.fail("Clean failed") run(["make"]) or self.fail("Make failed") run(["make","txt"]) or self.fail("Execution failed") self.assertFileEqualsBaseline("coverage.txt", "reference/coverage.txt") run(["make","clean"]) or self.fail("Clean failed") os.chdir(basedir) @unittest.nottest def gcovr_test_xml(self, name): os.chdir(os.path.join(basedir,name)) run(["make","clean"]) or self.fail("Clean failed") run(["make"]) or self.fail("Make failed") run(["make","xml"]) or self.fail("Execution failed") self.compare_xml() run(["make","clean"]) or self.fail("Clean failed") os.chdir(basedir) @unittest.nottest def gcovr_test_html(self, name): os.chdir(os.path.join(basedir,name)) run(["make"]) or self.fail("Make failed") run(["make","html"]) or self.fail("Execution failed") self.compare_html() run(["make","clean"]) or self.fail("Clean failed") os.chdir(basedir) skip_dirs = [ '.', '..', '.svn' ] for f in os.listdir(basedir): if os.path.isdir(os.path.join(basedir,f)) and f not in skip_dirs: if 'pycache' in f: continue GcovrTxt.add_fn_test(fn=gcovr_test_txt, name=f) GcovrXml.add_fn_test(fn=gcovr_test_xml, name=f) #GcovrHtml.add_fn_test(fn=gcovr_test_html, name=f) if __name__ == "__main__": unittest.main() gcovr-3.2/scripts/000077500000000000000000000000001235605057100141715ustar00rootroot00000000000000gcovr-3.2/scripts/gcovr000077500000000000000000002052321235605057100152430ustar00rootroot00000000000000#! /usr/bin/env python # # A report generator for gcov 3.4 # # This routine generates a format that is similar to the format generated # by the Python coverage.py module. This code is similar to the # data processing performed by lcov's geninfo command. However, we # don't worry about parsing the *.gcna files, and backwards compatibility for # older versions of gcov is not supported. # # Outstanding issues # - verify that gcov 3.4 or newer is being used # - verify support for symbolic links # # gcovr is a FAST project. For documentation, bug reporting, and # updates, see https://software.sandia.gov/trac/fast/wiki/gcovr # # _________________________________________________________________________ # # Gcovr: A parsing and reporting tool for gcov # Copyright (c) 2013 Sandia Corporation. # This software is distributed under the BSD License. # Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, # the U.S. Government retains certain rights in this software. # For more information, see the README.md file. # _________________________________________________________________________ # # $Revision$ # $Date$ # try: import html except: import cgi as html import copy import glob import os import re import subprocess import sys import time import xml.dom.minidom import datetime import posixpath from optparse import OptionParser from string import Template from os.path import normpath medium_coverage = 75.0 high_coverage = 90.0 low_color = "LightPink" medium_color = "#FFFF55" high_color = "LightGreen" covered_color = "LightGreen" uncovered_color = "LightPink" __version__ = "3.2" src_revision = "$Revision$" output_re = re.compile("[Cc]reating [`'](.*)'$") source_re = re.compile("cannot open (source|graph) file") starting_dir = os.getcwd() exclude_line_flag = "_EXCL_" exclude_line_pattern = re.compile('([GL]COVR?)_EXCL_(LINE|START|STOP)') c_style_comment_pattern = re.compile('/\*.*?\*/') cpp_style_comment_pattern = re.compile('//.*?$') def version_str(): ans = __version__ m = re.match('\$Revision:\s*(\S+)\s*\$', src_revision) if m: ans = ans + " (r%s)" % (m.group(1)) return ans # # Container object for coverage statistics # class CoverageData(object): def __init__(self, fname, uncovered, uncovered_exceptional, covered, branches, noncode): self.fname=fname # Shallow copies are cheap & "safe" because the caller will # throw away their copies of covered & uncovered after calling # us exactly *once* self.uncovered = copy.copy(uncovered) self.uncovered_exceptional = copy.copy(uncovered_exceptional) self.covered = copy.copy(covered) self.noncode = copy.copy(noncode) # But, a deep copy is required here self.all_lines = copy.deepcopy(uncovered) self.all_lines.update(uncovered_exceptional) self.all_lines.update(covered.keys()) self.branches = copy.deepcopy(branches) def update(self, uncovered, uncovered_exceptional, covered, branches, noncode): self.all_lines.update(uncovered) self.all_lines.update(uncovered_exceptional) self.all_lines.update(covered.keys()) self.uncovered.update(uncovered) self.uncovered_exceptional.update(uncovered_exceptional) self.noncode.intersection_update(noncode) for k in covered.keys(): self.covered[k] = self.covered.get(k,0) + covered[k] for k in branches.keys(): for b in branches[k]: d = self.branches.setdefault(k, {}) d[b] = d.get(b, 0) + branches[k][b] self.uncovered.difference_update(self.covered.keys()) self.uncovered_exceptional.difference_update(self.covered.keys()) def uncovered_str(self, exceptional): if options.show_branch: # Don't do any aggregation on branch results tmp = [] for line in self.branches.keys(): for branch in self.branches[line]: if self.branches[line][branch] == 0: tmp.append(line) break tmp.sort() return ",".join([str(x) for x in tmp]) or "" if exceptional: tmp = list(self.uncovered_exceptional) else: tmp = list(self.uncovered) if len(tmp) == 0: return "" tmp.sort() first = None last = None ranges=[] for item in tmp: if last is None: first=item last=item elif item == (last+1): last=item else: if len(self.noncode.intersection(range(last+1,item))) \ == item - last - 1: last = item continue if first==last: ranges.append(str(first)) else: ranges.append(str(first)+"-"+str(last)) first=item last=item if first==last: ranges.append(str(first)) else: ranges.append(str(first)+"-"+str(last)) return ",".join(ranges) def coverage(self): if ( options.show_branch ): total = 0 cover = 0 for line in self.branches.keys(): for branch in self.branches[line].keys(): total += 1 cover += self.branches[line][branch] > 0 and 1 or 0 else: total = len(self.all_lines) cover = len(self.covered) percent = total and str(int(100.0*cover/total)) or "--" return (total, cover, percent) def summary(self): tmp = options.root_filter.sub('',self.fname) if not self.fname.endswith(tmp): # Do no truncation if the filter does not start matching at # the beginning of the string tmp = self.fname tmp = tmp.ljust(40) if len(tmp) > 40: tmp=tmp+"\n"+" "*40 (total, cover, percent) = self.coverage() uncovered_lines = self.uncovered_str(False) if not options.show_branch: t = self.uncovered_str(True) if len(t): uncovered_lines += " [* " + t + "]"; return ( total, cover, tmp + str(total).rjust(8) + str(cover).rjust(8) + \ percent.rjust(6) + "% " + uncovered_lines ) def resolve_symlinks(orig_path): """ Return the normalized absolute path name with all symbolic links resolved """ return os.path.realpath(orig_path) # WEH - why doesn't os.path.realpath() suffice here? # drive,tmp = os.path.splitdrive(os.path.abspath(orig_path)) if not drive: drive = os.path.sep parts = tmp.split(os.path.sep) actual_path = [drive] while parts: actual_path.append(parts.pop(0)) if not os.path.islink(os.path.join(*actual_path)): continue actual_path[-1] = os.readlink(os.path.join(*actual_path)) tmp_drive, tmp_path = os.path.splitdrive( resolve_symlinks(os.path.join(*actual_path)) ) if tmp_drive: drive = tmp_drive actual_path = [drive] + tmp_path.split(os.path.sep) return os.path.join(*actual_path) # # Class that creates path aliases # class PathAliaser(object): def __init__(self): self.aliases = {} self.master_targets = set() self.preferred_name = {} def path_startswith(self, path, base): return path.startswith(base) and ( len(base) == len(path) or path[len(base)] == os.path.sep ) def master_path(self, path): match_found = False while True: for base, alias in self.aliases.items(): if self.path_startswith(path, base): path = alias + path[len(base):] match_found = True break for master_base in self.master_targets: if self.path_startswith(path, master_base): return path, master_base, True if match_found: sys.stderr.write( "(ERROR) violating fundamental assumption while walking " "directory tree.\n\tPlease report this to the gcovr " "developers.\n" ) return path, None, match_found def unalias_path(self, path): path = resolve_symlinks(path) path, master_base, known_path = self.master_path(path) if not known_path: return path # Try and resolve the preferred name for this location if master_base in self.preferred_name: return self.preferred_name[master_base] + path[len(master_base):] return path def add_master_target(self, master): self.master_targets.add(master) def add_alias(self, target, master): self.aliases[target] = master def set_preferred(self, master, preferred): self.preferred_name[master] = preferred aliases = PathAliaser() # This is UGLY. Here's why: UNIX resolves symbolic links by walking the # entire directory structure. What that means is that relative links # are always relative to the actual directory inode, and not the # "virtual" path that the user might have traversed (over symlinks) on # the way to that directory. Here's the canonical example: # # a / b / c / testfile # a / d / e --> ../../a/b # m / n --> /a # x / y / z --> /m/n/d # # If we start in "y", we will see the following directory structure: # y # |-- z # |-- e # |-- c # |-- testfile # # The problem is that using a simple traversal based on the Python # documentation: # # (os.path.join(os.path.dirname(path), os.readlink(result))) # # will not work: we will see a link to /m/n/d from /x/y, but completely # miss the fact that n is itself a link. If we then naively attempt to # apply the "c" relative link, we get an intermediate path that looks # like "/m/n/d/e/../../a/b", which would get normalized to "/m/n/a/b"; a # nonexistant path. The solution is that we need to walk the original # path, along with the full path of all links 1 directory at a time and # check for embedded symlinks. # # # NB: Users have complained that this code causes a performance issue. # I have replaced this logic with os.walk(), which works for Python >= 2.6 # def link_walker(path): if sys.version_info >= (2,6): for root, dirs, files in os.walk(os.path.abspath(path), followlinks=True): yield os.path.abspath(os.path.realpath(root)), dirs, files else: targets = [os.path.abspath(path)] while targets: target_dir = targets.pop(0) actual_dir = resolve_symlinks(target_dir) #print "target dir: %s (%s)" % (target_dir, actual_dir) master_name, master_base, visited = aliases.master_path(actual_dir) if visited: #print " ...root already visited as %s" % master_name aliases.add_alias(target_dir, master_name) continue if master_name != target_dir: aliases.set_preferred(master_name, target_dir) aliases.add_alias(target_dir, master_name) aliases.add_master_target(master_name) #print " ...master name = %s" % master_name #print " ...walking %s" % target_dir for root, dirs, files in os.walk(target_dir, topdown=True): #print " ...reading %s" % root for d in dirs: tmp = os.path.abspath(os.path.join(root, d)) #print " ...checking %s" % tmp if os.path.islink(tmp): #print " ...buffering link %s" % tmp targets.append(tmp) yield root, dirs, files def search_file(expr, path): """ Given a search path, recursively descend to find files that match a regular expression. """ ans = [] pattern = re.compile(expr) if path is None or path == ".": path = os.getcwd() elif not os.path.exists(path): raise IOError("Unknown directory '"+path+"'") for root, dirs, files in link_walker(path): for name in files: if pattern.match(name): name = os.path.join(root,name) if os.path.islink(name): ans.append( os.path.abspath(os.readlink(name)) ) else: ans.append( os.path.abspath(name) ) return ans # # Get the list of datafiles in the directories specified by the user # def get_datafiles(flist, options): allfiles=set() for dir_ in flist: if options.gcov_files: if options.verbose: sys.stdout.write( "Scanning directory %s for gcov files...\n" % (dir_, ) ) files = search_file(".*\.gcov$", dir_) gcov_files = [file for file in files if file.endswith('gcov')] if options.verbose: sys.stdout.write( "Found %d files (and will process %d)\n" % ( len(files), len(gcov_files) ) ) allfiles.update(gcov_files) else: if options.verbose: sys.stdout.write( "Scanning directory %s for gcda/gcno files...\n" % (dir_, ) ) files = search_file(".*\.gc(da|no)$", dir_) # gcno files will *only* produce uncovered results; however, # that is useful information for the case where a compilation # unit is never actually exercised by the test code. So, we # will process gcno files, but ONLY if there is no corresponding # gcda file. gcda_files = [file for file in files if file.endswith('gcda')] tmp = set(gcda_files) gcno_files = [ file for file in files if file.endswith('gcno') and file[:-2]+'da' not in tmp ] if options.verbose: sys.stdout.write( "Found %d files (and will process %d)\n" % ( len(files), len(gcda_files) + len(gcno_files) ) ) allfiles.update(gcda_files) allfiles.update(gcno_files) return allfiles # # Process a single gcov datafile # def process_gcov_data(data_fname, covdata, options): INPUT = open(data_fname,"r") # # Get the filename # line = INPUT.readline() segments=line.split(':',3) if len(segments) != 4 or not segments[2].lower().strip().endswith('source'): raise RuntimeError('Fatal error parsing gcov file, line 1: \n\t"%s"' % line.rstrip()) currdir = os.getcwd() os.chdir(starting_dir) if sys.version_info >= (2,6): fname = os.path.abspath((segments[-1]).strip()) else: fname = aliases.unalias_path(os.path.abspath((segments[-1]).strip())) os.chdir(currdir) if options.verbose: sys.stdout.write("Parsing coverage data for file %s\n" % fname) # # Return if the filename does not match the filter # filtered_fname = None for i in range(0,len(options.filter)): if options.filter[i].match(fname): filtered_fname = options.root_filter.sub('',fname) break if filtered_fname is None: if options.verbose: sys.stdout.write(" Filtering coverage data for file %s\n" % fname) return # # Return if the filename matches the exclude pattern # for i in range(0,len(options.exclude)): if (filtered_fname is not None and options.exclude[i].match(filtered_fname)) or \ options.exclude[i].match(fname) or \ options.exclude[i].match(os.path.abspath(fname)): if options.verbose: sys.stdout.write(" Excluding coverage data for file %s\n" % fname) return # # Parse each line, and record the lines # that are uncovered # excluding = [] noncode = set() uncovered = set() uncovered_exceptional = set() covered = {} branches = {} #first_record=True lineno = 0 last_code_line = "" last_code_lineno = 0 last_code_line_excluded = False for line in INPUT: segments=line.split(":",2) tmp = segments[0].strip() if len(segments) > 1: try: lineno = int(segments[1].strip()) except: pass # keep previous line number! if exclude_line_flag in line: excl_line = False for header, flag in exclude_line_pattern.findall(line): if flag == 'START': excluding.append((header, lineno)) elif flag == 'STOP': if excluding: _header, _line = excluding.pop() if _header != header: sys.stderr.write( "(WARNING) %s_EXCL_START found on line %s " "was terminated by %s_EXCL_STOP on line %s, " "when processing %s\n" % (_header, _line, header, lineno, fname) ) else: sys.stderr.write( "(WARNING) mismatched coverage exclusion flags.\n" "\t%s_EXCL_STOP found on line %s without " "corresponding %s_EXCL_START, when processing %s\n" % (header, lineno, header, fname) ) elif flag == 'LINE': # We buffer the line exclusion so that it is always # the last thing added to the exclusion list (and so # only ONE is ever added to the list). This guards # against cases where puts a _LINE and _START (or # _STOP) on the same line... it also guards against # duplicate _LINE flags. excl_line = True if excl_line: excluding.append(False) is_code_statement = False if tmp[0] == '-' or (excluding and tmp[0] in "#=0123456789"): is_code_statement = True code = segments[2].strip() # remember certain non-executed lines if excluding or len(code) == 0 or code == "{" or code == "}" or \ code.startswith("//") or code == 'else': noncode.add( lineno ) elif tmp[0] == '#': is_code_statement = True uncovered.add( lineno ) elif tmp[0] == '=': is_code_statement = True uncovered_exceptional.add( lineno ) elif tmp[0] in "0123456789": is_code_statement = True covered[lineno] = int(segments[0].strip()) elif tmp.startswith('branch'): exclude_branch = False if options.exclude_unreachable_branches and lineno == last_code_lineno: if last_code_line_excluded: exclude_branch = True exclude_reason = "marked with exclude pattern" else: code = last_code_line code = re.sub(cpp_style_comment_pattern, '', code) code = re.sub(c_style_comment_pattern, '', code) code = code.strip() code_nospace = code.replace(' ', '') exclude_branch = len(code) == 0 or code == '{' or code == '}' or code_nospace == '{}' exclude_reason = "detected as compiler-generated code" if exclude_branch: if options.verbose: sys.stdout.write("Excluding unreachable branch on line %d in file %s (%s).\n" % (lineno, fname, exclude_reason)) else: fields = line.split() try: count = int(fields[3]) except: count = 0 branches.setdefault(lineno, {})[int(fields[1])] = count elif tmp.startswith('call'): pass elif tmp.startswith('function'): pass elif tmp[0] == 'f': pass #if first_record: #first_record=False #uncovered.add(prev) #if prev in uncovered: #tokens=re.split('[ \t]+',tmp) #if tokens[3] != "0": #uncovered.remove(prev) #prev = int(segments[1].strip()) #first_record=True else: sys.stderr.write( "(WARNING) Unrecognized GCOV output: '%s'\n" "\tThis is indicitive of a gcov output parse error.\n" "\tPlease report this to the gcovr developers." % tmp ) # save the code line to use it later with branches if is_code_statement: last_code_line = "".join(segments[2:]) last_code_lineno = lineno last_code_line_excluded = False if excluding: last_code_line_excluded = True # clear the excluding flag for single-line excludes if excluding and not excluding[-1]: excluding.pop() ##print 'uncovered',uncovered ##print 'covered',covered ##print 'branches',branches ##print 'noncode',noncode # # If the file is already in covdata, then we # remove lines that are covered here. Otherwise, # initialize covdata # if not fname in covdata: covdata[fname] = CoverageData(fname,uncovered,uncovered_exceptional,covered,branches,noncode) else: covdata[fname].update(uncovered,uncovered_exceptional,covered,branches,noncode) INPUT.close() for header, line in excluding: sys.stderr.write("(WARNING) The coverage exclusion region start flag " "%s_EXCL_START\n\ton line %d did not have " "corresponding %s_EXCL_STOP flag\n\t in file %s.\n" % (header, line, header, fname)) # # Process a datafile (generated by running the instrumented application) # and run gcov with the corresponding arguments # # This is trickier than it sounds: The gcda/gcno files are stored in the # same directory as the object files; however, gcov must be run from the # same directory where gcc/g++ was run. Normally, the user would know # where gcc/g++ was invoked from and could tell gcov the path to the # object (and gcda) files with the --object-directory command. # Unfortunately, we do everything backwards: gcovr looks for the gcda # files and then has to infer the original gcc working directory. # # In general, (but not always) we can assume that the gcda file is in a # subdirectory of the original gcc working directory, so we will first # try ".", and on error, move up the directory tree looking for the # correct working directory (letting gcov's own error codes dictate when # we hit the right directory). This covers 90+% of the "normal" cases. # The exception to this is if gcc was invoked with "-o ../[...]" (i.e., # the object directory was a peer (not a parent/child) of the cwd. In # this case, things are really tough. We accept an argument # (--object-directory) that SHOULD BE THE SAME as the one povided to # gcc. We will then walk that path (backwards) in the hopes of # identifying the original gcc working directory (there is a bit of # trial-and-error here) # def process_datafile(filename, covdata, options): # # Launch gcov # abs_filename = os.path.abspath(filename) (dirname,fname) = os.path.split(abs_filename) #(name,ext) = os.path.splitext(base) potential_wd = [] errors=[] Done = False if options.objdir: src_components = abs_filename.split(os.sep) components = normpath(options.objdir).split(os.sep) idx = 1 while idx <= len(components): if idx > len(src_components): break if components[-1*idx] != src_components[-1*idx]: break idx += 1 if idx > len(components): pass # a parent dir; the normal process will find it elif components[-1*idx] == '..': # NB: os.path.join does not re-add leading '/' characters!?! dirs = [ os.path.sep.join(src_components[:len(src_components)-idx]) ] while idx <= len(components) and components[-1*idx] == '..': tmp = [] for d in dirs: for f in os.listdir(d): x = os.path.join(d,f) if os.path.isdir(x): tmp.append(x) dirs = tmp idx += 1 potential_wd = dirs else: if components[0] == '': # absolute path tmp = [ options.objdir ] else: # relative path: check relative to both the cwd and the # gcda file tmp = [ os.path.join(x, options.objdir) for x in [os.path.dirname(abs_filename), os.getcwd()] ] potential_wd = [ testdir for testdir in tmp if os.path.isdir(testdir) ] if len(potential_wd) == 0: errors.append("ERROR: cannot identify the location where GCC " "was run using --object-directory=%s\n" % options.objdir) # Revert to the normal #sys.exit(1) # no objdir was specified (or it was a parent dir); walk up the dir tree if len(potential_wd) == 0: wd = os.path.split(abs_filename)[0] while True: potential_wd.append(wd) wd = os.path.split(wd)[0] if wd == potential_wd[-1]: break cmd = [ options.gcov_cmd, abs_filename, "--branch-counts", "--branch-probabilities", "--preserve-paths", '--object-directory', dirname ] # NB: Currently, we will only parse English output env = dict(os.environ) env['LC_ALL'] = 'en_US' while len(potential_wd) > 0 and not Done: # NB: either len(potential_wd) == 1, or all entires are absolute # paths, so we don't have to chdir(starting_dir) at every # iteration. os.chdir(potential_wd.pop(0)) #if options.objdir: # cmd.extend(["--object-directory", Template(options.objdir).substitute(filename=filename, head=dirname, tail=base, root=name, ext=ext)]) if options.verbose: sys.stdout.write("Running gcov: '%s' in '%s'\n" % ( ' '.join(cmd), os.getcwd() )) (out, err) = subprocess.Popen( cmd, env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE ).communicate() out=out.decode('utf-8') err=err.decode('utf-8') # find the files that gcov created gcov_files = {'active':[], 'filter':[], 'exclude':[]} for line in out.splitlines(): found = output_re.search(line.strip()) if found is not None: fname = found.group(1) if not options.gcov_filter.match(fname): if options.verbose: sys.stdout.write("Filtering gcov file %s\n" % fname) gcov_files['filter'].append(fname) continue exclude=False for i in range(0,len(options.gcov_exclude)): if options.gcov_exclude[i].match(options.gcov_filter.sub('',fname)) or \ options.gcov_exclude[i].match(fname) or \ options.gcov_exclude[i].match(os.path.abspath(fname)): exclude=True break if not exclude: gcov_files['active'].append(fname) elif options.verbose: sys.stdout.write("Excluding gcov file %s\n" % fname) gcov_files['exclude'].append(fname) if source_re.search(err): # gcov tossed errors: try the next potential_wd errors.append(err) else: # Process *.gcov files for fname in gcov_files['active']: process_gcov_data(fname, covdata, options) Done = True if not options.keep: for group in gcov_files.values(): for fname in group: if os.path.exists(fname): # Only remove files that actually exist. os.remove(fname) os.chdir(starting_dir) if options.delete: if not abs_filename.endswith('gcno'): os.remove(abs_filename) if not Done: sys.stderr.write( "(WARNING) GCOV produced the following errors processing %s:\n" "\t %s" "\t(gcovr could not infer a working directory that resolved it.)\n" % ( filename, "\t ".join(errors) ) ) # # Process Already existing gcov files # def process_existing_gcov_file(filename, covdata, options): if not options.gcov_filter.match(filename): if options.verbose: sys.stdout.write("Filtering gcov file %s\n" % filename) return exclude=False for i in range(0,len(options.gcov_exclude)): if options.gcov_exclude[i].match(options.gcov_filter.sub('',filename)) or \ options.gcov_exclude[i].match(filename) or \ options.gcov_exclude[i].match(os.path.abspath(filename)): if options.verbose: sys.stdout.write("Excluding gcov file %s\n" % filename) return process_gcov_data(filename, covdata, options) if not options.keep: if os.path.exists(filename): # Only remove files that actually exist. os.remove(filename) # # Produce the classic gcovr text report # def print_text_report(covdata): def _num_uncovered(key): (total, covered, percent) = covdata[key].coverage() return total - covered def _percent_uncovered(key): (total, covered, percent) = covdata[key].coverage() if covered: return -1.0*covered/total else: return total or 1e6 def _alpha(key): return key if options.output: OUTPUT = open(options.output,'w') else: OUTPUT = sys.stdout total_lines=0 total_covered=0 # Header OUTPUT.write("-"*78 + '\n') OUTPUT.write(" "*27 + "GCC Code Coverage Report\n") OUTPUT.write("Directory: "+options.root+"\n") OUTPUT.write("-"*78 + '\n') a = options.show_branch and "Branches" or "Lines" b = options.show_branch and "Taken" or "Exec" c = "Missing" OUTPUT.write("File".ljust(40) + a.rjust(8) + b.rjust(8)+ " Cover " + c + "\n") OUTPUT.write("-"*78 + '\n') # Data keys = list(covdata.keys()) keys.sort(key=options.sort_uncovered and _num_uncovered or \ options.sort_percent and _percent_uncovered or _alpha) for key in keys: (t, n, txt) = covdata[key].summary() total_lines += t total_covered += n OUTPUT.write(txt + '\n') # Footer & summary OUTPUT.write("-"*78 + '\n') percent = total_lines and str(int(100.0*total_covered/total_lines)) or "--" OUTPUT.write("TOTAL".ljust(40) + str(total_lines).rjust(8) + \ str(total_covered).rjust(8) + str(percent).rjust(6)+"%" + '\n') OUTPUT.write("-"*78 + '\n') # Close logfile if options.output: OUTPUT.close() # # Prints a small report to the standard output # def print_summary(covdata): lines_total = 0 lines_covered = 0 branches_total = 0 branches_covered = 0 keys = list(covdata.keys()) for key in keys: options.show_branch = False (t, n, txt) = covdata[key].coverage() lines_total += t lines_covered += n options.show_branch = True (t, n, txt) = covdata[key].coverage() branches_total += t branches_covered += n percent = lines_total and (100.0*lines_covered/lines_total) percent_branches = branches_total and (100.0*branches_covered/branches_total) lines_out = "lines: %0.1f%% (%s out of %s)\n" % (percent, lines_covered, lines_total) branches_out = "branches: %0.1f%% (%s out of %s)\n" % (percent_branches, branches_covered, branches_total) sys.stdout.write(lines_out) sys.stdout.write(branches_out) # # CSS declarations for the HTML output # css = Template(''' body { color: #000000; background-color: #FFFFFF; } /* Link formats: use maroon w/underlines */ a:link { color: navy; text-decoration: underline; } a:visited { color: maroon; text-decoration: underline; } a:active { color: navy; text-decoration: underline; } /*** TD formats ***/ td { font-family: sans-serif; } td.title { text-align: center; padding-bottom: 10px; font-size: 20pt; font-weight: bold; } /* TD Header Information */ td.headerName { text-align: right; color: black; padding-right: 6px; font-weight: bold; vertical-align: top; white-space: nowrap; } td.headerValue { text-align: left; color: blue; font-weight: bold; white-space: nowrap; } td.headerTableEntry { text-align: right; color: black; font-weight: bold; white-space: nowrap; padding-left: 12px; padding-right: 4px; background-color: LightBlue; } td.headerValueLeg { text-align: left; color: black; font-size: 80%; white-space: nowrap; padding-left: 10px; padding-right: 10px; padding-top: 2px; } /* Color of horizontal ruler */ td.hr { background-color: navy; height:3px; } /* Footer format */ td.footer { text-align: center; padding-top: 3px; font-family: sans-serif; } /* Coverage Table */ td.coverTableHead { text-align: center; color: white; background-color: SteelBlue; font-family: sans-serif; font-size: 120%; white-space: nowrap; padding-left: 4px; padding-right: 4px; } td.coverFile { text-align: left; padding-left: 10px; padding-right: 20px; color: black; background-color: LightBlue; font-family: monospace; font-weight: bold; font-size: 110%; } td.coverBar { padding-left: 10px; padding-right: 10px; background-color: LightBlue; } td.coverBarOutline { background-color: white; } td.coverValue { padding-top: 2px; text-align: right; padding-left: 10px; padding-right: 10px; font-family: sans-serif; white-space: nowrap; font-weight: bold; } /* Link Details */ a.detail:link { color: #B8D0FF; font-size:80%; } a.detail:visited { color: #B8D0FF; font-size:80%; } a.detail:active { color: #FFFFFF; font-size:80%; } .graphcont{ color:#000; font-weight:700; float:left } .graph{ float:left; background-color: white; position:relative; width:280px; padding:0 } .graph .bar{ display:block; position:relative; border:black 1px solid; text-align:center; color:#fff; height:10px; font-family:Arial,Helvetica,sans-serif; font-size:12px; line-height:1.9em } .graph .bar span{ position:absolute; left:1em } td.coveredLine, span.coveredLine { background-color: ${covered_color}!important; } td.uncoveredLine, span.uncoveredLine { background-color: ${uncovered_color}!important; } .linecount { border-right: 1px gray solid; background-color: lightgray; } .src { padding-left: 12px; } .srcHeader { font-family: monospace; font-weight: bold; } pre { height : 15px; margin-top: 0; margin-bottom: 0; } .lineno { background-color: #EFE383; border-right: 1px solid #BBB15F; } ''') # # A string template for the root HTML output # root_page = Template(''' ${HEAD}
GCC Code Coverage Report
Directory: ${DIRECTORY} Exec Total Coverage
Date: ${DATE} Lines: ${LINES_EXEC} ${LINES_TOTAL} ${LINES_COVERAGE} %
Legend: low: < ${COVERAGE_MED} % medium: >= ${COVERAGE_MED} % high: >= ${COVERAGE_HIGH} % Branches: ${BRANCHES_EXEC} ${BRANCHES_TOTAL} ${BRANCHES_COVERAGE} %
${ROWS}

File Lines Branches


''') # # A string template for the source file HTML output # source_page = Template(''' ${HEAD}
GCC Code Coverage Report
Directory: ${DIRECTORY} Exec Total Coverage
File: ${FILENAME} Lines: ${LINES_EXEC} ${LINES_TOTAL} ${LINES_COVERAGE} %
Date: ${DATE} Branches: ${BRANCHES_EXEC} ${BRANCHES_TOTAL} ${BRANCHES_COVERAGE} %

${ROWS}
Line Exec Source


''') # # Produce an HTML report # def print_html_report(covdata, details): def _num_uncovered(key): (total, covered, percent) = covdata[key].coverage() return total - covered def _percent_uncovered(key): (total, covered, percent) = covdata[key].coverage() if covered: return -1.0*covered/total else: return total or 1e6 def _alpha(key): return key if options.output is None: details = False data = {} data['HEAD'] = "Head" data['VERSION'] = version_str() data['TIME'] = str(int(time.time())) data['DATE'] = datetime.date.today().isoformat() data['ROWS'] = [] data['low_color'] = low_color data['medium_color'] = medium_color data['high_color'] = high_color data['COVERAGE_MED'] = medium_coverage data['COVERAGE_HIGH'] = high_coverage data['CSS'] = css.substitute(low_color=low_color, medium_color=medium_color, high_color=high_color, covered_color=covered_color, uncovered_color=uncovered_color) data['DIRECTORY'] = '' branchTotal = 0 branchCovered = 0 options.show_branch = True for key in covdata.keys(): (total, covered, percent) = covdata[key].coverage() branchTotal += total branchCovered += covered data['BRANCHES_EXEC'] = str(branchCovered) data['BRANCHES_TOTAL'] = str(branchTotal) coverage = 0.0 if branchTotal == 0 else round(100.0*branchCovered / branchTotal,1) data['BRANCHES_COVERAGE'] = str(coverage) if coverage < medium_coverage: data['BRANCHES_COLOR'] = low_color elif coverage < high_coverage: data['BRANCHES_COLOR'] = medium_color else: data['BRANCHES_COLOR'] = high_color lineTotal = 0 lineCovered = 0 options.show_branch = False for key in covdata.keys(): (total, covered, percent) = covdata[key].coverage() lineTotal += total lineCovered += covered data['LINES_EXEC'] = str(lineCovered) data['LINES_TOTAL'] = str(lineTotal) coverage = 0.0 if lineTotal == 0 else round(100.0*lineCovered / lineTotal,1) data['LINES_COVERAGE'] = str(coverage) if coverage < medium_coverage: data['LINES_COLOR'] = low_color elif coverage < high_coverage: data['LINES_COLOR'] = medium_color else: data['LINES_COLOR'] = high_color # Generate the coverage output (on a per-package basis) #source_dirs = set() files = [] filtered_fname = '' keys = list(covdata.keys()) keys.sort(key=options.sort_uncovered and _num_uncovered or \ options.sort_percent and _percent_uncovered or _alpha) for f in keys: cdata = covdata[f] filtered_fname = options.root_filter.sub('',f) files.append(filtered_fname) cdata._filename = filtered_fname ttmp = os.path.abspath(options.output).split('.') if len(ttmp) > 1: cdata._sourcefile = '.'.join( ttmp[:-1] ) + '.' + cdata._filename.replace('/','_') + '.' + ttmp[-1] else: cdata._sourcefile = ttmp[0] + '.' + cdata._filename.replace('/','_') + '.html' # Define the common root directory, which may differ from options.root # when source files share a common prefix. if len(files) > 1: commondir = posixpath.commonprefix(files) if commondir != '': data['DIRECTORY'] = commondir else: dir_, file_ = os.path.split(filtered_fname) if dir_ != '': data['DIRECTORY'] = dir_ + os.sep for f in keys: cdata = covdata[f] class_lines = 0 class_hits = 0 class_branches = 0 class_branch_hits = 0 for line in cdata.all_lines: hits = cdata.covered.get(line, 0) class_lines += 1 if hits > 0: class_hits += 1 branches = cdata.branches.get(line) if branches is None: pass else: b_hits = 0 for v in branches.values(): if v > 0: b_hits += 1 coverage = 100*b_hits/len(branches) class_branch_hits += b_hits class_branches += len(branches) lines_covered = 100.0 if class_lines == 0 else 100.0*class_hits/class_lines branches_covered = 100.0 if class_branches == 0 else 100.0*class_branch_hits/class_branches data['ROWS'].append( html_row(details, cdata._sourcefile, directory=data['DIRECTORY'], filename=cdata._filename, LinesExec=class_hits, LinesTotal=class_lines, LinesCoverage=lines_covered, BranchesExec=class_branch_hits, BranchesTotal=class_branches, BranchesCoverage=branches_covered ) ) data['ROWS'] = '\n'.join(data['ROWS']) if data['DIRECTORY'] == '': data['DIRECTORY'] = "." htmlString = root_page.substitute(**data) if options.output is None: sys.stdout.write(htmlString+'\n') else: OUTPUT = open(options.output, 'w') OUTPUT.write(htmlString +'\n') OUTPUT.close() # Return, if no details are requested if not details: return # # Generate an HTML file for every source file # for f in keys: cdata = covdata[f] data['FILENAME'] = cdata._filename data['ROWS'] = '' options.show_branch = True branchTotal, branchCovered, tmp = cdata.coverage() data['BRANCHES_EXEC'] = str(branchCovered) data['BRANCHES_TOTAL'] = str(branchTotal) coverage = 0.0 if branchTotal == 0 else round(100.0*branchCovered / branchTotal,1) data['BRANCHES_COVERAGE'] = str(coverage) if coverage < medium_coverage: data['BRANCHES_COLOR'] = low_color elif coverage < high_coverage: data['BRANCHES_COLOR'] = medium_color else: data['BRANCHES_COLOR'] = high_color options.show_branch = False lineTotal, lineCovered, tmp = cdata.coverage() data['LINES_EXEC'] = str(lineCovered) data['LINES_TOTAL'] = str(lineTotal) coverage = 0.0 if lineTotal == 0 else round(100.0*lineCovered / lineTotal,1) data['LINES_COVERAGE'] = str(coverage) if coverage < medium_coverage: data['LINES_COLOR'] = low_color elif coverage < high_coverage: data['LINES_COLOR'] = medium_color else: data['LINES_COLOR'] = high_color data['ROWS'] = [] currdir = os.getcwd() os.chdir(root_dir) INPUT = open(data['FILENAME'], 'r') ctr = 1 for line in INPUT: data['ROWS'].append( source_row(ctr, line.rstrip(), cdata) ) ctr += 1 INPUT.close() os.chdir(currdir) data['ROWS'] = '\n'.join(data['ROWS']) htmlString = source_page.substitute(**data) OUTPUT = open(cdata._sourcefile, 'w') OUTPUT.write(htmlString +'\n') OUTPUT.close() def source_row(lineno, source, cdata): rowstr=Template('''
${lineno}
${linecount}
${source}
''') kwargs = {} kwargs['lineno'] = str(lineno) if lineno in cdata.covered: kwargs['covclass'] = 'coveredLine' kwargs['linecount'] = str(cdata.covered.get(lineno,0)) elif lineno in cdata.uncovered: kwargs['covclass'] = 'uncoveredLine' kwargs['linecount'] = '' else: kwargs['covclass'] = '' kwargs['linecount'] = '' kwargs['source'] = html.escape(source) return rowstr.substitute(**kwargs) # # Generate the table row for a single file # nrows = 0 def html_row(details, sourcefile, **kwargs): if options.relative_anchors: sourcefile = os.path.basename(sourcefile) rowstr=Template(''' ${filename}
${LinesCoverage} % ${LinesExec} / ${LinesTotal} ${BranchesCoverage} % ${BranchesExec} / ${BranchesTotal} ''') global nrows nrows += 1 if nrows % 2 == 0: kwargs['altstyle'] = 'style="background-color:LightSteelBlue"' else: kwargs['altstyle'] = '' if details: kwargs['filename'] = '%s' % (sourcefile, kwargs['filename'][len(kwargs['directory']):]) else: kwargs['filename'] = kwargs['filename'][len(kwargs['directory']):] kwargs['LinesCoverage'] = round(kwargs['LinesCoverage'],1) # Disable the border if the bar is too short to see the color if kwargs['LinesCoverage'] < 1e-7: kwargs['BarBorder'] = "border:white; " else: kwargs['BarBorder'] = "" if kwargs['LinesCoverage'] < medium_coverage: kwargs['LinesColor'] = low_color kwargs['LinesBar'] = 'red' elif kwargs['LinesCoverage'] < high_coverage: kwargs['LinesColor'] = medium_color kwargs['LinesBar'] = 'yellow' else: kwargs['LinesColor'] = high_color kwargs['LinesBar'] = 'green' kwargs['BranchesCoverage'] = round(kwargs['BranchesCoverage'],1) if kwargs['BranchesCoverage'] < medium_coverage: kwargs['BranchesColor'] = low_color kwargs['BranchesBar'] = 'red' elif kwargs['BranchesCoverage'] < high_coverage: kwargs['BranchesColor'] = medium_color kwargs['BranchesBar'] = 'yellow' else: kwargs['BranchesColor'] = high_color kwargs['BranchesBar'] = 'green' return rowstr.substitute(**kwargs) # # Produce an XML report in the Cobertura format # def print_xml_report(covdata): branchTotal = 0 branchCovered = 0 lineTotal = 0 lineCovered = 0 options.show_branch = True for key in covdata.keys(): (total, covered, percent) = covdata[key].coverage() branchTotal += total branchCovered += covered options.show_branch = False for key in covdata.keys(): (total, covered, percent) = covdata[key].coverage() lineTotal += total lineCovered += covered impl = xml.dom.minidom.getDOMImplementation() docType = impl.createDocumentType( "coverage", None, "http://cobertura.sourceforge.net/xml/coverage-03.dtd" ) doc = impl.createDocument(None, "coverage", docType) root = doc.documentElement root.setAttribute( "line-rate", lineTotal == 0 and '0.0' or str(float(lineCovered) / lineTotal) ) root.setAttribute( "branch-rate", branchTotal == 0 and '0.0' or str(float(branchCovered) / branchTotal) ) root.setAttribute( "timestamp", str(int(time.time())) ) root.setAttribute( "version", "gcovr %s" % (version_str(),) ) # Generate the element: this is either the root directory # (specified by --root), or the CWD. sources = doc.createElement("sources") root.appendChild(sources) # Generate the coverage output (on a per-package basis) packageXml = doc.createElement("packages") root.appendChild(packageXml) packages = {} source_dirs = set() keys = list(covdata.keys()) keys.sort() for f in keys: data = covdata[f] dir = options.root_filter.sub('',f) if f.endswith(dir): src_path = f[:-1*len(dir)] if len(src_path) > 0: while dir.startswith(os.path.sep): src_path += os.path.sep dir = dir[len(os.path.sep):] source_dirs.add(src_path) else: # Do no truncation if the filter does not start matching at # the beginning of the string dir = f (dir, fname) = os.path.split(dir) package = packages.setdefault( dir, [ doc.createElement("package"), {}, 0, 0, 0, 0 ] ) c = doc.createElement("class") # The Cobertura DTD requires a methods section, which isn't # trivial to get from gcov (so we will leave it blank) c.appendChild(doc.createElement("methods")) lines = doc.createElement("lines") c.appendChild(lines) class_lines = 0 class_hits = 0 class_branches = 0 class_branch_hits = 0 for line in data.all_lines: hits = data.covered.get(line, 0) class_lines += 1 if hits > 0: class_hits += 1 l = doc.createElement("line") l.setAttribute("number", str(line)) l.setAttribute("hits", str(hits)) branches = data.branches.get(line) if branches is None: l.setAttribute("branch", "false") else: b_hits = 0 for v in branches.values(): if v > 0: b_hits += 1 coverage = 100*b_hits/len(branches) l.setAttribute("branch", "true") l.setAttribute( "condition-coverage", "%i%% (%i/%i)" % (coverage, b_hits, len(branches)) ) cond = doc.createElement('condition') cond.setAttribute("number", "0") cond.setAttribute("type", "jump") cond.setAttribute("coverage", "%i%%" % ( coverage ) ) class_branch_hits += b_hits class_branches += float(len(branches)) conditions = doc.createElement("conditions") conditions.appendChild(cond) l.appendChild(conditions) lines.appendChild(l) className = fname.replace('.', '_') c.setAttribute("name", className) c.setAttribute("filename", os.path.join(dir, fname)) c.setAttribute("line-rate", str(class_hits / (1.0*class_lines or 1.0))) c.setAttribute( "branch-rate", str(class_branch_hits / (1.0*class_branches or 1.0)) ) c.setAttribute("complexity", "0.0") package[1][className] = c package[2] += class_hits package[3] += class_lines package[4] += class_branch_hits package[5] += class_branches keys = list(packages.keys()) keys.sort() for packageName in keys: packageData = packages[packageName] package = packageData[0]; packageXml.appendChild(package) classes = doc.createElement("classes") package.appendChild(classes) classNames = list(packageData[1].keys()) classNames.sort() for className in classNames: classes.appendChild(packageData[1][className]) package.setAttribute("name", packageName.replace(os.sep, '.')) package.setAttribute("line-rate", str(packageData[2]/(1.0*packageData[3] or 1.0))) package.setAttribute( "branch-rate", str(packageData[4] / (1.0*packageData[5] or 1.0) )) package.setAttribute("complexity", "0.0") # Populate the element: this is either the root directory # (specified by --root), or relative directories based # on the filter, or the CWD if options.root is not None: source = doc.createElement("source") source.appendChild(doc.createTextNode(options.root.strip())) sources.appendChild(source) elif len(source_dirs) > 0: cwd = os.getcwd() for d in source_dirs: source = doc.createElement("source") if d.startswith(cwd): reldir = d[len(cwd):].lstrip(os.path.sep) elif cwd.startswith(d): i = 1 while normpath(d) != normpath(os.path.join(*tuple([cwd]+['..']*i))): i += 1 reldir = os.path.join(*tuple(['..']*i)) else: reldir = d source.appendChild(doc.createTextNode(reldir.strip())) sources.appendChild(source) else: source = doc.createElement("source") source.appendChild(doc.createTextNode('.')) sources.appendChild(source) if options.prettyxml: import textwrap lines = doc.toprettyxml(" ").split('\n') for i in xrange(len(lines)): n=0 while n < len(lines[i]) and lines[i][n] == " ": n += 1 lines[i] = "\n".join(textwrap.wrap(lines[i], 78, break_long_words=False, break_on_hyphens=False, subsequent_indent=" "+ n*" ")) xmlString = "\n".join(lines) #print textwrap.wrap(doc.toprettyxml(" "), 80) else: xmlString = doc.toprettyxml(indent="") if options.output is None: sys.stdout.write(xmlString+'\n') else: OUTPUT = open(options.output, 'w') OUTPUT.write(xmlString +'\n') OUTPUT.close() ## ## MAIN ## # # Create option parser # parser = OptionParser() parser.add_option("--version", help="Print the version number, then exit", action="store_true", dest="version", default=False) parser.add_option("-v","--verbose", help="Print progress messages", action="store_true", dest="verbose", default=False) parser.add_option('--object-directory', help="Specify the directory that contains the gcov data files. gcovr must be able to identify the path between the *.gcda files and the directory where gcc was originally run. Normally, gcovr can guess correctly. This option overrides gcovr's normal path detection and can specify either the path from gcc to the gcda file (i.e. what was passed to gcc's '-o' option), or the path from the gcda file to gcc's original working directory.", action="store", dest="objdir", default=None) parser.add_option("-o","--output", help="Print output to this filename", action="store", dest="output", default=None) parser.add_option("-k","--keep", help="Keep the temporary *.gcov files generated by gcov. By default, these are deleted.", action="store_true", dest="keep", default=False) parser.add_option("-d","--delete", help="Delete the coverage files after they are processed. These are generated by the users's program, and by default gcovr does not remove these files.", action="store_true", dest="delete", default=False) parser.add_option("-f","--filter", help="Keep only the data files that match this regular expression", action="append", dest="filter", default=[]) parser.add_option("-e","--exclude", help="Exclude data files that match this regular expression", action="append", dest="exclude", default=[]) parser.add_option("--gcov-filter", help="Keep only gcov data files that match this regular expression", action="store", dest="gcov_filter", default=None) parser.add_option("--gcov-exclude", help="Exclude gcov data files that match this regular expression", action="append", dest="gcov_exclude", default=[]) parser.add_option("-r","--root", help="Defines the root directory for source files. This is also used to filter the files, and to standardize the output.", action="store", dest="root", default=None) parser.add_option("-x","--xml", help="Generate XML instead of the normal tabular output.", action="store_true", dest="xml", default=False) parser.add_option("--xml-pretty", help="Generate pretty XML instead of the normal dense format.", action="store_true", dest="prettyxml", default=False) parser.add_option("--html", help="Generate HTML instead of the normal tabular output.", action="store_true", dest="html", default=False) parser.add_option("--html-details", help="Generate HTML output for source file coverage.", action="store_true", dest="html_details", default=False) parser.add_option("--html-absolute-paths", help="Set the paths in the HTML report to be absolute instead of relative", action="store_false", dest="relative_anchors", default=True) parser.add_option("-b","--branches", help="Tabulate the branch coverage instead of the line coverage.", action="store_true", dest="show_branch", default=None) parser.add_option("-u","--sort-uncovered", help="Sort entries by increasing number of uncovered lines.", action="store_true", dest="sort_uncovered", default=None) parser.add_option("-p","--sort-percentage", help="Sort entries by decreasing percentage of covered lines.", action="store_true", dest="sort_percent", default=None) parser.add_option("--gcov-executable", help="Defines the name/path to the gcov executable [defaults to the " "GCOV environment variable, if present; else 'gcov'].", action="store", dest="gcov_cmd", default=os.environ.get('GCOV', 'gcov') ) parser.add_option("--exclude-unreachable-branches", help="Exclude from coverage branches which are marked to be excluded by LCOV/GCOV markers " "or are determined to be from lines containing only compiler-generated \"dead\" code.", action="store_true", dest="exclude_unreachable_branches", default=False) parser.add_option("-g", "--use-gcov-files", help="Use preprocessed gcov files for analysis.", action="store_true", dest="gcov_files", default=False) parser.add_option("-s", "--print-summary", help="Prints a small report to stdout with line & branch percentage coverage", action="store_true", dest="print_summary", default=False) parser.usage="gcovr [options]" parser.description="A utility to run gcov and generate a simple report that summarizes the coverage" # # Process options # (options, args) = parser.parse_args(args=sys.argv) if options.version: sys.stdout.write( "gcovr %s\n" "\n" "Copyright (2013) Sandia Corporation. Under the terms of Contract\n" "DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government\n" "retains certain rights in this software.\n" % (version_str(),) ) sys.exit(0) if options.objdir: tmp = options.objdir.replace('/',os.sep).replace('\\',os.sep) while os.sep+os.sep in tmp: tmp = tmp.replace(os.sep+os.sep, os.sep) if normpath(options.objdir) != tmp: sys.stderr.write( "(WARNING) relative referencing in --object-directory.\n" "\tthis could cause strange errors when gcovr attempts to\n" "\tidentify the original gcc working directory.\n") if not os.path.exists(normpath(options.objdir)): sys.stderr.write( "(ERROR) Bad --object-directory option.\n" "\tThe specified directory does not exist.\n") sys.exit(1) # # Setup filters # for i in range(0,len(options.exclude)): options.exclude[i] = re.compile(options.exclude[i]) if options.root is not None: if not options.root: sys.stderr.write( "(ERROR) empty --root option.\n" "\tRoot specifies the path to the root directory of your project.\n" "\tThis option cannot be an empty string.\n") sys.exit(1) root_dir = os.path.abspath(options.root) options.root_filter = re.compile(re.escape(root_dir+os.sep)) else: options.root_filter = re.compile('') root_dir = starting_dir for i in range(0,len(options.filter)): options.filter[i] = re.compile(options.filter[i]) if len(options.filter) == 0: options.filter.append(options.root_filter) for i in range(0,len(options.gcov_exclude)): options.gcov_exclude[i] = re.compile(options.gcov_exclude[i]) if options.gcov_filter is not None: options.gcov_filter = re.compile(options.gcov_filter) else: options.gcov_filter = re.compile('') # # Get data files # if len(args) == 1: if options.root is None: datafiles = get_datafiles(["."], options) else: datafiles = get_datafiles([options.root], options) else: datafiles = get_datafiles(args[1:], options) # # Get coverage data # covdata = {} for file_ in datafiles: if options.gcov_files: process_existing_gcov_file(file_,covdata,options) else: process_datafile(file_,covdata,options) if options.verbose: sys.stdout.write("Gathered coveraged data for "+str(len(covdata))+" files\n") # # Print report # if options.xml or options.prettyxml: print_xml_report(covdata) elif options.html: print_html_report(covdata, options.html_details) else: print_text_report(covdata) if options.print_summary: print_summary(covdata) gcovr-3.2/setup.py000066400000000000000000000033641235605057100142220ustar00rootroot00000000000000# _________________________________________________________________________ # # Gcovr: A parsing and reporting tool for gcov # Copyright (c) 2013 Sandia Corporation. # This software is distributed under the BSD License. # Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, # the U.S. Government retains certain rights in this software. # For more information, see the README.md file. # _________________________________________________________________________ """ Script to generate the installer for gcovr. """ import glob import os def read(*rnames): return open(os.path.join(os.path.dirname(__file__), *rnames)).read() from setuptools import setup import os.path if os.path.exists('README.md'): import shutil shutil.copyfile('README.md', 'README.txt') scripts = glob.glob("scripts/*") setup(name='gcovr', version='3.2', maintainer='William Hart', maintainer_email='wehart@sandia.gov', url = 'http://gcovr.com', license = 'BSD', platforms = ["any"], description = 'A Python script for summarizing gcov data.', long_description = read('README.txt'), classifiers = [ 'Development Status :: 4 - Beta', 'Intended Audience :: End Users/Desktop', 'Intended Audience :: Science/Research', 'License :: OSI Approved :: BSD License', 'Natural Language :: English', 'Operating System :: Microsoft :: Windows', 'Operating System :: Unix', 'Programming Language :: Python', 'Programming Language :: Unix Shell', 'Topic :: Software Development :: Libraries :: Python Modules' ], packages=['gcovr'], keywords=['utility'], scripts=scripts )