I made standalone executables for Linux and Windows of my Python programs Microbe, Replicator Machine, Neural Construct and Biomorph Entity. These executables were built using cx_Freeze, and have Python and all dependent libraries included, no installation needed.
Some details of the cx_Freeze build follows:
The cx_Freeze utility is similar to py2exe, but can build both Linux and Windows executables. To build the executable from the Python script using cx_Freeze, build options can be entered in a setup.py script, a basic one being:
#setup.py
from cx_Freeze import setup, Executable
setup( name = "Microbe",
version = "1.21",
description = "Microbial Simulation",
executables = [Executable("microbe.py")] )
Then from the command line run:
python setup.py build
There were a few glitches in the build. This was mainly because my Python scripts use the Pygame and NumPy modules. I used Ubuntu Linux 9.10 to build, and tested executables on an Arch Linux virtual machine (VM) installed in VirtualBox with no external Python modules. The build system has Python 2.64(Linux)/2.54(Windows), Pygame 1.81, NumPy 1.30, and used cx_Freeze 4.11 (4.01 appeared to give the same results; latest version had an import error, solved by renaming /cx_Freeze/util to util.so (Linux) or util.pyd (Windows)). I will describe the process with the Microbe build, which requires both Pygame and NumPy. The described process yielded successful builds, and is meant as advice but not necessary the most appropriate solutions for other situations. I first started a build with the basic setup.py script. This should create a build folder with the Python script, Python and all dependencies included, and in this example, the program can be launched on Linux by the command ‘./microbe’. The build ran properly, except there were hidden dependencies still on the build machine that may not be available on another Linux machine. A good way on Linux to check what files are opened when the program is launched is to use from the command line:
top
lsop | grep ID
The top utility shows the ID of running programs, so get the ID of the executable and put it on the lsop line to list all files opened by the executable. Doing this, you can see which are being open through the build package, and those that are being obtained from the system.
To get back to the build, attempt at running the build on the VM got:
File "/usr/lib/python2.6/dist-packages/numpy/lib/polynomial.py", line 11, in
AttributeError: 'module' object has no attribute 'core'
This concerned missing NumPy dependencies libblas.so.3gf/liblapack.so.3gf that were not packaged by cx_Freeze and can be fixed by adding to cx_Freeze setup.py:
includeDependencies =
[("/usr/lib/libblas.so.3gf.0","libblas.so.3gf"),
("/usr/lib/liblapack.so.3gf.0","liblapack.so.3gf")]
These dependencies comprise a Linear Algebra Package (lapack), and are quite a large addition. If NumPy is built from source, without the lapack library installed, it will compile a lite version that would be better to package. On Ubuntu Linux, NumPy depends on the full version of lapack. I do not have a requirement for the full lapack version, and decided to removed NumPy/libatlas3gf-base/libblas3gf; if you do have another program requiring lapack then do not do this or reinstall later. With the removal of NumPy, several other programs dependent on this module may be removed and need to be reinstalled after NumPy is reinstalled. I then compiled NumPy 1.4.0rc1 from source:
sudo apt-get install python-dev
python setup.py build --fcompiler=gnu95
sudo checkinstall python setup.py install
This compilation requires the Python Headers (python-dev) and the gfortran compiler. The install should be to /usr/local/lib/; Ubuntu should have the path in /etc/ld.so.conf, if missing add it then run ‘sudo ldconfig’. Checkinstall makes a deb package while installing (during install info is requested – update name: pygame-numpy and version: 1:1.4.0), and inserts it in the package manager, which updates dependencies to the installed version. Since Pygame was removed when I removed NumPy before, I reinstalled that. This allowed a cx_Freeze build of Microbe with NumPy/lapack-lite added using the basic setup.py. The build ran on the system after a required change to my program. Though it was due to an unrelated issue concerning a computer-intensive amoeba animate function that I compiled with Cython, a Python-like to C compiler. The program terminated immediately with ‘ValueError: numpy.dtype does not appear to be the correct type object’. It seems that the new version of NumPy had changed numpy.dtype and broke the Cython compiled code. Needed to recompile the animate code to have a separate version that works with the installed NumPy module.
When the new build was run on the VM, the previous error was gone, but another appeared:
File "microbe.py", line 75, in __init__
pygame.error: File is not a Windows BMP file
This concerned the inability to find Pygame dependencies, libjpeg.so.62/libpng12.so.0, due to discrepancies with names between Ubuntu and Arch Linux. I decided to include these in the build using the buildOptions includeDependencies. This can be seen below in the final setup.py used for the cx_Freeze build. I used this to build in Linux and Windows. However, upon exit of the program in Windows, there was an error that the program ‘encountered a problem and needs to be close – ModName: python25.dll’, and I found that the build did not like the code ‘sys.exit’, fixed by changing the program to exit by main loop termination. One final note, for some of my other programs such as Biomorph Entity that do not import NumPy, NumPy is still packaged in the build. I believe this is because Pygame is dependent on NumPy for its surfarray module. Since I do not use this module in those programs, I was able to cx_Freeze build with the buildOptions ‘excludes = [“numpy”]’, and possibly excludes of other unnecessary Python modules can make a lighter executable.
The executables for the Microbe program was built on both Linux and Windows with cx_Freeze by issuing the command ‘python setup.py build’ from the program folder with the following setup.py:
#setup.py:
from cx_Freeze import setup, Executable
import sys
if sys.platform == "win32":
base = "Win32GUI"
includeDependencies = []
else:
base = None
includeDependencies =
[ ("/usr/lib/libjpeg.so.62.0.0","libjpeg.so.62"),
("/usr/lib/libpng12.so.0.37.0","libpng.so.0") ]
includePersonalFiles =
[ ("data","data"), ("readme.txt","readme.txt") ]
includeFiles = includeDependencies + includePersonalFiles
buildOptions =
dict( include_files = includeFiles,
icon = "microbe.ico",
optimize = 2,
compressed = True )
setup( name = "Microbe",
version = "1.21",
description = "Microbial Simulation",
options = dict(build_exe = buildOptions),
executables = [Executable("microbe.py", base = base)] )
Submitted by Jim on December 23, 2009 at 11:00 pm