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