| # -*- coding: ascii -*- |
| # |
| # Copyright 2007, 2008, 2009, 2010, 2011 |
| # Andr\xe9 Malo or his licensors, as applicable |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| """ |
| =================== |
| C extension tools |
| =================== |
| |
| C extension tools. |
| """ |
| __author__ = "Andr\xe9 Malo" |
| __docformat__ = "restructuredtext en" |
| __test__ = False |
| |
| from distutils import core as _core |
| from distutils import errors as _distutils_errors |
| from distutils import log |
| import os as _os |
| import posixpath as _posixpath |
| import shutil as _shutil |
| import tempfile as _tempfile |
| |
| from _setup import commands as _commands |
| |
| def _install_finalizer(installer): |
| if installer.without_c_extensions: |
| installer.distribution.ext_modules = [] |
| |
| def _build_finalizer(builder): |
| if builder.without_c_extensions: |
| builder.extensions = [] |
| |
| |
| class Extension(_core.Extension): |
| """ |
| Extension with prerequisite check interface |
| |
| If your check is cacheable (during the setup run), override |
| `cached_check_prerequisites`, `check_prerequisites` otherwise. |
| |
| :IVariables: |
| `cached_check` : ``bool`` |
| The cached check result |
| """ |
| cached_check = None |
| |
| def __init__(self, *args, **kwargs): |
| """ Initialization """ |
| if 'depends' in kwargs: |
| self.depends = kwargs['depends'] or [] |
| else: |
| self.depends = [] |
| _core.Extension.__init__(self, *args, **kwargs) |
| |
| # add include path |
| included = _posixpath.join('_setup', 'include') |
| if included not in self.include_dirs: |
| self.include_dirs.append(included) |
| |
| # add cext.h to the dependencies |
| cext_h = _posixpath.join(included, 'cext.h') |
| if cext_h not in self.depends: |
| self.depends.append(cext_h) |
| |
| _commands.add_option('install_lib', 'without-c-extensions', |
| help_text='Don\'t install C extensions', |
| inherit='install', |
| ) |
| _commands.add_finalizer('install_lib', 'c-extensions', |
| _install_finalizer |
| ) |
| _commands.add_option('build_ext', 'without-c-extensions', |
| help_text='Don\'t build C extensions', |
| inherit=('build', 'install_lib'), |
| ) |
| _commands.add_finalizer('build_ext', 'c-extensions', _build_finalizer) |
| |
| def check_prerequisites(self, build): |
| """ |
| Check prerequisites |
| |
| The check should cover all dependencies needed for the extension to |
| be built and run. The method can do the following: |
| |
| - return a false value: the extension will be built |
| - return a true value: the extension will be skipped. This is useful |
| for optional extensions |
| - raise an exception. This is useful for mandatory extensions |
| |
| If the check result is cacheable (during the setup run), override |
| `cached_check_prerequisites` instead. |
| |
| :Parameters: |
| `build` : `BuildExt` |
| The extension builder |
| |
| :Return: Skip the extension? |
| :Rtype: ``bool`` |
| """ |
| if self.cached_check is None: |
| log.debug("PREREQ check for %s" % self.name) |
| self.cached_check = self.cached_check_prerequisites(build) |
| else: |
| log.debug("PREREQ check for %s (cached)" % self.name) |
| return self.cached_check |
| |
| def cached_check_prerequisites(self, build): |
| """ |
| Check prerequisites |
| |
| The check should cover all dependencies needed for the extension to |
| be built and run. The method can do the following: |
| |
| - return a false value: the extension will be built |
| - return a true value: the extension will be skipped. This is useful |
| for optional extensions |
| - raise an exception. This is useful for mandatory extensions |
| |
| If the check result is *not* cacheable (during the setup run), |
| override `check_prerequisites` instead. |
| |
| :Parameters: |
| `build` : `BuildExt` |
| The extension builder |
| |
| :Return: Skip the extension? |
| :Rtype: ``bool`` |
| """ |
| # pylint: disable = W0613 |
| log.debug("Nothing to check for %s!" % self.name) |
| return False |
| |
| |
| class ConfTest(object): |
| """ |
| Single conftest abstraction |
| |
| :IVariables: |
| `_tempdir` : ``str`` |
| The tempdir created for this test |
| |
| `src` : ``str`` |
| Name of the source file |
| |
| `target` : ``str`` |
| Target filename |
| |
| `compiler` : ``CCompiler`` |
| compiler instance |
| |
| `obj` : ``list`` |
| List of object filenames (``[str, ...]``) |
| """ |
| _tempdir = None |
| |
| def __init__(self, build, source): |
| """ |
| Initialization |
| |
| :Parameters: |
| `build` : ``distuils.command.build_ext.build_ext`` |
| builder instance |
| |
| `source` : ``str`` |
| Source of the file to compile |
| """ |
| self._tempdir = tempdir = _tempfile.mkdtemp() |
| src = _os.path.join(tempdir, 'conftest.c') |
| fp = open(src, 'w', encoding='utf-8') |
| try: |
| fp.write(source) |
| finally: |
| fp.close() |
| self.src = src |
| self.compiler = compiler = build.compiler |
| self.target = _os.path.join(tempdir, 'conftest') |
| self.obj = compiler.object_filenames([src], output_dir=tempdir) |
| |
| def __del__(self): |
| """ Destruction """ |
| self.destroy() |
| |
| def destroy(self): |
| """ Destroy the conftest leftovers on disk """ |
| tempdir, self._tempdir = self._tempdir, None |
| if tempdir is not None: |
| _shutil.rmtree(tempdir) |
| |
| def compile(self, **kwargs): |
| """ |
| Compile the conftest |
| |
| :Parameters: |
| `kwargs` : ``dict`` |
| Optional keyword parameters for the compiler call |
| |
| :Return: Was the compilation successful? |
| :Rtype: ``bool`` |
| """ |
| kwargs['output_dir'] = self._tempdir |
| try: |
| self.compiler.compile([self.src], **kwargs) |
| except _distutils_errors.CompileError: |
| return False |
| return True |
| |
| def link(self, **kwargs): |
| r""" |
| Link the conftest |
| |
| Before you can link the conftest objects they need to be `compile`\d. |
| |
| :Parameters: |
| `kwargs` : ``dict`` |
| Optional keyword parameters for the linker call |
| |
| :Return: Was the linking successful? |
| :Rtype: ``bool`` |
| """ |
| try: |
| self.compiler.link_executable(self.obj, self.target, **kwargs) |
| except _distutils_errors.LinkError: |
| return False |
| return True |
| |
| def pipe(self, mode="r"): |
| r""" |
| Execute the conftest binary and connect to it using a pipe |
| |
| Before you can pipe to or from the conftest binary it needs to |
| be `link`\ed. |
| |
| :Parameters: |
| `mode` : ``str`` |
| Pipe mode - r/w |
| |
| :Return: The open pipe |
| :Rtype: ``file`` |
| """ |
| return _os.popen(self.compiler.executable_filename(self.target), mode) |