以前我们一般用Nuitka或者Pyinstaller来将python源码编译成二进制可执行文件。今天我们学习如何直接用gcc来编译。
很简单的一个python程序,结构如下。包含一个model.py和main.py
步骤1:处理main.py
处理main.py。即主程序入口
cython -D -2 --embed main.py
gcc -c -fPIC -fwrapv -O2 -Wall -fno-strict-aliasing -I/usr/include/python2.7 -o main.o main.c
gcc -I/usr/include/python2.7 -o main main.o -lpython2.7
这三行命令用于将一个 Python 脚本 (main.py
) 编译成一个可执行的 C 程序,使用了 Cython 工具。下面逐行解释每个命令的作用:
1. cython -D -2 --embed main.py
- cython: Cython 是一个编译器,可以将 Python 代码转换为 C 代码,以提高性能。
- -D: 这个选项启用调试信息,方便调试生成的 C 代码。
- -2: 表示使用 Python 2 语法来编译。如果你的代码是用 Python 3 编写的,可以将其改为
-3
。 - –embed: 这个选项指示 Cython 生成一个可以作为嵌入式 Python 解释器的 C 程序的主函数。生成的
main.c
文件将包含一个main()
函数,使其可以直接运行。 - main.py: 这是要编译的 Python 脚本的名称。
2. gcc -c -fPIC -fwrapv -O2 -Wall -fno-strict-aliasing -I/usr/include/python2.7 -o main.o main.c
- gcc: GNU C 编译器,用于编译 C 代码。
- -c: 这个选项指示 GCC 只编译源文件,而不进行链接操作。它会生成一个目标文件(
.o
文件)。 - -fPIC: 生成位置无关代码(Position Independent Code),通常用于共享库。
- -fwrapv: 使编译器在处理整数溢出时遵循 Python 的行为。
- -O2: 启用优化级别 2,以提高生成代码的性能。
- -Wall: 启用所有警告信息,帮助发现潜在问题。
- -fno-strict-aliasing: 禁用严格别名规则,避免可能的未定义行为。
- -I/usr/include/python2.7: 指定 Python 头文件的路径,以便编译器能够找到 Python 的 C API。
- -o main.o: 指定生成的目标文件的名称为
main.o
。 - main.c: 这是之前由 Cython 生成的 C 源文件。
3. gcc -I/usr/include/python2.7 -o main main.o -lpython2.7
- gcc: 再次调用 GNU C 编译器。
- -I/usr/include/python2.7: 同样指定 Python 头文件的路径,以便链接时找到 Python C API。
- -o main: 指定生成的可执行文件的名称为
main
。 - main.o: 指定要链接的目标文件。
- -lpython2.7: 链接 Python 2.7 的共享库,使生成的可执行文件能够调用 Python 的函数和对象。
总结
这三行命令的整体作用是将一个 Python 脚本编译为一个 C 程序,并生成可执行文件 main
。首先,使用 Cython 将 Python 代码转换为 C 代码,然后编译该 C 代码为目标文件,最后链接目标文件生成可执行程序。这样可以提高执行效率,并允许在 C 程序中嵌入 Python 解释器。
步骤2: 处理model.py
处理model.py。因为它不是程序的入口,所以我们将它编译成动态链接库
cython -D -2 model.py
gcc -shared -pthread -fPIC -fwrapv -O2 -Wall -fno-strict-aliasing -I/usr/include/python2.7 -o model.so model.c
这两句命令用于将一个 Python 脚本 (model.py
) 编译成一个共享库(.so
文件),使用 Cython 进行转换。下面逐句分析每个命令的作用:
1. cython -D -2 model.py
- cython: Cython 是一个将 Python 代码转换为 C 代码的编译器,能够提高 Python 代码的执行效率。
- -D: 启用调试信息,生成的 C 代码会包含调试符号,有助于调试。
- -2: 指定使用 Python 2 的语法。如果您的代码是用 Python 3 编写的,可以将其改为
-3
。 - model.py: 这是要编译的 Python 脚本的名称。运行此命令后,Cython 会生成一个名为
model.c
的 C 源文件。
2. gcc -shared -pthread -fPIC -fwrapv -O2 -Wall -fno-strict-aliasing -I/usr/include/python2.7 -o model.so model.c
- gcc: GNU C 编译器,用于编译 C 代码。
- -shared: 创建一个共享库,而不是一个可执行文件。这个选项会生成
.so
文件,允许其他程序使用这个库。 - -pthread: 启用 POSIX 线程支持,允许在共享库中使用多线程功能。
- -fPIC: 生成位置无关代码(Position Independent Code),适用于共享库的创建。
- -fwrapv: 使编译器在处理整数溢出时遵循 Python 的行为。
- -O2: 启用优化级别 2,以提高生成的代码性能。
- -Wall: 启用所有警告信息,有助于发现潜在问题。
- -fno-strict-aliasing: 禁用严格别名规则,以避免可能导致未定义行为的情况。
- -I/usr/include/python2.7: 指定 Python 头文件的路径,以便编译器能够找到 Python 的 C API。
- -o model.so: 指定生成的共享库的名称为
model.so
。 - model.c: 这是由 Cython 生成的 C 源文件,包含了
model.py
的实现。
总结
这两句命令的整体作用是将一个 Python 脚本编译为一个共享库 (model.so
),以便可以在其他 C/C++ 程序中调用其中的 Python 函数和对象。首先,使用 Cython 将 Python 代码转换为 C 代码,然后使用 GCC 编译该 C 代码生成共享库,提供给其他程序使用。
步骤3:验证
编译后的目录如下
其实现在我们只需要main和model.so这两个文件即可,效果如下。即现在已经不再需要依赖python源文件了。