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