Home | History | Annotate | Download | only in coverage
      1 # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
      2 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
      3 
      4 """Code coverage measurement for Python"""
      5 
      6 # Distutils setup for coverage.py
      7 # This file is used unchanged under all versions of Python, 2.x and 3.x.
      8 
      9 import os
     10 import sys
     11 
     12 from setuptools import setup
     13 from distutils.core import Extension                # pylint: disable=no-name-in-module, import-error
     14 from distutils.command.build_ext import build_ext   # pylint: disable=no-name-in-module, import-error
     15 from distutils import errors                        # pylint: disable=no-name-in-module
     16 
     17 # Get or massage our metadata.  We exec coverage/version.py so we can avoid
     18 # importing the product code into setup.py.
     19 
     20 classifiers = """\
     21 Environment :: Console
     22 Intended Audience :: Developers
     23 License :: OSI Approved :: Apache Software License
     24 Operating System :: OS Independent
     25 Programming Language :: Python :: 2.6
     26 Programming Language :: Python :: 2.7
     27 Programming Language :: Python :: 3.3
     28 Programming Language :: Python :: 3.4
     29 Programming Language :: Python :: 3.5
     30 Programming Language :: Python :: Implementation :: CPython
     31 Programming Language :: Python :: Implementation :: PyPy
     32 Topic :: Software Development :: Quality Assurance
     33 Topic :: Software Development :: Testing
     34 """
     35 
     36 cov_ver_py = os.path.join(os.path.split(__file__)[0], "coverage/version.py")
     37 with open(cov_ver_py) as version_file:
     38     # __doc__ will be overwritten by version.py.
     39     doc = __doc__
     40     # Keep pylint happy.
     41     __version__ = __url__ = version_info = ""
     42     # Execute the code in version.py.
     43     exec(compile(version_file.read(), cov_ver_py, 'exec'))
     44 
     45 with open("README.rst") as readme:
     46     long_description = readme.read().replace("http://coverage.readthedocs.org", __url__)
     47 
     48 classifier_list = classifiers.splitlines()
     49 
     50 if version_info[3] == 'alpha':
     51     devstat = "3 - Alpha"
     52 elif version_info[3] in ['beta', 'candidate']:
     53     devstat = "4 - Beta"
     54 else:
     55     assert version_info[3] == 'final'
     56     devstat = "5 - Production/Stable"
     57 classifier_list.append("Development Status :: " + devstat)
     58 
     59 # Create the keyword arguments for setup()
     60 
     61 setup_args = dict(
     62     name='coverage',
     63     version=__version__,
     64 
     65     packages=[
     66         'coverage',
     67     ],
     68 
     69     package_data={
     70         'coverage': [
     71             'htmlfiles/*.*',
     72         ]
     73     },
     74 
     75     entry_points={
     76         # Install a script as "coverage", and as "coverage[23]", and as
     77         # "coverage-2.7" (or whatever).
     78         'console_scripts': [
     79             'coverage = coverage.cmdline:main',
     80             'coverage%d = coverage.cmdline:main' % sys.version_info[:1],
     81             'coverage-%d.%d = coverage.cmdline:main' % sys.version_info[:2],
     82         ],
     83     },
     84 
     85     # We need to get HTML assets from our htmlfiles directory.
     86     zip_safe=False,
     87 
     88     author='Ned Batchelder and others',
     89     author_email='ned (at] nedbatchelder.com',
     90     description=doc,
     91     long_description=long_description,
     92     keywords='code coverage testing',
     93     license='Apache 2.0',
     94     classifiers=classifier_list,
     95     url=__url__,
     96 )
     97 
     98 # A replacement for the build_ext command which raises a single exception
     99 # if the build fails, so we can fallback nicely.
    100 
    101 ext_errors = (
    102     errors.CCompilerError,
    103     errors.DistutilsExecError,
    104     errors.DistutilsPlatformError,
    105 )
    106 if sys.platform == 'win32':
    107     # distutils.msvc9compiler can raise an IOError when failing to
    108     # find the compiler
    109     ext_errors += (IOError,)
    110 
    111 
    112 class BuildFailed(Exception):
    113     """Raise this to indicate the C extension wouldn't build."""
    114     def __init__(self):
    115         Exception.__init__(self)
    116         self.cause = sys.exc_info()[1]      # work around py 2/3 different syntax
    117 
    118 
    119 class ve_build_ext(build_ext):
    120     """Build C extensions, but fail with a straightforward exception."""
    121 
    122     def run(self):
    123         """Wrap `run` with `BuildFailed`."""
    124         try:
    125             build_ext.run(self)
    126         except errors.DistutilsPlatformError:
    127             raise BuildFailed()
    128 
    129     def build_extension(self, ext):
    130         """Wrap `build_extension` with `BuildFailed`."""
    131         try:
    132             # Uncomment to test compile failure handling:
    133             #   raise errors.CCompilerError("OOPS")
    134             build_ext.build_extension(self, ext)
    135         except ext_errors:
    136             raise BuildFailed()
    137         except ValueError as err:
    138             # this can happen on Windows 64 bit, see Python issue 7511
    139             if "'path'" in str(err):    # works with both py 2/3
    140                 raise BuildFailed()
    141             raise
    142 
    143 # There are a few reasons we might not be able to compile the C extension.
    144 # Figure out if we should attempt the C extension or not.
    145 
    146 compile_extension = True
    147 
    148 if sys.platform.startswith('java'):
    149     # Jython can't compile C extensions
    150     compile_extension = False
    151 
    152 if '__pypy__' in sys.builtin_module_names:
    153     # Pypy can't compile C extensions
    154     compile_extension = False
    155 
    156 if compile_extension:
    157     setup_args.update(dict(
    158         ext_modules=[
    159             Extension(
    160                 "coverage.tracer",
    161                 sources=[
    162                     "coverage/ctracer/datastack.c",
    163                     "coverage/ctracer/filedisp.c",
    164                     "coverage/ctracer/module.c",
    165                     "coverage/ctracer/tracer.c",
    166                 ],
    167             ),
    168         ],
    169         cmdclass={
    170             'build_ext': ve_build_ext,
    171         },
    172     ))
    173 
    174 # Py3.x-specific details.
    175 
    176 if sys.version_info >= (3, 0):
    177     setup_args.update(dict(
    178         use_2to3=False,
    179     ))
    180 
    181 
    182 def main():
    183     """Actually invoke setup() with the arguments we built above."""
    184     # For a variety of reasons, it might not be possible to install the C
    185     # extension.  Try it with, and if it fails, try it without.
    186     try:
    187         setup(**setup_args)
    188     except BuildFailed as exc:
    189         msg = "Couldn't install with extension module, trying without it..."
    190         exc_msg = "%s: %s" % (exc.__class__.__name__, exc.cause)
    191         print("**\n** %s\n** %s\n**" % (msg, exc_msg))
    192 
    193         del setup_args['ext_modules']
    194         setup(**setup_args)
    195 
    196 if __name__ == '__main__':
    197     main()
    198