# -*- 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__ = u"Andr\xe9 Malo" __docformat__ = "restructuredtext en" __test__ = False from distutils import core as _core from distutils import errors as _distutils_errors import os as _os import posixpath as _posixpath import shutil as _shutil import tempfile as _tempfile from _setup import commands as _commands from _setup.util import log 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 kwargs.has_key('depends'): 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') 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)