完全指南:从默认安装限制到声明式包管理)
Nixpkgs 全局配置config.nix完全指南从默认安装限制到声明式包管理【免费下载链接】nixpkgsNix Packages collection NixOS项目地址: https://gitcode.com/GitHub_Trending/ni/nixpkgs导读Nixpkgs 根据包的元数据meta对能装什么、不能装什么有一套默认策略损坏broken、平台不支持unsupported、许可证非自由unfree、存在已知安全漏洞insecure的包默认都被拒绝安装。本文基于 Nixpkgs 手册的《Global configuration》章节结合仓库中的 配置选项定义、元数据检查实现 与 problems 机制实现 等源码系统讲解用户级配置文件~/.config/nixpkgs/config.nix的查找规则、各类允许安装开关环境变量 / 配置项 / 谓词函数、problems 处理机制以及如何用packageOverrides实现声明式包管理。读完本文你将能精准掌控本机 Nixpkgs 的安装策略并能搭建一套属于自己的声明式用户环境。默认安装限制Nix 为何拒绝某些包Nix 本身带有关于哪些包可以安装、哪些包不可以安装的默认判断依据是包的元数据。默认情况下只要满足以下任一条件Nix 就会阻止安装包被认为已损坏meta.broken被设置为true包不适用于当前系统meta.platforms中没有与当前系统匹配的条目包的meta.license属于被视为非自由unfree的许可证包存在已知安全漏洞且因故无法或尚未更新其meta.knownVulnerabilities中记录了一系列问题包存在必须被用户知晓的问题例如弃用deprecation通告。注意以上所有检查在求值evaluation阶段就已执行且检查范围包括一切被求值的包。特别是所有构建期依赖也会被检查。这五类限制的每一条都可以在 Nixpkgs 配置中被调整。其中包存在问题这一类第 5 条对应的正是本仓库中 problems.nix 所实现的meta.problems机制将在后文详述。从源码看上述默认策略的落地位置在 check-meta.nix 的checkValidity函数中第 343 行起它会依次检查非自由许可证unfree、许可证黑名单blocklisted、非源码构建non-source、平台不支持unsupported与不安全insecure一旦命中即返回带reason与msg的错误结构而assertValidity第 656 行起则把checkValidity的结果与 problems 检查结果合并最终决定包的valid状态是yes、warn还是no。用户配置文件的位置与查找顺序用户的 Nixpkgs 配置存放在用户专属的配置文件~/.config/nixpkgs/config.nix中。最简单的例子{ allowUnfree true; }::: {.caution} 非自由软件在 Nixpkgs 持续集成CI中不会被测试或构建因此也没有缓存。大多数非自由许可证禁止执行或分发该软件启用前请自行确认许可条款。 :::NIXPKGS_CONFIG环境变量可以覆盖配置文件的位置。Nixpkgs 按以下顺序解析配置$NIXPKGS_CONFIG若已设置且文件存在~/.config/nixpkgs/config.nix若存在~/.nixpkgs/config.nix旧式路径若存在空配置{}。这段查找逻辑在 pkgs/top-level/impure.nix 中直接对应它依次读取builtins.getEnv NIXPKGS_CONFIG、homeDir /.config/nixpkgs/config.nix、homeDir /.nixpkgs/config.nix注释里明确标注了 obsolete逐一用builtins.pathExists判断后import第一个存在的文件全部不存在则回退为{ }。几点需要特别注意的适用范围在 NixOS 上NIXPKGS_CONFIG系统级地指向/etc/nix/nixpkgs-config.nix。把配置文件放到该路径即可让nix-env、nix-shell等用户级命令生效NixOS不会自动创建这个文件NixOS 模块系统中的nixpkgs.config选项不影响nix-env、nix-shell等用户级命令这套查找规则仅适用于非 flake 用法channel 与nixpkgs。Flakes 会忽略该查找需要在import nixpkgs时直接传入config例如import nixpkgs { config { allowUnfree true; }; }。从源码结构可以确认用户配置最终会被并入 pkgs/top-level/config.nix 所声明的config选项中。该文件为整个config定义了模块化结构声明了allowUnfree、allowBroken、allowUnsupportedSystem、problems等全部选项并允许通过freeformType接受未声明的键配合warnUndeclaredOptions可对未声明选项发出警告。安装损坏broken的包有几种方式可以尝试编译被标记为 broken 的包。方式一单次允许环境变量——为某一次 nix 工具调用放行$ export NIXPKGS_ALLOW_BROKEN1方式二按包名永久放行problems.handlers——在用户配置文件中为特定包设置对应问题的处理方式{ problems.handlers.hello.broken warn; # 或 ignore }方式三全局永久放行配置项——在用户配置文件中添加{ allowBroken true; }从 config.nix 的选项定义看allowBroken的默认值是false其defaultText为false || builtins.getEnv NIXPKGS_ALLOW_BROKEN 1——也就是说环境变量NIXPKGS_ALLOW_BROKEN1实际等效于运行时把该选项置真。这一判断的getEnv部分在 problems.nix 的broken自动问题条件中实现config.allowBroken || builtins.getEnv NIXPKGS_ALLOW_BROKEN 1。同文件还指出旧选项config.allowBrokenPredicate已弃用建议改用config.problems.handlers.我的包.broken warn对单个包放行。仓库测试 pkgs/test/problems/cases/allow-broken-env 验证了这一机制其default.nix声明了一个meta.broken true的包而env.nix设置NIXPKGS_ALLOW_BROKEN 1测试期望的 stderr 中不再出现拒绝求值的报错证明环境变量确实生效。安装平台不支持unsupported的包同样有两种方式尝试编译在给定系统上被标记为不支持的包。方式一单次允许环境变量$ export NIXPKGS_ALLOW_UNSUPPORTED_SYSTEM1方式二永久允许配置项{ allowUnsupportedSystem true; }不支持与损坏之间的界限确实有些模糊。如果一个程序理应能在某平台上工作却没有那么该平台应被包含进meta.platforms但包应被标记为 broken例如meta.broken !hostPlatform.isWindows。当然什么叫理应最终由包维护者决定。在 check-meta.nix 中hasUnsupportedPlatform第 140 行起给出了精确判定当allowUnsupportedSystem为真时恒返回 false否则检查pkg.meta.platforms是否包含当前hostPlatform.system先做快速字符串包含判断再用platformMatch做属性集匹配以及pkg.meta.badPlatforms是否命中当前平台。meta.badPlatforms是与meta.platforms互补的明确禁止的平台字段同样来自 metaTypes 定义 中的badPlatforms platforms;。安装非自由unfree的包Nixpkgs 的用户都是自由软件用户很多人包括开发者希望严格控制自己对非自由软件的暴露与此同时许多人确实需要或希望运行某些专有软件。Nixpkgs 为此也收录了一些非自由软件包的表达式。默认情况下非自由软件无法安装也不会出现在搜索结果中。有几种方式可以调整 Nix 对非自由包的处理策略。方式一临时放行全部非自由包环境变量$ export NIXPKGS_ALLOW_UNFREE1方式二按谓词永久放行个别非自由包allowUnfreePredicate——保持默认阻止非自由包的同时放行特定包。该选项是一个接收包为参数、返回布尔值的函数。下面这个配置接受包并恒返回 false即不允许任何非自由包{ allowUnfreePredicate (pkg: false); }更有用的例子——只放行名为 roon-server 和 Visual Studio Code 的非自由包{ allowUnfreePredicate pkg: builtins.elem (lib.getName pkg) [ roon-server vscode ]; }方式三按许可证清单放行/阻止allowlistedLicenses 与 blocklistedLicenses下面的配置把amd和wtfpl许可证加入允许清单{ allowlistedLicenses with lib.licenses; [ amd wtfpl ]; }下面的配置把gpl3Only和agpl3Only许可证加入阻止清单{ blocklistedLicenses with lib.licenses; [ agpl3Only gpl3Only ]; }需要注意allowlistedLicenses只作用于非自由许可证除非同时启用allowUnfree它并不是对所有许可证类型的通用白名单而blocklistedLicenses作用于所有许可证。许可证的完整清单可在仓库的 lib/licenses/licenses.nix 中查阅。在 check-meta.nix 的实现中这些选项的优先级逻辑清晰可见hasDeniedUnfreeLicense第 178 行起当allowUnfree含环境变量为真时恒返回 false否则依次考虑config.allowUnfreePackages按lib.getName匹配的名字清单与config.allowUnfreePredicate两个谓词只要包名命中其中之一即放行即使包被判定为 unfree若nonEmptyAllowList hasAllowlistedLicense attrs成立即许可证在允许清单中则仍可通过第 372 行的checkValidity分支;areLicenseListsValid第 90 行强制要求允许清单与阻止清单互斥否则直接throw从机制上杜绝了配置自相矛盾。此外config.nix 还提供了与allowUnfreePredicate互补的allowUnfreePackages选项默认[ ]它以加法方式合并便于在多个模块中就近声明需要放行的非自由包而无需集中声明或全局开启allowUnfree。安装不安全insecure的包有几种方式可以调整 Nix 对被标记为不安全的包的处理策略。方式一临时放行全部不安全包环境变量$ export NIXPKGS_ALLOW_INSECURE1方式二按名字永久放行个别不安全包permittedInsecurePackages——下面的配置允许安装假设的不安全包hello的1.2.3版本{ permittedInsecurePackages [ hello-1.2.3 ]; }方式三自定义安全策略allowInsecurePredicate——与allowUnfreePredicate类似它是一个接收包并返回布尔值的函数。下面的配置放行ovftool包的任何版本{ allowInsecurePredicate pkg: builtins.elem (lib.getName pkg) [ ovftool ]; }注意只有未指定allowInsecurePredicate时permittedInsecurePackages才会被检查。源码层面的判定逻辑在 check-meta.nix 的hasDisallowedInsecure第 202 行起allowInsecure环境变量为真则全部放行否则若配置了allowInsecurePredicate则只放行谓词返回 true 的包若配置了permittedInsecurePackages则内部把它编译成一个elem (getNameWithVersion x) permittedInsecurePackages的谓词注意这里匹配的是带版本的完整名字如hello-1.2.3两者都未配置时只要meta.knownVulnerabilities非空即拒绝。isMarkedInsecure第 162 行则定义了不安全的判定标准attrs ? meta.knownVulnerabilities attrs.meta.knownVulnerabilities ! []。仓库测试 pkgs/test/config.nix 专门验证了一个回归场景permittedInsecurePackages必须允许使用pkgs获取部分信息该测试用builtins.seq pkgs.glibc.version [ ]构造清单确认了此选项在求值期间的可用性边界。存在问题的包Packages with problems一个包可能关联多个问题problem。问题既可以手动声明在meta.problems中也可以根据包的其他meta属性自动生成。每个问题有一个名字、一种kind、一条消息以及可选的 URL 列表。并非所有 kind 都能在meta.problems中手动指定某些 kind 每个包至多只能出现一次。目前已知的问题 kind 如下未来还会预留更多removal该包计划在将来某个时间被移除。唯一unique每个包至多一个deprecated该包依赖的软件已到达生命周期终点end of lifemaintainerless当meta.maintainers []时自动生成。唯一不可手动指定broken当meta.broken true时自动生成。每个问题都有一个处理它的 handler取值可为error、warn或ignore。error将禁止求值该包warn则仅在日志中打印一条消息。在 problems.nix 中kinds属性集给出了每个 kind 的完整定义例如maintainerless的自动生成条件是meta.maintainers与meta.teams均为空、不是固定输出派生FOD即无outputHash、且有meta.description——后两个启发式条件用于避免误报 fetcher 等内部派生文件注释明确说明定义outputHash即 FOD如 fetcher 的输出未定义description的大概率不是包broken的自动条件即meta.broken为真且未被allowBroken/ 环境变量放行removal与deprecated没有自动条件automatic null只能手动声明。manualKinds/uniqueKinds则分别过滤出允许手动声明的 kind 与必须唯一的 kind供meta.problems的类型校验使用。为具体问题指定 handler特定包、特定问题的 handler 可以用如下语法指定config.problems.handlers.${packageName}.${problemName} ${handler};例如把hello包的broken问题降级为警告{ problems.handlers.hello.broken warn; }problems.handlers的配置结构在 problems.nix 的configOptions中定义handlers attrsOf (attrsOf handlerType)其优先级高于problems.matchers。用 matchers 批量匹配问题还可以指定通用 matcher一次为多个包、多个问题设置 handler。这通过config.problems.matchers选项实现{ problems.matchers [ # 任何即将被移除的包都直接构建失败 { kind removal; handler error; } # 使用没有声明维护者的包时给出警告 { kind maintainerless; handler warn; } # 你非常关心 hello 这个包想绝对掌握它的任何问题 { package hello; handler error; } ]; }matcher 可以匹配包名、问题名或问题 kind 中的一项或多项如果设置了多个条件则全部满足才匹配。如果多个 matcher 同时匹配一个问题会选用最高严重级别的 handler。当前默认值中包含{ kind removal; handler warn; }即提前通知用户包的移除计划。从 problems.nix 的实现看matcher 的package、name、kind三个字段均默认为null不限制handler必填配置断言禁止同时设置package与name提示改用problems.handlersconfig.nix 中实际注入的默认 matchers 为{ kind broken; handler error; }、{ kind removal; handler warn; }以及当旧选项showDerivationWarnings [ maintainerless ]被设置时补充的 maintainerless 警告并给出迁移提示genHandlerSwitch第 358 行起把所有matchers优先级 0与handlers优先级 1折叠成一个按kind → name → package三级分层的查找结构匹配时取最高优先级与最高严重级别handlerForProblem即为查询入口handlers.levels定义了ignore warn error的严重级别次序processProblems第 537 行起把待处理问题按 handler 分组warn组生成警告消息error组生成拒绝求值的错误并在 remediation 中提示如何通过problems.handlers放行。包名的获取规则无论problems.handlers还是problems.matchers包名都取自lib.getName它优先看pname找不到时才从name属性中解析出 pname 部分。其定义位于 lib/strings.nixgetName let parse drv: (parseDrvName drv).name; in x: if isString x then parse x else x.pname or (parse x.name);因此getName youtube-dl-2016.01.01与getName pkgs.youtube-dl都会得到youtube-dl。这正是配置里写problems.handlers.hello.broken不带版本号的原因。用packageOverrides修改包可以在本地~/.config/nixpkgs/config.nix中定义packageOverrides函数来覆写 Nix 包。它必须是接收pkgs作为参数、返回一组修改后包的函数{ packageOverrides pkgs: rec { foo pkgs.foo.override { # ... }; }; }pkgs.foo.override是 Nixpkgs 提供的能力覆写机制常用于修改依赖、开关特性或更换编译器配合packageOverrides即可把覆写固化到用户级配置中对所有基于该配置的求值生效。声明式包管理利用packageOverrides可以实现声明式包管理把所有需要的包列在一份声明式 Nix 表达式里。构建一个环境例如要在~/.config/nixpkgs/config.nix中声明 aspell、bc、ffmpeg、coreutils、gdb、nix、emscripten、jq、nox 和 silver-searcher{ packageOverrides pkgs: with pkgs; { myPackages pkgs.buildEnv { name my-packages; paths [ aspell bc coreutils gdb ffmpeg nix emscripten jq nox silver-searcher ]; }; }; }安装进环境只需运行nix-env -iA nixpkgs.myPackages若想从一份 nixpkgs 工作副本构建这些包则运行nix-env -f . -iA myPackages。装完可以查看~/.nix-profile/里的内容会发现安装了大量东西有些有用、有些没必要。可以告诉 Nixpkgs 只链接我们想要的路径通过pathsToLink实现{ packageOverrides pkgs: with pkgs; { myPackages pkgs.buildEnv { name my-packages; paths [ aspell bc coreutils gdb ffmpeg nix emscripten jq nox silver-searcher ]; pathsToLink [ /share /bin ]; }; }; }pathsToLink告诉 Nixpkgs 只链接列出的路径从而去掉 profile 里的多余内容。/bin和/share是用户环境的不错默认值。如果运行的是 macOS 上的 Nix可能还想加上/Applications让 GUI 应用可用。获取文档构建好新环境后检查~/.nix-profile确保需要的都在。细心的读者会发现有些文件缺失查看~/.nix-profile/share/man/man1/会发现没有任何 Nix 工具的 man page这是因为有些包如 Nix 本身把文档等内容拆成了多个输出output。让我们把文档也装上{ packageOverrides pkgs: with pkgs; { myPackages pkgs.buildEnv { name my-packages; paths [ aspell bc coreutils ffmpeg nix emscripten jq nox silver-searcher ]; pathsToLink [ /share/man /share/doc /bin ]; extraOutputsToInstall [ man doc ]; }; }; }extraOutputsToInstall指定除默认输出外还要安装的派生输出这里补装了man与doc。不过若想让 man 真正找到这些 man page还需要配置环境。这件事同样可以在 Nix 表达式中管理{ packageOverrides pkgs: { myProfile pkgs.writeText my-profile export PATH$HOME/.nix-profile/bin:/nix/var/nix/profiles/default/bin:/sbin:/bin:/usr/sbin:/usr/bin export MANPATH$HOME/.nix-profile/share/man:/nix/var/nix/profiles/default/share/man:/usr/share/man ; myPackages pkgs.buildEnv { name my-packages; paths with pkgs; [ (runCommand profile { } mkdir -p $out/etc/profile.d cp ${myProfile} $out/etc/profile.d/my-profile.sh ) aspell bc coreutils ffmpeg man nix emscripten jq nox silver-searcher ]; pathsToLink [ /share/man /share/doc /bin /etc ]; extraOutputsToInstall [ man doc ]; }; }; }要让这一切完整生效还需要在登录时 source 这个脚本。可以往~/.profile里添加如下内容#!/bin/sh if [ -d ${HOME}/.nix-profile/etc/profile.d ]; then for i in ${HOME}/.nix-profile/etc/profile.d/*.sh; do if [ -r $i ]; then . $i fi done fi然后运行. ${HOME}/.profile就可以从环境中加载 man page 了。GNU info 配置配置 GNU info 比 man page 稍麻烦些info 需要生成数据库才能正常工作。这可以通过对环境脚本做少量修改实现{ packageOverrides pkgs: { myProfile pkgs.writeText my-profile export PATH$HOME/.nix-profile/bin:/nix/var/nix/profiles/default/bin:/sbin:/bin:/usr/sbin:/usr/bin export MANPATH$HOME/.nix-profile/share/man:/nix/var/nix/profiles/default/share/man:/usr/share/man export INFOPATH$HOME/.nix-profile/share/info:/nix/var/nix/profiles/default/share/info:/usr/share/info ; myPackages pkgs.buildEnv { name my-packages; paths with pkgs; [ (runCommand profile { } mkdir -p $out/etc/profile.d cp ${myProfile} $out/etc/profile.d/my-profile.sh ) aspell bc coreutils ffmpeg man nix emscripten jq nox silver-searcher texinfoInteractive ]; pathsToLink [ /share/man /share/doc /share/info /bin /etc ]; extraOutputsToInstall [ man doc info ]; postBuild if [ -x $out/bin/install-info -a -w $out/share/info ]; then shopt -s nullglob for i in $out/share/info/*.info $out/share/info/*.info.gz; do $out/bin/install-info $i $out/share/info/dir done fi ; }; }; }postBuild告诉 Nixpkgs 在构建环境后运行一条命令。这里的install-info把安装的 info 页加入dir——GNU info 的默认根节点。注意texinfoInteractive被加入环境正是为了提供install-info这个命令。配置选项速查与测试验证本文涉及的核心配置选项均定义于 pkgs/top-level/config.nix汇总如下配置选项类型默认值作用allowUnfreeboolfalse或NIXPKGS_ALLOW_UNFREE1是否允许非自由包allowUnfreePredicate包 → bool未设置按谓词放行非自由包allowUnfreePackageslistOf str[ ]按包名放行非自由包可加法合并allowlistedLicenses许可证列表[ ]允许清单中的许可证仅作用于非自由许可blocklistedLicenses许可证列表[ ]阻止清单中的许可证作用于所有许可allowBrokenboolfalse或NIXPKGS_ALLOW_BROKEN1是否允许损坏的包allowUnsupportedSystemboolfalse或NIXPKGS_ALLOW_UNSUPPORTED_SYSTEM1是否允许平台不支持的包permittedInsecurePackageslistOf str未设置按名称-版本放行不安全包allowInsecurePredicate包 → bool未设置按谓词放行不安全包优先于上一项problems.handlersattrsOf (attrsOf handler){ }按包名.问题名指定 handlerproblems.matchersmatcher 列表broken→error、removal→warn按 kind/name/package 批量指定 handlerpackageOverridespkgs → pkgs未设置覆写/扩展包集合上述机制均有仓库测试覆盖配置结构测试在 pkgs/test/config.nix可用nix-build -A tests.config运行该文件头部有明确说明problems 机制在 pkgs/test/problems 下以用例目录 期望 stderr的形式逐场景验证如allow-broken、allow-broken-env、removal-warn、maintainerless-warn、package-name-matcher等运行入口为nix-build -A tests.problemsproblems.nix 文件头同样注明。这些用例把每个 kind 的 error / warn / ignore 行为、环境变量放行路径以及 matcher 匹配规则都固化为可回归的测试是理解与验证本文所述行为的最终参考。【免费下载链接】nixpkgsNix Packages collection NixOS项目地址: https://gitcode.com/GitHub_Trending/ni/nixpkgs创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考