How do I include gpg
(and all its dependencies) when I build my app with PyInstaller?
I'm releasing my python app as standalone executables for Linux, Windows, and MacOS using PyInstaller
. I need to use gpg
, so I'm using the python-gnupg
module -- which basically just shells out to the locally-installed gpg
binary on the system.
Because my app is designed to be a "portable" executable (I use PyInstaller
to ship it with the python
interpreter and all the other dependencies), I want to include gpg
with my releases so the user doesn't have to install it on their machine.
How can I add the gpg
binary and all its dependencies for the python-gnupg
module using PyInstaller
targeting Linux, Windows, and MacOS?
You can package GnuPG with your PyInstaller-built app by adding the gpg
binary and all its dependencies using the Analysis.datas
key.
On windows, I got this to work with trial and error. First I just searched the entire system for the gpg.exe
binary. In powershell:
> Get-Command gpg
0.0.0.0 C:\Program Files\Git\usr\bin\gpg.exe
>
Then I added it to my PyInstaller .spec
file by changing this section
a = Analysis(['..\\src\\main.py'],
pathex=['.\\'],
binaries=[],
datas=[],
hiddenimports=['pkg_resources.py2_warn', 'libusb1'],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
Into this
a = Analysis(['..\\src\\main.py'],
pathex=['.\\'],
binaries=[],
datas=
[
('C:\\Program Files\\Git\\usr\\bin\\gpg.exe', '.'), ],
hiddenimports=['pkg_resources.py2_warn', 'libusb1'],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
After the above change, my app would popup a warning error, such as:
The code execution cannot proceed because msys-bz2-1.dll was not found. Reinstalling the program may fix this problem.
One-by-one, I kept finding (using Get-Command
in powershell as shown above) & adding the .dll
files it complained about until my datas
in the spec file above had the following contents, which worked:
datas=
[
( '..\\KEYS', '.' ),
('C:\\Program Files\\Git\\usr\\bin\\gpg.exe', '.'),
('C:\\msys64\\usr\\bin\\msys-bz2-1.dll', '.'),
('C:\\msys64\\usr\\bin\\msys-assuan-0.dll', '.'),
('C:\\msys64\\usr\\bin\\msys-gcrypt-20.dll', '.'),
('C:\\msys64\\usr\\bin\\msys-gpg-error-0.dll', '.'),
('C:\\msys64\\usr\\bin\\msys-2.0.dll', '.'),
('C:\\msys64\\usr\\bin\\msys-readline8.dll', '.'),
('C:\\msys64\\usr\\bin\\msys-z.dll', '.'),
('C:\\msys64\\usr\\bin\\msys-sqlite3-0.dll', '.'),
('C:\\msys64\\usr\\bin\\msys-iconv-2.dll', '.'),
('C:\\msys64\\usr\\bin\\msys-intl-8.dll', '.'),
('C:\\msys64\\usr\\bin\\msys-ncursesw6.dll', '.'),
],
You can see my full build script and .spec
file here:
In MacOS, I found that I didn't have to add any dependencies--only the executable.
datas=[ ('/usr/local/bin/gpg', '.') ],
You can see my full build script and .spec
file here:
I actually didn't build a release for Linux using PyInstaller as I'm using AppImage instead.
To see my full build script for Linux, see: