mirror of
https://github.com/python/cpython.git
synced 2025-08-03 16:39:00 +00:00
gh-106111: Remove zipapp documentation on creating a Windows executable (#106112)
Remove zipapp documentation on creating a Windows executable
This commit is contained in:
parent
1a2bc94fc2
commit
5d4dbf0e30
1 changed files with 9 additions and 106 deletions
|
@ -303,115 +303,18 @@ the Python interpreter registers the ``.pyz`` and ``.pyzw`` file extensions
|
|||
when installed.
|
||||
|
||||
|
||||
Making a Windows executable
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
On Windows, registration of the ``.pyz`` extension is optional, and
|
||||
furthermore, there are certain places that don't recognise registered
|
||||
extensions "transparently" (the simplest example is that
|
||||
``subprocess.run(['myapp'])`` won't find your application - you need to
|
||||
explicitly specify the extension).
|
||||
|
||||
On Windows, therefore, it is often preferable to create an executable from the
|
||||
zipapp. This is relatively easy, although it does require a C compiler. The
|
||||
basic approach relies on the fact that zipfiles can have arbitrary data
|
||||
prepended, and Windows exe files can have arbitrary data appended. So by
|
||||
creating a suitable launcher and tacking the ``.pyz`` file onto the end of it,
|
||||
you end up with a single-file executable that runs your application.
|
||||
|
||||
A suitable launcher can be as simple as the following::
|
||||
|
||||
#define Py_LIMITED_API 1
|
||||
#include "Python.h"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
#ifdef WINDOWS
|
||||
int WINAPI wWinMain(
|
||||
HINSTANCE hInstance, /* handle to current instance */
|
||||
HINSTANCE hPrevInstance, /* handle to previous instance */
|
||||
LPWSTR lpCmdLine, /* pointer to command line */
|
||||
int nCmdShow /* show state of window */
|
||||
)
|
||||
#else
|
||||
int wmain()
|
||||
#endif
|
||||
{
|
||||
wchar_t **myargv = _alloca((__argc + 1) * sizeof(wchar_t*));
|
||||
myargv[0] = __wargv[0];
|
||||
memcpy(myargv + 1, __wargv, __argc * sizeof(wchar_t *));
|
||||
return Py_Main(__argc+1, myargv);
|
||||
}
|
||||
|
||||
If you define the ``WINDOWS`` preprocessor symbol, this will generate a
|
||||
GUI executable, and without it, a console executable.
|
||||
|
||||
To compile the executable, you can either just use the standard MSVC
|
||||
command line tools, or you can take advantage of the fact that distutils
|
||||
knows how to compile Python source::
|
||||
|
||||
>>> from distutils.ccompiler import new_compiler
|
||||
>>> import distutils.sysconfig
|
||||
>>> import sys
|
||||
>>> import os
|
||||
>>> from pathlib import Path
|
||||
|
||||
>>> def compile(src):
|
||||
>>> src = Path(src)
|
||||
>>> cc = new_compiler()
|
||||
>>> exe = src.stem
|
||||
>>> cc.add_include_dir(distutils.sysconfig.get_python_inc())
|
||||
>>> cc.add_library_dir(os.path.join(sys.base_exec_prefix, 'libs'))
|
||||
>>> # First the CLI executable
|
||||
>>> objs = cc.compile([str(src)])
|
||||
>>> cc.link_executable(objs, exe)
|
||||
>>> # Now the GUI executable
|
||||
>>> cc.define_macro('WINDOWS')
|
||||
>>> objs = cc.compile([str(src)])
|
||||
>>> cc.link_executable(objs, exe + 'w')
|
||||
|
||||
>>> if __name__ == "__main__":
|
||||
>>> compile("zastub.c")
|
||||
|
||||
The resulting launcher uses the "Limited ABI", so it will run unchanged with
|
||||
any version of Python 3.x. All it needs is for Python (``python3.dll``) to be
|
||||
on the user's ``PATH``.
|
||||
|
||||
For a fully standalone distribution, you can distribute the launcher with your
|
||||
application appended, bundled with the Python "embedded" distribution. This
|
||||
will run on any PC with the appropriate architecture (32 bit or 64 bit).
|
||||
|
||||
|
||||
Caveats
|
||||
~~~~~~~
|
||||
|
||||
There are some limitations to the process of bundling your application into
|
||||
a single file. In most, if not all, cases they can be addressed without
|
||||
needing major changes to your application.
|
||||
|
||||
1. If your application depends on a package that includes a C extension, that
|
||||
package cannot be run from a zip file (this is an OS limitation, as executable
|
||||
code must be present in the filesystem for the OS loader to load it). In this
|
||||
case, you can exclude that dependency from the zipfile, and either require
|
||||
your users to have it installed, or ship it alongside your zipfile and add code
|
||||
to your ``__main__.py`` to include the directory containing the unzipped
|
||||
module in ``sys.path``. In this case, you will need to make sure to ship
|
||||
appropriate binaries for your target architecture(s) (and potentially pick the
|
||||
correct version to add to ``sys.path`` at runtime, based on the user's machine).
|
||||
|
||||
2. If you are shipping a Windows executable as described above, you either need to
|
||||
ensure that your users have ``python3.dll`` on their PATH (which is not the
|
||||
default behaviour of the installer) or you should bundle your application with
|
||||
the embedded distribution.
|
||||
|
||||
3. The suggested launcher above uses the Python embedding API. This means that in
|
||||
your application, ``sys.executable`` will be your application, and *not* a
|
||||
conventional Python interpreter. Your code and its dependencies need to be
|
||||
prepared for this possibility. For example, if your application uses the
|
||||
:mod:`multiprocessing` module, it will need to call
|
||||
:func:`multiprocessing.set_executable` to let the module know where to find the
|
||||
standard Python interpreter.
|
||||
If your application depends on a package that includes a C extension, that
|
||||
package cannot be run from a zip file (this is an OS limitation, as executable
|
||||
code must be present in the filesystem for the OS loader to load it). In this
|
||||
case, you can exclude that dependency from the zipfile, and either require
|
||||
your users to have it installed, or ship it alongside your zipfile and add code
|
||||
to your ``__main__.py`` to include the directory containing the unzipped
|
||||
module in ``sys.path``. In this case, you will need to make sure to ship
|
||||
appropriate binaries for your target architecture(s) (and potentially pick the
|
||||
correct version to add to ``sys.path`` at runtime, based on the user's machine).
|
||||
|
||||
|
||||
The Python Zip Application Archive Format
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue