Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 5322f00

Browse files
committed
Overhauled to expect 'self.extensions' (taken from 'ext_modules' in the
setup script) to be a list of Extension instances, rather than a list of of (ext_name, build_info) tuples. This is mostly a simplification, but 'check_extension_list()' got a lot more complicated because of the need to convert the old-style tuples to Extension instances. Temporarily dropped support for defining/undefining macros in the 'extensions' list -- I want to change the interface, but haven't yet made the required changes in CCompiler and friends to support this nicely. Also neatened up the code that merges 'extra_compile_flags' and the CFLAGS environment variable.
1 parent 3ca54bc commit 5322f00

1 file changed

Lines changed: 128 additions & 66 deletions

File tree

Lib/distutils/command/build_ext.py

Lines changed: 128 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from distutils.core import Command
1414
from distutils.errors import *
1515
from distutils.dep_util import newer_group
16+
from distutils.extension import Extension
1617

1718
# An extension name is just a dot-separated list of Python NAMEs (ie.
1819
# the same as a fully-qualified module name).
@@ -152,13 +153,17 @@ def run (self):
152153

153154
from distutils.ccompiler import new_compiler
154155

155-
# 'self.extensions', as supplied by setup.py, is a list of 2-tuples.
156-
# Each tuple is simple:
156+
# 'self.extensions', as supplied by setup.py, is a list of
157+
# Extension instances. See the documentation for Extension (in
158+
# distutils.core) for details.
159+
#
160+
# For backwards compatibility with Distutils 0.8.2 and earlier, we
161+
# also allow the 'extensions' list to be a list of tuples:
157162
# (ext_name, build_info)
158-
# build_info is a dictionary containing everything specific to
159-
# building this extension. (Info pertaining to all extensions
160-
# should be handled by general distutils options passed from
161-
# setup.py down to right here, but that's not taken care of yet.)
163+
# where build_info is a dictionary containing everything that
164+
# Extension instances do except the name, with a few things being
165+
# differently named. We convert these 2-tuples to Extension
166+
# instances as needed.
162167

163168
if not self.extensions:
164169
return
@@ -208,32 +213,82 @@ def run (self):
208213

209214
def check_extensions_list (self, extensions):
210215
"""Ensure that the list of extensions (presumably provided as a
211-
command option 'extensions') is valid, i.e. it is a list of
212-
2-tuples, where the tuples are (extension_name, build_info_dict).
213-
Raise DistutilsSetupError if the structure is invalid anywhere;
214-
just returns otherwise."""
215-
216-
if type (extensions) is not ListType:
216+
command option 'extensions') is valid, i.e. it is a list of
217+
Extension objects. We also support the old-style list of 2-tuples,
218+
where the tuples are (ext_name, build_info), which are converted to
219+
Extension instances here.
220+
221+
Raise DistutilsSetupError if the structure is invalid anywhere;
222+
just returns otherwise.
223+
"""
224+
if type(extensions) is not ListType:
217225
raise DistutilsSetupError, \
218-
"'ext_modules' option must be a list of tuples"
226+
"'ext_modules' option must be a list of Extension instances"
219227

220-
for ext in extensions:
221-
if type (ext) is not TupleType and len (ext) != 2:
228+
for i in range(len(extensions)):
229+
ext = extensions[i]
230+
if isinstance(ext, Extension):
231+
continue # OK! (assume type-checking done
232+
# by Extension constructor)
233+
234+
(ext_name, build_info) = ext
235+
self.warn(("old-style (ext_name, build_info) tuple found in "
236+
"ext_modules for extension '%s'"
237+
"-- please convert to Extension instance" % ext_name))
238+
if type(ext) is not TupleType and len(ext) != 2:
222239
raise DistutilsSetupError, \
223-
"each element of 'ext_modules' option must be a 2-tuple"
240+
("each element of 'ext_modules' option must be an "
241+
"Extension instance or 2-tuple")
224242

225-
if not (type (ext[0]) is StringType and
226-
extension_name_re.match (ext[0])):
243+
if not (type(ext_name) is StringType and
244+
extension_name_re.match(ext_name)):
227245
raise DistutilsSetupError, \
228-
"first element of each tuple in 'ext_modules' " + \
229-
"must be the extension name (a string)"
246+
("first element of each tuple in 'ext_modules' "
247+
"must be the extension name (a string)")
230248

231-
if type (ext[1]) is not DictionaryType:
249+
if type(build_info) is not DictionaryType:
232250
raise DistutilsSetupError, \
233-
"second element of each tuple in 'ext_modules' " + \
234-
"must be a dictionary (build info)"
235-
236-
# end sanity-check for
251+
("second element of each tuple in 'ext_modules' "
252+
"must be a dictionary (build info)")
253+
254+
# OK, the (ext_name, build_info) dict is type-safe: convert it
255+
# to an Extension instance.
256+
ext = Extension(ext_name, build_info['sources'])
257+
258+
# Easy stuff: one-to-one mapping from dict elements to
259+
# instance attributes.
260+
for key in ('include_dirs',
261+
'library_dirs',
262+
'libraries',
263+
'extra_objects',
264+
'extra_compile_args',
265+
'extra_link_args'):
266+
setattr(ext, key, build_info.get(key))
267+
268+
# Medium-easy stuff: same syntax/semantics, different names.
269+
ext.runtime_library_dirs = build_info.get('rpath')
270+
ext.export_symbol_file = build_info.get('def_file')
271+
272+
# Non-trivial stuff: 'macros' split into 'define_macros'
273+
# and 'undef_macros'.
274+
macros = build_info.get('macros')
275+
if macros:
276+
ext.define_macros = []
277+
ext.undef_macros = []
278+
for macro in macros:
279+
if not (type(macro) is TupleType and
280+
1 <= len(macros) <= 2):
281+
raise DistutilsSetupError, \
282+
("'macros' element of build info dict "
283+
"must be 1- or 2-tuple")
284+
if len(macro) == 1:
285+
ext.undef_macros.append(macro[0])
286+
elif len(macro) == 2:
287+
ext.define_macros.append(macro)
288+
289+
extensions[i] = ext
290+
291+
# for extensions
237292

238293
# check_extensions_list ()
239294

@@ -243,10 +298,8 @@ def get_source_files (self):
243298
filenames = []
244299

245300
# Wouldn't it be neat if we knew the names of header files too...
246-
for (extension_name, build_info) in self.extensions:
247-
sources = build_info.get ('sources')
248-
if type (sources) in (ListType, TupleType):
249-
filenames.extend (sources)
301+
for ext in self.extensions:
302+
filenames.extend (ext.sources)
250303

251304
return filenames
252305

@@ -262,8 +315,8 @@ def get_outputs (self):
262315
# ignores the 'inplace' flag, and assumes everything goes in the
263316
# "build" tree.
264317
outputs = []
265-
for (extension_name, build_info) in self.extensions:
266-
fullname = self.get_ext_fullname (extension_name)
318+
for ext in self.extensions:
319+
fullname = self.get_ext_fullname (ext.name)
267320
outputs.append (os.path.join (self.build_lib,
268321
self.get_ext_filename(fullname)))
269322
return outputs
@@ -276,16 +329,16 @@ def build_extensions (self):
276329
# First, sanity-check the 'extensions' list
277330
self.check_extensions_list (self.extensions)
278331

279-
for (extension_name, build_info) in self.extensions:
280-
sources = build_info.get ('sources')
332+
for ext in self.extensions:
333+
sources = ext.sources
281334
if sources is None or type (sources) not in (ListType, TupleType):
282335
raise DistutilsSetupError, \
283336
("in 'ext_modules' option (extension '%s'), " +
284337
"'sources' must be present and must be " +
285-
"a list of source filenames") % extension_name
338+
"a list of source filenames") % ext.name
286339
sources = list (sources)
287340

288-
fullname = self.get_ext_fullname (extension_name)
341+
fullname = self.get_ext_fullname (ext.name)
289342
if self.inplace:
290343
# ignore build-lib -- put the compiled extension into
291344
# the source tree along with pure Python modules
@@ -302,46 +355,54 @@ def build_extensions (self):
302355
ext_filename = os.path.join (self.build_lib,
303356
self.get_ext_filename(fullname))
304357

305-
if not newer_group(sources, ext_filename, 'newer'):
358+
if not (self.force or newer_group(sources, ext_filename, 'newer')):
306359
self.announce ("skipping '%s' extension (up-to-date)" %
307-
extension_name)
360+
ext.name)
308361
continue # 'for' loop over all extensions
309362
else:
310-
self.announce ("building '%s' extension" % extension_name)
363+
self.announce ("building '%s' extension" % ext.name)
311364

312365
# First step: compile the source code to object files. This
313366
# drops the object files in the current directory, regardless
314367
# of where the source is (may be a bad thing, but that's how a
315368
# Makefile.pre.in-based system does it, so at least there's a
316369
# precedent!)
317-
macros = build_info.get ('macros')
318-
include_dirs = build_info.get ('include_dirs')
319-
extra_args = build_info.get ('extra_compile_args')
320-
# honor CFLAGS enviroment variable
321-
# XXX do we *really* need this? or is it just a hack until
322-
# the user can put compiler flags in setup.cfg?
370+
371+
# XXX not honouring 'define_macros' or 'undef_macros' -- the
372+
# CCompiler API needs to change to accomodate this, and I
373+
# want to do one thing at a time!
374+
375+
# Two possible sources for extra compiler arguments:
376+
# - 'extra_compile_args' in Extension object
377+
# - CFLAGS environment variable (not particularly
378+
# elegant, but people seem to expect it and I
379+
# guess it's useful)
380+
# The environment variable should take precedence, and
381+
# any sensible compiler will give precendence to later
382+
# command line args. Hence we combine them in order:
383+
extra_args = ext.extra_compile_args
384+
385+
# XXX and if we support CFLAGS, why not CC (compiler
386+
# executable), CPPFLAGS (pre-processor options), and LDFLAGS
387+
# (linker options) too?
388+
# XXX should we use shlex to properly parse CFLAGS?
389+
323390
if os.environ.has_key('CFLAGS'):
324-
if not extra_args:
325-
extra_args = []
326-
extra_args = string.split(os.environ['CFLAGS']) + extra_args
391+
extra_args.extend(string.split(os.environ['CFLAGS']))
327392

328393
objects = self.compiler.compile (sources,
329394
output_dir=self.build_temp,
330-
macros=macros,
331-
include_dirs=include_dirs,
395+
#macros=macros,
396+
include_dirs=ext.include_dirs,
332397
debug=self.debug,
333398
extra_postargs=extra_args)
334399

335400
# Now link the object files together into a "shared object" --
336401
# of course, first we have to figure out all the other things
337402
# that go into the mix.
338-
extra_objects = build_info.get ('extra_objects')
339-
if extra_objects:
340-
objects.extend (extra_objects)
341-
libraries = build_info.get ('libraries')
342-
library_dirs = build_info.get ('library_dirs')
343-
rpath = build_info.get ('rpath')
344-
extra_args = build_info.get ('extra_link_args') or []
403+
if ext.extra_objects:
404+
objects.extend (ext.extra_objects)
405+
extra_args = ext.extra_link_args
345406

346407
# XXX this is a kludge! Knowledge of specific compilers or
347408
# platforms really doesn't belong here; in an ideal world, the
@@ -354,18 +415,18 @@ def build_extensions (self):
354415
# excuse for committing more platform- and compiler-specific
355416
# kludges; they are to be avoided if possible!)
356417
if self.compiler.compiler_type == 'msvc':
357-
def_file = build_info.get ('def_file')
418+
def_file = ext.export_symbol_file
358419
if def_file is None:
359420
source_dir = os.path.dirname (sources[0])
360-
ext_base = (string.split (extension_name, '.'))[-1]
421+
ext_base = (string.split (ext.name, '.'))[-1]
361422
def_file = os.path.join (source_dir, "%s.def" % ext_base)
362423
if not os.path.exists (def_file):
363424
def_file = None
364425

365426
if def_file is not None:
366427
extra_args.append ('/DEF:' + def_file)
367428
else:
368-
modname = string.split (extension_name, '.')[-1]
429+
modname = string.split (ext.name, '.')[-1]
369430
extra_args.append('/export:init%s'%modname)
370431

371432
# The MSVC linker generates unneeded .lib and .exp files,
@@ -374,17 +435,18 @@ def build_extensions (self):
374435
# directory.
375436
implib_file = os.path.join (
376437
self.build_temp,
377-
self.get_ext_libname (extension_name))
438+
self.get_ext_libname (ext.name))
378439
extra_args.append ('/IMPLIB:' + implib_file)
379440
self.mkpath (os.path.dirname (implib_file))
380441
# if MSVC
381442

382-
self.compiler.link_shared_object (objects, ext_filename,
383-
libraries=libraries,
384-
library_dirs=library_dirs,
385-
runtime_library_dirs=rpath,
386-
extra_postargs=extra_args,
387-
debug=self.debug)
443+
self.compiler.link_shared_object (
444+
objects, ext_filename,
445+
libraries=ext.libraries,
446+
library_dirs=ext.library_dirs,
447+
runtime_library_dirs=ext.runtime_library_dirs,
448+
extra_postargs=extra_args,
449+
debug=self.debug)
388450

389451
# build_extensions ()
390452

0 commit comments

Comments
 (0)