build_clib.py 4.4 KB

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