26

I am trying to build a Python multi-file code with PyInstaller. For that I have compiled the code with Cython, and am using .so files generated in place of .py files.

Assuming the 1st file is main.py and the imported ones are file_a.py and file_b.py, I get file_a.so and file_b.so after Cython compilation.

When I put main.py, file_a.so and file_b.so in a folder and run it by "python main.py", it works.

But when I build it with PyInstaller and try to run the executable generated, it throws errors for imports done in file_a and file_b.

How can this be fixed? One solution is to import all standard modules in main.py and this works. But if I do not wish to change my code, what can be the solution?

NorthCat
  • 9,643
  • 16
  • 47
  • 50
Confused
  • 617
  • 1
  • 9
  • 17

2 Answers2

30

So I got this to work for you.

Please have a look at Bundling Cython extensions with Pyinstaller

Quick Start:

git clone https://github.com/prologic/pyinstaller-cython-bundling.git
cd pyinstaller-cython-bundling
./dist/build.sh

This produces a static binary:

$ du -h dist/hello
4.2M    dist/hello
$ ldd dist/hello
    not a dynamic executable

And produces the output:

$ ./dist/hello 
Hello World!
FooBar

Basically this came down to producing a simple setup.py that builds the extensions file_a.so and file_b.so and then uses pyinstaller to bundle the application the extensions into a single executeble.

Example setup.py:

from glob import glob
from setuptools import setup
from Cython.Build import cythonize


setup(
    name="test",
    scripts=glob("bin/*"),
    ext_modules=cythonize("lib/*.pyx")
)

Building the extensions:

$ python setup.py develop

Bundling the application:

$ pyinstaller -r file_a.so,dll,file_a.so -r file_b.so,dll,file_b.so -F ./bin/hello
Turn
  • 6,656
  • 32
  • 41
James Mills
  • 18,669
  • 3
  • 49
  • 62
  • 1
    This works perfectly. Thanks for putting it all together! – rth Jun 24 '15 at 12:40
  • 1
    Yeah no worries at all! It was *quite* fun! – James Mills Jun 24 '15 at 12:59
  • Is there a windows equivalent for this? ./dist/build.sh are not recognised even with cygwin – Tetora Oct 30 '17 at 04:31
  • 150mb/exe seems like way too big – Tetora Oct 30 '17 at 05:52
  • Is this specific to Windows platform ? I saw in the docs that the -r flag is just for Windows, but you're bundling Unix .so files so I'm a bit confused – Overdrivr Jul 25 '18 at 12:59
  • Hey, How can I add packaged that are imported in my source code? Your code is working fine. But it gives an error when outside modules are imported in the code. – Nomiluks Apr 16 '19 at 08:48
  • I have faced this problem and sharing the link: https://stackoverflow.com/questions/55707869/build-cython-compiled-modules-and-python-code-into-executable-binary-using-pyins/55744155#55744155 May this will help too – Nomiluks Apr 18 '19 at 10:34
2

Just in case someone's looking for a quick fix.

I ran into the same situation and found a quick/dirty way to do the job. The issue is that pyinstaller is not adding the necessary libraries in the .exe file that are needed to run your program.

All you need to do is import all the libraries (and the .so files) needed into your main.py file (the file which calls file_a.py and file_b.py). For example, assume that file_a.py uses opencv library (cv2) and file_b.py uses matplotlib library. Now in your main.py file you need to import cv2 and matplotlib as well. Basically, whatever you import in file_a.py and file_b.py, you have to import that in main.py as well. This tells pyinstaller that the program needed these libraries and it includes those libraries in the exe file.

Chris Henry
  • 396
  • 2
  • 8