At Infinidat, we write products in Python, and not just a bunch of scripts.
We want our products to be isolated from global Python installations and
The solution we came with is as follows:
- Bundle the interpreter itself in the application, along with all the dependencies.
- If there are binary dependencies, compile it when building the application package
This is our buildout recipe for creating the platform-specific packages.
This recipe, along with our isolated python builds, provides the following features:
- building operating systems packages for Python projects:
RPMpackages on RedHat-based linux distributions **
DEBpackages on Debian-based linux distributions, altough we test it only on Ubuntu **
MSIpackages for Windows, starting from Windows NT 5.0
- creating single-file, statically-linked, portable, executables for console scripts
- building a static library of the interpreter along with all the 3rd-party dependcies
What deployment solution should I use
If you need to build a shared library but want to write it in Python, then you can pack the Python code as a static library and link you shared library with it.
If you need to a sinlge standalone, no-installation-required executable then use the executable recipe.
if you need a full Python interpreter, or access to package resources, then you should pack your Python project in an OS package.
Using this recipe
In order create an application with proper packaging, you'll need more than just this recipe.
- Create a project using projector, build the development environment using the isolated python option.
companyattribute to the
projectsection in your
projector devenv pack; this will build all the sections in
buildout.cfgthat either of the recipes of this module. ** the default recipe:
infi.recipe.application_packager, will build the OS package depending on the operating system. ** instead, you can explicitly define one of the following recipes: ***
At the end, OS packages will be available under the
parts directory, and single-file executables and static libraries will be placed under
To debug exceptions, add 'pdb = true' to the recipe. The results are stored under the follwing directories:
parts. stores the deb, msi and rpm intermediate files and final packages
build/Python-<x.y.z>. the Python source
build/static. this this where we copy to all the static libraries from the isolated python that will link the static python with
build/dependencies. this is where all the dependencies are being built
build/embedded. we the embedded python and the static library are being built
bild/executables. where we build the final console executables
Recipe configuration options
pack recipe in your
buildout.cfg, you can define the following options:
|Key||Applied to||Default value||Description|
|pdb||executable, deb, msi, rpm, static_library||false||enter pdb if exception is raise for post mortem|
|dependent-scripts||executable, deb, msi, rpm||false|
|eggs||executable, deb, msi, rpm||<project name>|
|scripts||executable, deb, msi, rpm||<empty list>|
|gui-scripts||deb, msi, rpm||<empty list>|
|minimal-packages||deb, msi, rpm||Adds code to the entry point wrapper that tries to use less packages|
|shortcuts-icon||msi||~/.msi-ui/icon.exe||Icon file in EXE binary format to be used as icon for shortcuts|
|shrink-cache-dist||deb, msi, rpm||true||delete sources from .cache/dist that are under the install-requires tree|
|deb-dependencies||deb||List of debian packages to be required prior installing your package|
|sign-executables-and-msi||msi||false||Digitally signed the MSI using Authenticode certificate|
|pfx-file||msi||~/.authenticode/certificate.pfx||Absolute location of the certificate file|
|pfx-password-file||msi||~/.authenticode/certificate-password.txt||Absolute locaton for the private txt of the certificate|
|add-remove-programs-icon||msi||~/.msi-ui/icon.ico||ICO file to use in the add/remove program applet|
|msi-dialog-bmp||msi||~/.msi-ui/WixUIDialog.bmp||Background bitmap used on the welcome and completion dialogs|
|startmenu-shortcuts||msi||||['shortcut_name' = 'executable_name', ...]|
|rpm-dependencies||rpm||List of redhat packages to be required prior installing your package|
|python-source-url||executable, static_library||ftp://python.infinidat.com/python/sources/Python-2.7.6.tgz||tgz archive for Python|
|LINKFLAGS||executable, static_library||extra flags to pass (as a string) to pystick|
|CC||executable, static_library||extra flags to pass (as a string) to pystick|
|CXX||executable, static_library||extra flags to pass (as a string) to pystick|
|PATH||executable, static_library||extra flags to pass (as a string) to pystick|
|LD_LIBRARY_PATH||executable, static_library||extra flags to pass (as a string) to pystick|
|LIBRARY_PATH||executable, static_library||extra flags to pass (as a string) to pystick|
|LIBS||executable, static_library||extra flags to pass (as a string) to pystick|
|always-build||executable, static_library||always build from scratch, even when artifacts exist on disk|
|exclude-eggs||executable, static_library||||eggs not to include in the build|
|existing-installdir||msi||None||path to registry value to look for installation directory|
|custom-installdir||msi||None||override if want to use something other than [EXISTINGINSTALLDIR]|
|eula-rtf||msi||None||path to end-user license agreement RTF file|
|documentation-url||msi||None||gives the user an option to launch the online docs after install|
Using the installers
The basic flow of the installer is as follows:
- Copy all the files to the target directory. The target directory is either
/opt/<company>/<product name>or `%ProgramFiles%<company><product name>
- Run bootstrap
- Create the executable scripts
- Run the user-defined post install script, if was defined in
The uninstall procedure is:
- Run the user-defined pre uninstall script
- Delete all the files that were copied during the installation, and remove temporary python files
- If the installation directory is empty, delete it
This module also provides Installer classes that'll help you create integration tests for your installer, and to test it out before going to production.
The buildout logs are available at:
- /var/log/ if writable, else /tmp/
- %TEMP% if exists or %SystemRoot%\Windows\Temp
The installer accept two variables:
|NO_CUSTOM_ACTIONS||Disables scripts during install/uninstall, just creates/deletes files|
To use these (example):
NO_CUSTOM_ACTIONS=1 rpm ....
msiexec /i ... NO_CUSTOM_ACTIONS=1
Using fixed installation directory in Windows
In some cases, you'd want to install the product in a specific location, without giving the end-user a choice to change that.
When you set a value to
custom-installdir, this will change the UI to jump from the welcome dialog to the verify-installation dialog.
Looking for installation directory inside the Windows Registry
If you want to install the product under a directory that's written in the registry, add the following items to the buildout recipe:
- existing-installdir: HKLM\Software\Key\SubKey\Value
- custom-insalldir: [EXISTINGINSTALLDIR]
Checking out the code
Run the following:
easy_install -U infi.projector projector devenv build