如何用 virtualenv_install_with_resources 把一个 Python 应用打包成 Homebrew formula?
【免费下载链接】brew🍺 The Package Manager for Everywhere项目地址: https://gitcode.com/GitHub_Trending/br/brew
如果你要把一个提供命令行界面的 Python 应用做成 Homebrew formula,让终端用户brew install之后直接运行,而不需要知道它是用 Python 写的、更不用手动改sys.path,那么 docs/Language-Specific-Formulae.md 给出的标准做法是:在 formula 里include Language::Python::Virtualenv,在install阶段调用virtualenv_install_with_resources,并用brew update-python-resources生成依赖的 resource 块。下面的路径假设你要打包的是一个 PyPI 上的 Python 应用(普通可 import 的库一般不满足收录条件,见 Acceptable Formulae),且已安装 Homebrew。
一、准备本地编辑 formula 的环境
在写 formula 之前,先让 Homebrew 支持本地编辑 formula(命令来自 Formula Cookbook):
brew update brew tap homebrew/core之后在 shell 环境里设置HOMEBREW_NO_INSTALL_FROM_API=1(或写在每条install/reinstall/upgrade命令前面),强制 Homebrew 使用本地的 homebrew/core 仓库而不是 API。下文所有安装、测试命令都带上这个环境变量。
二、生成 formula 骨架并改成标准应用布局
用上游发布页上的源 tarball 地址生成骨架:
brew create https://files.pythonhosted.org/packages/.../foo-1.0.tar.gz如果 Homebrew 报Warning: Version cannot be determined from URL,需要在 formula 里显式补充version;如果猜出的名字不对,用brew create <URL> --set-name <name>覆盖。
brew create生成的文件里,以下字段是硬要求,缺失会导致 audit 不通过:
desc:一句话说明公式做什么;homepage:SSL/TLS 优先,且不接受没有 homepage 的 formula;license:使用 SPDX 标识符,例如license "BSD-2-Clause";url/sha256:不可变、带校验和的源;depends_on "python@3.y":3.y是文档中的占位写法,表示homebrew/core当前使用的版本化 Python formula,实际要填当前 minor 版本(见文末限制)。
改完后的公式结构与 docs/Language-Specific-Formulae.md 的文档示例一致(示例中的 URL 与 SHA-256 是占位值,需要你换成自己的源地址和真实校验和):
class Foo < Formula include Language::Python::Virtualenv desc "Example Python command-line application" homepage "https://example.com/foo" url "https://files.pythonhosted.org/packages/.../foo-1.0.tar.gz" sha256 "abc123abc123abc123abc123abc123abc123abc123abc123abc123abc1" depends_on "python@3.y" resource "dependency" do url "https://files.pythonhosted.org/packages/.../dependency-1.2.3.tar.gz" sha256 "abc123abc123abc123abc123abc123abc123abc123abc123abc123abc1" end def install virtualenv_install_with_resources end test do assert_match version.to_s, shell_output("#{bin}/foo --version") end endvirtualenv_install_with_resources的行为在 language/python.rb 中实现,它做三件事:在libexec下创建虚拟环境;把 formula 里声明的所有resource装进该环境;最后安装 formula 本体,并把虚拟环境bin中新增的命令链接进 formula 的bin。也就是说命令链接不需要你手工处理。
三、用 update-python-resources 生成 resource 块
所有没有由其他 formula 提供的 Python 模块依赖(含递归依赖)都必须声明为resource,把每个源的版本和 SHA-256 留在 formula 里——Homebrew 的 pip 辅助函数会以禁用依赖解析的方式安装,保证依赖集合可复现。用官方命令生成或更新这些块:
brew update-python-resources foo按需调整:
--print-only:只打印结果,不修改 formula,适合先预览;--ignore-errors:记录所有发现的 resource,对无法解析的项留下RESOURCE-ERROR注释。该选项只对第三方 tap 有效,所有官方 Homebrew tap 都要求完整解析,此选项会被忽略;--install-dependencies:安装更新 resource 所缺少的依赖;--version=:按指定版本查找 resource,默认使用 formula 当前版本;--package-name=:指定查找用的包名,默认从 formula 的 stable URL 推断;--extra-packages/--exclude-packages:逗号分隔,在查找时加入或排除额外包。
这些命令行选项对应的持久化写法是 formula 里的pypi_packagesstanza,记录应随 formula 保存的解析器配置:package_name用于 formula 名或 URL 不能识别正确 PyPI 包的情况,extra_packages用于额外的依赖根,exclude_packages用于由其他 formula 提供的包,dependencies用于解析 resource 期间必须先安装的其他 formula:
pypi_packages package_name: "upstream-name", extra_packages: "extra-package", exclude_packages: "package-from-homebrew", dependencies: "resolver-dependency"生成完成后,按文档要求核对生成的 URL 与校验和。
四、写测试并跑 audit
test do块会被brew test foo和 BrewTestBot 执行。共享要求里写明:测试要演练安装后的真实行为,而不是只检查可执行文件存在。Language-Specific-Formulae 给出的是断言版本输出的写法(文档示例,实际命令换成你应用的入口名):
test do assert_match version.to_s, shell_output("#{bin}/foo --version") end注意 Formula Cookbook 同时指出foo --version这类测试偏弱;如果你的应用有真实可运行的功能(如foo build-foo input.foo),优先写功能测试。Cookbook 提到过标准测试 fixture 和从test阶段用resource安装测试文件等方式,可按需参考。
然后运行新 formula 必须通过的审计:
HOMEBREW_NO_INSTALL_FROM_API=1 brew audit --new --formula foobrew audit会把版本猜测结果等问题一并暴露;用brew info foo核对 Homebrew 从 URL 猜出的版本是否正确,不正确就补显式version。
五、本地安装验证
HOMEBREW_NO_INSTALL_FROM_API=1 brew install --build-from-source --verbose --debug foo失败时--debug会提示你打开交互式 shell 排查;构建阶段还可以用HOMEBREW_NO_INSTALL_FROM_API=1 brew install --build-from-source --interactive foo在解压后的沙箱里检查上游 README 和构建方式。安装成功后的验证点:
- 应用的命令已链接进 formula 的
bin(virtualenv_install_with_resources自动完成),按 Cookbook 的说法$(brew --prefix)/bin在构建期间不在 PATH 里,运行时通过 Homebrew 的链接可见; brew test foo通过;- 虚拟环境位于
libexec下,不污染用户的全局语言环境——这正是共享要求中“应用依赖必须装在 formula prefix 内”的落点。
六、可选分支:调整资源顺序与额外安装步骤
virtualenv_install_with_resources支持start_with:、end_with:和without:关键字,只在 resource 的安装顺序或选取不同时使用。三个关键字都接受 resource 名字符串或数组,安装顺序为start_with列出的资源、其余资源、end_with列出的资源,without列出的资源被跳过。仓库内测试(virtualenv_spec.rb)验证了组合行为:without: "resource-c", start_with: "resource-d", end_with: "resource-b"时按[resource-d, resource-c, resource-b]顺序安装。传入的 resource 名必须已在 formula 中声明,否则会抛ArgumentError。
如果标准布局不够、还需要额外的安装步骤(比如显式调用pip_install或pip_install_and_link),文档建议直接改用virtualenv_create,它创建虚拟环境后把控制权交给你。
限制与后续维护
- Python 依赖必须声明为版本化 formula(示例中的
python@3.y),不能依赖贡献者机器上恰好有的 Python;homebrew/core切换到新的 Python minor 版本时,依赖要跟着更新。 - 每个 resource 都带有不可变 URL 和 SHA-256,Homebrew 的 pip 辅助函数以禁用依赖解析的方式安装,所以依赖集合由 formula 本身锁定,而不是运行时解析。
- 不要允许应用安装后在运行时自动下载其运行所需的代码(共享要求,Language-Specific-Formulae)。
--ignore-errors、--ignore-main-package-cooldown只对第三方 tap 生效;官方 tap 要求完整解析。- 提交前还需满足 Acceptable Formulae 与 Package-Acceptance-Policy.md:用
brew search <formula>确认未被收录、确认没有重复的 PR 在途。
更多构建系统的模板选项见brew create --help(如--python直接生成 Python 构建模板,且brew create对 PyPI 包会顺带跑一遍 resource 解析),完整 formula API 参考 Formula Cookbook。
【免费下载链接】brew🍺 The Package Manager for Everywhere项目地址: https://gitcode.com/GitHub_Trending/br/brew
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考