在Cython中生成可执行文件

58

最近学习使用cython。我通常用Python编写程序,但以前曾使用C语言。

我无法想出如何创建一个独立的可执行文件。

我已经下载了cython,并且可以创建一个带有.pyx扩展名的文件(这只是一个普通的Python文件),并在Python shell中执行,方法是:

import pyximport; pyximport.install()

我可以通过在命令行中输入“cython file.pyx”来生成.c文件。

我可以通过构建标准的setup.py并执行以下命令来生成.so文件:

setup.py build_ext --inplace

我已经尝试使用各种选项,使用gcc将.so文件制作成可执行文件,但总是缺少大量文件、头文件等。我试着从几乎所有地方指向头文件,但没有成功,并且对gcc的所有选项都不是很熟悉,甚至不确定是否应该使用gcc。

我在这里遇到了一个问题,即我可以在Python shell中运行程序,但不能在命令行中运行(我不希望用户必须进入shell,导入模块等)。

我漏掉了什么?


使用命令 "cython myfile.pyx"(不带选项)会生成一个 .c 文件。当我使用 "--embed" 选项时,它会生成一个几百行更长的 .c 文件。通过比较这两个文件,可以看到添加了一堆 ifdef。但问题仍然存在:我该如何使用这个 .so 文件?我似乎无法找到正确的编译器选项组合。根据 Cython 针对 NumPy 的文档,其中一种方法是创建一个包装 Python 程序来导入 .so 文件。令人惊讶的是,当我隐藏了 .c 和 .pyx 文件,然后输入 "import NameOfMyFile"(没有扩展名),它似乎选择了 .so 文件。 - Paul Nelson
@ostrokach 还链接到 Cython可以编译成EXE吗? - Basj
4个回答

72
你需要使用Cython编译器的--embed标志。虽然它没有很多文档,但我找到了一个链接到简单工作示例的网页this
要将Cython源代码编译为可编译成可执行文件的C文件,您可以使用类似cython myfile.pyx --embed的命令,然后使用您正在使用的任何C编译器进行编译。
当您编译C源代码时,仍然需要包含Python头文件的目录并链接到您系统上对应的Python共享库(如果您使用Python2.7,则该文件名可能是libpython27.solibpython27.a)。 编辑:以下是有关如何获取包括适当头文件和链接适当库的命令的更多说明。
正如我之前所说,您需要像这样运行Cython编译器:
cython <cython_file> --embed

使用gcc编译,需要找到系统中Python头文件的位置(您可以通过运行distutils.sysconfig.get_python_inc()来获取此位置(您需要首先导入它)。 在您的Python安装目录中,这可能只是/include子目录。

您还需要找到Python共享库。 对于Python 2.7,Windows上是libpython27.a,Linux上是libpython2.7.so

然后您的gcc命令将为

gcc <C_file_from_cython> -I<include_directory> -L<directory_containing_libpython> -l<name_of_libpython_without_lib_on_the_front> -o <output_file_name>

在编译时加上-fPIC标志可能是明智的选择。 在64位Windows机器上,您还需要包括-D MS_WIN64标志,告诉mingw编译为64位Windows。

如果您要编译一些依赖NumPy的东西,您还需要包括包含NumPy头文件的目录。 您可以通过运行numpy.get_include()(在导入numpy后再次运行)来找到此文件夹。 然后您的gcc命令变成:

gcc <C_file_from_cython> -I<include_directory> -I<numpy_include_directory> -L<directory_containing_libpython> -l<name_of_libpython_without_lib_on_the_front> -o <output_file_name>

这个gcc命令选项指南可能会有帮助。

另外,如果可能的话,我建议您使用Cython内存视图。 这样您就不必在Cython文件中包含NumPy头文件和NumPy pxd文件。 它还使得切片操作更容易为C编译器进行优化。


1
@HaydenDarcy 我最初是使用 MinGW 进行测试的,尽管 MinGW 与最近的 Python 发行版不兼容,即使对于较旧的版本,libpython.a 也可能需要单独安装。类似的方法应该适用于 MSVC。您必须更改为 MSVC 样式标志,并链接到 libpython.dll 而不是 libpython.a 或 libpython.so。主要思想是 Cython 可以为独立可执行文件生成代码,但是当您编译生成的文件时,您必须包含 Python 头文件并链接到 Python 运行时(并确保编译器搜索的路径中都可以找到它们)。 - IanH
1
不要忘记添加Chrome驱动程序更改,因为它在另一台电脑上也无法工作,还有其他问题(正如我发现的那样)。虽然它可以工作,但并不像我想象的那么简单。或者,分发Winpython barebones + python jobs。如果您想要安全性,请添加cython。正如人们所发现的那样,试图让Python在没有Python的情况下运行是可以理解的复杂和容易出错的。 - Tetora
1
Pyinstaller在提供正确的Chrome驱动程序路径时运行得非常好。但是,当结合使用pyinstaller + Cython时会出现问题。请参见:https://stackoverflow.com/questions/47026649/pyinstaller-and-cython-application-files-way-too-huge。文件非常庞大。Winpython +标准Python文件也可以工作。我还没有看到一个可独立运行的Cython或Nuitka解决方案,尽管两者都似乎很难单独使用。 - Tetora
1
没错,不管怎样,你最终都得依赖解释器和相应的运行时。重新分发这些文件会使文件变大。即使是静态编译方法也要依赖解释器,并专注于编译扩展而不是进行某种源到源的转换。由于Python中所有动态特性的存在,这种事情对于任意用户代码来说都是不可能的。如果一个给定的可执行文件可以嵌入任意Python语义,那么它已经实现了某种形式的解释器。对于任意用户代码来说,这是无法避免的。 - IanH
1
有没有人在一个带有makefile的repo中拥有一个完整可重现的示例? - mathtick
显示剩余9条评论

24

我在Ubuntu上测试过:

按照以下步骤安装 Cython(适用于Python 2):

Tested this on Ubuntu:

Install Cython using the following (Python 2):

sudo apt-get install cython

对于Python 3:

sudo apt-get install cython3

要将Python代码编译为C代码,请运行以下命令(对于Python 3,将cython更改为cython3):

cython --embed -o example.c example.py

这将生成example.c文件。

现在编译example.c文件:

gcc -Os -I /usr/include/python2.7 example.c -lpython2.7 -o example

运行文件:

./example

现在对于Python 3,像这样做就可以了(未经测试):

gcc -Os -I /usr/include/python3.6 example.c -lpython3.6 -o example

其中 python3.x 是安装在您计算机上的 Python 版本。


1
这会加载所有使用Cython创建的导入和模块,对吧? - btc4cash
1
如果你是指Python的导入和模块,那么是的。 - Rafael
我如何从多个.py文件生成独立的文件?我有多个Python文件,我想使用Cython将它们全部编译成单个包。 - Nomiluks
1
你尝试过编译导入所有其他文件的主文件吗? - Rafael
1
请确保您要嵌入的.py文件名称中不包含“-”符号。使用类似于“foo_bar.py”的名称(而不是“foo-bar.py”)。 - G.Vanem
我在 Mac 上通过这篇文章让它工作了。如果有人需要样例,这是我所做的:https://github.com/sarnobat/cython/blob/main/run.sh - Sridhar Sarnobat

8

这是针对Windows + MS Visual Studio 14的解决方案(因为还没有人提到cl.exe参数)。

首先使用embed参数生成test.c文件:

cython test.pyx --embed

然后编译它:

call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64
cl test.c /I C:\Python37\include /link C:\Python37\libs\python37.lib

输出的是一个小型可执行文件test.exe(例如,在我这里,使用 print("Hello World") 生成的大小为140 KB)。

注:


3
在 Rafael 的回答基础上,这里提供了使用 Cython 和 Python 3(在 Termux 上测试过)创建“Hello, World”可执行文件的说明,以及 Python 2 的说明:
由于我的系统上似乎没有“cython3”选项,所以我做了以下操作:
文件名为 test.py:
print("Hello, world!")

然后执行这个操作:

cython -3 --embed -o test.c test.py

然后你需要弄清楚你需要什么包含路径,以及可能需要的库。

我正在使用Python 3.10.6;因此,我使用一个名为python3.10-config的程序来获取信息,然后将其输入到gcc中:

python3.10-config --includes

对我来说,这将输出:

-I/data/data/com.termux/files/usr/include/python3.10 -I/data/data/com.termux/files/usr/include/python3.10

它给了我两次相同的路径(我不知道为什么),所以只需使用该路径(我想你只需要使用一次)。

然后执行

python3.10-config --libs

对我来说,这将输出:

-lpython3.10 -lcrypt -ldl  -lm -lm

我不确定每个程序都需要这些,因为只要我加上-lpython3.10,'Hello, world'就可以工作。

无论如何,然后我这样做,它会给我一个可执行文件:

gcc -Os -I/data/data/com.termux/files/usr/include/python3.10 test.c -lpython3.10 -o test

还有一个叫做python2.7-config的程序。

如果我想要获得一个Python 2.7.18可执行文件,可以使用以下命令代替上面的命令:

cython -2 --embed -o test.c test.py

那么

python2.7-config --includes
python2.7-config --libs

这给了我这些:

  • -I/data/data/com.termux/files/usr/include/python2.7 -I/data/data/com.termux/files/usr/include/python2.7
  • -lpython2.7 -ldl -lm

最后,我运行以下命令以生成可执行文件:

gcc -Os -I/data/data/com.termux/files/usr/include/python2.7 test.c -lpython2.7 -o test

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接