准备使用 Python 2.7 开发一个 C++ 扩展库,该库需要调用 Kaldi 的一部分 C++ 库。
之前,通过创建共享库并将共享库与 Python 扩展库一起分发的方式进行开发,但当时需要设置 LD_LIBRARY_PATH。
现在想改为静态编译扩展库。Kaldi 库可以通过 --fPIC 标志进行静态编译。然而,编译过程仍需要依赖其他库,这些库的标志由 configure 脚本生成。
希望可以使用 setup.py 编译扩展库,并将 Makefile 中的编译设置“窃取”过来。
2、解决方案
2.1 从 Makefile 中提取链接参数
首先,需要从 Makefile 中提取链接参数。
可以首先将 Makefile 中用于链接共享库的命令复制到 .txt 文件中,然后从 .txt 文件中提取出所需的链接参数。
在 Makefile 中,用于链接共享库的命令一般类似于:
$(CXX) -shared -DPIC -o $@ -Wl,-soname=$@,--whole-archive $^ -Wl,--no-whole-archive $(EXTRA_LDLIBS) $(LDFLAGS) $(LDLIBS)
其中,
- $(CXX) 是 C++ 编译器,
- -shared 表示生成共享库,
- -DPIC 表示生成位置无关代码,
- -o $@ 表示输出文件,
- -Wl,-soname=$@ 表示设置共享库的文件名,
- --whole-archive 和 --no-whole-archive 表示对所有目标库进行链接,
- $(EXTRA_LDLIBS) 是额外的链接器标志,
- $(LDFLAGS) 是链接器标志,
- $(LDLIBS) 是库文件。
从中提取出所需的链接参数,包括 (LDFLAGS) 和 (EXTRA_LDLIBS) 是一个列表,(LDLIBS) 是字符串。
2.2 在 setup.py 中应用链接参数
接下来,在 setup.py 中应用这些链接参数。
在 setup.py 中,可以设置 library_dirs 参数来指定库文件的目录,还可以设置 libraries 参数来指定需要链接的库。
ext_modules = [
Extension('pykaldi.decoders',
language='c++',
include_dirs=['..', 'fst'],
library_dirs=['.'],
libraries=['pykaldi'],
sources=['pykaldi/decoders.pyx'],
extra_objects=['pykaldi.a', ]
)
]
其中,
- ext_modules 是一个列表,用于存储所有扩展模块,
- Extension() 函数用于创建扩展模块,
- pykaldi.decoders 是扩展模块的名称,
- language='c++' 表示该扩展模块是用 C++ 编写的,
- include_dirs=['..', 'fst'] 是包含头文件的目录,
- library_dirs=['.'] 是包含库文件的目录,
- libraries=['pykaldi'] 是需要链接的库,
- sources=['pykaldi/decoders.pyx'] 是源文件,
- extra_objects=['pykaldi.a', ] 是额外的对象文件。
通过设置 library_dirs 参数来指定库文件的目录,通过设置 libraries 参数来指定需要链接的库,就可以在 setup.py 中应用从 Makefile 中提取出的链接参数。
2.3 编译扩展模块
最后,运行 setup.py 来编译扩展模块。
python setup.py build_ext --inplace
其中,
- python 是 Python 解释器,
- setup.py 是 setup.py 脚本,
- build_ext 是命令,用于编译扩展模块,
- --inplace 是选项,表示在当前目录中编译扩展模块。
运行该命令后,就可以在当前目录中找到编译好的扩展模块。