Home | History | Annotate | Download | only in py2
      1 # -*- coding: ascii -*-
      2 #
      3 # Copyright 2007, 2008, 2009, 2010, 2011
      4 # Andr\xe9 Malo or his licensors, as applicable
      5 #
      6 # Licensed under the Apache License, Version 2.0 (the "License");
      7 # you may not use this file except in compliance with the License.
      8 # You may obtain a copy of the License at
      9 #
     10 #     http://www.apache.org/licenses/LICENSE-2.0
     11 #
     12 # Unless required by applicable law or agreed to in writing, software
     13 # distributed under the License is distributed on an "AS IS" BASIS,
     14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15 # See the License for the specific language governing permissions and
     16 # limitations under the License.
     17 """
     18 ===================
     19  C extension tools
     20 ===================
     21 
     22 C extension tools.
     23 """
     24 __author__ = u"Andr\xe9 Malo"
     25 __docformat__ = "restructuredtext en"
     26 __test__ = False
     27 
     28 from distutils import core as _core
     29 from distutils import errors as _distutils_errors
     30 import os as _os
     31 import posixpath as _posixpath
     32 import shutil as _shutil
     33 import tempfile as _tempfile
     34 
     35 from _setup import commands as _commands
     36 from _setup.util import log
     37 
     38 
     39 def _install_finalizer(installer):
     40     if installer.without_c_extensions:
     41         installer.distribution.ext_modules = []
     42 
     43 def _build_finalizer(builder):
     44     if builder.without_c_extensions:
     45         builder.extensions = []
     46 
     47 
     48 class Extension(_core.Extension):
     49     """
     50     Extension with prerequisite check interface
     51 
     52     If your check is cacheable (during the setup run), override
     53     `cached_check_prerequisites`, `check_prerequisites` otherwise.
     54 
     55     :IVariables:
     56       `cached_check` : ``bool``
     57         The cached check result
     58     """
     59     cached_check = None
     60 
     61     def __init__(self, *args, **kwargs):
     62         """ Initialization """
     63         if kwargs.has_key('depends'):
     64             self.depends = kwargs['depends'] or []
     65         else:
     66             self.depends = []
     67         _core.Extension.__init__(self, *args, **kwargs)
     68 
     69         # add include path
     70         included = _posixpath.join('_setup', 'include')
     71         if included not in self.include_dirs:
     72             self.include_dirs.append(included)
     73 
     74         # add cext.h to the dependencies
     75         cext_h = _posixpath.join(included, 'cext.h')
     76         if cext_h not in self.depends:
     77             self.depends.append(cext_h)
     78 
     79         _commands.add_option('install_lib', 'without-c-extensions',
     80             help_text='Don\'t install C extensions',
     81             inherit='install',
     82         )
     83         _commands.add_finalizer('install_lib', 'c-extensions',
     84             _install_finalizer
     85         )
     86         _commands.add_option('build_ext', 'without-c-extensions',
     87             help_text='Don\'t build C extensions',
     88             inherit=('build', 'install_lib'),
     89         )
     90         _commands.add_finalizer('build_ext', 'c-extensions', _build_finalizer)
     91 
     92     def check_prerequisites(self, build):
     93         """
     94         Check prerequisites
     95 
     96         The check should cover all dependencies needed for the extension to
     97         be built and run. The method can do the following:
     98 
     99         - return a false value: the extension will be built
    100         - return a true value: the extension will be skipped. This is useful
    101           for optional extensions
    102         - raise an exception. This is useful for mandatory extensions
    103 
    104         If the check result is cacheable (during the setup run), override
    105         `cached_check_prerequisites` instead.
    106 
    107         :Parameters:
    108           `build` : `BuildExt`
    109             The extension builder
    110 
    111         :Return: Skip the extension?
    112         :Rtype: ``bool``
    113         """
    114         if self.cached_check is None:
    115             log.debug("PREREQ check for %s" % self.name)
    116             self.cached_check = self.cached_check_prerequisites(build)
    117         else:
    118             log.debug("PREREQ check for %s (cached)" % self.name)
    119         return self.cached_check
    120 
    121     def cached_check_prerequisites(self, build):
    122         """
    123         Check prerequisites
    124 
    125         The check should cover all dependencies needed for the extension to
    126         be built and run. The method can do the following:
    127 
    128         - return a false value: the extension will be built
    129         - return a true value: the extension will be skipped. This is useful
    130           for optional extensions
    131         - raise an exception. This is useful for mandatory extensions
    132 
    133         If the check result is *not* cacheable (during the setup run),
    134         override `check_prerequisites` instead.
    135 
    136         :Parameters:
    137           `build` : `BuildExt`
    138             The extension builder
    139 
    140         :Return: Skip the extension?
    141         :Rtype: ``bool``
    142         """
    143         # pylint: disable = W0613
    144         log.debug("Nothing to check for %s!" % self.name)
    145         return False
    146 
    147 
    148 class ConfTest(object):
    149     """
    150     Single conftest abstraction
    151 
    152     :IVariables:
    153       `_tempdir` : ``str``
    154         The tempdir created for this test
    155 
    156       `src` : ``str``
    157         Name of the source file
    158 
    159       `target` : ``str``
    160         Target filename
    161 
    162       `compiler` : ``CCompiler``
    163         compiler instance
    164 
    165       `obj` : ``list``
    166         List of object filenames (``[str, ...]``)
    167     """
    168     _tempdir = None
    169 
    170     def __init__(self, build, source):
    171         """
    172         Initialization
    173 
    174         :Parameters:
    175           `build` : ``distuils.command.build_ext.build_ext``
    176             builder instance
    177 
    178           `source` : ``str``
    179             Source of the file to compile
    180         """
    181         self._tempdir = tempdir = _tempfile.mkdtemp()
    182         src = _os.path.join(tempdir, 'conftest.c')
    183         fp = open(src, 'w')
    184         try:
    185             fp.write(source)
    186         finally:
    187             fp.close()
    188         self.src = src
    189         self.compiler = compiler = build.compiler
    190         self.target = _os.path.join(tempdir, 'conftest')
    191         self.obj = compiler.object_filenames([src], output_dir=tempdir)
    192 
    193     def __del__(self):
    194         """ Destruction """
    195         self.destroy()
    196 
    197     def destroy(self):
    198         """ Destroy the conftest leftovers on disk """
    199         tempdir, self._tempdir = self._tempdir, None
    200         if tempdir is not None:
    201             _shutil.rmtree(tempdir)
    202 
    203     def compile(self, **kwargs):
    204         """
    205         Compile the conftest
    206 
    207         :Parameters:
    208           `kwargs` : ``dict``
    209             Optional keyword parameters for the compiler call
    210 
    211         :Return: Was the compilation successful?
    212         :Rtype: ``bool``
    213         """
    214         kwargs['output_dir'] = self._tempdir
    215         try:
    216             self.compiler.compile([self.src], **kwargs)
    217         except _distutils_errors.CompileError:
    218             return False
    219         return True
    220 
    221     def link(self, **kwargs):
    222         r"""
    223         Link the conftest
    224 
    225         Before you can link the conftest objects they need to be `compile`\d.
    226 
    227         :Parameters:
    228           `kwargs` : ``dict``
    229             Optional keyword parameters for the linker call
    230 
    231         :Return: Was the linking successful?
    232         :Rtype: ``bool``
    233         """
    234         try:
    235             self.compiler.link_executable(self.obj, self.target, **kwargs)
    236         except _distutils_errors.LinkError:
    237             return False
    238         return True
    239 
    240     def pipe(self, mode="r"):
    241         r"""
    242         Execute the conftest binary and connect to it using a pipe
    243 
    244         Before you can pipe to or from the conftest binary it needs to
    245         be `link`\ed.
    246 
    247         :Parameters:
    248           `mode` : ``str``
    249             Pipe mode - r/w
    250 
    251         :Return: The open pipe
    252         :Rtype: ``file``
    253         """
    254         return _os.popen(self.compiler.executable_filename(self.target), mode)
    255