如何在Unix/Linux系统中生成lxml的静态版本?
我现在有两个Unix系统,一个用来提供服务,另一个用来构建(这个构建的环境比较老旧)。我需要在服务机器上使用Python的lxml库。我尝试了以下命令:
python setup.py build --static-deps
或者
CFLAGS="-g -O2 -fPIC"
python setup.py build --static-deps
但是结果是:
ld: fatal: relocations remain against allocatable but non-writable sections
collect2: ld returned 1 exit status
error: command '/usr/lib/python2.6/pycc' failed with exit status 1
我在想,怎样才能做一个静态构建,这样我就可以轻松地部署到我的服务机器上?
另外,如果我
python setup.py build
没有错误,但如果我:
Python 2.6.4 (r264:75706, Apr 17 2011, 11:24:50) [C] on sunos5
Type "help", "copyright", "credits" or "license" for more information.
>>> from lxml import etree
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: ld.so.1: isapython2.6: fatal: relocation error: file /usr/lib/python2.6/site-packages/lxml/etree.so: symbol __xmlStructuredErrorContext: referenced symbol not found
我搜索了一下:导入lxml.etree到Python时出错,似乎没有好的答案。我认为这是链接的问题,所以我觉得做一个静态链接应该是更好的解决方案。
但我主要的目标是减少部署时的工作量,所以我会接受任何简单的方法。
请帮帮我。提前谢谢!
2 个回答
问题看起来是你在尝试构建一个没有静态依赖的静态库。
静态库,比如说 libxml2.a
,其实就是一堆对象文件(.o
文件)的集合。当你链接这个库时,链接器会把你调用的函数的代码从这些对象文件中复制到你的目标文件里。这样,最终生成的目标文件就可以独立运行,不需要 libxml2.a
;因为当你调用 libxml2
的函数时,相关的代码已经在你的库里了。
而共享库,比如 libxml2.so
,基本上就是一个可执行文件。当你链接这个库时,链接器会创建一些重定位条目,这样当你的目标文件和 libxml2.so
同时加载到内存时,你调用的 libxml2
的函数就能正常工作,因为相关的代码就在 libxml2.so
里。
你想做的其实是构建一个混合体:一个共享库(这样可以作为模块加载),它可能动态链接到 libpython.so
(也许还有其他操作系统自带的库),但同时又静态链接 libxml2
和 libxslt2
的代码。
现代的链接器完全可以做到这一点,但它需要 libxml2
的静态库才能实现。
所以,你需要先安装 libxml2.a
(还有 libxslt2.a
,甚至可能需要 libz.a
),才能构建你想要的东西。
我这里简化了一些。在某些平台上,.so
文件实际上只是一个共享对象的前端,基本上是重定位表加上指向真实文件的链接。在其他平台上,代码可以从 .so
文件中提取出来并进行“重定位”,而 .a
文件也可以只是共享对象的类似前端。而且有些平台与 ELF 的差异如此之大,以至于这些术语可能会让人感到困惑。
如果你想了解更多,可以从 这个问题开始,看看相关的问题。
如果你不想了解那么多,只需相信你需要 .a
文件来进行静态链接。
如果你查看 安装 文档,你会注意到:
在 Linux(以及大多数其他正常工作的操作系统)上,只要
libxml2
和libxslt
正确安装,包括开发包(即头文件等),pip
就能成功构建源代码分发。如果构建失败,可以使用你的包管理工具查找像libxml2-dev
或libxslt-devel
这样的包,并确保它们已安装。另外,设置STATIC_DEPS=true
会自动下载并构建这两个库。
所以,如果你根本没有这些库,构建 lxml
会自动下载依赖,帮你构建 .a
文件并链接。
但如果你 有 这些库,那么 (a) pkg-config
和/或 xml2-config
会找到它们并使用,(b) 即使没有找到,lxml
也可能会用 -lxml2
来构建,而不是指向 libxml2.a
的绝对路径,这样就可能链接错误的库。
那怎么解决这个问题呢?你可能会想会有一个简单的标志来告诉它不要这样做,但据我所知,并没有。所以,你的选择包括:
- 在一个不包含
libxml2
和libxslt2
的假根目录下构建。这在 构建 Debian 包 的文档中有提到。 - 手动构建仅静态的
libxml2
和libxslt2
,并将生成的xml2-config
和xslt2-config
的绝对路径传递给setup.py
,具体说明见 构建源代码。 - 临时修改
xml2-config
和xslt2-config
(或者如果没有这些,可以修改pkg-config
使用的.pc
文件),让它们在LDFLAGS
中使用.a
文件的绝对路径,而不是-L
和-l
标志。 - 在构建
lxml
时,临时将.so
文件从你的库路径中移除(可以通过重命名、设置不同的路径等方式)。 - 假设版本号与已安装的
.so
文件不同(或可以不同),你可以按照 Mac 构建说明 指定明确的版本号。你可能需要手动下载压缩包,但不需要自己构建。
如果这一切听起来有点麻烦,像是应该很简单的事情……那么,记住你在做什么。Linux 是围绕为你的特定系统构建源代码分发的最佳方式而设计的。如果你的系统有 xml2 和 xslt2 的共享库,所有的设置都是为了尝试使用它们。如果你想构建的代码要部署到没有这些库的系统上,你的开发系统也应该没有这些库;如果有,你就得知道怎么解决这个问题。