sdist.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. import os
  2. import re
  3. import sys
  4. from glob import glob
  5. import pkg_resources
  6. import distutils.command.sdist as orig
  7. from distutils.util import convert_path
  8. from distutils import log
  9. from setuptools import svn_utils
  10. READMES = ('README', 'README.rst', 'README.txt')
  11. def walk_revctrl(dirname=''):
  12. """Find all files under revision control"""
  13. for ep in pkg_resources.iter_entry_points('setuptools.file_finders'):
  14. for item in ep.load()(dirname):
  15. yield item
  16. #TODO will need test case
  17. class re_finder(object):
  18. """
  19. Finder that locates files based on entries in a file matched by a
  20. regular expression.
  21. """
  22. def __init__(self, path, pattern, postproc=lambda x: x):
  23. self.pattern = pattern
  24. self.postproc = postproc
  25. self.entries_path = convert_path(path)
  26. def _finder(self, dirname, filename):
  27. f = open(filename,'rU')
  28. try:
  29. data = f.read()
  30. finally:
  31. f.close()
  32. for match in self.pattern.finditer(data):
  33. path = match.group(1)
  34. # postproc was formerly used when the svn finder
  35. # was an re_finder for calling unescape
  36. path = self.postproc(path)
  37. yield svn_utils.joinpath(dirname, path)
  38. def find(self, dirname=''):
  39. path = svn_utils.joinpath(dirname, self.entries_path)
  40. if not os.path.isfile(path):
  41. # entries file doesn't exist
  42. return
  43. for path in self._finder(dirname,path):
  44. if os.path.isfile(path):
  45. yield path
  46. elif os.path.isdir(path):
  47. for item in self.find(path):
  48. yield item
  49. __call__ = find
  50. def _default_revctrl(dirname=''):
  51. 'Primary svn_cvs entry point'
  52. for finder in finders:
  53. for item in finder(dirname):
  54. yield item
  55. finders = [
  56. re_finder('CVS/Entries', re.compile(r"^\w?/([^/]+)/", re.M)),
  57. svn_utils.svn_finder,
  58. ]
  59. class sdist(orig.sdist):
  60. """Smart sdist that finds anything supported by revision control"""
  61. user_options = [
  62. ('formats=', None,
  63. "formats for source distribution (comma-separated list)"),
  64. ('keep-temp', 'k',
  65. "keep the distribution tree around after creating " +
  66. "archive file(s)"),
  67. ('dist-dir=', 'd',
  68. "directory to put the source distribution archive(s) in "
  69. "[default: dist]"),
  70. ]
  71. negative_opt = {}
  72. def run(self):
  73. self.run_command('egg_info')
  74. ei_cmd = self.get_finalized_command('egg_info')
  75. self.filelist = ei_cmd.filelist
  76. self.filelist.append(os.path.join(ei_cmd.egg_info,'SOURCES.txt'))
  77. self.check_readme()
  78. # Run sub commands
  79. for cmd_name in self.get_sub_commands():
  80. self.run_command(cmd_name)
  81. # Call check_metadata only if no 'check' command
  82. # (distutils <= 2.6)
  83. import distutils.command
  84. if 'check' not in distutils.command.__all__:
  85. self.check_metadata()
  86. self.make_distribution()
  87. dist_files = getattr(self.distribution,'dist_files',[])
  88. for file in self.archive_files:
  89. data = ('sdist', '', file)
  90. if data not in dist_files:
  91. dist_files.append(data)
  92. def __read_template_hack(self):
  93. # This grody hack closes the template file (MANIFEST.in) if an
  94. # exception occurs during read_template.
  95. # Doing so prevents an error when easy_install attempts to delete the
  96. # file.
  97. try:
  98. orig.sdist.read_template(self)
  99. except:
  100. sys.exc_info()[2].tb_next.tb_frame.f_locals['template'].close()
  101. raise
  102. # Beginning with Python 2.7.2, 3.1.4, and 3.2.1, this leaky file handle
  103. # has been fixed, so only override the method if we're using an earlier
  104. # Python.
  105. has_leaky_handle = (
  106. sys.version_info < (2,7,2)
  107. or (3,0) <= sys.version_info < (3,1,4)
  108. or (3,2) <= sys.version_info < (3,2,1)
  109. )
  110. if has_leaky_handle:
  111. read_template = __read_template_hack
  112. def add_defaults(self):
  113. standards = [READMES,
  114. self.distribution.script_name]
  115. for fn in standards:
  116. if isinstance(fn, tuple):
  117. alts = fn
  118. got_it = 0
  119. for fn in alts:
  120. if os.path.exists(fn):
  121. got_it = 1
  122. self.filelist.append(fn)
  123. break
  124. if not got_it:
  125. self.warn("standard file not found: should have one of " +
  126. ', '.join(alts))
  127. else:
  128. if os.path.exists(fn):
  129. self.filelist.append(fn)
  130. else:
  131. self.warn("standard file '%s' not found" % fn)
  132. optional = ['test/test*.py', 'setup.cfg']
  133. for pattern in optional:
  134. files = list(filter(os.path.isfile, glob(pattern)))
  135. if files:
  136. self.filelist.extend(files)
  137. # getting python files
  138. if self.distribution.has_pure_modules():
  139. build_py = self.get_finalized_command('build_py')
  140. self.filelist.extend(build_py.get_source_files())
  141. # This functionality is incompatible with include_package_data, and
  142. # will in fact create an infinite recursion if include_package_data
  143. # is True. Use of include_package_data will imply that
  144. # distutils-style automatic handling of package_data is disabled
  145. if not self.distribution.include_package_data:
  146. for _, src_dir, _, filenames in build_py.data_files:
  147. self.filelist.extend([os.path.join(src_dir, filename)
  148. for filename in filenames])
  149. if self.distribution.has_ext_modules():
  150. build_ext = self.get_finalized_command('build_ext')
  151. self.filelist.extend(build_ext.get_source_files())
  152. if self.distribution.has_c_libraries():
  153. build_clib = self.get_finalized_command('build_clib')
  154. self.filelist.extend(build_clib.get_source_files())
  155. if self.distribution.has_scripts():
  156. build_scripts = self.get_finalized_command('build_scripts')
  157. self.filelist.extend(build_scripts.get_source_files())
  158. def check_readme(self):
  159. for f in READMES:
  160. if os.path.exists(f):
  161. return
  162. else:
  163. self.warn(
  164. "standard file not found: should have one of " +', '.join(READMES)
  165. )
  166. def make_release_tree(self, base_dir, files):
  167. orig.sdist.make_release_tree(self, base_dir, files)
  168. # Save any egg_info command line options used to create this sdist
  169. dest = os.path.join(base_dir, 'setup.cfg')
  170. if hasattr(os,'link') and os.path.exists(dest):
  171. # unlink and re-copy, since it might be hard-linked, and
  172. # we don't want to change the source version
  173. os.unlink(dest)
  174. self.copy_file('setup.cfg', dest)
  175. self.get_finalized_command('egg_info').save_version_info(dest)
  176. def _manifest_is_not_generated(self):
  177. # check for special comment used in 2.7.1 and higher
  178. if not os.path.isfile(self.manifest):
  179. return False
  180. fp = open(self.manifest, 'rbU')
  181. try:
  182. first_line = fp.readline()
  183. finally:
  184. fp.close()
  185. return first_line != '# file GENERATED by distutils, do NOT edit\n'.encode()
  186. def read_manifest(self):
  187. """Read the manifest file (named by 'self.manifest') and use it to
  188. fill in 'self.filelist', the list of files to include in the source
  189. distribution.
  190. """
  191. log.info("reading manifest file '%s'", self.manifest)
  192. manifest = open(self.manifest, 'rbU')
  193. for line in manifest:
  194. # The manifest must contain UTF-8. See #303.
  195. if sys.version_info >= (3,):
  196. try:
  197. line = line.decode('UTF-8')
  198. except UnicodeDecodeError:
  199. log.warn("%r not UTF-8 decodable -- skipping" % line)
  200. continue
  201. # ignore comments and blank lines
  202. line = line.strip()
  203. if line.startswith('#') or not line:
  204. continue
  205. self.filelist.append(line)
  206. manifest.close()