跳转到主要内容

zc.buildout 编译和安装源分布区的配方。

项目描述

该配方提供了使用 configuremake 以及其他类似工具编译和安装源分发的手段。它灵感来源于 hexagonit.recipe.cmmi 配方,但提供了对构建过程的更多控制。

变更

0.21 (2024-07-09)

  • 当使用download-cache时修复钩子

0.21 (2024-06-13)

  • 适配python 3.12

0.20 (2024-03-19)

  • 适配slapos.recipe.build中移动路径的导入

0.19 (2022-01-12)

  • 修复Python 3上的shebang绕过问题

  • 如果keep-compile-dir为true,则生成slapos.recipe.build.env.sh。

0.18 (2021-11-30)

  • 更新依赖项:zc.buildout>=2, slapos.recipe.build>=0.49

  • 修复使用新的slapos.recipe.build时下载失败的错误处理。

  • 编译时,使用位于部分位置内的临时目录。

  • 移除slapos.recipe.cmmi的具有条件部分的用法

  • 更可靠地清理临时下载文件。

  • 切换到slapos.recipe.build实现的环境和共享选项。删除“environment-section”选项。

0.17 (2021-02-26)

  • fix_shebang:不要触摸符号链接。

0.16 (2020-05-08)

  • 将strip_top_level_dir选项传播到slapos.recipe.build:downloadunpacked

0.15 (2020-04-23)

  • slapos.recipe.build.env.sh改进/修复。

0.14 (2020-04-22)

  • 在共享签名中包含部分签名。

  • 删除“dependencies”选项。

  • 从共享签名中删除无用的“_profile_base_location_”条目。

  • 在安装期间(而不是在初始化期间)扩展环境变量。

0.13 (2020-03-31)

  • 为shell命令设置-e

0.12 (2019-12-12)

  • shared:修复中断构建后的恢复

0.11 (2019-10-02)

  • 支持共享部分的多个目录。现在使用 ${buildout:shared-part-list} 作为要使用的目录列表。

0.10 (2018-11-30)

  • 在生成子进程之前确保关闭FD

0.9 (2018-10-29)

  • 更多的Py3修复

0.8 (2018-08-27)

  • 添加共享功能

0.7 (2017-06-06)

  • 修复MANIFEST.in:一些文件丢失。

0.6 (2017-06-05)

  • 添加对Python 3的支持。

  • 优化具有长shebang的脚本的包装器

0.5 (2017-04-07)

  • 为非常长的shebang脚本创建包装器shell脚本。

0.4 (2017-03-08)

  • 使用slapos.recipe.build:downloadunpacked而不是hexagonit.recipe.download

0.1.1 (2013-04-12)

  • 修复错误的名称“path_filename”

0.1 (2013-04-12)

支持选项

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 选项。

strip-top-level-dir

解包时省略包的最顶层目录。true 或 false。默认为 false。

prefix

传递给 configure 脚本 --prefix 选项的自定义安装前缀。默认为部分位置。请注意,这是一个便利的快捷方式,假设默认的 configure 命令用于配置包。如果使用 configure-command 选项定义自定义配置命令,则不会自动进行 --prefix 注入。您还可以在 configure-options 中显式设置 --prefix 参数。

shared

请参阅 slapos.recipe.build 默认食谱的文档。

md5sum

包文件的 MD5 校验和。如果可用,将下载的包的 MD5 校验和与此值进行比较,如果值不匹配,则食谱执行将失败。

make-binary

make 程序的路径。默认为 ‘make’,这应该在具有系统 PATHmake 程序的任何系统上工作。

make-options

包含在 make 程序调用中的额外 KEY=VALUE 选项。可以通过单独的行给出多个选项以提高可读性。

make-targets

make 命令的目标。默认为 ‘install’,这足以安装大多数软件包。只有当您想要构建其他目标时才需要使用此选项。每个目标必须单独一行给出。

configure-command

运行以生成 Makefile 的配置命令的名称。默认为 ./configure,这对于带有配置脚本的包来说是好的。您可能希望在编译不同设置的包时更改此设置。请参阅 编译 Perl 包 部分。

configure-options

传递给 configure 脚本的额外选项。默认情况下,仅传递 --prefix 选项,该选项设置为部分目录。每个选项必须单独一行给出。

patch-binary

patch 程序的路径。默认为 ‘patch’,这应该在具有系统 PATHpatch 程序的任何系统上工作。

patch-options

传递给 patch 程序的选项。默认为 -p0

patches

应用于提取源代码的补丁文件列表。每个文件应在单独一行给出。

pre-configure-hook

在运行 configure 脚本之前将执行的 Python 脚本。选项的格式为

/path/to/the/module.py:name_of_callable
url:name_of_callable
url#md5sum:name_of_callable

其中,第一部分是文件系统路径或 URL 到 Python 模块,第二部分是该模块中将调用的可调用对象的名称。可调用对象将按以下顺序传递三个参数

  1. 食谱的 options 字典。

  2. 全局 buildout 字典。

  3. 包含当前 os.environ 并增加部分特定覆盖的字典。

可调用对象不需要返回任何内容。

pre-make-hook

在运行 make 之前执行的 Python 脚本。其格式和语义与 pre-configure-hook 选项相同。

post-make-hook

在运行 make 之后执行的 Python 脚本。其格式和语义与 pre-configure-hook 选项相同。

pre-configure

在运行 configure 脚本之前执行的 Shell 命令。它具有与 pre-configure-hook 选项相同的效果,但它是一个 Shell 命令。

pre-build

在运行 make 之前执行的 Shell 命令。它具有与 pre-make-hook 选项相同的效果,但它是一个 Shell 命令。

pre-install

在运行 make install 之前执行的 Shell 命令。

post-install

在运行 make 之后执行的 Shell 命令。它具有与 post-make-hook 选项相同的效果,但它是一个 Shell 命令。

keep-compile-dir

启用选项以保留编译软件包的临时目录。这主要适用于使用此配方编译软件但希望执行此配方未处理的额外步骤的其他配方。编译目录的位置存储在 options['compile-directory'] 中。接受值为 truefalse,默认为 false

promises

列出安装部分之后应存在的路径和文件。文件或路径必须是绝对路径。每行一个项目

如果任何项目不存在,则配方将显示警告消息。默认值为空。

environment

请参阅 slapos.recipe.build 默认食谱的文档。

此外,配方还尊重在 [buildout] 节中设置的 download-cache 选项,并将下载的文件存储在该目录下。如果没有设置值,将在 buildout 的根目录中创建一个名为 downloads 的目录,并将 download-cache 选项相应地设置。

配方将首先检查是否有本地包副本,然后再从网络下载。可以通过将 download-cache 设置为相同的位置来在不同 buildout 之间共享文件。

配方还尊重在 [buildout] 节中设置的 prefix 选项。在 buildout 过程中,所有配方是 slapos.recipe.cmmi 的部分都将安装在与 [buildout] 中的 prefix 选项相同的目录下。此外,一旦生效,配方将返回前缀目录中的所有安装文件。部分的自身 prefix 将禁用此行为。

如果 buildout 节有一个有效的 prefix 选项,则配方将将其添加到环境变量中,如下所示

PATH=${buildout:prefix}/bin:$PATH
CPPFLAGS=-I${buildout:prefix} $CPPFLAGS
CFLAGS=-I${buildout:prefix} $CFFLAGS
CXXFLAGS=-I${buildout:prefix} $CXXFLAGS
LDFLAGS=-L${buildout:prefix}/lib

示例用法

我们将使用几个 tarball 来演示配方。我们将修改其中的一个,但不会修改源树。

>>> import os
>>> 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
>>> package_path = join(tmpdir('testdata'), 'package-0.0.0.tar.gz')
>>> os.symlink(join(src, 'package-0.0.0.tar.gz'), package_path)

该软件包包含一个模拟 configure 脚本,该脚本将简单地输出其被调用的选项,并创建一个 Makefile 做同样的事情。

让我们创建一个 buildout 来构建和安装该软件包。

>>> write('buildout.cfg',
... """
... [buildout]
... newest = true
... parts = package
...
... [package]
... recipe = slapos.recipe.cmmi
... url = file://%s
... """ % package_path)

这将使用默认构建选项下载、提取和构建我们的演示包。

>>> print(system(buildout)) #doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
Installing package.
configure --prefix=/sample_buildout/parts/package
building package
installing package
<BLANKLINE>

检查“promises”选项

>>> write('buildout.cfg',
... """
... [buildout]
... newest = false
... parts = packagex
...
... [packagex]
... recipe = slapos.recipe.cmmi
... url = file://%s
... promises = /usr/bin/myfoo
... """ % package_path)

这将使用默认构建选项下载、提取和构建我们的演示包。

>>> print(system(buildout))
Uninstalling package.
Installing packagex.
configure --prefix=/sample_buildout/parts/packagex
building package
installing package
packagex: could not find promise '/usr/bin/myfoo'
<BLANKLINE>

如我们所见,配置脚本默认使用了--prefix选项,然后是makemake install的调用。

安装Perl包

这个配方可以用来安装使用略有不同构建过程的包。Perl包通常附带一个Makefile.PL脚本,该脚本执行与configure脚本相同的功能,并生成一个Makefile

我们可以通过覆盖configure-command选项来构建和安装这样的包。以下示例构建了一个Foo::Bar perl模块,并将其安装到构建出的自定义位置

>>> write('buildout.cfg',
... """
... [buildout]
... newest = false
... parts = foobar
... perl_lib = ${buildout:directory}/perl_lib
...
... [foobar]
... recipe = slapos.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 packagex.
Installing foobar.
building package
installing package

在没有类似autoconf的系统上安装包

某些包不使用配置机制,而只是提供用于构建的Makefile。在这些情况下,构建过程完全由直接传递给make的选项控制。我们可以通过模拟一个什么都不做的配置命令,并将适当的选项传递给make来构建这样的包。大多数shell环境中的true工具是这种操作的合适候选者,尽管任何返回零退出代码的东西都可以。

我们使用一个虚拟的“HAProxy”包作为只有一个Makefile且使用显式make选项控制构建过程的包的示例。

>>> write('buildout.cfg',
... """
... [buildout]
... newest = false
... parts = haproxy
...
... [haproxy]
... recipe = slapos.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.
Building HAProxy 1.4.8 (dummy package)
TARGET: linux26
CPU: i686
USE_PCRE: 1
Installing haproxy

安装签出

有时,我们不需要下载和构建现有的tarball,而是需要处理已经存在于文件系统中的代码,例如SVN检出。

我们不会提供url选项,而是提供一个指向包含源代码的目录的path选项。

让我们通过首先将我们的测试包解压到文件系统,然后构建它来演示这一点。

>>> checkout_dir = tmpdir('checkout')
>>> import setuptools.archive_util
>>> setuptools.archive_util.unpack_archive(package_path, checkout_dir)
>>> ls(checkout_dir)
d package-0.0.0
>>> write('buildout.cfg',
... """
... [buildout]
... newest = false
... parts = package
...
... [package]
... recipe = slapos.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

由于使用path意味着源代码是在配方控制之外获得的,因此管理它的责任也在配方之外。

根据软件的不同,如果您在构建之间对代码进行了更改,可能需要手动运行make clean等。另外,当使用path时,keep-compile-dir没有作用。

高级配置

上述选项足以构建大多数包。然而,在某些情况下,这还不够,我们需要更多地控制构建过程。让我们尝试一个新的构建,并提供更多的选项。

>>> write('buildout.cfg',
... """
... [buildout]
... newest = false
... parts = package
...
... [package]
... recipe = slapos.recipe.cmmi
... url = file://%s
... md5sum = 6b94295c042a91ea3203857326bc9209
... prefix = /somewhere/else
... environment =
...     CFLAGS=-I/sw/include
...     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
... """ % package_path)

此配置使用自定义配置选项、环境变量、自定义前缀、多个make目标,并在脚本运行之前修补源代码。

>>> print(system(buildout))
Uninstalling package.
Installing package.
package: Applying patches
package: [ENV] CFLAGS = -I/sw/include
package: [ENV] LDFLAGS = -L/sw/lib -L/some/extra/lib
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
<BLANKLINE>

自定义构建过程

有时即使上述操作也不够,你需要能够更详细地控制这个过程。一个这样的用例可能是执行基于构建信息(可能的来自构建)的源代码动态替换(基于静态修补或简单地运行任意命令)。

该配方允许您编写自定义Python脚本,以便将其钩入构建过程。您可以定义一个在配置脚本执行之前运行的脚本

  • (pre-configure-hook)

  • 在make过程执行之前(pre-make-hook)

  • 在make过程完成后(post-make-hook)

每个选项都需要包含以下信息

/full/path/to/the/python/module.py:name_of_callable

其中可调用对象(此处为name_of_callable)应接受三个参数

  1. 食谱的 options 字典。

  2. 全局 buildout 字典。

  3. 包含当前 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!')
...
... """)

并尝试一个新的buildout

>>> write('buildout.cfg',
... """
... [buildout]
... newest = false
... parts = package
...
... [package]
... recipe = slapos.recipe.cmmi
... url = file://%(package_path)s
... pre-configure-hook = %(module)s:preconfigure
... pre-make-hook = %(module)s:premake
... post-make-hook = %(module)s:postmake
... """ % dict(package_path=package_path,
...            module=join(hooks, 'customhandlers.py')))
>>> print(system(buildout))
Uninstalling package.
Installing package.
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!
如果您更喜欢使用shell脚本,那么请尝试以下选项

预配置 预构建 预安装 安装后

让我们创建一个buildout来使用这些选项。

>>> write('buildout.cfg',
... """
... [buildout]
... newest = false
... parts = package
...
... [package]
... recipe = slapos.recipe.cmmi
... url = file://%s
... pre-configure = echo "Configure part: ${:_buildout_section_name_}"
... pre-build = echo "OH OH OH" > a.txt
... pre-install = cat a.txt
... post-install = rm -f a.txt && echo "Finished."
... """ % package_path)

这将作为相应阶段的shell命令运行预配置、预构建、预安装、安装后。

>>> print(system(buildout))
Uninstalling package.
Installing package.
package: Executing pre-configure
Configure part: package
configure --prefix=/sample_buildout/parts/package
package: Executing pre-build
building package
package: Executing pre-install
OH OH OH
installing package
package: Executing post-install
Finished.

联合前缀

如果配方在buildout部分找到prefix选项,它将

  • 首先,如果该部分未设置configure-command,则使用此prefix作为配置前缀,或者如果make-binary等于makemake-target包含模式s+install.*

  • 其次,在配方安装后返回时,返回prefix中的所有新安装文件。

  • 最后,更改一些环境变量(见第一部分)。

让我们看看在buildout部分设置prefix时会发生什么。

>>> write('buildout.cfg',
... """
... [buildout]
... newest = false
... parts = package
... prefix = ${buildout:directory}/mylocal
...
... [package]
... recipe = slapos.recipe.cmmi
... url = file://%s
... pre-configure = mkdir -p "${buildout:prefix}"
... """ % package_path)
>>> print(system(buildout))
Uninstalling package.
Installing package.
package: Executing pre-configure
configure --prefix=/sample_buildout/mylocal
building package
installing package
<BLANKLINE>

查看这些环境变量和prefix的值,您就会知道它们之间的区别。

如果部分有自己的prefix,则将禁用上述行为。例如,

>>> write('buildout.cfg',
... """
... [buildout]
... newest = false
... parts = package
... prefix = ${buildout:directory}/mylocal
...
... [package]
... recipe = slapos.recipe.cmmi
... prefix = ${buildout:parts-directory}/package
... url = file://%s
... pre-configure = rm -rf "${buildout:prefix}"
... post-install = test -d "${buildout:prefix}" || echo "None"
... """ % package_path)
>>> print(system(buildout))
Uninstalling package.
Installing package.
package: Executing pre-configure
configure --prefix=/sample_buildout/parts/package
building package
installing package
package: Executing post-install
None

则不会创建额外的环境变量,如CFLAGS等,也不会创建${buildout:prefix}目录。

以下示例展示了如何在同一个prefix中安装包package-1和package-2。

>>> write('buildout.cfg',
... """
... [buildout]
... newest = false
... parts = package package-2
... prefix = ${buildout:directory}/mylocal
...
... [package]
... recipe = slapos.recipe.cmmi
... url = file://%s
... pre-install = sleep 2; mkdir -p "${buildout:prefix}" ; echo x >"${buildout:prefix}/a.txt"
... [package-2]
... recipe = slapos.recipe.cmmi
... url = file://%s
... pre-install = sleep 2; mkdir -p "${buildout:prefix}" ; echo x >"${buildout:prefix}/b.txt"; echo
... """ % (package_path, package_path))
>>> print(system(buildout))
Uninstalling package.
Installing package.
configure --prefix=/sample_buildout/mylocal
building package
package: Executing pre-install
installing package
Installing package-2.
configure --prefix=/sample_buildout/mylocal
building package
package-2: Executing pre-install
<BLANKLINE>
installing package
<BLANKLINE>
>>> ls('mylocal')
- a.txt
- b.txt

接下来,我们将卸载package-2,它应该只删除文件b.txt(目前看起来已损坏,因为它没有删除任何内容)

>>> write('buildout.cfg',
... """
... [buildout]
... newest = false
... parts = package
... prefix = ${buildout:directory}/mylocal
...
... [package]
... recipe = slapos.recipe.cmmi
... url = file://%s
... pre-install = sleep 2; mkdir -p "${buildout:prefix}" ; echo x >"${buildout:prefix}/a.txt"
... """ % package_path)
>>> print(system(buildout))
Uninstalling package-2.
Updating package.
>>> ls('mylocal')
- a.txt
- b.txt

魔法前缀

如果设置了configure-command,则配方不会将“–prefix”插入到configure-options中。然后它检查是否设置了make-binary和make-targets,如果没有设置,则将在make-targets中追加字符串“prefix=xxx”。xxx是此配方的最终prefix。我们称之为魔法prefix。

在这些选项中,魔法prefix可以用%(prefix)s表示。

configure-commandconfigure-optionsmake-binarymake-optionsmake-targetspre-configurepre-buildpre-installpost-install

例如

[bzip2]
post-install = rm %(prefix)s/*.h

其他部分可以通过${part:prefix}引用此部分的魔法prefix,它将返回魔法prefix,而不是部分部分中的字面值。例如

>>> write('buildout.cfg',
... """
... [buildout]
... newest = false
... parts = package package-2
... prefix = /mytemp
...
... [package]
... recipe = slapos.recipe.cmmi
... url = file://%s
... configure-command = true
... make-binary = true
...
... [package-2]
... recipe = slapos.recipe.cmmi
... url = file://%s
... configure-command = true
... make-binary = true
... post-install = echo package magic prefix is ${package:prefix}
... """ % (package_path, package_path))

>>> print(system(buildout))
Uninstalling package.
Installing package.
Installing package-2.
package-2: Executing post-install
package magic prefix is /mytemp
<BLANKLINE>

这里是一个另一个示例,我们在安装之前更改Makefile,以便它可以在stdout中显示“prefix”值。

>>> write('buildout.cfg',
... """
... [buildout]
... newest = false
... parts = package
...
... [package]
... recipe = slapos.recipe.cmmi
... url = file://%s
... configure-command = ./configure
... pre-install = sed -i -e "s/installing package/installing package at \\$\\$prefix /g" Makefile
... """ % package_path)
>>> print(system(buildout))
Uninstalling package-2.
Uninstalling package.
Installing package.
configure
building package
package: Executing pre-install
installing package at /sample_buildout/parts/package

您甚至可以将模式%(prefix)s包含在此选项中,它将被替换为配方的最终prefix。

>>> write('buildout.cfg',
... """
... [buildout]
... newest = false
... parts = package
...
... [package]
... recipe = slapos.recipe.cmmi
... url = file://%s
... configure-command = ./configure
... make-targets = install-lib prefix=%%(prefix)s
... pre-install = sed -i -e "s/installing package/installing package at \\$\\$prefix /g" Makefile
... """ % package_path)
>>> print(system(buildout))
Uninstalling package.
Installing package.
configure
building package
package: Executing pre-install
installing package at /sample_buildout/parts/package -lib

对于更具体的需求,您可以编写自己的配方,该配方使用slapos.recipe.cmmi,并将keep-compile-dir选项设置为true。然后您可以从此配方完成的地方继续,通过从您自己的配方中的options['compile-directory']读取编译目录的位置。

贡献者

  • Kai Lautaportti (dokai),作者

  • Cédric de Saint Martin (desaintmartin)

  • Marc Abramowitz (msabramo)

  • Nicolas Dumazet (nicdumz)

  • Guy Rozendorn (grzn)

  • Marco Mariani (mmariani)

  • galpin

下载

项目详情


下载文件

下载适合您平台的文件。如果您不确定选择哪个,请了解有关安装包的更多信息。

源分发

slapos.recipe.cmmi-0.22.tar.gz (36.1 kB 查看哈希值)

上传时间

由以下支持

AWS AWS 云计算和安全赞助商 Datadog Datadog 监控 Fastly Fastly CDN Google Google 下载分析 Microsoft Microsoft PSF 赞助商 Pingdom Pingdom 监控 Sentry Sentry 错误日志 StatusPage StatusPage 状态页面