zc.buildout 编译和安装源代码的分发配方。
项目描述
该配方提供了使用configure和make以及其他类似工具编译和安装源代码分发的手段。它受到了zc.recipe.cmmi配方的影响,但提供了对构建过程更多的控制。
仓库: http://github.com/hexagonit/hexagonit.recipe.cmmi
克隆URL:git clone git://github.com/hexagonit/hexagonit.recipe.cmmi.git
问题跟踪器: http://github.com/hexagonit/hexagonit.recipe.cmmi/issues
支持的Python版本:2.6, 2.7, 3.2, 3.3
支持的zc.buildout版本:1.x, 2.x
Travis 构建项目:
变更历史
2.0 (2013-04-07)
不再支持 Python 2.4/2.5。使用 1.x 版本。当前支持的版本是 Python 2.6、2.7、3.2 和 3.3。[dokai]
支持 Python 3.2/3.3。[dokai, mmariani]
支持 Tox。[msabramo] https://github.com/hexagonit/hexagonit.recipe.cmmi/pull/7
集成 Travis CI。[msabramo] https://travis-ci.org/hexagonit/hexagonit.recipe.cmmi 见 https://github.com/hexagonit/hexagonit.recipe.cmmi/pull/7
自动清理先前失败运行留下的编译目录。见 https://github.com/hexagonit/hexagonit.recipe.cmmi/pull/9 [desaintmartin]
1.6 (2012-06-28)
重新许可为 3-clause BSD 许可证。[dokai]
1.5.1 (2012-05-21)
执行 PEP8 / Pyflakes 清理。[dokai]
引用 --prefix 路径以支持路径中的空格字符。关闭 https://github.com/hexagonit/hexagonit.recipe.cmmi/pull/4 [galpin]
修复了在 Windows 上导入钩子脚本的问题。关闭 https://github.com/hexagonit/hexagonit.recipe.cmmi/pull/6 [grzn]
1.5.0 (2010-12-17)
重构了环境变量处理逻辑。Python 2.6 之前的版本在使用 os.environ.clear() 清除环境变量时存在问题(见 http://bugs.python.org/issue3227)。[dokai]
我们不是直接修改 os.environ,而是使用 subprocess 模块在子进程中运行命令,这些子进程被赋予一个显式环境,该环境是当前 os.environ 的副本,并增加了每个部分的覆盖。因此,os.environ 不会被此配方修改。
Python 钩子脚本将作为第三个参数传递修改后的环境字典。
有关详细信息,请参阅 https://github.com/hexagonit/hexagonit.recipe.cmmi/issues/issue/1/#issue/1/comment/605362。
1.4.0 (2010-08-27)
添加了对使用新 make-options 选项传递 make 选项的支持。见下面的 在没有 autoconf 类似的系统上安装软件包 部分,以获取示例。[dokai]
只有当
未使用 configure-command 指定自定义配置命令时,
且在 configure-options 选项中未显式提供 --prefix 时,--prefix 参数才会自动传递给配置命令。
[dokai]
移除了 is_build_dir() 启发式。
以前,配方检查下载的包的内容以确定它是否包含构建包所需的文件(它检查是否存在名为 configure 或 Makefile.PL 的文件),如果它们缺失,则提供错误消息。然而,该配方对于构建许多不同类型的软件包很有用,检查特定文件严重限制了其使用。
现在,配方省略了对下载的包中特定文件的任何检查。建议您在部分配置中使用 md5sum 选项来断言您正在下载您期望的包。[dokai]
1.3.1 (2010-08-23)
重构了 is_build_dir() 辅助方法,使其更容易在定制配方中进行测试和覆盖。[dokai]
修复了处理工作目录的方法,使得无论配方执行成功与否,工作目录都会恢复到执行配方之前的状态。感谢Jonathan Ballet的报告和初步补丁。[dokai]
修复了http://github.com/hexagonit/hexagonit.recipe.cmmi/issues#issue/1 环境变量定义在某一部分,将不会泄露到后续的其他部分。[dokai]
1.3.0 (2009-09-20)
添加了新的选项 environment-section 和 environment,用于在执行配方之前控制环境变量。
添加了新的选项 prefix,用于覆盖安装前缀。默认值为该部分的硬编码位置值。
1.2.0
添加了新的 configure-command 选项,用于控制生成 Makefile 的命令。这使得构建略有不同的包成为可能,例如,Perl项目,其中Makefile.PL替换了configure脚本。
1.1.1
如果选项是空字符串,则不要尝试执行钩子。这将使得在扩展现有部分时禁用钩子成为可能。
1.1.0
添加了新的选项 path,允许构建和安装本地源代码树。 path 选项与 url 是互斥的。
1.0.1
修复了“keep-compile-dir”选项的bug。编译目录的位置无法通过文档中提到的 options['compile-directory'] 选项获取。
1.0.0
首次公开发布。
详细文档
支持选项
url
将要下载和提取的软件包的URL。支持的包格式为 .tar.gz、.tar.bz2 和 .zip。值必须是一个完整的URL,例如 https://pythonlang.cn/ftp/python/2.4.4/Python-2.4.4.tgz。不能同时使用 path 选项和 url 选项。
path
包含要构建和安装的源代码的本地目录的路径。该目录必须包含 configure 脚本。不能同时使用 url 选项和 path 选项。
prefix
传递给 configure 脚本 --prefix 选项的自定义安装前缀。默认值为该部分的位置。请注意,这是一个方便的快捷方式,它假定默认的 configure 命令用于配置软件包。如果使用 configure-command 选项定义自定义configure命令,则不会自动注入 --prefix。您还可以在 configure-options 中显式设置 --prefix 参数。
md5sum
软件包文件的MD5校验和。如果可用,将比较下载的软件包的MD5校验和与此值,如果值不匹配,则执行配方将失败。
make-binary
make 程序的路径。默认值为 'make',它应该在具有系统 PATH 中可用的 make 程序的任何系统上工作。
make-options
包含在 make 程序调用中的额外 KEY=VALUE 选项。可以在单独的行上提供多个选项以提高可读性。
make-targets
make 命令的目标。默认值为 'install',这通常足以安装大多数软件包。只有当您想要构建不同的目标时才需要使用此选项。每个目标必须单独一行给出。
configure-command
将要运行的配置命令的名称,用于生成Makefile。默认为./configure,这对于带有配置脚本的软件包来说是合适的。如果您在编译不同设置的软件包时,可能需要更改此设置。请参阅编译Perl软件包部分以获取示例。
configure-options
传递给configure脚本的额外选项。默认情况下,只有--prefix选项被传递,它设置为部分目录。每个选项必须在单独的一行上给出。
patch-binary
patch程序的路径。默认为‘patch’,它应该在任何具有系统PATH中可用patch程序的系统上工作。
patch-options
传递给patch程序的选项。默认为-p0。
patches
要应用到提取源中的补丁文件列表。每个文件必须在单独的一行上给出。
pre-configure-hook
将在运行configure脚本之前执行的定制Python脚本。选项的格式为
/path/to/the/module.py:name_of_callable其中第一部分是Python模块的文件系统路径,第二部分是该模块中将调用的可调用对象名称。可调用对象将按以下顺序传递三个参数
配方中的options字典。
全局的buildout字典。
包含当前os.environ并增加特定部分的覆盖的字典。
可调用对象不需要返回任何内容。
pre-make-hook
将在运行make之前执行的定制Python脚本。其格式和语义与pre-configure-hook选项相同。
post-make-hook
将在运行make之后执行的定制Python脚本。其格式和语义与pre-configure-hook选项相同。
keep-compile-dir
可选开关,用于保留编译软件包时使用的临时目录。这主要用于其他使用此配方编译软件但希望执行此配方未处理的额外步骤的配方。编译目录的位置存储在options['compile-directory']中。接受的值是true或false,默认为false。
environment-section
提供将要用于在执行配方之前增强从os.environ读取的变量的节名称。
此配方不会直接修改os.environ。作为配方一部分运行的外部命令(例如,make,configure等)在它们被衍生时获得增强的环境。Python钩子脚本将作为参数传递增强的环境。
环境变量值可能包含对其他现有环境变量(包括自身)的引用,这些引用以Python字符串插值变量的字典表示法存在。这些引用将使用os.environ中的值进行展开。例如,可以使用此方法来追加到PATH变量,例如:
[component] recipe = hexagonit.recipe.cmmi environment-section = environment [environment] PATH = %(PATH)s:${buildout:directory}/bin
环境
由换行符分隔的KEY=VALUE对序列,用于在执行配方之前更新os.environ所使用的附加环境变量。
此选项的语义与environment-section相同。如果同时提供了environment-section和environment,则前者的值将被后者覆盖,允许针对每个部分进行定制。
此外,配方会遵守在[buildout]部分中设置的download-cache选项,并将下载的文件存储在它下面。如果没有设置值,将在buildout的根目录下创建一个名为downloads的目录,并相应地设置download-cache选项。
在从网络下载之前,配方将首先检查是否有本地副本。通过将download-cache设置到相同的位置,可以由不同的buildout共享文件。
示例用法
我们将使用一个简单的tar包来演示这个配方。
>>> import os.path >>> src = join(os.path.dirname(__file__), 'testdata') >>> ls(src) - Foo-Bar-0.0.0.tar.gz - haproxy-1.4.8-dummy.tar.gz - package-0.0.0.tar.gz
该包包含一个虚拟的configure脚本,该脚本将简单地回显其被调用的选项,并创建一个Makefile,该脚本也将执行相同的操作。
让我们创建一个buildout来构建和安装这个包。
>>> write('buildout.cfg', ... """ ... [buildout] ... newest = false ... parts = package ... ... [package] ... recipe = hexagonit.recipe.cmmi ... url = file://%s/package-0.0.0.tar.gz ... """ % src)
这将使用默认构建选项下载、提取和构建我们的演示包。
>>> print(system(buildout)) Installing package. package: Extracting package to /sample_buildout/parts/package__compile__ configure --prefix=/sample_buildout/parts/package building package installing package
正如我们所看到的,默认情况下通过--prefix选项调用了configure脚本,然后是调用make和make install。
安装Perl包
此配方可以用于安装使用略有不同构建过程的包。Perl包通常附带一个Makefile.PL脚本,它执行与configure脚本相同的功能并生成一个Makefile。
我们可以通过覆盖configure-command选项来构建和安装这样的包。以下示例构建了一个Foo::Bar Perl模块并将其安装到buildout中的自定义位置。
>>> write('buildout.cfg', ... """ ... [buildout] ... newest = false ... parts = foobar ... perl_lib = ${buildout:directory}/perl_lib ... ... [foobar] ... recipe = hexagonit.recipe.cmmi ... configure-command = perl -I${buildout:perl_lib}/lib/perl5 Makefile.PL INSTALL_BASE=${buildout:perl_lib} ... url = file://%s/Foo-Bar-0.0.0.tar.gz ... """ % src) >>> print(system(buildout)) Uninstalling package. Installing foobar. foobar: Extracting package to /sample_buildout/parts/foobar__compile__ building package installing package
在没有autoconf类似系统的包上安装
某些包不使用配置机制,而只提供用于构建的Makefile。在这些情况下,构建过程通常完全由直接传递给make的选项控制。我们可以通过模拟一个什么也不做的configure命令并将适当的选项传递给make来构建这样的包。大多数shell环境中找到的true实用程序是这一点的良好候选者,尽管任何返回零退出代码的东西都可以。
我们使用一个虚拟的“HAProxy”包作为仅包含Makefile且使用显式make选项来控制构建过程的包的示例。
>>> write('buildout.cfg', ... """ ... [buildout] ... newest = false ... parts = haproxy ... ... [haproxy] ... recipe = hexagonit.recipe.cmmi ... configure-command = true ... make-options = ... TARGET=linux26 ... CPU=i686 ... USE_PCRE=1 ... url = file://%s/haproxy-1.4.8-dummy.tar.gz ... """ % src)>>> print(system(buildout)) Uninstalling foobar. Installing haproxy. haproxy: Extracting package to /sample_buildout/parts/haproxy__compile__ Building HAProxy 1.4.8 (dummy package) TARGET: linux26 CPU: i686 USE_PCRE: 1 Installing haproxy
安装签出
有时,我们不仅需要下载和构建现有的tar包,还需要处理已经存在于文件系统中的代码,例如SVN检出。
我们将提供一个path选项到包含源代码的目录,而不是提供url选项。
让我们通过首先将测试包解包到文件系统中并构建它来演示这一点。
>>> checkout_dir = tmpdir('checkout') >>> import setuptools.archive_util >>> setuptools.archive_util.unpack_archive('%s/package-0.0.0.tar.gz' % src, ... checkout_dir) >>> ls(checkout_dir) d package-0.0.0>>> write('buildout.cfg', ... """ ... [buildout] ... newest = false ... parts = package ... ... [package] ... recipe = hexagonit.recipe.cmmi ... path = %s/package-0.0.0 ... """ % checkout_dir)>>> print(system(buildout)) Uninstalling haproxy. Installing package. package: Using local source directory: /checkout/package-0.0.0 configure --prefix=/sample_buildout/parts/package building package installing package
由于使用路径意味着源代码是在配方控制之外获得的,因此管理它的责任也超出了配方的范畴。
根据软件的不同,您可能需要在构建运行之间手动运行make clean等命令,如果您修改了代码。此外,当使用路径时,keep-compile-dir选项不起作用。
高级配置
上述选项足以构建大多数包。然而,在某些情况下,这些选项并不足够,我们需要更多地控制构建过程。让我们尝试使用一个新的构建配置并提供更多选项。
>>> write('buildout.cfg', ... """ ... [buildout] ... newest = false ... parts = package ... ... [build-environment] ... CFLAGS = -I/sw/include ... LDFLAGS = -I/sw/lib ... ... [package] ... recipe = hexagonit.recipe.cmmi ... url = file://%(src)s/package-0.0.0.tar.gz ... md5sum = 6b94295c042a91ea3203857326bc9209 ... prefix = /somewhere/else ... environment-section = build-environment ... environment = ... LDFLAGS=-L/sw/lib -L/some/extra/lib ... configure-options = ... --with-threads ... --without-foobar ... make-targets = ... install ... install-lib ... patches = ... patches/configure.patch ... patches/Makefile.dist.patch ... """ % dict(src=src))
此配置使用自定义配置选项,环境部分,对环境的每个部分进行定制,自定义前缀,多个make目标,并在运行脚本之前修补源代码。
>>> print(system(buildout)) Uninstalling package. Installing package. package: [ENV] CFLAGS = -I/sw/include package: [ENV] LDFLAGS = -L/sw/lib -L/some/extra/lib package: Extracting package to /sample_buildout/parts/package__compile__ package: Applying patches patching file configure patching file Makefile.dist patched-configure --prefix=/somewhere/else --with-threads --without-foobar building patched package installing patched package installing patched package-lib
自定义构建过程
有时即使是上述内容也不够,您需要能够更详细地控制过程。一个这样的用例可能是对源代码执行动态替换(基于构建信息),这不能使用静态补丁来完成,或者简单地运行任意命令。
该配方允许您编写自定义Python脚本来钩入构建过程。您可以定义一个在配置脚本执行之前运行的脚本
(预配置钩子)
在make过程执行之前 (预make钩子)
在make过程完成后 (post-make钩子)
每个选项都需要包含以下信息
/full/path/to/the/python/module.py:name_of_callable
其中可调用对象(此处为name_of_callable)应接受三个参数
配方中的options字典。
全局的buildout字典。
包含当前os.environ并增加特定部分的覆盖的字典。
这些参数应提供必要的信息,以便可调用对象执行任何特定部分的构建过程定制。
让我们创建一个简单的Python脚本来演示此功能。您当然可以针对每个钩子单独拥有模块,或者简单地使用一个或两个钩子。这里我们只使用一个模块。
>>> hooks = tmpdir('hooks') >>> write(hooks, 'customhandlers.py', ... """ ... import logging ... log = logging.getLogger('hook') ... ... def preconfigure(options, buildout, environment): ... log.info('This is pre-configure-hook!') ... ... def premake(options, buildout, environment): ... log.info('This is pre-make-hook!') ... ... def postmake(options, buildout, environment): ... log.info('This is post-make-hook!') ... ... """)
并尝试一个新的构建配置
>>> write('buildout.cfg', ... """ ... [buildout] ... newest = false ... parts = package ... ... [package] ... recipe = hexagonit.recipe.cmmi ... url = file://%(src)s/package-0.0.0.tar.gz ... pre-configure-hook = %(module)s:preconfigure ... pre-make-hook = %(module)s:premake ... post-make-hook = %(module)s:postmake ... """ % dict(src=src, module='%s/customhandlers.py' % hooks))>>> print(system(buildout)) Uninstalling package. Installing package. package: Extracting package to /sample_buildout/parts/package__compile__ package: Executing pre-configure-hook hook: This is pre-configure-hook! configure --prefix=/sample_buildout/parts/package package: Executing pre-make-hook hook: This is pre-make-hook! building package installing package package: Executing post-make-hook hook: This is post-make-hook!
对于更具体的需求,您可以编写自己的配方,使用hexagonit.recipe.cmmi并设置keep-compile-dir选项为true。然后您可以从该配方完成的地方继续,通过从您自己的配方中读取编译目录的位置来实现这一点。
贡献者
作者:Kai Lautaportti (dokai)
Cédric de Saint Martin (desaintmartin)
Marc Abramowitz (msabramo)
Nicolas Dumazet (nicdumz)
Guy Rozendorn (grzn)
Marco Mariani (mmariani)
galpin