Building Luxocator for distribution

To instruct PyInstaller how to build Luxocator, we must create a specification file, which we will call Luxocator.spec. Actually, the specification file is a Python script that uses a PyInstaller class called Analysis and PyInstaller functions called PYZ, EXE, and BUNDLE. The Analysis class is responsible for analyzing one or more Python scripts (in our case, just Luxocator.py) and tracing all the dependencies that must be bundled with these scripts in order to make a redistributable application. Sometimes, Analysis makes mistakes or omissions, so we will modify the list of dependencies after it is initialized. Then, we will zip the scripts, make an executable, and make an app bundle using PYZ, EXE, and BUNDLE, respectively. Here is the implementation:

import os

a = Analysis(['Luxocator.py'],
  pathex=['.'],
  hiddenimports=[],
  hookspath=None,
  runtime_hooks=None)

# Determine the platform.
platform = os.name

if platform == 'nt':
  # We are on Windows.
  # A bug causes the 'pyconfig' module to be included twice.
  # Remove the duplicate.
  for data in a.datas:
    if 'pyconfig' in data[0]:
      a.datas.remove(data)
      break

# Include SSL certificates for the sake of the 'requests' module.
a.datas.append(('cacert.pem', 'cacert.pem', 'DATA'))

# Include our app's classifier data.
a.datas.append(('classifier.mat', 'classifier.mat', 'DATA'))

pyz = PYZ(a.pure)

exe = EXE(pyz,
  a.scripts,
  a.binaries,
  a.zipfiles,
  a.datas,
  name='Luxocator',
  icon='win\icon-windowed.ico',
  debug=False,
  strip=None,
  upx=True,
  console=False )

app = BUNDLE(exe,
  name='Luxocator.app',
  icon=None)

Note that this script specifies three resource files that must be bundled with the app: cacert.pem, classifier.mat, and win\icon-windowed.ico. We have already discussed cacert.pem in the previous section and classifier.mat is the output of our main function in HistogramClassifier.py. The Windows icon file, win\icon-windowed.ico, is included in this chapter's code bundle, which is downloadable from my website at http://nummist.com/opencv/7376_02.zip. Alternatively, you can provide your own icon file if you prefer.

Now, let's write a platform-specific shell script to clean any old builds, train our classifier, and then bundle the app using PyInstaller. On Windows, create a script called build.bat, which contains the following commands:

set PYINSTALLER=C:\PyInstaller\pyinstaller.py

REM Remove any previous build of the app.
rmdir build /s /q
rmdir dist /s /q

REM Train the classifier.
python HistogramClassifier.py

REM Build the app.
python "%PYINSTALLER%" Luxocator.spec

REM Make the app an executable.
rename dist\Luxocator Luxocator.exe

Similarly, on Mac or Linux, create a script called build.sh. Make it executable (for example, by running $ chmod +x build.sh in Terminal). The file should contain the following commands:

#!/bin/sh

PYINSTALLER=~/PyInstaller/pyinstaller.py

# Remove any previous build of the app.
rm -rf build
rm -rf dist

# Train the classifier.
python HistogramClassifier.py

# Build the app.
python "$PYINSTALLER" Luxocator.spec

# Determine the platform.
platform=`uname -s`

if [ "$platform" = 'Darwin' ]; then
  # We are on Mac.
  # Copy custom metadata and resources into the app bundle.
  cp -r mac/Contents dist/Luxocator.app
fi

Note that on Mac (the 'Darwin' platform), we are manually modifying the app bundle's contents as a post-build step. We do this in order to overwrite the default app icon and default properties file that PyInstaller puts in all Mac apps. (Notably, the default properties do not include support for the Retina mode, so they make the app look pixelated on recent Mac hardware. Our customizations fix this issue.) This chapter's code bundle, which is downloadable from my website at http://nummist.com/opencv/7376_02.zip, includes the custom Mac app contents in a folder called mac/Contents. You can modify its files to provide any icon and properties you want.

After running the platform-specific build script, we should have a redistributable build of Luxocator at dist/Luxocator.exe (in Windows), dist/Luxocator.app (in Mac), or dist/Luxocator (in Linux). If we are using 64-bit Python libraries on our development machine, this build will only work on 64-bit systems. Otherwise, it should work on both 32-bit and 64-bit systems. The best test of the build is to run it on another machine that does not have any of the relevant libraries (such as OpenCV) installed.