bdist.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. """distutils.command.bdist
  2. Implements the Distutils 'bdist' command (create a built [binary]
  3. distribution)."""
  4. from __future__ import annotations
  5. import os
  6. import warnings
  7. from collections.abc import Callable
  8. from typing import TYPE_CHECKING, ClassVar
  9. from ..core import Command
  10. from ..errors import DistutilsOptionError, DistutilsPlatformError
  11. from ..util import get_platform
  12. if TYPE_CHECKING:
  13. from typing_extensions import deprecated
  14. else:
  15. def deprecated(message):
  16. return lambda fn: fn
  17. def show_formats():
  18. """Print list of available formats (arguments to "--format" option)."""
  19. from ..fancy_getopt import FancyGetopt
  20. formats = [
  21. ("formats=" + format, None, bdist.format_commands[format][1])
  22. for format in bdist.format_commands
  23. ]
  24. pretty_printer = FancyGetopt(formats)
  25. pretty_printer.print_help("List of available distribution formats:")
  26. class ListCompat(dict[str, tuple[str, str]]):
  27. # adapter to allow for Setuptools compatibility in format_commands
  28. @deprecated("format_commands is now a dict. append is deprecated.")
  29. def append(self, item: object) -> None:
  30. warnings.warn(
  31. "format_commands is now a dict. append is deprecated.",
  32. DeprecationWarning,
  33. stacklevel=2,
  34. )
  35. class bdist(Command):
  36. description = "create a built (binary) distribution"
  37. user_options = [
  38. ('bdist-base=', 'b', "temporary directory for creating built distributions"),
  39. (
  40. 'plat-name=',
  41. 'p',
  42. "platform name to embed in generated filenames "
  43. f"[default: {get_platform()}]",
  44. ),
  45. ('formats=', None, "formats for distribution (comma-separated list)"),
  46. (
  47. 'dist-dir=',
  48. 'd',
  49. "directory to put final built distributions in [default: dist]",
  50. ),
  51. ('skip-build', None, "skip rebuilding everything (for testing/debugging)"),
  52. (
  53. 'owner=',
  54. 'u',
  55. "Owner name used when creating a tar file [default: current user]",
  56. ),
  57. (
  58. 'group=',
  59. 'g',
  60. "Group name used when creating a tar file [default: current group]",
  61. ),
  62. ]
  63. boolean_options: ClassVar[list[str]] = ['skip-build']
  64. help_options: ClassVar[list[tuple[str, str | None, str, Callable[[], object]]]] = [
  65. ('help-formats', None, "lists available distribution formats", show_formats),
  66. ]
  67. # The following commands do not take a format option from bdist
  68. no_format_option: ClassVar[tuple[str, ...]] = ('bdist_rpm',)
  69. # This won't do in reality: will need to distinguish RPM-ish Linux,
  70. # Debian-ish Linux, Solaris, FreeBSD, ..., Windows, Mac OS.
  71. default_format: ClassVar[dict[str, str]] = {'posix': 'gztar', 'nt': 'zip'}
  72. # Define commands in preferred order for the --help-formats option
  73. format_commands = ListCompat({
  74. 'rpm': ('bdist_rpm', "RPM distribution"),
  75. 'gztar': ('bdist_dumb', "gzip'ed tar file"),
  76. 'bztar': ('bdist_dumb', "bzip2'ed tar file"),
  77. 'xztar': ('bdist_dumb', "xz'ed tar file"),
  78. 'ztar': ('bdist_dumb', "compressed tar file"),
  79. 'tar': ('bdist_dumb', "tar file"),
  80. 'zip': ('bdist_dumb', "ZIP file"),
  81. })
  82. # for compatibility until consumers only reference format_commands
  83. format_command = format_commands
  84. def initialize_options(self):
  85. self.bdist_base = None
  86. self.plat_name = None
  87. self.formats = None
  88. self.dist_dir = None
  89. self.skip_build = False
  90. self.group = None
  91. self.owner = None
  92. def finalize_options(self) -> None:
  93. # have to finalize 'plat_name' before 'bdist_base'
  94. if self.plat_name is None:
  95. if self.skip_build:
  96. self.plat_name = get_platform()
  97. else:
  98. self.plat_name = self.get_finalized_command('build').plat_name
  99. # 'bdist_base' -- parent of per-built-distribution-format
  100. # temporary directories (eg. we'll probably have
  101. # "build/bdist.<plat>/dumb", "build/bdist.<plat>/rpm", etc.)
  102. if self.bdist_base is None:
  103. build_base = self.get_finalized_command('build').build_base
  104. self.bdist_base = os.path.join(build_base, 'bdist.' + self.plat_name)
  105. self.ensure_string_list('formats')
  106. if self.formats is None:
  107. try:
  108. self.formats = [self.default_format[os.name]]
  109. except KeyError:
  110. raise DistutilsPlatformError(
  111. "don't know how to create built distributions "
  112. f"on platform {os.name}"
  113. )
  114. if self.dist_dir is None:
  115. self.dist_dir = "dist"
  116. def run(self) -> None:
  117. # Figure out which sub-commands we need to run.
  118. commands = []
  119. for format in self.formats:
  120. try:
  121. commands.append(self.format_commands[format][0])
  122. except KeyError:
  123. raise DistutilsOptionError(f"invalid format '{format}'")
  124. # Reinitialize and run each command.
  125. for i in range(len(self.formats)):
  126. cmd_name = commands[i]
  127. sub_cmd = self.reinitialize_command(cmd_name)
  128. if cmd_name not in self.no_format_option:
  129. sub_cmd.format = self.formats[i]
  130. # passing the owner and group names for tar archiving
  131. if cmd_name == 'bdist_dumb':
  132. sub_cmd.owner = self.owner
  133. sub_cmd.group = self.group
  134. # If we're going to need to run this command again, tell it to
  135. # keep its temporary files around so subsequent runs go faster.
  136. if cmd_name in commands[i + 1 :]:
  137. sub_cmd.keep_temp = True
  138. self.run_command(cmd_name)