1import distutils.command.build_clib as orig 2from distutils.errors import DistutilsSetupError 3from distutils import log 4from setuptools.dep_util import newer_pairwise_group 5 6 7class build_clib(orig.build_clib): 8 """ 9 Override the default build_clib behaviour to do the following: 10 11 1. Implement a rudimentary timestamp-based dependency system 12 so 'compile()' doesn't run every time. 13 2. Add more keys to the 'build_info' dictionary: 14 * obj_deps - specify dependencies for each object compiled. 15 this should be a dictionary mapping a key 16 with the source filename to a list of 17 dependencies. Use an empty string for global 18 dependencies. 19 * cflags - specify a list of additional flags to pass to 20 the compiler. 21 """ 22 23 def build_libraries(self, libraries): 24 for (lib_name, build_info) in libraries: 25 sources = build_info.get('sources') 26 if sources is None or not isinstance(sources, (list, tuple)): 27 raise DistutilsSetupError( 28 "in 'libraries' option (library '%s'), " 29 "'sources' must be present and must be " 30 "a list of source filenames" % lib_name) 31 sources = list(sources) 32 33 log.info("building '%s' library", lib_name) 34 35 # Make sure everything is the correct type. 36 # obj_deps should be a dictionary of keys as sources 37 # and a list/tuple of files that are its dependencies. 38 obj_deps = build_info.get('obj_deps', dict()) 39 if not isinstance(obj_deps, dict): 40 raise DistutilsSetupError( 41 "in 'libraries' option (library '%s'), " 42 "'obj_deps' must be a dictionary of " 43 "type 'source: list'" % lib_name) 44 dependencies = [] 45 46 # Get the global dependencies that are specified by the '' key. 47 # These will go into every source's dependency list. 48 global_deps = obj_deps.get('', list()) 49 if not isinstance(global_deps, (list, tuple)): 50 raise DistutilsSetupError( 51 "in 'libraries' option (library '%s'), " 52 "'obj_deps' must be a dictionary of " 53 "type 'source: list'" % lib_name) 54 55 # Build the list to be used by newer_pairwise_group 56 # each source will be auto-added to its dependencies. 57 for source in sources: 58 src_deps = [source] 59 src_deps.extend(global_deps) 60 extra_deps = obj_deps.get(source, list()) 61 if not isinstance(extra_deps, (list, tuple)): 62 raise DistutilsSetupError( 63 "in 'libraries' option (library '%s'), " 64 "'obj_deps' must be a dictionary of " 65 "type 'source: list'" % lib_name) 66 src_deps.extend(extra_deps) 67 dependencies.append(src_deps) 68 69 expected_objects = self.compiler.object_filenames( 70 sources, 71 output_dir=self.build_temp, 72 ) 73 74 if ( 75 newer_pairwise_group(dependencies, expected_objects) 76 != ([], []) 77 ): 78 # First, compile the source code to object files in the library 79 # directory. (This should probably change to putting object 80 # files in a temporary build directory.) 81 macros = build_info.get('macros') 82 include_dirs = build_info.get('include_dirs') 83 cflags = build_info.get('cflags') 84 self.compiler.compile( 85 sources, 86 output_dir=self.build_temp, 87 macros=macros, 88 include_dirs=include_dirs, 89 extra_postargs=cflags, 90 debug=self.debug 91 ) 92 93 # Now "link" the object files together into a static library. 94 # (On Unix at least, this isn't really linking -- it just 95 # builds an archive. Whatever.) 96 self.compiler.create_static_lib( 97 expected_objects, 98 lib_name, 99 output_dir=self.build_clib, 100 debug=self.debug 101 ) 102