How to create Windows EXEs for Python on Linux, using PyInstaller and WINE

Oct 4th, 2021
by makeworld

Packaging a Python project into a standalone executable is often a struggle. Thankfully we have tools like PyInstaller to make this easier. Recently I ran into another struggle of my own, which was that it seemed impossible to “cross-compile” with PyInstaller. That is to say, run PyInstaller on one OS, and have it generate an executable for another. It was hinted at that Linux → Windows was possible using Wine, but I couldn’t find any guides on it.

Eventually, I got it working, so here is that guide. Hope this helps! This guide might also work on macOS, but I have not tested it.

Thanks to Noah Broyles for the script that helped me figure this all out.

First off, I was only able to use Python 3.8. I tried with 3.9, but I was not able to install it under WINE. If you are able to, feel free to use that, and change references to 38 in this post to 39. I was also using PyInstaller 4.5.1, but other versions should work fine.

The first step is to install WINE for your Linux system. WINE is a compatibility layer that allows the running of Windows software on non-Windows systems. You can install WINE easily enough from your distro’s package manager. I am using wine-6.16, but most likely your distro’s version will work fine.

Next, download the Python 3.8 Windows installer. You can download that directly from the official Python Software Foundation here. With it downloaded, you can run wine python-3.8.9.exe to begin the installation. WINE may prompt you to install something like .NET or Mono, you should click Yes and let it install.

You should not use the default installation, follow these steps:

  1. Check “Add Python 3.8 to PATH”
  2. Click “Customize installation
  3. Click “Next”
  4. Click “Install for all users”
  5. Set the install location as C:\\Python38
  6. Click “Install”
  7. Close the window.

Using the C:\\Python38 path helps keep things standard for this blog post.

Now Python 3.8 should be successfully installed in WINE. You can check this by running wine C:/Python38/python.exe --version, which will run Windows Python through WINE. Amazing!

Next we’ll need to upgrade pip, to make sure things will install fine. Run wine C:/Python38/python.exe -m pip install --upgrade pip. WINE might spit out a lot of warnings about libgnutls or something or other, but as long as Pip doesn’t report any errors it’s fine.

Now if your project has any dependencies, you should export them into the standard Python requirements.txt file. You can then install them into your WINE Python with this command:

wine C:/Python38/python.exe -m pip install -r requirements.txt

If you’re using a superior dependency manager like Poetry, you should be able to install and use that in WINE, although I haven’t tested that.

In any case, you’ll need PyInstaller to be installed, so run this command if PyInstaller wasn’t in requirements.txt already:

wine C:/Python38/python.exe -m pip install pyinstaller

Now you can run PyInstaller and an EXE will be built! Just run

wine C:/Python38/Scripts/pyinstaller.exe ...

and replace ... with whatever arguments you used to build a Linux executable.