1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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 from distutils.command import build_ext as _build_ext
31 try:
32 from distutils import log
33 except ImportError:
35
36 - def info(self, value):
40 log = log()
41 import os as _os
42 import shutil as _shutil
43 import tempfile as _tempfile
44
45
47 """
48 Extended extension builder class
49
50 This class allows extensions to provide a ``check_prerequisites`` method
51 which is called before actually building it. The method takes the
52 `BuildExt` instance and returns whether the extension should be skipped or
53 not.
54 """
55
57 """
58 Build C extension - with extended functionality
59
60 The following features are added here:
61
62 - ``ext.check_prerequisites`` is called before the extension is being
63 built. See `Extension` for details. If the method does not exist,
64 simply no check will be run.
65 - The macros ``EXT_PACKAGE`` and ``EXT_MODULE`` will be filled (or
66 unset) depending on the extensions name, but only if they are not
67 already defined.
68 - the include subdirectory is added to the include path
69
70 :Parameters:
71 `ext` : `Extension`
72 The extension to build. If it's a pure
73 ``distutils.core.Extension``, simply no prequisites check is
74 applied.
75
76 :Return: whatever ``distutils.command.build_ext.build_ext`` returns
77 :Rtype: any
78 """
79
80 included = _os.path.join(_os.path.dirname(__file__), 'include')
81 if included not in ext.include_dirs:
82 ext.include_dirs.append(included)
83
84
85 cext_h = _os.path.join(included, 'cext.h')
86 if cext_h not in ext.depends:
87 ext.depends.append(cext_h)
88
89
90 macros = dict(ext.define_macros or ())
91 tup = ext.name.split('.')
92 if len(tup) == 1:
93 pkg, mod = None, tup[0]
94 else:
95 pkg, mod = '.'.join(tup[:-1]), tup[-1]
96 if pkg is not None and 'EXT_PACKAGE' not in macros:
97 ext.define_macros.append(('EXT_PACKAGE', pkg))
98 if 'EXT_MODULE' not in macros:
99 ext.define_macros.append(('EXT_MODULE', mod))
100 if pkg is None:
101 macros = dict(ext.undef_macros or ())
102 if 'EXT_PACKAGE' not in macros:
103 ext.undef_macros.append('EXT_PACKAGE')
104
105 try:
106 checker = ext.check_prerequisites
107 except AttributeError:
108 pass
109 else:
110 if checker(self):
111 log.info("Skipping %s extension" % ext.name)
112 return
113
114 return _build_ext.build_ext.build_extension(self, ext)
115
116
118 """
119 Extension with prerequisite check interface
120
121 If your check is cacheable (during the setup run), override
122 `cached_check_prerequisites`, `check_prerequisites` otherwise.
123
124 :IVariables:
125 `cached_check` : ``bool``
126 The cached check result
127 """
128 cached_check = None
129
131 """ Initialization """
132 if kwargs.has_key('depends'):
133 self.depends = kwargs['depends'] or []
134 else:
135 self.depends = []
136 _core.Extension.__init__(self, *args, **kwargs)
137
139 """
140 Check prerequisites
141
142 The check should cover all dependencies needed for the extension to
143 be built and run. The method can do the following:
144
145 - return a false value: the extension will be built
146 - return a true value: the extension will be skipped. This is useful
147 for optional extensions
148 - raise an exception. This is useful for mandatory extensions
149
150 If the check result is cacheable (during the setup run), override
151 `cached_check_prerequisites` instead.
152
153 :Parameters:
154 `build` : `BuildExt`
155 The extension builder
156
157 :Return: Skip the extension?
158 :Rtype: ``bool``
159 """
160 if self.cached_check is None:
161 log.debug("PREREQ check for %s" % self.name)
162 self.cached_check = self.cached_check_prerequisites(build)
163 else:
164 log.debug("PREREQ check for %s (cached)" % self.name)
165 return self.cached_check
166
168 """
169 Check prerequisites
170
171 The check should cover all dependencies needed for the extension to
172 be built and run. The method can do the following:
173
174 - return a false value: the extension will be built
175 - return a true value: the extension will be skipped. This is useful
176 for optional extensions
177 - raise an exception. This is useful for mandatory extensions
178
179 If the check result is *not* cacheable (during the setup run),
180 override `check_prerequisites` instead.
181
182 :Parameters:
183 `build` : `BuildExt`
184 The extension builder
185
186 :Return: Skip the extension?
187 :Rtype: ``bool``
188 """
189
190 log.debug("Nothing to check for %s!" % self.name)
191 return False
192
193
195 """
196 Single conftest abstraction
197
198 :IVariables:
199 `_tempdir` : ``str``
200 The tempdir created for this test
201
202 `src` : ``str``
203 Name of the source file
204
205 `target` : ``str``
206 Target filename
207
208 `compiler` : ``CCompiler``
209 compiler instance
210
211 `obj` : ``list``
212 List of object filenames (``[str, ...]``)
213 """
214 _tempdir = None
215
217 """
218 Initialization
219
220 :Parameters:
221 `build` : ``distuils.command.build_ext.build_ext``
222 builder instance
223
224 `source` : ``str``
225 Source of the file to compile
226 """
227 self._tempdir = tempdir = _tempfile.mkdtemp()
228 src = _os.path.join(tempdir, 'conftest.c')
229 fp = file(src, 'w')
230 try:
231 fp.write(source)
232 finally:
233 fp.close()
234 self.src = src
235 self.compiler = compiler = build.compiler
236 self.target = _os.path.join(tempdir, 'conftest')
237 self.obj = compiler.object_filenames([src], output_dir=tempdir)
238
240 """ Destruction """
241 self.destroy()
242
244 """ Destroy the conftest leftovers on disk """
245 tempdir, self._tempdir = self._tempdir, None
246 if tempdir is not None:
247 _shutil.rmtree(tempdir)
248
250 """
251 Compile the conftest
252
253 :Parameters:
254 `kwargs` : ``dict``
255 Optional keyword parameters for the compiler call
256
257 :Return: Was the compilation successful?
258 :Rtype: ``bool``
259 """
260 kwargs['output_dir'] = self._tempdir
261 try:
262 self.compiler.compile([self.src], **kwargs)
263 except _distutils_errors.CompileError:
264 return False
265 return True
266
267 - def link(self, **kwargs):
268 r"""
269 Link the conftest
270
271 Before you can link the conftest objects they need to be `compile`\d.
272
273 :Parameters:
274 `kwargs` : ``dict``
275 Optional keyword parameters for the linker call
276
277 :Return: Was the linking successful?
278 :Rtype: ``bool``
279 """
280 try:
281 self.compiler.link_executable(self.obj, self.target, **kwargs)
282 except _distutils_errors.LinkError:
283 return False
284 return True
285
286 - def pipe(self, mode="r"):
287 r"""
288 Execute the conftest binary and connect to it using a pipe
289
290 Before you can pipe to or from the conftest binary it needs to
291 be `link`\ed.
292
293 :Parameters:
294 `mode` : ``str``
295 Pipe mode - r/w
296
297 :Return: The open pipe
298 :Rtype: ``file``
299 """
300 return _os.popen(self.compiler.executable_filename(self.target), mode)
301