Friday, 19 August 2011

How to create Windows executable from Python script

After some online research I decided to give PyInstaller a try. It converts Python scripts into executable Windows programs, able to run without requiring a Python installation on a target machine. PyInstaller works under any version of Python from 2.2 up to 2.7 (it still does not support Python 3) so I installed the latest Python 2 release - 2.7.2.

NOTE: Make sure Python installation path (e.g. in C:\Python27) has been added to PATH environmental variable.

PyInstaller requires PyWin32 to be installed for Python versions 2.6+. It is a Python extension for Microsoft Windows which provides access to the Win32 API, COM and Pythonwin environment. If you try to perform initial PyInstaller configuration without it, a following error is reported:

..\pyinstaller-1.5.1>python Configure.py
ERROR: Python 2.6+ on Windows support needs pywin32
Please install http://sourceforge.net/projects/pywin32/




I picked installer for Python 2.7 - the version I will be using. PyInstaller configuration now runs with no problems:

..\pyinstaller-1.5.1>python Configure.py
I: computing EXE_dependencies
I: Finding TCL/TK...
W: library coredll.dll required via ctypes not found
I: Analyzing c:\Python27\DLLs\_tkinter.pyd
I: Adding tcl85.dll dependency of _tkinter.pyd
I: Adding tk85.dll dependency of _tkinter.pyd
I: Skipping KERNEL32.dll dependency of _tkinter.pyd
I: Adding python27.dll dependency of _tkinter.pyd
I: Skipping MSVCR90.dll dependency of _tkinter.pyd
I: Analyzing c:\Python27\DLLs\_hashlib.pyd
I: Skipping KERNEL32.dll dependency of _hashlib.pyd
I: Skipping USER32.dll dependency of _hashlib.pyd
I: Skipping ADVAPI32.dll dependency of _hashlib.pyd
I: Skipping MSVCR90.dll dependency of _hashlib.pyd
I: Analyzing c:\Python27\DLLs\_ctypes.pyd
I: Skipping KERNEL32.dll dependency of _ctypes.pyd
I: Skipping ole32.dll dependency of _ctypes.pyd
I: Skipping OLEAUT32.dll dependency of _ctypes.pyd
I: Skipping MSVCR90.dll dependency of _ctypes.pyd
I: Analyzing c:\Python27\DLLs\select.pyd
I: Skipping WS2_32.dll dependency of select.pyd
I: Skipping MSVCR90.dll dependency of select.pyd
I: Skipping KERNEL32.dll dependency of select.pyd
I: Analyzing c:\Python27\DLLs\unicodedata.pyd
I: Skipping MSVCR90.dll dependency of unicodedata.pyd
I: Skipping KERNEL32.dll dependency of unicodedata.pyd
I: Analyzing c:\Python27\DLLs\bz2.pyd
I: Skipping MSVCR90.dll dependency of bz2.pyd
I: Skipping KERNEL32.dll dependency of bz2.pyd
I: Analyzing c:\Python27\DLLs\tcl85.dll
I: Dependent assemblies of c:\Python27\DLLs\tcl85.dll:
I: x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.21022.8_none
I: Searching for assembly x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.21022.8_no
ne...
I: Found manifest C:\Windows\WinSxS\Manifests\x86_microsoft.vc90.crt_1fc8b3b9a1e
18e3b_9.0.21022.8_none_bcb86ed6ac711f91.manifest
I: Searching for file msvcr90.dll
I: Found file C:\Windows\WinSxS\x86_microsoft.vc90.crt_1fc8b3b9a1e18e3b_9.0.2102
2.8_none_bcb86ed6ac711f91\msvcr90.dll
I: Searching for file msvcp90.dll
I: Found file C:\Windows\WinSxS\x86_microsoft.vc90.crt_1fc8b3b9a1e18e3b_9.0.2102
2.8_none_bcb86ed6ac711f91\msvcp90.dll
I: Searching for file msvcm90.dll
I: Found file C:\Windows\WinSxS\x86_microsoft.vc90.crt_1fc8b3b9a1e18e3b_9.0.2102
2.8_none_bcb86ed6ac711f91\msvcm90.dll
I: Adding Microsoft.VC90.CRT.manifest
I: Adding msvcr90.dll
I: Adding msvcp90.dll
I: Adding msvcm90.dll
I: Skipping KERNEL32.dll dependency of tcl85.dll
I: Skipping USER32.dll dependency of tcl85.dll
I: Skipping WS2_32.dll dependency of tcl85.dll
I: Skipping ADVAPI32.dll dependency of tcl85.dll
I: Analyzing c:\Python27\DLLs\tk85.dll
I: Dependent assemblies of c:\Python27\DLLs\tk85.dll:
I: X86_Microsoft.Windows.Common-Controls_6595b64144ccf1df_6.0.0.0_none
I: Dependent assemblies of c:\Python27\DLLs\tk85.dll:
I: x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.21022.8_none
I: Skipping assembly X86_Microsoft.Windows.Common-Controls_6595b64144ccf1df_6.0.
0.0_none
I: Skipping KERNEL32.dll dependency of tk85.dll
I: Skipping USER32.dll dependency of tk85.dll
I: Skipping GDI32.dll dependency of tk85.dll
I: Skipping COMDLG32.dll dependency of tk85.dll
I: Skipping SHELL32.dll dependency of tk85.dll
I: Skipping ole32.dll dependency of tk85.dll
I: Skipping OLEAUT32.dll dependency of tk85.dll
I: Skipping COMCTL32.dll dependency of tk85.dll
I: Skipping ADVAPI32.dll dependency of tk85.dll
I: Skipping IMM32.dll dependency of tk85.dll
I: Analyzing C:\Windows\system32\python27.dll
I: Dependent assemblies of C:\Windows\system32\python27.dll:
I: x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.21022.8_none
I: Skipping KERNEL32.dll dependency of python27.dll
I: Skipping USER32.dll dependency of python27.dll
I: Skipping ADVAPI32.dll dependency of python27.dll
I: Skipping SHELL32.dll dependency of python27.dll
I: Analyzing c:\Python27\python.exe
I: Dependent assemblies of c:\Python27\python.exe:
I: x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.21022.8_none
I: Skipping KERNEL32.dll dependency of python.exe
I: found TCL/TK version 8.5
I: found TCL/TK version 8.5
I: could not find TCL/TK
I: testing for Zlib...
I: ... Zlib available
I: Testing for ability to set icons, version resources...
I: ... resource update available
I: Testing for Unicode support...
I: ... Unicode available
I: testing for UPX...
I: ...UPX unavailable
I: computing PYZ dependencies...
I: done generating config.dat





Let us try now to create a binary example.exe from a script example.py, which just prints "Hello World" to the console window. It is a two-step process. The first step is generating a spec file. Makespec creates it and places it in a directory named after the script:

..\pyinstaller-1.5.1>python Makespec.py "J:\Users\Bojan\wor
kspace\Test1\src\root\nested\example.py"
wrote ..\pyinstaller-1.5.1\example\example.spec
now run Build.py to build the executable

If we follow instruction given above, making sure we are providing correct (relative) path to spec file, Build creates executable:

..\pyinstaller-1.5.1>python Build.py example\example.spec
I: Dependent assemblies of c:\Python27\python.exe:
I: x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.21022.8_none
checking Analysis
building Analysis because outAnalysis0.toc non existent
running Analysis outAnalysis0.toc
Analyzing: support\_mountzlib.py
Analyzing: support\useUnicode.py
Analyzing: C:\Users\Bojan\workspace\Test1\src\root\nested\example.py
I: Analyzing c:\Python27\python.exe
I: Dependent assemblies of c:\Python27\python.exe:
I: x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.21022.8_none
Adding Microsoft.VC90.CRT to dependent assemblies of final executable
I: Searching for assembly x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.21022.8_no
ne...
I: Found manifest C:\Windows\WinSxS\Manifests\x86_microsoft.vc90.crt_1fc8b3b9a1e
18e3b_9.0.21022.8_none_bcb86ed6ac711f91.manifest
I: Searching for file msvcr90.dll
I: Found file C:\Windows\WinSxS\x86_microsoft.vc90.crt_1fc8b3b9a1e18e3b_9.0.2102
2.8_none_bcb86ed6ac711f91\msvcr90.dll
I: Searching for file msvcp90.dll
I: Found file C:\Windows\WinSxS\x86_microsoft.vc90.crt_1fc8b3b9a1e18e3b_9.0.2102
2.8_none_bcb86ed6ac711f91\msvcp90.dll
I: Searching for file msvcm90.dll
I: Found file C:\Windows\WinSxS\x86_microsoft.vc90.crt_1fc8b3b9a1e18e3b_9.0.2102
2.8_none_bcb86ed6ac711f91\msvcm90.dll
I: Adding Microsoft.VC90.CRT.manifest
I: Adding msvcr90.dll
I: Adding msvcp90.dll
I: Adding msvcm90.dll
I: Adding python27.dll dependency of python.exe
I: Skipping KERNEL32.dll dependency of python.exe
I: Analyzing C:\Windows\system32\python27.dll
I: Dependent assemblies of C:\Windows\system32\python27.dll:
I: x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.21022.8_none
I: Skipping KERNEL32.dll dependency of python27.dll
I: Skipping USER32.dll dependency of python27.dll
I: Skipping ADVAPI32.dll dependency of python27.dll
I: Skipping SHELL32.dll dependency of python27.dll
I: Analyzing c:\Python27\DLLs\_hashlib.pyd
I: Skipping KERNEL32.dll dependency of _hashlib.pyd
I: Skipping USER32.dll dependency of _hashlib.pyd
I: Skipping ADVAPI32.dll dependency of _hashlib.pyd
I: Analyzing c:\Python27\lib\site-packages\win32\win32api.pyd
I: Skipping USER32.dll dependency of win32api.pyd
I: Skipping ADVAPI32.dll dependency of win32api.pyd
I: Skipping SHELL32.dll dependency of win32api.pyd
I: Skipping VERSION.dll dependency of win32api.pyd
I: Skipping POWRPROF.dll dependency of win32api.pyd
I: Adding pywintypes27.dll dependency of win32api.pyd
I: Skipping KERNEL32.dll dependency of win32api.pyd
I: Analyzing c:\Python27\DLLs\select.pyd
I: Skipping WS2_32.dll dependency of select.pyd
I: Skipping KERNEL32.dll dependency of select.pyd
I: Analyzing c:\Python27\DLLs\unicodedata.pyd
I: Skipping KERNEL32.dll dependency of unicodedata.pyd
I: Analyzing c:\Python27\DLLs\bz2.pyd
I: Skipping KERNEL32.dll dependency of bz2.pyd
I: Analyzing C:\Windows\system32\pywintypes27.dll
I: Skipping ADVAPI32.dll dependency of pywintypes27.dll
I: Skipping USER32.dll dependency of pywintypes27.dll
I: Skipping ole32.dll dependency of pywintypes27.dll
I: Skipping OLEAUT32.dll dependency of pywintypes27.dll
I: Skipping KERNEL32.dll dependency of pywintypes27.dll
Warnings written to example\warnexample.txt
checking PYZ
rebuilding outPYZ1.toc because outPYZ1.pyz is missing
building PYZ outPYZ1.toc
checking PKG
rebuilding outPKG3.toc because outPKG3.pkg is missing
building PKG outPKG3.pkg
checking EXE
rebuilding outEXE2.toc because example.exe missing
building EXE from outEXE2.toc
Appending archive to EXE example\dist\example.exe

Binary has been created and placed in dist directory. We can run it:

..\pyinstaller-1.5.1\example\dist>example
Hello World

Basically, dist directory contains everything that should be distributed. By default, --onedir option is applied and PyInstaller creates a distribution that contains executable and other files it depends on. For example, distribution can contain following files:

..\example\dist\example\
msvcm90.dll
msvcp90.dll
msvcr90.dll
python27.dll
pywintypes27.dll
example.exe
example.exe.manifest
Microsoft.VC90.CRT.manifest
_hashlib.pyd
_socket.pyd
_ssl.pyd
bz2.pyd
pyexpat.pyd
select.pyd
unicodedata.pyd
win32api.pyd

If we want to have everything within a single binary, we need to apply --onefile option for Makespec.py. Build output is only a single file:

..\example\dist\example\example.exe

This single file is actually a self-extracting executable (bootloader) which contains all shared libraries and other helper files. When run, this executable extracts the following files into temporary _MEIXXXXX directory in user's Temp directory:

C:\Users\Bojan\AppData\Local\Temp\_MEI16522\
msvcm90.dll
msvcp90.dll
msvcr90.dll
python27.dll
pywintypes27.dll
copyupdate.exe.manifest
Microsoft.VC90.CRT.manifest
_hashlib.pyd
_socket.pyd
_ssl.pyd
bz2.pyd
pyexpat.pyd
select.pyd
unicodedata.pyd
win32api.pyd

This directory will be automatically deleted upon program termination.

Binary distributable should have its information embedded so File description, File versionProduct descriptionProduct version and Copyrights can be displayed when user does right-click >> Properties >>Details. This can be achieved by applying --version=PATH option where PATH is a path to the version resource file which can look something like this:

version.txt:

VSVersionInfo(
  ffi=FixedFileInfo(
    filevers=(1, 0, 0, 0),
    prodvers=(1, 0, 0, 0),
    mask=0x3f,
    flags=0x0,
    OS=0x40004,
    fileType=0x1,
    subtype=0x0,
    date=(0, 0)
    ),
  kids=[
    StringFileInfo(
      [
      StringTable(
        '040904B0',
        [StringStruct('FileDescription', 'This is a PyInstaller example application'),
        StringStruct('FileVersion', '1.0.0.0'),
        StringStruct('OriginalFilename', 'example.exe'),
        StringStruct('ProductName', 'example'),
        StringStruct('ProductVersion', '1.0.0.0')])
      ]),
    VarFileInfo([VarStruct('Translation', [1033, 1200])])
  ]
)

We can specify name of the executable by applying --name=NAME option where NAME is the name of the application, without extension (.exe) suffix.

This is one typical build configuration which includes some of options stated above:

..\pyinstaller-1.5.1>Makespec.py --onedir --version="d:\dev\workspace\example\src\version.txt" --name="example" "d:\dev\workspace\example\src\main.py"
wrote ..\pyinstaller-1.5.1\example\example.spec
now run Build.py to build the executable

..\pyinstaller-1.5.1>Build.py example\example.spec
I: Dependent assemblies of c:\Python27\python.exe:
I: x86_Microsoft.VC90.CRT_1fc8b3b9a1e18e3b_9.0.21022.8_none
checking Analysis
building Analysis because outAnalysis0.toc non existent
running Analysis outAnalysis0.toc
...
Appending archive to EXE example\build\pyi.win32\example\example.exe
checking COLLECT
building because outCOLLECT4.toc missing or bad
building COLLECT outCOLLECT4.toc

If you're using PyDev for Eclipse and you needed to add modules' paths to PYTHONPATH (External libraries) then you need to add all those paths to Makespec.py within --paths argument.

Links and references:

PyInstaller Manual

12 comments:

Maxim said...

Maybe it's my eyes, but I find the color choice for the terminal output very hard to read. Thanks for the article.

360digitmgas said...
This comment has been removed by the author.
Data Science pune said...

Such a very useful article. Very interesting to read this article. I would like to thank you for the efforts you had made for writing this awesome article.
Data Science Course in Pune
Data Science Training in Pune

DataScience Datascience said...

I wish more writers of this sort of substance would take the time you did to explore and compose so well. I am exceptionally awed with your vision and knowledge.
Data Science Course in Bangalore

DataScience Datascience said...

This is a fabulous post I seen because of offer it. It is really what I expected to see trust in future you will continue in sharing such a mind boggling post
Data Science Course in Bangalore

DataScience Datascience said...

Cool you write, the information is very good and interesting, I'll give you a link to my site.
Data Science Training in Bangalore

Data Science Institute In Banglore said...

I feel very grateful that I read this. It is very helpful and very informative and I really learned a lot from it.
Data Science Institute in Bangalore

Best Data Science Courses In Bangalore said...

I really enjoy simply reading all of your weblogs. Simply wanted to inform you that you have people like me who appreciate your work. Definitely a great post. Hats off to you! The information that you have provided is very helpful.
Data Science Certification in Bangalore

Damien Grant said...

https://digitalweekday.com/
https://digitalweekday.com/
https://digitalweekday.com/


https://digitalweekday.com/
https://digitalweekday.com/
https://digitalweekday.com/
https://digitalweekday.com/
https://digitalweekday.com/

Damien Grant said...

https://digitalweekday.com/
https://digitalweekday.com/
https://digitalweekday.com/


https://digitalweekday.com/
https://digitalweekday.com/
https://digitalweekday.com/
https://digitalweekday.com/
https://digitalweekday.com/

Tableau Course in Raipur - 360DigiTMG said...

Truly incredible blog found to be very impressive due to which the learners who ever go through it will try to explore themselves with the content to develop the skills to an extreme level. Eventually, thanking the blogger to come up with such an phenomenal content. Hope you aarrive with the similar content in future as well.

360DigiTMG Ethical Hacking Course

Data Analytics Course in Raipur - 360DigiTMG said...

Extraordinary blog went amazed with the content that they have developed in a very descriptive manner. This type of content surely ensures the participants to explore themselves. Hope you deliver the same near the future as well. Gratitude to the blogger for the efforts.

360DigiTMG Cloud Computing Course