Develop Windows Executables from Python Scripts for 32-bit and 64-bit Architecures

In this post I’ll discuss building a Windows executable from a Python script for 32-bit and 64-bit Windows. Producing a 64-bit executable on a 64-bit machine in Windows is easy using PyInstaller, but producing a 32-bit executable on a 64-bit machine takes some tinkering. I ended up setting up a chroot environment on Ubuntu for this task.

64-bit Executable on 64-bit Host

This is relatively easy. You download and install PyInstaller, and for a windowed GUI application you run

pyinstaller -F -w --name=executable gui_script.py

And for a regular script you run,

pyinstaller -F --name=executable script.py

32-bit Executable on 64-bit Host

First, you need to have PyInstaller downloaded and unzipped somewhere, like your Downloads folder. Next, you need to produce an almost virtual environment on your linux machine. This will be a minimal installation that is separated from the rest of your system. This can be used for developing applications, or for trying out untrusted software. I used this resource for the next few steps. First, install some stuff.

sudo apt-get install debootstrap
sudo apt-get install schroot

Next create a configuration file for schroot at /etc/schroot/chroot.d/precise_i386.conf

[precise_i386]
description=Ubuntu 10.04 Precise for i386
location=/srv/chroot/precise_i386
personality=linux32
root-users=bob
run-setup-scripts=true
run-exec-scripts=true
type=directory
users=alice,bob

Then make a directory for chroot, and run debootstrap,

sudo mkdir -p /srv/chroot/precise_i386
sudo debootstrap --variant=buildd --arch=i386 precise /srv/chroot/precise_i386 http://archive.ubuntu.com/ubuntu/

Now you can enter this alternate linuxverse as root by running,

schroot -c precise_i386 -u root

Or you can enter it as a normal user by leaving off the -u root, which we will do later. When I ran this, I got a handful of warnings about blah blah blah, but everything worked out in the end. Next I used this resource for these commands that seemed to help.

sudo mount -o bind /proc /srv/chroot/precise_i386/proc
sudo cp /etc/resolv.conf /srv/chroot/precise_i386/etc/resolv.conf

Finally, we should have a working little chroot thingy. We can log in as root,

schroot -c precise_i386 -u root

To make sure you did everything right, you can do a sanity-check to see if you’re really using 32-bit tools. Call Python from the command line and type the following,

import ss
sys.maxsize > 2**32

You should get False if the Python interpreter is 32-bit. Now Ctrl-Z to get out of the Python interpreter and install python-dev and setuptools to get PyInstaller working,

apt-get install curl
apt-get install python-dev
curl https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py -o - | python

Now close out of that terminal session and log back into chroot as a regular user, not as root as we have been previously,

schroot -c precise_i386

Now we can run PyInstaller for a GUI application as we did in 64-bit land,

pyinstaller -F -w --name=executable gui_script.py

Or for a console application

pyinstaller -F --name=executable script.py

Now you can test your 32-bit executable out on your 64-bit machine. (J/k, if you could do that, you wouldn’t be here in the first place.)