build_clib.py 4.4 KB

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