PyInstaller (http://www.pyinstaller.org/) is by far the most advanced program to freeze Python packages into standalone executables. It provides the most extensive multiplatform compatibility among every available solution at the moment, so it is the most highly recommended one. PyInstaller supports the following platforms:
- Windows (32-bit and 64-bit)
- Linux (32-bit and 64-bit)
- macOS (32-bit and 64-bit)
- FreeBSD, Solaris, and AIX
Supported versions of Python are Python 2.7 and Python 3.3, 3.4, and 3.5. It is available on PyPI, so it can be installed in your working environment using pip. If you have problems installing it this way, you can always download the installer from the project's page.
Unfortunately, cross-platform building (cross-compilation) is not supported, so if you want to build your standalone executable for a specific platform, then you need to perform building on that platform. This is not a big problem today with the advent of many virtualization tools. If you don't have a specific system installed on your computer, you can always use Vagrant, which will provide you with the desired operating system as a virtual machine.
Usage for simple applications is pretty straightforward. Let's assume our application is contained in the script named myscript.py. This is a simple hello world application. We want to create a standalone executable for Windows users and we have our sources located under D://dev/app in the filesystem. Our application can be bundled with the following short command:
$ pyinstaller myscript.py
2121 INFO: PyInstaller: 3.1
2121 INFO: Python: 2.7.10
2121 INFO: Platform: Windows-7-6.1.7601-SP1
2121 INFO: wrote D:\dev\app\myscript.spec
2137 INFO: UPX is not available.
2138 INFO: Extending PYTHONPATH with paths ['D:\\dev\\app', 'D:\\dev\\app']
2138 INFO: checking Analysis
2138 INFO: Building Analysis because out00-Analysis.toc is non existent
2138 INFO: Initializing module dependency graph...
2154 INFO: Initializing module graph hooks...
2325 INFO: running Analysis out00-Analysis.toc
(...)
25884 INFO: Updating resource type 24 name 2 language 1033
PyInstaller's standard output is quite long, even for simple applications, so it was truncated in the preceding example for the sake of brevity. If run on Windows, the resulting structure of directories and files will be as follows:
$ tree /0066
│ myscript.py
│ myscript.spec
│
├───build
│ └───myscript
│ myscript.exe
│ myscript.exe.manifest
│ out00-Analysis.toc
│ out00-COLLECT.toc
│ out00-EXE.toc
│ out00-PKG.pkg
│ out00-PKG.toc
│ out00-PYZ.pyz
│ out00-PYZ.toc
│ warnmyscript.txt
│
└───dist
└───myscript
bz2.pyd
Microsoft.VC90.CRT.manifest
msvcm90.dll
msvcp90.dll
msvcr90.dll
myscript.exe
myscript.exe.manifest
python27.dll
select.pyd
unicodedata.pyd
_hashlib.pyd
The dist/myscript directory contains the built application that can now be distributed to the users. Note that whole directory must be distributed. It contains all the additional files that are required to run our application (DLLs, compiled extension libraries, and so on). A more compact distribution can be obtained with the --onefile switch of the pyinstaller command as follows:
$ pyinstaller --onefile myscript.py
(...)
$ tree /f
├───build
│ └───myscript
│ myscript.exe.manifest
│ out00-Analysis.toc
│ out00-EXE.toc
│ out00-PKG.pkg
│ out00-PKG.toc
│ out00-PYZ.pyz
│ out00-PYZ.toc
│ warnmyscript.txt
│
└───dist
myscript.exe
When built with the --onefile option, the only file you need to distribute to other users is the single executable found in the dist directory (here, myscript.exe). For small applications, this is probably the preferred option.
One of the side effects of running the pyinstaller command is the creation of the *.spec file. This is an auto generated Python module containing specification on how to create executables from your sources. This is the example specification file created automatically for myscript.py code:
# -*- mode: python -*- block_cipher = None a = Analysis(['myscript.py'], pathex=['D:\\dev\\app'], binaries=None, datas=None, hiddenimports=[], hookspath=[], runtime_hooks=[], excludes=[], win_no_prefer_redirects=False, win_private_assemblies=False, cipher=block_cipher) pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) exe = EXE(pyz, a.scripts, a.binaries, a.zipfiles, a.datas, name='myscript', debug=False, strip=False, upx=True, console=True )
This .spec file contains all pyinstaller arguments specified earlier. This is very useful if you have performed a lot of customizations to your build. Once created, you can use it as an argument to the pyinstaller command instead of your Python script as follows:
$ pyinstaller.exe myscript.spec
Note that this is a real Python module, so you can extend it and perform more complex customizations to the building procedure. Customizing the .spec file is especially useful when you are targeting many different platforms. Also, not all of the pyinstaller options are available through the command-line interface and can be used only when modifying .spec file.
PyInstaller is an extensive tool, which by its usage is very simple for the great majority of programs. Anyway, thorough reading of its documentation is recommended if you are interested in using it as a tool to distribute your applications.
Let's take a look at cx_Freeze in the next section