在Cython中创建可执行文件
我最近在玩Cython。平时我用Python编程,但之前学过C语言。现在我想弄一个可以独立运行的程序,但不知道怎么做。
我已经下载了Cython,并且可以创建一个.pyx文件(这其实就是一个普通的Python文件,只是后缀名是.pyx),然后我可以在Python的命令行里运行它,方法是使用:
import pyximport; pyximport.install()我可以在命令行用这个命令生成一个.c文件:cython file.pyx。然后我可以通过建立一个标准的setup.py文件来生成一个.so文件,并执行:
setup.py build_ext --inplace
我尝试用gcc把.so文件做成可执行文件,试了很多选项,但总是缺少很多文件、头文件等等。我试着从各个地方指向这些头文件,但都没有成功。而且我对gcc的选项也不太了解,甚至不知道我是不是应该用gcc。
我现在有点困惑,因为我可以在Python命令行里运行我的程序,但在命令行下却不行(我不想让用户进入命令行,去导入模块什么的)。
我到底漏掉了什么呢?
4 个回答
基于Rafael的回答,这里有一个关于如何用Cython和Python 3制作一个“Hello, World”可执行文件的简单说明(在Termux上测试过),接下来是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
我不确定每一个都对每个程序都重要,因为“Hello, world”这个程序只用-lpython3.10
就能工作。
无论如何,然后我这样做,它给了我一个可执行文件:
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
这是一个关于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)。
注意事项:
如果你想做一个图形界面应用程序(而不是控制台应用程序),可以参考@longgangfan的回答,看看这个链接 Cython能编译成EXE吗?,也就是说,在
cl.exe
的参数中添加/subsystem:windows /entry:wmainCRTStartup
。关于源代码是否仍然存在的更多信息,可以查看 用Cython生成的可执行文件真的没有源代码吗? 和 编译后的独立Cython可执行文件仍然包含所有原始源代码吗?。
另外,.exe文件不能直接在任何机器上运行;还需要一些其他文件,具体可以参考 分发嵌入Cython编译代码并使其在任何机器上工作所需的最小文件集。
在Ubuntu上测试过这个:
使用以下方法安装 Cython
(适用于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版本。
你需要的是Cython编译器的--embed
选项。虽然关于这个选项的文档不多,但我找到了一些信息,这里有个简单的示例可以参考。
要把Cython源代码编译成可以生成可执行文件的C文件,你可以使用类似cython myfile.pyx --embed
的命令,然后用你选择的C编译器进行编译。
在编译C源代码时,你还需要包含Python头文件所在的目录,并链接到你系统上对应的Python共享库(如果你使用的是Python 2.7,文件名可能是libpython27.so
或libpython27.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的内存视图。这样你就不需要包含NumPy的头文件,也不需要在Cython文件中包含NumPy的pxd文件。这也会让C编译器在优化切片操作时更容易。