1313from distutils .core import Command
1414from distutils .errors import *
1515from 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