
nixpkgs bmake 钩子指南在 Nixpkgs 构建阶段中驾驭 NetBSD 风格的可移植 make【免费下载链接】nixpkgsNix Packages collection NixOS项目地址: https://gitcode.com/GitHub_Trending/ni/nixpkgs本文基于 nixpkgs 的 hooks 参考文档doc/hooks/bmake.section.md围绕 bmake 钩子展开介绍 bmake 这个 NetBSDmake可移植变体在 Nixpkgs 中的定位逐行剖析 setup-hook.sh 对 build、check、install、dist 四个阶段的覆盖实现并结合 bmake 包定义 说明补丁、测试策略与 musl 适配细节。读完后你能掌握如何为使用 bmake 的项目编写派生包、各dontUseBmake*开关的作用机制以及 hook 自动探测测试目标的原理。什么是 bmakebmake 是 NetBSDmake工具的可移植portable变体源自 crufty.net 的 sjg 版本。它遵循 BSD 风格的Makefile语法bsd.mk体系与 GNU make 在语法、变量修改算子和内置变量上均有差异因此许多源自 BSD 系统的软件无法直接用 GNU make 构建。文档原文明确指出在 Nixpkgs 中bmake附带一个钩子hook用于覆盖默认的 build、check、install 和 dist 阶段。这个钩子就定义在包目录内的 setup-hook.sh 中通过 Nixpkgs 标准的setupHook机制package.nix在派生包构建时自动注入。bmake包本身的关键元数据见 package.nix版本20260313以fetchurl从上游 tarball 抓取并固定 sha256L12-L18outputs [ out man ]man 页单独输出L29-L32strictDeps true声明严格的依赖跟踪L72doCheck true且通过nativeCheckInputs [ bc tzdata ]提供测试所需环境L38-L41meta.license lib.licenses.bsd3meta.platforms lib.platforms.unix不构建静态平台L123-L132。钩子如何覆盖默认阶段Nixpkgs 的 stdenv 允许构建系统包通过setupHook变量在setup.sh执行末尾追加自定义逻辑。bmake的 setup-hook.sh 末尾L97-L117正是这一机制的体现preConfigureHooks(addMakeFlags) if [ -z ${dontUseBmakeBuild-} ] [ -z ${buildPhase-} ]; then buildPhasebmakeBuildPhase fi if [ -z ${dontUseBmakeCheck-} ] [ -z ${checkPhase-} ]; then checkPhasebmakeCheckPhase fi if [ -z ${dontUseBmakeInstall-} ] [ -z ${installPhase-} ]; then installPhasebmakeInstallPhase fi if [ -z ${dontUseBmakeDist-} ] [ -z ${distPhase-} ]; then distPhasebmakeDistPhase fi覆盖逻辑遵循 Nixpkgs 钩子的通用约定只有当用户既没有设置dontUseBmakeX关闭开关、也没有自行覆盖对应xPhase变量时钩子才会接管该阶段。这意味着你在自己的派生包中写buildPhase ...钩子会自动退让不会破坏自定义构建流程。addMakeFlagsconfigure 阶段的环境准备钩子通过preConfigureHooks(addMakeFlags)把addMakeFlags挂入 configure 阶段之前L97。该函数L3-L9导出 bmake 安装逻辑所需的 BSD 风格变量addMakeFlags() { export prefix$out # 安装前缀指向 Nix 输出 export MANDIR${!outputMan}/share/man # man 页输出到 man 输出indirect expansion export MANTARGETman export BINOWN # 不设属主Nix store 中不需要 export STRIP_FLAG # 关闭 bmake 内置的 strip 行为 }这里利用 bash 的间接变量展开${!outputMan}当派生包只输出out时MANDIR指向$out/share/man而当包声明了man输出时则自动指向$man/share/man与 bmake 自身的outputs设计保持一致。bmakeBuildPhase并行构建bmakeBuildPhase 的完整实现bmakeBuildPhase() { runHook preBuild local flagsArray( ${enableParallelBuilding:-j${NIX_BUILD_CORES}} SHELL$SHELL ) concatTo flagsArray makeFlags makeFlagsArray buildFlags buildFlagsArray nixInfoLog ${FUNCNAME[0]}: flagsArray: ${flagsArray[]} bmake ${makefile:-f $makefile} ${flagsArray[]} runHook postBuild }要点遵循 stdenv 的runHook preBuild/postBuild扩展点用户的preBuild/postBuild字符串钩子依然生效enableParallelBuilding为真时自动传入-j${NIX_BUILD_CORES}启用并行构建显式传SHELL$SHELL保证 bmake 子进程使用 Nix 构建环境的 shellbmake 的默认 shell 探测在沙箱中不可靠通过concatTo把 stdenv 通用的makeFlags与 bmake 专属的buildFlags合并支持 stdenv 的makefile变量${makefile:-f $makefile}允许指定非默认的 Makefile 文件名。bmakeCheckPhase自动探测 check/test 目标check 阶段L26-L52体现了 Nixpkgs 一贯的“可探测、可配置、无测试则跳过”策略目标探测若未设置checkTarget先用bmake -ndry-run不实际执行依次探测check和test目标是否存在if [ -z ${checkTarget:-} ]; then if bmake -n ${makefile:-f $makefile} check /dev/null 21; then checkTargetcheck elif bmake -n ${makefile:-f $makefile} test /dev/null 21; then checkTargettest fi fi无目标时跳过若两个目标都不存在仅打印nixInfoLog no test target found in bmake, doing nothingcheck 阶段直接通过避免构建失败有目标时执行合并makeFlags、checkFlags额外强制VERBOSEy以保留测试输出日志并支持enableParallelChecking触发-j${NIX_BUILD_CORES}concatTo flagsArray makeFlags makeFlagsArray checkFlagsVERBOSEy checkFlagsArray checkTarget bmake ${makefile:-f $makefile} ${flagsArray[]}因此在自己的派生包中只需checkTarget test或别的目标名即可精确控制测试目标无需自定义checkPhase。bmakeInstallPhase 与 bmakeDistPhase安装阶段L54-L71默认安装install目标并支持installTargets覆盖bmakeInstallPhase() { runHook preInstall if [ -n $prefix ]; then mkdir -p $prefix fi local flagsArray( ${enableParallelInstalling:-j${NIX_BUILD_CORES}} SHELL$SHELL ) concatTo flagsArray makeFlags makeFlagsArray installFlags installFlagsArray installTargetsinstall bmake ${makefile:-f $makefile} ${flagsArray[]} runHook postInstall }$prefix由前面的addMakeFlags设为$out因此 bmake 的 BSD 式PREFIX安装语义被无缝映射到 Nix 输出目录。dist 阶段L73-L95负责为使用dist目标发布源码归档的包做处理执行dist目标后把生成的 tarball 复制到$out/tarballsif [ ${dontCopyDist:-0} ! 1 ]; then mkdir -p $out/tarballs # 允许通配符刻意不加引号 cp -pvd ${tarballs:-*.tar.gz} $out/tarballs fi两个控制开关dontCopyDist 1可跳过复制tarballs变量可指定要复制的具体归档支持通配符。派生包中的典型用法结合上述 hook 行为一个使用 bmake 构建的派生包写法为{ stdenv, fetchurl, bmake }: stdenv.mkDerivation (finalAttrs: { pname my-bmake-package; version 1.0; src fetchurl { url https://example.org/my-bmake-package-${finalAttrs.version}.tar.gz; sha256 …; }; nativeBuildInputs [ bmake ]; # 引入 bmake 即自动激活 hook # 可选指定自定义 Makefile # makefile bsd/Makefile; # 可选精确指定 check 目标否则 hook 自动探测 check/test # checkTarget test; # 可选只关闭某个阶段的覆盖 # dontUseBmakeInstall true; meta { description Example package built with bmake; license lib.licenses.bsd3; }; })关键点是把bmake加入nativeBuildInputs后package.nix 声明的setupHook ./setup-hook.sh会在 stdenv 初始化时执行四个阶段即被自动覆盖。如需退回 GNU make 行为或自定义某阶段直接设置对应的dontUseBmakeX开关或xPhase变量即可两者任一生效都会阻止钩子接管。bmake 自身的构建与测试策略bmake 包定义 本身就展示了该 hook 相关变量的实际使用场景可作为编写派生包时的参考样板三个本地补丁L20-L27002-dont-test-while-installing.diff把测试与安装解耦003-fix-unexport-env-test.diff修复单元测试中 PATH 被导出的问题004-unconditional-ksh-test.diff始终启用 ksh 测试。对应补丁文件位于 002-dont-test-while-installing.diff、003-fix-unexport-env-test.diff、004-unconditional-ksh-test.diff--without-makefileL47-L49不生成会覆盖随附 Makefile 的包装 Makefile避免在大小写不敏感文件系统上引发测试死循环BROKEN_TESTS白名单L56-L68显式列出因环境而异的失败用例如cmd-interrupt、TZ 相关的varmod-localtime在 musl/Darwin 上还会追加musl 适配L80-L85删除deptgt-delete_on_error、shell-ksh两个测试用例并修正strerror(3)输出差异导致的断言自定义 build/install/check 阶段L87-L113bmake 自身通过./boot-strap自举构建其中installPhase特意不用boot-strap opinstall它会运行新构建的 bmake破坏交叉编译而是直接install二进制、man 页与mk/系统宏目录。bmake 在 Nixpkgs 中的其他出现除钩子本身外bmake 还在 Nixpkgs 内部另有两处用途从源码可以直接确认Darwin stdenvpkgs/stdenv/darwin/default.nix 在构建 locale 时注入bmakesuperDarwin.locale.override { inherit (prevStage) bmake; }注释说明目的是“避免仅因 locale 变化而重新构建 bmake 和 Python”bmake也列入了 darwin stdenv 的工具集L191FreeBSD 系统工具pkgs/os-specific/bsd/freebsd/pkgs/make.nix 以path contrib/bmake从 FreeBSD 源码树构建版本 9.2 的 make并安装share/mk系统宏extraPaths [ share/mk ]。注意这与本文讨论的 crufty 可移植 bmake 是两条独立的构建路径前者服务于 FreeBSD 二进制派生包。小结特性说明源码依据覆盖阶段build / check / install / distsetup-hook.sh L99-L117关闭开关dontUseBmakeBuild/dontUseBmakeCheck/dontUseBmakeInstall/dontUseBmakeDist同上check 目标自动探测check、test可用checkTarget指定setup-hook.sh L26-L52并行构建enableParallelBuilding/enableParallelChecking/enableParallelInstalling分别控制-jsetup-hook.sh L15, L42, L62安装前缀addMakeFlags将prefix、MANDIR映射到$out/$mansetup-hook.sh L3-L9dist 归档复制到$out/tarballsdontCopyDist/tarballs控制setup-hook.sh L73-L95bmake 钩子遵循 Nixpkgs hooks 参考doc/hooks/index.md中“增强 stdenv 阶段”的设计范式包声明setupHook钩子在尊重用户自定义的前提下接管默认阶段并通过dontUse*开关与xTarget变量提供细粒度控制。对于维护 BSD 系软件包的开发者而言这套机制让bmake的使用体验与 Nixpkgs 中其他构建系统钩子如 cmake、meson保持了行为上的一致性和可预期性。【免费下载链接】nixpkgsNix Packages collection NixOS项目地址: https://gitcode.com/GitHub_Trending/ni/nixpkgs创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考