如何处理Linux/Python依赖?
因为我想用的一些库在Windows上不支持,所以我把一些Python开发从Windows转移到了Linux上。我花了大部分时间在处理依赖关系上,但一直没能解决。
问题
每次我开始使用Linux时,通常都会遇到某种依赖问题,尤其是开发库,不管是通过apt-get、easy_install还是pip安装的。我可能会在一些本该简单的任务上浪费好几天,花在让库正常工作上的时间比写代码还要多。我在哪里可以学习如何处理这些问题的策略,而不是无目的地在网上搜索别人遇到过的相同问题呢?
一个例子
举个例子:我想生成一些二维码。所以,我想用github.com/bitly/pyqrencode,这个库是基于pyqrcode.sourceforge.net的,但据说没有Java的依赖。还有其他的库(pyqrnative,github.com/Arachnid/pyqrencode),但这个看起来最符合我的需求。
于是,我在pypi上找到了这个包,觉得用它会让事情变得简单:
(我可能因为使用virtualenv来保持环境整洁而让事情变得更复杂了。)
(myenv3)mat@ubuntu:~/myenv3$ bin/pip install pyqrencode
Downloading/unpacking pyqrencode
Downloading pyqrencode-0.2.tar.gz
Running setup.py egg_info for package pyqrencode
Installing collected packages: pyqrencode
Running setup.py install for pyqrencode
building 'qrencode' extension
gcc -pthread -fno-strict-aliasing -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fPIC -I/usr/include/python2.7 -c qrencode.c -o build/temp.linux-i686-2.7/qrencode.o
gcc -pthread -shared -Wl,-O1 -Wl,-Bsymbolic-functions -Wl,-Bsymbolic-functions build/temp.linux-i686-2.7/qrencode.o -lqrencode -o build/lib.linux-i686-2.7/qrencode.so
Successfully installed pyqrencode
Cleaning up...
(我想我之前可能也用过sudo apt-get install libqrencode-dev
。)
然后我尝试运行测试脚本:
(myenv3)mat@ubuntu:~/myenv3$ python test_qr.py
Traceback (most recent call last):
File "test_qr.py", line 1, in <module>
from qrencode import Encoder
File "qrencode.pyx", line 1, in init qrencode (qrencode.c:1520)
ImportError: No module named ImageOps
:(
好吧,调查显示ImageOps似乎是PIL的一部分...
(myenv3)mat@ubuntu:~/myenv3$ pip install pil
Downloading/unpacking pil
Downloading PIL-1.1.7.tar.gz (506Kb): 122Kb downloaded
Operation cancelled by user
Storing complete log in /home/mat/.pip/pip.log
(myenv3)mat@ubuntu:~/myenv3$ bin/pip install pil
Downloading/unpacking pil
Downloading PIL-1.1.7.tar.gz (506Kb): 506Kb downloaded
Running setup.py egg_info for package pil
WARNING: '' not a valid package name; please use only.-separated package names in setup.py
Installing collected packages: pil
Running setup.py install for pil
WARNING: '' not a valid package name; please use only.-separated package names in setup.py
building '_imaging' extension
gcc ...
building '_imagingmath' extension
gcc ...
--------------------------------------------------------------------
PIL 1.1.7 SETUP SUMMARY
--------------------------------------------------------------------
version 1.1.7
platform linux2 2.7.1+ (r271:86832, Apr 11 2011, 18:05:24)
[GCC 4.5.2]
--------------------------------------------------------------------
*** TKINTER support not available
*** JPEG support not available
*** ZLIB (PNG/ZIP) support not available
*** FREETYPE2 support not available
*** LITTLECMS support not available
--------------------------------------------------------------------
To add a missing option, make sure you have the required
library, and set the corresponding ROOT variable in the
setup.py script.
To check the build, run the selftest.py script.
...
Successfully installed pil
Cleaning up...
嗯,PIL已经安装了,但没有找到我之前用sudo apt-get install libjpeg62 libjpeg62-dev libpng12-dev zlib1g zlib1g-dev
安装的库。我不确定如何告诉pip把库的位置传给setup.py。网上搜索了一些不同的方法,我试过,但似乎都没什么用,反而让我绕了很多圈。
Ubuntu 11.04: 在虚拟环境中用PIP安装PIL建议使用pillow包来代替,所以我们试试这个:
(myenv3)mat@ubuntu:~/myenv3$ pip install pillow
Downloading/unpacking pillow
Downloading Pillow-1.7.5.zip (637Kb): 637Kb downloaded
Running setup.py egg_info for package pillow
...
Installing collected packages: pillow
Running setup.py install for pillow
building '_imaging' extension
gcc ...
--------------------------------------------------------------------
SETUP SUMMARY (Pillow 1.7.5 / PIL 1.1.7)
--------------------------------------------------------------------
version 1.7.5
platform linux2 2.7.1+ (r271:86832, Apr 11 2011, 18:05:24)
[GCC 4.5.2]
--------------------------------------------------------------------
*** TKINTER support not available
--- JPEG support available
--- ZLIB (PNG/ZIP) support available
--- FREETYPE2 support available
*** LITTLECMS support not available
--------------------------------------------------------------------
To add a missing option, make sure you have the required
library, and set the corresponding ROOT variable in the
setup.py script.
To check the build, run the selftest.py script.
...
Successfully installed pillow
Cleaning up...
好吧,这次我们似乎有了JPEG和PNG的支持,太好了!
(myenv3)mat@ubuntu:~/myenv3$ python test_qr.py
Traceback (most recent call last):
File "test_qr.py", line 1, in <module>
from qrencode import Encoder
File "qrencode.pyx", line 1, in init qrencode (qrencode.c:1520)
ImportError: No module named ImageOps
不过,ImageOps还是没有。我现在很困惑,ImageOps是pillow缺失的吗,还是说这是一个和PIL一样的其他问题。
2 个回答
你的故事让我想起了我刚接触Linux时的经历,以及我为什么喜欢APT这个工具。
对于你提到的问题,没有一种通用的解决办法;最好的办法就是利用别人已经做好的工作。Debian的打包者在处理软件包的依赖关系方面做得非常好,所以使用apt-get的时候,它会自动帮你下载所需的东西。因此,我的策略就是尽量避免自己去构建和安装软件,而是尽可能使用apt-get。
需要注意的是,Ubuntu是基于Debian的,所以它也能享受到Debian打包者的成果。我没有使用过Fedora,但听说它的软件包管理没有Debian那么有条理。
我看到这里有两个问题:
如何跟踪你项目所需的所有Python模块。
如何跟踪你项目中Python模块所需的所有动态库。
对于第一个问题,我发现buildout是个不错的工具,虽然刚开始用的时候可能需要花点时间去理解。
在你的情况下,我建议先为你的新项目创建一个文件夹。然后进入这个文件夹,下载bootstrap.py。
wget http://python-distribute.org/bootstrap.py
接着,我会创建一个buildout.cfg文件:
[buildout]
parts = qrproject
python
eggs = pyqrencode
[qrproject]
recipe = z3c.recipe.scripts
eggs = ${buildout:eggs}
entry-points= qrproject=qrprojectmodule:run
extra-paths = ${buildout:directory}
# This is a simple way of creating an interpreter that will have
# access to all the eggs / modules that this project uses.
[python]
recipe = z3c.recipe.scripts
interpreter = python
eggs = ${buildout:eggs}
extra-paths = ${buildout:directory}
在这个buildout.cfg文件中,我引用了模块qrprojectmodule(在[qrproject]下的entry-points部分)。这样会创建一个bin/qrproject,它会运行模块qrprojectmodule中的run函数。因此,我还需要创建一个qrprojectmodule.py文件。
import qrencode
def run():
print "Entry point for qrproject. Happily imports qrencode module"
现在是时候用你想用的Python版本来运行bootstrap.py了:
python bootstrap.py
然后运行生成的bin/buildout。
bin/buildout
这会在bin/目录下创建两个额外的可执行文件——bin/qrproject和bin/python。前者是你项目的主要可执行文件。每次运行buildout时,它都会自动创建,并加载你需要的所有模块和包。后者则是一个方便的方式,可以让你进入一个Python提示符,所有模块和包都已加载,方便调试。这里的好处是,bin/buildout会自动安装任何你所需的Python包的依赖项(在你的情况下是pyqrencode)。
实际上,你在运行bin/buildout时可能会遇到编译错误。这是因为你需要解决第二个问题:确保所有动态库在你的系统上可用。在Linux上,通常最好通过你的包管理系统来解决这个问题。我假设你使用的是像Ubuntu这样的Debian衍生版。
pyqrencode网站上说明,使用pyqrencode需要libqrencode库。所以我用我的包管理器去搜索这个库:
$ apt-cache search libqrencode
libqrencode-dev - QR Code encoding library -- development
libqrencode3 - QR Code encoding library
qrencode - QR Code encoder into PNG image
在这种情况下,我需要的是-dev包,因为它会安装可链接的库和编译Python C模块所需的头文件。此外,包管理器的依赖系统会确保如果我安装libqrencode-dev,我也会得到libqrencode3,因为它在模块编译后运行时是必需的。
所以,我安装这个包:
sudo apt-get install libqrencode-dev
安装完成后,重新运行bin/buildout,希望pyqrencode模块能成功编译和安装。现在试着运行bin/qrproject。
$ bin/qrproject
Entry point for qrproject. Happily imports qrencode module
成功了!:-)
总结一下:
使用buildout自动下载和安装你项目所需的所有Python模块/包。
使用系统的包管理器安装你所用Python模块所需的任何动态(C)库。
要注意的是,在很多情况下,你的Python模块在包管理系统中已经有打包好的版本。例如,在Ubuntu上安装python-imaging包就可以获得pil。在这种情况下,你不需要通过buildout来安装它,也不需要担心库是否可用——包管理器会安装模块运行所需的所有依赖项。不过,通过buildout来做可以让你的项目更容易分发,并在其他系统上运行。