Appendix之setup.py:附录文件内容记录setup.py

举报
一个处女座的程序猿 发表于 2021/03/26 23:57:02 2021/03/26
【摘要】 Appendix之setup.py:附录文件内容记录setup.py     附录文件内容记录setup.py from __future__ import print_function from distutils.ccompiler import new_compiler as _new_compilerfrom distutils.comma...

Appendix之setup.py:附录文件内容记录setup.py

 

 

附录文件内容记录setup.py


  
  1. from __future__ import print_function
  2. from distutils.ccompiler import new_compiler as _new_compiler
  3. from distutils.command.clean import clean, log
  4. from distutils.core import Command
  5. from distutils.dir_util import remove_tree
  6. from distutils.errors import DistutilsExecError
  7. from distutils.msvccompiler import MSVCCompiler
  8. from setuptools import setup, find_packages, Extension, Distribution
  9. from setuptools.command.build_ext import build_ext
  10. from subprocess import Popen, PIPE
  11. import argparse
  12. import errno
  13. import os
  14. import platform
  15. import re
  16. import shlex
  17. import sys
  18. # We don't need six...
  19. PY3 = sys.version_info[0] >= 3
  20. if PY3:
  21. from shlex import quote as shell_quote
  22. else:
  23. from pipes import quote as shell_quote
  24. try:
  25. # This depends on _winreg, which is not availible on not-Windows.
  26. from distutils.msvc9compiler import MSVCCompiler as MSVC9Compiler
  27. except ImportError:
  28. MSVC9Compiler = None
  29. try:
  30. from distutils._msvccompiler import MSVCCompiler as MSVC14Compiler
  31. except ImportError:
  32. MSVC14Compiler = None
  33. try:
  34. from Cython import __version__ as cython_version
  35. from Cython.Build import cythonize
  36. except ImportError:
  37. cythonize = None
  38. else:
  39. # We depend upon some features in Cython 0.27; reject older ones.
  40. if tuple(map(int, cython_version.split('.'))) < (0, 27):
  41. print("Cython {} is too old for PyAV; ignoring it.".format(cython_version))
  42. cythonize = None
  43. # We will embed this metadata into the package so it can be recalled for debugging.
  44. version = open('VERSION.txt').read().strip()
  45. try:
  46. git_commit, _ = Popen(['git', 'describe', '--tags'], stdout=PIPE, stderr=PIPE).communicate()
  47. except OSError:
  48. git_commit = None
  49. else:
  50. git_commit = git_commit.decode().strip()
  51. _cflag_parser = argparse.ArgumentParser(add_help=False)
  52. _cflag_parser.add_argument('-I', dest='include_dirs', action='append')
  53. _cflag_parser.add_argument('-L', dest='library_dirs', action='append')
  54. _cflag_parser.add_argument('-l', dest='libraries', action='append')
  55. _cflag_parser.add_argument('-D', dest='define_macros', action='append')
  56. _cflag_parser.add_argument('-R', dest='runtime_library_dirs', action='append')
  57. def parse_cflags(raw_cflags):
  58. raw_args = shlex.split(raw_cflags.strip())
  59. args, unknown = _cflag_parser.parse_known_args(raw_args)
  60. config = {k: v or [] for k, v in args.__dict__.items()}
  61. for i, x in enumerate(config['define_macros']):
  62. parts = x.split('=', 1)
  63. value = x[1] or None if len(x) == 2 else None
  64. config['define_macros'][i] = (parts[0], value)
  65. return config, ' '.join(shell_quote(x) for x in unknown)
  66. def get_library_config(name):
  67. """Get distutils-compatible extension extras for the given library.
  68. This requires ``pkg-config``.
  69. """
  70. try:
  71. proc = Popen(['pkg-config', '--cflags', '--libs', name], stdout=PIPE, stderr=PIPE)
  72. except OSError:
  73. print('pkg-config is required for building PyAV')
  74. exit(1)
  75. raw_cflags, err = proc.communicate()
  76. if proc.wait():
  77. return
  78. known, unknown = parse_cflags(raw_cflags.decode('utf8'))
  79. if unknown:
  80. print("pkg-config returned flags we don't understand: {}".format(unknown))
  81. exit(1)
  82. return known
  83. def update_extend(dst, src):
  84. """Update the `dst` with the `src`, extending values where lists.
  85. Primiarily useful for integrating results from `get_library_config`.
  86. """
  87. for k, v in src.items():
  88. existing = dst.setdefault(k, [])
  89. for x in v:
  90. if x not in existing:
  91. existing.append(x)
  92. def unique_extend(a, *args):
  93. a[:] = list(set().union(a, *args))
  94. # Obtain the ffmpeg dir from the "--ffmpeg-dir=<dir>" argument
  95. # FFMPEG_DIR = None
  96. FFMPEG_DIR = 'D://Program Files//ffmpeg'
  97. for i, arg in enumerate(sys.argv):
  98. if arg.startswith('--ffmpeg-dir='):
  99. FFMPEG_DIR = arg.split('=')[1]
  100. break
  101. if FFMPEG_DIR is not None:
  102. # delete the --ffmpeg-dir arg so that distutils does not see it
  103. del sys.argv[i]
  104. if not os.path.isdir(FFMPEG_DIR):
  105. print('The specified ffmpeg directory does not exist')
  106. exit(1)
  107. else:
  108. # Check the environment variable FFMPEG_DIR
  109. FFMPEG_DIR = os.environ.get('FFMPEG_DIR')
  110. if FFMPEG_DIR is not None:
  111. if not os.path.isdir(FFMPEG_DIR):
  112. FFMPEG_DIR = None
  113. if FFMPEG_DIR is not None:
  114. ffmpeg_lib = os.path.join(FFMPEG_DIR, 'lib')
  115. ffmpeg_include = os.path.join(FFMPEG_DIR, 'include')
  116. if os.path.exists(ffmpeg_lib):
  117. ffmpeg_lib = [ffmpeg_lib]
  118. else:
  119. ffmpeg_lib = [FFMPEG_DIR]
  120. if os.path.exists(ffmpeg_include):
  121. ffmpeg_include = [ffmpeg_include]
  122. else:
  123. ffmpeg_include = [FFMPEG_DIR]
  124. else:
  125. ffmpeg_lib = []
  126. ffmpeg_include = []
  127. # The "extras" to be supplied to every one of our modules.
  128. # This is expanded heavily by the `config` command.
  129. extension_extra = {
  130. 'include_dirs': ['include'] + ffmpeg_include, # The first are PyAV's includes.
  131. 'libraries' : [],
  132. 'library_dirs': ffmpeg_lib,
  133. }
  134. # The macros which describe the current PyAV version.
  135. config_macros = {
  136. "PYAV_VERSION": version,
  137. "PYAV_VERSION_STR": '"%s"' % version,
  138. "PYAV_COMMIT_STR": '"%s"' % (git_commit or 'unknown-commit'),
  139. }
  140. def dump_config():
  141. """Print out all the config information we have so far (for debugging)."""
  142. print('PyAV:', version, git_commit or '(unknown commit)')
  143. print('Python:', sys.version.encode('unicode_escape' if PY3 else 'string-escape'))
  144. print('platform:', platform.platform())
  145. print('extension_extra:')
  146. for k, vs in extension_extra.items():
  147. print('\t%s: %s' % (k, [x.encode('utf8') for x in vs]))
  148. print('config_macros:')
  149. for x in sorted(config_macros.items()):
  150. print('\t%s=%s' % x)
  151. # Monkey-patch for CCompiler to be silent.
  152. def _CCompiler_spawn_silent(cmd, dry_run=None):
  153. """Spawn a process, and eat the stdio."""
  154. proc = Popen(cmd, stdout=PIPE, stderr=PIPE)
  155. out, err = proc.communicate()
  156. if proc.returncode:
  157. raise DistutilsExecError(err)
  158. def new_compiler(*args, **kwargs):
  159. """Create a C compiler.
  160. :param bool silent: Eat all stdio? Defaults to ``True``.
  161. All other arguments passed to ``distutils.ccompiler.new_compiler``.
  162. """
  163. make_silent = kwargs.pop('silent', True)
  164. cc = _new_compiler(*args, **kwargs)
  165. # If MSVC10, initialize the compiler here and add /MANIFEST to linker flags.
  166. # See Python issue 4431 (https://bugs.python.org/issue4431)
  167. if is_msvc(cc):
  168. from distutils.msvc9compiler import get_build_version
  169. if get_build_version() == 10:
  170. cc.initialize()
  171. for ldflags in [cc.ldflags_shared, cc.ldflags_shared_debug]:
  172. unique_extend(ldflags, ['/MANIFEST'])
  173. # If MSVC14, do not silence. As msvc14 requires some custom
  174. # steps before the process is spawned, we can't monkey-patch this.
  175. elif get_build_version() == 14:
  176. make_silent = False
  177. # monkey-patch compiler to suppress stdout and stderr.
  178. if make_silent:
  179. cc.spawn = _CCompiler_spawn_silent
  180. return cc
  181. _msvc_classes = tuple(filter(None, (MSVCCompiler, MSVC9Compiler, MSVC14Compiler)))
  182. def is_msvc(cc=None):
  183. cc = _new_compiler() if cc is None else cc
  184. return isinstance(cc, _msvc_classes)
  185. if os.name == 'nt':
  186. if is_msvc():
  187. config_macros['inline'] = '__inline'
  188. # Since we're shipping a self contained unit on Windows, we need to mark
  189. # the package as such. On other systems, let it be universal.
  190. class BinaryDistribution(Distribution):
  191. def is_pure(self):
  192. return False
  193. distclass = BinaryDistribution
  194. else:
  195. # Nothing to see here.
  196. distclass = Distribution
  197. # Monkey-patch Cython to not overwrite embedded signatures.
  198. if cythonize:
  199. from Cython.Compiler.AutoDocTransforms import EmbedSignature
  200. old_embed_signature = EmbedSignature._embed_signature
  201. def new_embed_signature(self, sig, doc):
  202. # Strip any `self` parameters from the front.
  203. sig = re.sub(r'\(self(,\s+)?', '(', sig)
  204. # If they both start with the same signature; skip it.
  205. if sig and doc:
  206. new_name = sig.split('(')[0].strip()
  207. old_name = doc.split('(')[0].strip()
  208. if new_name == old_name:
  209. return doc
  210. if new_name.endswith('.' + old_name):
  211. return doc
  212. return old_embed_signature(self, sig, doc)
  213. EmbedSignature._embed_signature = new_embed_signature
  214. # Construct the modules that we find in the "av" directory.
  215. ext_modules = []
  216. for dirname, dirnames, filenames in os.walk('av'):
  217. for filename in filenames:
  218. # We are looing for Cython sources.
  219. if filename.startswith('.') or os.path.splitext(filename)[1] != '.pyx':
  220. continue
  221. pyx_path = os.path.join(dirname, filename)
  222. base = os.path.splitext(pyx_path)[0]
  223. # Need to be a little careful because Windows will accept / or \
  224. # (where os.sep will be \ on Windows).
  225. mod_name = base.replace('/', '.').replace(os.sep, '.')
  226. c_path = os.path.join('src', base + '.c')
  227. # We go with the C sources if Cython is not installed, and fail if
  228. # those also don't exist. We can't `cythonize` here though, since the
  229. # `pyav/include.h` must be generated (by `build_ext`) first.
  230. if not cythonize and not os.path.exists(c_path):
  231. print('Cython is required to build PyAV from raw sources.')
  232. print('Please `pip install Cython`.')
  233. exit(3)
  234. ext_modules.append(Extension(
  235. mod_name,
  236. sources=[c_path if not cythonize else pyx_path],
  237. ))
  238. class ConfigCommand(Command):
  239. user_options = [
  240. ('no-pkg-config', None,
  241. "do not use pkg-config to configure dependencies"),
  242. ('compiler=', 'c',
  243. "specify the compiler type"), ]
  244. boolean_options = ['no-pkg-config']
  245. def initialize_options(self):
  246. self.compiler = None
  247. self.no_pkg_config = None
  248. def finalize_options(self):
  249. self.set_undefined_options('build',
  250. ('compiler', 'compiler'),)
  251. self.set_undefined_options('build_ext',
  252. ('no_pkg_config', 'no_pkg_config'),)
  253. def run(self):
  254. # For some reason we get the feeling that CFLAGS is not respected, so we parse
  255. # it here. TODO: Leave any arguments that we can't figure out.
  256. for name in 'CFLAGS', 'LDFLAGS':
  257. known, unknown = parse_cflags(os.environ.pop(name, ''))
  258. if unknown:
  259. print("Warning: We don't understand some of {} (and will leave it in the envvar): {}".format(name, unknown))
  260. os.environ[name] = unknown
  261. update_extend(extension_extra, known)
  262. if is_msvc(new_compiler(compiler=self.compiler)):
  263. # Assume we have to disable /OPT:REF for MSVC with ffmpeg
  264. config = {
  265. 'extra_link_args': ['/OPT:NOREF'],
  266. }
  267. update_extend(extension_extra, config)
  268. # Check if we're using pkg-config or not
  269. if self.no_pkg_config:
  270. # Simply assume we have everything we need!
  271. config = {
  272. 'libraries': ['avformat', 'avcodec', 'avdevice', 'avutil', 'avfilter',
  273. 'swscale', 'swresample'],
  274. 'library_dirs': [],
  275. 'include_dirs': []
  276. }
  277. update_extend(extension_extra, config)
  278. for ext in self.distribution.ext_modules:
  279. for key, value in extension_extra.items():
  280. setattr(ext, key, value)
  281. return
  282. # We're using pkg-config:
  283. errors = []
  284. # Get the config for the libraries that we require.
  285. for name in 'libavformat', 'libavcodec', 'libavdevice', 'libavutil', 'libavfilter', 'libswscale', 'libswresample':
  286. config = get_library_config(name)
  287. if config:
  288. update_extend(extension_extra, config)
  289. # We don't need macros for these, since they all must exist.
  290. else:
  291. errors.append('Could not find ' + name + ' with pkg-config.')
  292. # Don't continue if we have errors.
  293. # TODO: Warn Ubuntu 12 users that they can't satisfy requirements with the
  294. # default package sources.
  295. if errors:
  296. print('\n'.join(errors))
  297. exit(1)
  298. # Normalize the extras.
  299. extension_extra.update(
  300. dict((k, sorted(set(v))) for k, v in extension_extra.items())
  301. )
  302. # Apply them.
  303. for ext in self.distribution.ext_modules:
  304. for key, value in extension_extra.items():
  305. setattr(ext, key, value)
  306. class CleanCommand(clean):
  307. user_options = clean.user_options + [
  308. ('sources', None,
  309. "remove Cython build output (C sources)")]
  310. boolean_options = clean.boolean_options + ['sources']
  311. def initialize_options(self):
  312. clean.initialize_options(self)
  313. self.sources = None
  314. def run(self):
  315. clean.run(self)
  316. if self.sources:
  317. if os.path.exists('src'):
  318. remove_tree('src', dry_run=self.dry_run)
  319. else:
  320. log.info("'%s' does not exist -- can't clean it", 'src')
  321. class CythonizeCommand(Command):
  322. user_options = []
  323. def initialize_options(self):
  324. pass
  325. def finalize_options(self):
  326. pass
  327. def run(self):
  328. # Cythonize, if required. We do it individually since we must update
  329. # the existing extension instead of replacing them all.
  330. for i, ext in enumerate(self.distribution.ext_modules):
  331. if any(s.endswith('.pyx') for s in ext.sources):
  332. if is_msvc():
  333. ext.define_macros.append(('inline', '__inline'))
  334. new_ext = cythonize(
  335. ext,
  336. compiler_directives=dict(
  337. c_string_type='str',
  338. c_string_encoding='ascii',
  339. embedsignature=True,
  340. language_level=2,
  341. ),
  342. build_dir='src',
  343. include_path=ext.include_dirs,
  344. )[0]
  345. ext.sources = new_ext.sources
  346. class BuildExtCommand(build_ext):
  347. if os.name != 'nt':
  348. user_options = build_ext.user_options + [
  349. ('no-pkg-config', None,
  350. "do not use pkg-config to configure dependencies")]
  351. boolean_options = build_ext.boolean_options + ['no-pkg-config']
  352. def initialize_options(self):
  353. build_ext.initialize_options(self)
  354. self.no_pkg_config = None
  355. else:
  356. no_pkg_config = 1
  357. def run(self):
  358. # Propagate build options to config
  359. obj = self.distribution.get_command_obj('config')
  360. obj.compiler = self.compiler
  361. obj.no_pkg_config = self.no_pkg_config
  362. obj.include_dirs = self.include_dirs
  363. obj.libraries = self.libraries
  364. obj.library_dirs = self.library_dirs
  365. self.run_command('config')
  366. # We write a header file containing everything we have discovered by
  367. # inspecting the libraries which exist. This is the main mechanism we
  368. # use to detect differenced between FFmpeg and Libav.
  369. include_dir = os.path.join(self.build_temp, 'include')
  370. pyav_dir = os.path.join(include_dir, 'pyav')
  371. try:
  372. os.makedirs(pyav_dir)
  373. except OSError as e:
  374. if e.errno != errno.EEXIST:
  375. raise
  376. header_path = os.path.join(pyav_dir, 'config.h')
  377. print('writing', header_path)
  378. with open(header_path, 'w') as fh:
  379. fh.write('#ifndef PYAV_COMPAT_H\n')
  380. fh.write('#define PYAV_COMPAT_H\n')
  381. for k, v in sorted(config_macros.items()):
  382. fh.write('#define %s %s\n' % (k, v))
  383. fh.write('#endif\n')
  384. self.include_dirs = self.include_dirs or []
  385. self.include_dirs.append(include_dir)
  386. # Propagate config to cythonize.
  387. for i, ext in enumerate(self.distribution.ext_modules):
  388. unique_extend(ext.include_dirs, self.include_dirs)
  389. unique_extend(ext.library_dirs, self.library_dirs)
  390. unique_extend(ext.libraries, self.libraries)
  391. self.run_command('cythonize')
  392. build_ext.run(self)
  393. setup(
  394. name='av',
  395. version=version,
  396. description="Pythonic bindings for FFmpeg's libraries.",
  397. author="Mike Boers",
  398. author_email="pyav@mikeboers.com",
  399. url="https://github.com/mikeboers/PyAV",
  400. packages=find_packages(exclude=['build*', 'examples*', 'scratchpad*', 'tests*']),
  401. zip_safe=False,
  402. ext_modules=ext_modules,
  403. cmdclass={
  404. 'build_ext': BuildExtCommand,
  405. 'clean': CleanCommand,
  406. 'config': ConfigCommand,
  407. 'cythonize': CythonizeCommand,
  408. },
  409. test_suite='tests',
  410. entry_points={
  411. 'console_scripts': [
  412. 'pyav = av.__main__:main',
  413. ],
  414. },
  415. classifiers=[
  416. 'Development Status :: 5 - Production/Stable',
  417. 'Intended Audience :: Developers',
  418. 'License :: OSI Approved :: BSD License',
  419. 'Natural Language :: English',
  420. 'Operating System :: MacOS :: MacOS X',
  421. 'Operating System :: POSIX',
  422. 'Operating System :: Unix',
  423. 'Operating System :: Microsoft :: Windows',
  424. 'Programming Language :: Cython',
  425. 'Programming Language :: Python :: 2.7',
  426. 'Programming Language :: Python :: 3.4',
  427. 'Programming Language :: Python :: 3.5',
  428. 'Programming Language :: Python :: 3.6',
  429. 'Programming Language :: Python :: 3.7',
  430. 'Topic :: Software Development :: Libraries :: Python Modules',
  431. 'Topic :: Multimedia :: Sound/Audio',
  432. 'Topic :: Multimedia :: Sound/Audio :: Conversion',
  433. 'Topic :: Multimedia :: Video',
  434. 'Topic :: Multimedia :: Video :: Conversion',
  435. ],
  436. distclass=distclass,
  437. )

文章来源: yunyaniu.blog.csdn.net,作者:一个处女座的程序猿,版权归原作者所有,如需转载,请联系作者。

原文链接:yunyaniu.blog.csdn.net/article/details/103890294

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。