compat.py 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755
  1. #-----------------------------------------------------------------------------
  2. # Copyright (c) 2005-2021, PyInstaller Development Team.
  3. #
  4. # Distributed under the terms of the GNU General Public License (version 2
  5. # or later) with exception for distributing the bootloader.
  6. #
  7. # The full license is in the file COPYING.txt, distributed with this software.
  8. #
  9. # SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
  10. #-----------------------------------------------------------------------------
  11. """
  12. Various classes and functions to provide some backwards-compatibility with previous versions of Python onward.
  13. """
  14. import errno
  15. import importlib.machinery
  16. import importlib.util
  17. import os
  18. import platform
  19. import site
  20. import subprocess
  21. import sys
  22. import shutil
  23. from PyInstaller._shared_with_waf import _pyi_machine
  24. from PyInstaller.exceptions import ExecCommandFailed
  25. # Copied from https://docs.python.org/3/library/platform.html#cross-platform.
  26. is_64bits = sys.maxsize > 2**32
  27. # Distinguish specific code for various Python versions. Variables 'is_pyXY' mean that Python X.Y and up is supported.
  28. # Keep even unsupported versions here to keep 3rd-party hooks working.
  29. is_py35 = sys.version_info >= (3, 5)
  30. is_py36 = sys.version_info >= (3, 6)
  31. is_py37 = sys.version_info >= (3, 7)
  32. is_py38 = sys.version_info >= (3, 8)
  33. is_py39 = sys.version_info >= (3, 9)
  34. is_py310 = sys.version_info >= (3, 10)
  35. is_win = sys.platform.startswith('win')
  36. is_win_10 = is_win and (platform.win32_ver()[0] == '10')
  37. is_cygwin = sys.platform == 'cygwin'
  38. is_darwin = sys.platform == 'darwin' # Mac OS X
  39. # Unix platforms
  40. is_linux = sys.platform.startswith('linux')
  41. is_solar = sys.platform.startswith('sun') # Solaris
  42. is_aix = sys.platform.startswith('aix')
  43. is_freebsd = sys.platform.startswith('freebsd')
  44. is_openbsd = sys.platform.startswith('openbsd')
  45. is_hpux = sys.platform.startswith('hp-ux')
  46. # Some code parts are similar to several unix platforms (e.g. Linux, Solaris, AIX).
  47. # Mac OS is not considered as unix since there are many platform-specific details for Mac in PyInstaller.
  48. is_unix = is_linux or is_solar or is_aix or is_freebsd or is_hpux or is_openbsd
  49. # Linux distributions such as Alpine or OpenWRT use musl as their libc implementation and resultantly need specially
  50. # compiled bootloaders. On musl systems, ldd with no arguments prints 'musl' and its version.
  51. is_musl = is_linux and "musl" in subprocess.getoutput(["ldd"])
  52. # macOS version
  53. _macos_ver = tuple(int(x) for x in platform.mac_ver()[0].split('.')) if is_darwin else None
  54. # macOS 11 (Big Sur): if python is not compiled with Big Sur support, it ends up in compatibility mode by default, which
  55. # is indicated by platform.mac_ver() returning '10.16'. The lack of proper Big Sur support breaks find_library()
  56. # function from ctypes.util module, as starting with Big Sur, shared libraries are not visible on disk anymore. Support
  57. # for the new library search mechanism was added in python 3.9 when compiled with Big Sur support. In such cases,
  58. # platform.mac_ver() reports version as '11.x'. The behavior can be further modified via SYSTEM_VERSION_COMPAT
  59. # environment variable; which allows explicitly enabling or disabling the compatibility mode. However, note that
  60. # disabling the compatibility mode and using python that does not properly support Big Sur still leaves find_library()
  61. # broken (which is a scenario that we ignore at the moment).
  62. # The same logic applies to macOS 12 (Monterey).
  63. is_macos_11_compat = _macos_ver and _macos_ver[0:2] == (10, 16) # Big Sur or newer in compat mode
  64. is_macos_11_native = _macos_ver and _macos_ver[0:2] >= (11, 0) # Big Sur or newer in native mode
  65. is_macos_11 = is_macos_11_compat or is_macos_11_native # Big Sur or newer
  66. # On different platforms is different file for dynamic python library.
  67. # TODO: When removing support for is_py37, the "m" variants can be
  68. # removed, see <https://docs.python.org/3/whatsnew/3.8.html#build-and-c-api-changes>
  69. _pyver = sys.version_info[:2]
  70. if is_win or is_cygwin:
  71. PYDYLIB_NAMES = {
  72. 'python%d%d.dll' % _pyver,
  73. 'libpython%d%d.dll' % _pyver,
  74. 'libpython%d%dm.dll' % _pyver,
  75. 'libpython%d.%d.dll' % _pyver,
  76. 'libpython%d.%dm.dll' % _pyver
  77. } # For MSYS2 environment
  78. elif is_darwin:
  79. # libpython%d.%dm.dylib for Conda virtual environment installations
  80. PYDYLIB_NAMES = {
  81. 'Python', '.Python',
  82. 'Python%d' % _pyver[0],
  83. 'libpython%d.%d.dylib' % _pyver,
  84. 'libpython%d.%dm.dylib' % _pyver
  85. }
  86. elif is_aix:
  87. # Shared libs on AIX may be archives with shared object members, hence the ".a" suffix. However, starting with
  88. # python 2.7.11 libpython?.?.so and Python3 libpython?.?m.so files are produced.
  89. PYDYLIB_NAMES = {
  90. 'libpython%d.%d.a' % _pyver,
  91. 'libpython%d.%dm.a' % _pyver,
  92. 'libpython%d.%d.so' % _pyver,
  93. 'libpython%d.%dm.so' % _pyver
  94. }
  95. elif is_freebsd:
  96. PYDYLIB_NAMES = {
  97. 'libpython%d.%d.so.1' % _pyver,
  98. 'libpython%d.%dm.so.1' % _pyver,
  99. 'libpython%d.%d.so.1.0' % _pyver,
  100. 'libpython%d.%dm.so.1.0' % _pyver
  101. }
  102. elif is_openbsd:
  103. PYDYLIB_NAMES = {'libpython%d.%d.so.0.0' % _pyver, 'libpython%d.%dm.so.0.0' % _pyver}
  104. elif is_hpux:
  105. PYDYLIB_NAMES = {'libpython%d.%d.so' % _pyver}
  106. elif is_unix:
  107. # Other *nix platforms.
  108. # Python 2 .so library on Linux is: libpython2.7.so.1.0
  109. # Python 3 .so library on Linux is: libpython3.2mu.so.1.0, libpython3.3m.so.1.0
  110. PYDYLIB_NAMES = {
  111. 'libpython%d.%d.so.1.0' % _pyver,
  112. 'libpython%d.%dm.so.1.0' % _pyver,
  113. 'libpython%d.%dmu.so.1.0' % _pyver,
  114. 'libpython%d.%dm.so' % _pyver,
  115. 'libpython%d.%d.so' % _pyver
  116. }
  117. else:
  118. raise SystemExit('Your platform is not yet supported. Please define constant PYDYLIB_NAMES for your platform.')
  119. # Function with which to open files.
  120. open_file = open
  121. text_read_mode = 'r'
  122. # In Python 3 built-in function raw_input() was renamed to just 'input()'.
  123. stdin_input = input
  124. # Safe repr that always outputs ascii
  125. safe_repr = ascii
  126. # String types to replace `isinstance(foo, str)`. Obsolete since dropping support for Python 2.x.
  127. string_types = str
  128. # Correct extension ending: 'c' or 'o'
  129. if __debug__:
  130. PYCO = 'c'
  131. else:
  132. PYCO = 'o'
  133. # Options for python interpreter when invoked in a subprocess.
  134. if __debug__:
  135. # Python started *without* -O
  136. _PYOPTS = ''
  137. else:
  138. _PYOPTS = '-O'
  139. # In a virtual environment created by virtualenv (github.com/pypa/virtualenv) there exists sys.real_prefix with the path
  140. # to the base Python installation from which the virtual environment was created. This is true regardless of the version
  141. # of Python used to execute the virtualenv command.
  142. #
  143. # In a virtual environment created by the venv module available in the Python standard lib, there exists sys.base_prefix
  144. # with the path to the base implementation. This does not exist in a virtual environment created by virtualenv.
  145. #
  146. # The following code creates compat.is_venv and is.virtualenv that are True when running a virtual environment, and also
  147. # compat.base_prefix with the path to the base Python installation.
  148. base_prefix = os.path.abspath(getattr(sys, 'real_prefix', getattr(sys, 'base_prefix', sys.prefix)))
  149. # Ensure `base_prefix` is not containing any relative parts.
  150. is_venv = is_virtualenv = base_prefix != os.path.abspath(sys.prefix)
  151. # Conda environments sometimes have different paths or apply patches to packages that can affect how a hook or package
  152. # should access resources. Method for determining conda taken from https://stackoverflow.com/questions/47610844#47610844
  153. is_conda = os.path.isdir(os.path.join(base_prefix, 'conda-meta'))
  154. # Similar to ``is_conda`` but is ``False`` using another ``venv``-like manager on top. In this case, no packages
  155. # encountered will be conda packages meaning that the default non-conda behaviour is generally desired from PyInstaller.
  156. is_pure_conda = os.path.isdir(os.path.join(sys.prefix, 'conda-meta'))
  157. # Full path to python interpreter.
  158. python_executable = getattr(sys, '_base_executable', sys.executable)
  159. # Is this Python from Microsoft App Store (Windows only)? Python from Microsoft App Store has executable pointing at
  160. # empty shims.
  161. is_ms_app_store = is_win and os.path.getsize(python_executable) == 0
  162. if is_ms_app_store:
  163. # Locate the actual executable inside base_prefix.
  164. python_executable = os.path.join(base_prefix, os.path.basename(python_executable))
  165. if not os.path.exists(python_executable):
  166. raise SystemExit(
  167. 'PyInstaller cannot locate real python executable belonging to Python from Microsoft App Store!'
  168. )
  169. # Bytecode magic value
  170. BYTECODE_MAGIC = importlib.util.MAGIC_NUMBER
  171. # List of suffixes for Python C extension modules.
  172. EXTENSION_SUFFIXES = importlib.machinery.EXTENSION_SUFFIXES
  173. ALL_SUFFIXES = importlib.machinery.all_suffixes()
  174. # In Python 3 'Tkinter' has been made lowercase - 'tkinter'.
  175. # TODO: remove once all references are gone from both pyinstaller and pyinstaller-hooks-contrib!
  176. modname_tkinter = 'tkinter'
  177. # On Windows we require pywin32-ctypes.
  178. # -> all pyinstaller modules should use win32api from PyInstaller.compat to
  179. # ensure that it can work on MSYS2 (which requires pywin32-ctypes)
  180. if is_win:
  181. try:
  182. from win32ctypes.pywin32 import pywintypes # noqa: F401, E402
  183. from win32ctypes.pywin32 import win32api # noqa: F401, E402
  184. except ImportError:
  185. # This environment variable is set by setup.py
  186. # - It's not an error for pywin32 to not be installed at that point
  187. if not os.environ.get('PYINSTALLER_NO_PYWIN32_FAILURE'):
  188. raise SystemExit(
  189. 'PyInstaller cannot check for assembly dependencies.\n'
  190. 'Please install pywin32-ctypes.\n\n'
  191. 'pip install pywin32-ctypes\n'
  192. )
  193. except Exception:
  194. if sys.flags.optimize == 2:
  195. raise SystemExit(
  196. "pycparser, a Windows only indirect dependency of PyInstaller, is incompatible with "
  197. "Python's \"discard docstrings\" (-OO) flag mode. For more information see:\n"
  198. " https://github.com/pyinstaller/pyinstaller/issues/6345"
  199. )
  200. raise
  201. # macOS's platform.architecture() can be buggy, so we do this manually here. Based off the python documentation:
  202. # https://docs.python.org/3/library/platform.html#platform.architecture
  203. if is_darwin:
  204. architecture = '64bit' if sys.maxsize > 2**32 else '32bit'
  205. else:
  206. architecture = platform.architecture()[0]
  207. # Cygwin needs special handling, because platform.system() contains identifiers such as MSYS_NT-10.0-19042 and
  208. # CYGWIN_NT-10.0-19042 that do not fit PyInstaller's OS naming scheme. Explicitly set `system` to 'Cygwin'.
  209. if is_cygwin:
  210. system = 'Cygwin'
  211. else:
  212. system = platform.system()
  213. # Machine suffix for bootloader.
  214. machine = _pyi_machine(platform.machine(), platform.system())
  215. # Set and get environment variables does not handle unicode strings correctly on Windows.
  216. # Acting on os.environ instead of using getenv()/setenv()/unsetenv(), as suggested in
  217. # <http://docs.python.org/library/os.html#os.environ>: "Calling putenv() directly does not change os.environ, so it is
  218. # better to modify os.environ." (Same for unsetenv.)
  219. def getenv(name, default=None):
  220. """
  221. Returns unicode string containing value of environment variable 'name'.
  222. """
  223. return os.environ.get(name, default)
  224. def setenv(name, value):
  225. """
  226. Accepts unicode string and set it as environment variable 'name' containing value 'value'.
  227. """
  228. os.environ[name] = value
  229. def unsetenv(name):
  230. """
  231. Delete the environment variable 'name'.
  232. """
  233. # Some platforms (e.g., AIX) do not support `os.unsetenv()` and thus `del os.environ[name]` has no effect on the
  234. # real environment. For this case, we set the value to the empty string.
  235. os.environ[name] = ""
  236. del os.environ[name]
  237. # Exec commands in subprocesses.
  238. def exec_command(*cmdargs, **kwargs):
  239. """
  240. Run the command specified by the passed positional arguments, optionally configured by the passed keyword arguments.
  241. .. DANGER::
  242. **Ignore this function's return value** -- unless this command's standard output contains _only_ pathnames, in
  243. which case this function returns the correct filesystem-encoded string expected by PyInstaller. In all other
  244. cases, this function's return value is _not_ safely usable. Consider calling the general-purpose
  245. `exec_command_stdout()` function instead.
  246. For backward compatibility, this function's return value non-portably depends on the current Python version and
  247. passed keyword arguments:
  248. * Under Python 2.7, this value is an **encoded `str` string** rather than a decoded `unicode` string. This value
  249. _cannot_ be safely used for any purpose (e.g., string manipulation or parsing), except to be passed directly to
  250. another non-Python command.
  251. * Under Python 3.x, this value is a **decoded `str` string**. However, even this value is _not_ necessarily
  252. safely usable:
  253. * If the `encoding` parameter is passed, this value is guaranteed to be safely usable.
  254. * Else, this value _cannot_ be safely used for any purpose (e.g., string manipulation or parsing), except to be
  255. passed directly to another non-Python command. Why? Because this value has been decoded with the encoding
  256. specified by `sys.getfilesystemencoding()`, the encoding used by `os.fsencode()` and `os.fsdecode()` to
  257. convert from platform-agnostic to platform-specific pathnames. This is _not_ necessarily the encoding with
  258. which this command's standard output was encoded. Cue edge-case decoding exceptions.
  259. Parameters
  260. ----------
  261. cmdargs :
  262. Variadic list whose:
  263. 1. Mandatory first element is the absolute path, relative path, or basename in the current `${PATH}` of the
  264. command to run.
  265. 2. Optional remaining elements are arguments to pass to this command.
  266. encoding : str, optional
  267. Optional keyword argument specifying the encoding with which to decode this command's standard output under
  268. Python 3. As this function's return value should be ignored, this argument should _never_ be passed.
  269. __raise_ENOENT__ : boolean, optional
  270. Optional keyword argument to simply raise the exception if the executing the command fails since to the command
  271. is not found. This is useful to checking id a command exists.
  272. All remaining keyword arguments are passed as is to the `subprocess.Popen()` constructor.
  273. Returns
  274. ----------
  275. str
  276. Ignore this value. See discussion above.
  277. """
  278. encoding = kwargs.pop('encoding', None)
  279. raise_enoent = kwargs.pop('__raise_ENOENT__', None)
  280. proc = subprocess.Popen(cmdargs, stdout=subprocess.PIPE, **kwargs)
  281. try:
  282. out = proc.communicate(timeout=60)[0]
  283. except OSError as e:
  284. if raise_enoent and e.errno == errno.ENOENT:
  285. raise
  286. print('--' * 20, file=sys.stderr)
  287. print("Error running '%s':" % " ".join(cmdargs), file=sys.stderr)
  288. print(e, file=sys.stderr)
  289. print('--' * 20, file=sys.stderr)
  290. raise ExecCommandFailed("Error: Executing command failed!") from e
  291. except subprocess.TimeoutExpired:
  292. proc.kill()
  293. raise
  294. # stdout/stderr are returned as a byte array NOT as string, so we need to convert that to proper encoding.
  295. try:
  296. if encoding:
  297. out = out.decode(encoding)
  298. else:
  299. # If no encoding is given, assume we are reading filenames from stdout only because it is the common case.
  300. out = os.fsdecode(out)
  301. except UnicodeDecodeError as e:
  302. # The sub-process used a different encoding; provide more information to ease debugging.
  303. print('--' * 20, file=sys.stderr)
  304. print(str(e), file=sys.stderr)
  305. print('These are the bytes around the offending byte:', file=sys.stderr)
  306. print('--' * 20, file=sys.stderr)
  307. raise
  308. return out
  309. def exec_command_rc(*cmdargs, **kwargs):
  310. """
  311. Return the exit code of the command specified by the passed positional arguments, optionally configured by the
  312. passed keyword arguments.
  313. Parameters
  314. ----------
  315. cmdargs : list
  316. Variadic list whose:
  317. 1. Mandatory first element is the absolute path, relative path, or basename in the current `${PATH}` of the
  318. command to run.
  319. 2. Optional remaining elements are arguments to pass to this command.
  320. All keyword arguments are passed as is to the `subprocess.call()` function.
  321. Returns
  322. ----------
  323. int
  324. This command's exit code as an unsigned byte in the range `[0, 255]`, where 0 signifies success and all other
  325. values signal a failure.
  326. """
  327. # 'encoding' keyword is not supported for 'subprocess.call'; remove it from kwargs.
  328. if 'encoding' in kwargs:
  329. kwargs.pop('encoding')
  330. return subprocess.call(cmdargs, **kwargs)
  331. def exec_command_stdout(*command_args, **kwargs):
  332. """
  333. Capture and return the standard output of the command specified by the passed positional arguments, optionally
  334. configured by the passed keyword arguments.
  335. Unlike the legacy `exec_command()` and `exec_command_all()` functions, this modern function is explicitly designed
  336. for cross-platform portability. The return value may be safely used for any purpose, including string manipulation
  337. and parsing.
  338. .. NOTE::
  339. If this command's standard output contains _only_ pathnames, this function does _not_ return the correct
  340. filesystem-encoded string expected by PyInstaller. If this is the case, consider calling the filesystem-specific
  341. `exec_command()` function instead.
  342. Parameters
  343. ----------
  344. cmdargs : list
  345. Variadic list whose:
  346. 1. Mandatory first element is the absolute path, relative path, or basename in the current `${PATH}` of the
  347. command to run.
  348. 2. Optional remaining elements are arguments to pass to this command.
  349. encoding : str, optional
  350. Optional name of the encoding with which to decode this command's standard output (e.g., `utf8`), passed as a
  351. keyword argument. If unpassed , this output will be decoded in a portable manner specific to to the current
  352. platform, shell environment, and system settings with Python's built-in `universal_newlines` functionality.
  353. All remaining keyword arguments are passed as is to the `subprocess.check_output()` function.
  354. Returns
  355. ----------
  356. str
  357. Unicode string of this command's standard output decoded according to the "encoding" keyword argument.
  358. """
  359. # Value of the passed "encoding" parameter, defaulting to None.
  360. encoding = kwargs.pop('encoding', None)
  361. # If no encoding was specified, the current locale is defaulted to. Else, an encoding was specified. To ensure this
  362. # encoding is respected, the "universal_newlines" option is disabled if also passed. Nice, eh?
  363. kwargs['universal_newlines'] = encoding is None
  364. # Standard output captured from this command as a decoded Unicode string if "universal_newlines" is enabled or an
  365. # encoded byte array otherwise.
  366. stdout = subprocess.check_output(command_args, **kwargs)
  367. # Return a Unicode string, decoded from this encoded byte array if needed.
  368. return stdout if encoding is None else stdout.decode(encoding)
  369. def exec_command_all(*cmdargs: str, **kwargs):
  370. """
  371. Run the command specified by the passed positional arguments, optionally configured by the passed keyword arguments.
  372. .. DANGER::
  373. **Ignore this function's return value.** If this command's standard output consists solely of pathnames, consider
  374. calling `exec_command()`; otherwise, consider calling `exec_command_stdout()`.
  375. Parameters
  376. ----------
  377. cmdargs : str
  378. Variadic list whose:
  379. 1. Mandatory first element is the absolute path, relative path, or basename in the current `${PATH}` of the
  380. command to run.
  381. 2. Optional remaining elements are arguments to pass to this command.
  382. encoding : str, optional
  383. Optional keyword argument specifying the encoding with which to decode this command's standard output. As this
  384. function's return value should be ignored, this argument should _never_ be passed.
  385. All remaining keyword arguments are passed as is to the `subprocess.Popen()` constructor.
  386. Returns
  387. ----------
  388. (int, str, str)
  389. Ignore this 3-element tuple `(exit_code, stdout, stderr)`. See the `exec_command()` function for discussion.
  390. """
  391. encoding = kwargs.pop('encoding', None)
  392. proc = subprocess.Popen(
  393. cmdargs,
  394. bufsize=-1, # Default OS buffer size.
  395. stdout=subprocess.PIPE,
  396. stderr=subprocess.PIPE,
  397. **kwargs
  398. )
  399. # Waits for subprocess to complete.
  400. try:
  401. out, err = proc.communicate(timeout=60)
  402. except subprocess.TimeoutExpired:
  403. proc.kill()
  404. raise
  405. # stdout/stderr are returned as a byte array NOT as string. Thus we need to convert that to proper encoding.
  406. try:
  407. if encoding:
  408. out = out.decode(encoding)
  409. err = err.decode(encoding)
  410. else:
  411. # If no encoding is given, assume we're reading filenames from stdout only because it's the common case.
  412. out = os.fsdecode(out)
  413. err = os.fsdecode(err)
  414. except UnicodeDecodeError as e:
  415. # The sub-process used a different encoding, provide more information to ease debugging.
  416. print('--' * 20, file=sys.stderr)
  417. print(str(e), file=sys.stderr)
  418. print('These are the bytes around the offending byte:', file=sys.stderr)
  419. print('--' * 20, file=sys.stderr)
  420. raise
  421. return proc.returncode, out, err
  422. def __wrap_python(args, kwargs):
  423. cmdargs = [sys.executable]
  424. # Mac OS X supports universal binaries (binary for multiple architectures. We need to ensure that subprocess
  425. # binaries are running for the same architecture as python executable. It is necessary to run binaries with 'arch'
  426. # command.
  427. if is_darwin:
  428. if architecture == '64bit':
  429. if platform.machine() == 'arm64':
  430. py_prefix = ['arch', '-arm64'] # Apple M1
  431. else:
  432. py_prefix = ['arch', '-x86_64'] # Intel
  433. elif architecture == '32bit':
  434. py_prefix = ['arch', '-i386']
  435. else:
  436. py_prefix = []
  437. # Since Mac OS 10.11, the environment variable DYLD_LIBRARY_PATH is no more inherited by child processes, so we
  438. # proactively propagate the current value using the `-e` option of the `arch` command.
  439. if 'DYLD_LIBRARY_PATH' in os.environ:
  440. path = os.environ['DYLD_LIBRARY_PATH']
  441. py_prefix += ['-e', 'DYLD_LIBRARY_PATH=%s' % path]
  442. cmdargs = py_prefix + cmdargs
  443. if _PYOPTS:
  444. cmdargs.append(_PYOPTS)
  445. cmdargs.extend(args)
  446. env = kwargs.get('env')
  447. if env is None:
  448. env = dict(**os.environ)
  449. # Ensure python 3 subprocess writes 'str' as utf-8
  450. env['PYTHONIOENCODING'] = 'UTF-8'
  451. # ... and ensure we read output as utf-8
  452. kwargs['encoding'] = 'UTF-8'
  453. return cmdargs, kwargs
  454. def exec_python(*args, **kwargs):
  455. """
  456. Wrap running python script in a subprocess.
  457. Return stdout of the invoked command.
  458. """
  459. cmdargs, kwargs = __wrap_python(args, kwargs)
  460. return exec_command(*cmdargs, **kwargs)
  461. def exec_python_rc(*args, **kwargs):
  462. """
  463. Wrap running python script in a subprocess.
  464. Return exit code of the invoked command.
  465. """
  466. cmdargs, kwargs = __wrap_python(args, kwargs)
  467. return exec_command_rc(*cmdargs, **kwargs)
  468. # Path handling.
  469. def expand_path(path):
  470. """
  471. Replace initial tilde '~' in path with user's home directory, and also expand environment variables
  472. (i.e., ${VARNAME} on Unix, %VARNAME% on Windows).
  473. """
  474. return os.path.expandvars(os.path.expanduser(path))
  475. # Site-packages functions - use native function if available.
  476. def getsitepackages(prefixes=None):
  477. """
  478. Returns a list containing all global site-packages directories.
  479. For each directory present in ``prefixes`` (or the global ``PREFIXES``), this function finds its `site-packages`
  480. subdirectory depending on the system environment, and returns a list of full paths.
  481. """
  482. # This implementation was copied from the ``site`` module, python 3.7.3.
  483. sitepackages = []
  484. seen = set()
  485. if prefixes is None:
  486. prefixes = [sys.prefix, sys.exec_prefix]
  487. for prefix in prefixes:
  488. if not prefix or prefix in seen:
  489. continue
  490. seen.add(prefix)
  491. if os.sep == '/':
  492. sitepackages.append(os.path.join(prefix, "lib", "python%d.%d" % sys.version_info[:2], "site-packages"))
  493. else:
  494. sitepackages.append(prefix)
  495. sitepackages.append(os.path.join(prefix, "lib", "site-packages"))
  496. return sitepackages
  497. # Backported for virtualenv. Module 'site' in virtualenv might not have this attribute.
  498. getsitepackages = getattr(site, 'getsitepackages', getsitepackages)
  499. # Wrapper to load a module from a Python source file. This function loads import hooks when processing them.
  500. def importlib_load_source(name, pathname):
  501. # Import module from a file.
  502. mod_loader = importlib.machinery.SourceFileLoader(name, pathname)
  503. return mod_loader.load_module()
  504. # Patterns of module names that should be bundled into the base_library.zip.
  505. PY3_BASE_MODULES = {
  506. # These modules are direct or indirect dependencies of encodings.* modules. encodings modules must be recursively
  507. # included to set the I/O encoding during python startup.
  508. '_collections_abc',
  509. '_weakrefset',
  510. 'abc',
  511. 'codecs',
  512. 'collections',
  513. 'copyreg',
  514. 'encodings',
  515. 'enum',
  516. 'functools',
  517. 'io',
  518. 'heapq',
  519. 'keyword',
  520. 'linecache',
  521. 'locale',
  522. 'operator',
  523. 're',
  524. 'reprlib',
  525. 'sre_compile',
  526. 'sre_constants',
  527. 'sre_parse',
  528. 'tokenize', # used by loader/pymod03_importers.py
  529. 'traceback', # for startup errors
  530. 'types',
  531. 'weakref',
  532. 'warnings',
  533. }
  534. if not is_py310:
  535. PY3_BASE_MODULES.add('_bootlocale')
  536. # Object types of Pure Python modules in modulegraph dependency graph.
  537. # Pure Python modules have code object (attribute co_code).
  538. PURE_PYTHON_MODULE_TYPES = {
  539. 'SourceModule',
  540. 'CompiledModule',
  541. 'Package',
  542. 'NamespacePackage',
  543. # Deprecated.
  544. # TODO Could these module types be removed?
  545. 'FlatPackage',
  546. 'ArchiveModule',
  547. }
  548. # Object types of special Python modules (built-in, run-time, namespace package) in modulegraph dependency graph that do
  549. # not have code object.
  550. SPECIAL_MODULE_TYPES = {
  551. 'AliasNode',
  552. 'BuiltinModule',
  553. 'RuntimeModule',
  554. 'RuntimePackage',
  555. # PyInstaller handles scripts differently and not as standard Python modules.
  556. 'Script',
  557. }
  558. # Object types of Binary Python modules (extensions, etc) in modulegraph dependency graph.
  559. BINARY_MODULE_TYPES = {
  560. 'Extension',
  561. 'ExtensionPackage',
  562. }
  563. # Object types of valid Python modules in modulegraph dependency graph.
  564. VALID_MODULE_TYPES = PURE_PYTHON_MODULE_TYPES | SPECIAL_MODULE_TYPES | BINARY_MODULE_TYPES
  565. # Object types of bad/missing/invalid Python modules in modulegraph dependency graph.
  566. # TODO: should be 'Invalid' module types also in the 'MISSING' set?
  567. BAD_MODULE_TYPES = {
  568. 'BadModule',
  569. 'ExcludedModule',
  570. 'InvalidSourceModule',
  571. 'InvalidCompiledModule',
  572. 'MissingModule',
  573. # Runtime modules and packages are technically valid rather than bad, but exist only in-memory rather than on-disk
  574. # (typically due to pre_safe_import_module() hooks), and hence cannot be physically frozen. For simplicity, these
  575. # nodes are categorized as bad rather than valid.
  576. 'RuntimeModule',
  577. 'RuntimePackage',
  578. }
  579. ALL_MODULE_TYPES = VALID_MODULE_TYPES | BAD_MODULE_TYPES
  580. # TODO: review this mapping to TOC, remove useless entries.
  581. # Dictionary to map ModuleGraph node types to TOC typecodes.
  582. MODULE_TYPES_TO_TOC_DICT = {
  583. # Pure modules.
  584. 'AliasNode': 'PYMODULE',
  585. 'Script': 'PYSOURCE',
  586. 'SourceModule': 'PYMODULE',
  587. 'CompiledModule': 'PYMODULE',
  588. 'Package': 'PYMODULE',
  589. 'FlatPackage': 'PYMODULE',
  590. 'ArchiveModule': 'PYMODULE',
  591. # Binary modules.
  592. 'Extension': 'EXTENSION',
  593. 'ExtensionPackage': 'EXTENSION',
  594. # Special valid modules.
  595. 'BuiltinModule': 'BUILTIN',
  596. 'NamespacePackage': 'PYMODULE',
  597. # Bad modules.
  598. 'BadModule': 'bad',
  599. 'ExcludedModule': 'excluded',
  600. 'InvalidSourceModule': 'invalid',
  601. 'InvalidCompiledModule': 'invalid',
  602. 'MissingModule': 'missing',
  603. 'RuntimeModule': 'runtime',
  604. 'RuntimePackage': 'runtime',
  605. # Other.
  606. 'does not occur': 'BINARY',
  607. }
  608. def check_requirements():
  609. """
  610. Verify that all requirements to run PyInstaller are met.
  611. Fail hard if any requirement is not met.
  612. """
  613. # Fail hard if Python does not have minimum required version
  614. if sys.version_info < (3, 6):
  615. raise EnvironmentError('PyInstaller requires at Python 3.6 or newer.')
  616. # There are some old packages which used to be backports of libraries which are now part of the standard library.
  617. # These backports are now unmaintained and contain only an older subset of features leading to obscure errors like
  618. # "enum has not attribute IntFlag" if installed.
  619. if is_py38:
  620. from importlib.metadata import distribution, PackageNotFoundError
  621. else:
  622. from importlib_metadata import distribution, PackageNotFoundError
  623. for name in ["enum34", "typing"]:
  624. try:
  625. distribution(name)
  626. except PackageNotFoundError:
  627. pass
  628. else:
  629. raise SystemExit(
  630. f"The '{name}' package is an obsolete backport of a standard library package and is "
  631. f"incompatible with PyInstaller. Please "
  632. f"`{'conda remove' if is_conda else 'pip uninstall'} {name}` then try again."
  633. )
  634. # Bail out if binutils is not installed.
  635. if is_linux and shutil.which("objdump") is None:
  636. raise SystemExit(
  637. "On Linux, objdump is required. It is typically provided by the 'binutils' package "
  638. "installable via your Linux distribution's package manager."
  639. )