十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

nixpkgs 中的 Idris 开发指南:idrisPackages 作用域、with-packages 环境与 build-idris-package 打包实践

nixpkgs 中的 Idris 开发指南:idrisPackages 作用域、with-packages 环境与 build-idris-package 打包实践 nixpkgs 中的 Idris 开发指南idrisPackages 作用域、with-packages 环境与 build-idris-package 打包实践【免费下载链接】nixpkgsNix Packages collection NixOS项目地址: https://gitcode.com/GitHub_Trending/ni/nixpkgs本文以 nixpkgs 官方文档doc/languages-frameworks/idris.section.md为主线讲解如何在 Nix / NixOS 环境下安装配置 Idris 编译器、用idrisPackages.with-packages构造带库环境、通过-p参数与--listlibs管理库路径并完整继承官方idrisPackages.yaml示例演示如何用build-idris-package构建一个 Idris 库包。文中结合pkgs/development/idris-modules/下的源码实现说明各参数在构建阶段中的真实落点读完后你可以直接在 nixpkgs 中完成 Idris 环境配置、库包构建与idris命令选项注入。一、安装 Idris从开箱即用的idris属性说起最简单的做法是安装顶层的idris属性$ nix-env -f nixpkgs -iA idris这条命令提供的编译器只附带prelude和base两个基础库。这一说法可以直接从 all-packages.nix 中得到印证顶层idris属性实际上是把base注入包装器得到的# pkgs/top-level/all-packages.nix约 3706 行起 idrisPackages recurseIntoAttrs ( callPackage ../development/idris-modules { idris-no-deps haskellPackages.idris; } ); idris idrisPackages.with-packages [ idrisPackages.base ];从源码结构看两个值得注意的实现细节是idrisPackages作用域由 idris-modules/default.nix 定义其中prelude无依赖base依赖preludecontrib、effects、pruviloj依赖preludebase该文件第 27–46 行的builtins_部分。因此with-packages [ base ]会连带传播出prelude与文档只提供 prelude 和 base的描述一致。编译器本体idris-no-deps取自haskellPackages.idris——即 Idris 1 编译器本身是用 Haskell 编写的nixpkgs 通过 Haskell 工具链构建它再叠加 idris-wrapper.nix 做二次包装用makeWrapper注入IDRIS_CC默认指向stdenv.cc、为 C 编译前缀-I${gmp}/include与链接前缀-L${gmp}/lib从而让 Idris 生成的 C 代码能找到 GMP 大整数库。1.1 用idrisPackages.with-packages构造自定义库环境要安装附带更多库的 idris官方推荐idrisPackages.with-packages函数。例如在 overlay 文件~/.config/nixpkgs/overlays/my-idris.nix中self: super: { myIdris with self.idrisPackages; with-packages [ contrib pruviloj ]; }然后安装$ # On NixOS $ nix-env -iA nixos.myIdris $ # On non-NixOS $ nix-env -iA nixpkgs.myIdriswith-packages的实现非常精炼见 with-packages.nixpackages: let paths lib.closePropagation packages; in lib.appendToName with-packages (symlinkJoin { inherit (idris) pname version; paths paths [ idris ]; nativeBuildInputs [ makeWrapper ]; postBuild wrapProgram $out/bin/idris \ --set IDRIS_LIBRARY_PATH $out/libs ; })要点通过lib.closePropagation把所选库及其传递依赖闭包合入symlinkJoin使$out/libs中出现每个库的.ibc接口文件与.lidris文件包装器为idris二进制设置环境变量IDRIS_LIBRARY_PATH$out/libs编译器启动时即能搜索这些库——这就是后续-p参数与--listlibs能看到这些库的根本原因。1.2 查看所有可用的 Idris 库包$ # On NixOS $ nix-env -qaPA nixos.idrisPackages $ # On non-NixOS $ nix-env -qaPA nixpkgs.idrisPackagesidrisPackages是一个自引用作用域default.nix 中fix (extends overrides idrisPackages)且对顶层用recurseIntoAttrs展开因此每个库属性都可被独立查询与引用。该作用域除内置库外还聚合了约六十个社区库例如array、bifunctors、config、containers、effects、lens、lightyear、pipes、tparsec、vdom、yaml、yampa等全部以callPackage形式注册在同一文件第 72 行之后。注意作用域尾部还有三个已移除别名第 226–231 行descncrunch、protobuf、sdl访问它们会直接throw报错并说明原因如protobuf被标记为上游已弃维护迁移旧表达式时应留意。1.3 免安装的临时环境nix-shell$ nix-shell -p idrisPackages.with-packages (with idrisPackages; [ contrib pruviloj ])这与安装等价只是会话级别的包装器环境适合临时实验。二、带着库启动 Idris-p参数与--listlibs进入上一节的环境后调用idris时通过-p library name为每个要加载的库显式声明$ nix-shell -p idrisPackages.with-packages (with idrisPackages; [ contrib pruviloj ]) [nix-shell:~]$ idris -p contrib -p pruviloj--listlibs可以列出当前idris二进制能访问到的全部库$ idris --listlibs 00prelude-idx.ibc pruviloj base contrib prelude 00pruviloj-idx.ibc 00base-idx.ibc 00contrib-idx.ibc输出中的00xxx-idx.ibc条目对应各库索引index模块。结合 with-packages.nix 的实现可以确认库的可见性由包装器设置的IDRIS_LIBRARY_PATH决定-p只是按库名在已挂载的$out/libs中选中对应库并加载其.ipkg声明所以只要包装器环境里没有该库-p也无法凭空引入。三、用 Nix 构建一个 Idris 包build-idris-package完整示例官方文档给出的例子是idrisPackages.yaml库仓库中对应的真实文件即 yaml.nix{ build-idris-package, fetchFromGitHub, contrib, lightyear, lib, }: build-idris-package { pname yaml; version 2018-01-25; # 要构建的 .ipkg 文件名不含扩展名默认取包名 # 这里因为上游 ipkg 文件叫 Yaml.ipkg 而非 yaml.ipkg必须显式指定 ipkgName Yaml; # 构建时提供并传播的 Idris 依赖 idrisDeps [ contrib lightyear ]; src fetchFromGitHub { owner Heather; repo Idris.Yaml; rev 5afa51ffc839844862b8316faba3bafa15656db4; sha256 1g4pi0swmg214kndj85hj50ccmckni7piprsxfdzdfhg87s0avw7; }; meta { description Idris YAML lib; license lib.licenses.mit; maintainers [ lib.maintainers.brainrape ]; }; }说明早期文档中示例写作name yaml与hash sha256-…的形式当前仓库版本已统一为pname字段与fetchFromGitHub的标准sha256字段以仓库内文件为准。该文件被default.nix以yaml callPackage ./yaml.nix { };注册进idrisPackages作用域default.nix 第 220 行。因此有两种构建方式$ nix-build -E (import nixpkgs {}).idrisPackages.callPackage ./yaml.nix {}或者在另一个文件如default.nix中写with import nixpkgs { }; { yaml idrisPackages.callPackage ./yaml.nix { }; }再执行nix-build -A yaml。callPackage的魔力在于yaml.nix参数中的build-idris-package、contrib、lightyear都会自动从idrisPackages作用域解析无需手工传参。3.1 构建阶段剖析build-idris-package到底做了什么阅读 build-idris-package.nix 可以看到它是一个stdenv.mkDerivation核心参数与阶段如下参数默认值作用pname/version必填包名与版本产物名会是idris-pnameipkgNamepname要构建的.ipkg文件主名上游文件命名与包名不一致时必须显式给出idrisDeps[ ]要构建并propagatedBuildInputs传播的 Idris 库依赖noPrelude/noBasefalse默认会额外注入prelude与base置真可关闭extraBuildInputs[ ]额外 Nix 构建依赖idrisBuildOptions/idrisTestOptions/idrisInstallOptions/idrisDocOptions[ ]分别注入idris构建/测试/安装/文档命令的额外选项各阶段与源码对应关系patchPhase第 59–63 行用sed把.ipkg中opts -i ../../path/to/package这类相对路径重写为-i idris-with-packages/libs/。从源码注释看这是为了兼容部分库用命令式-i相对路径而非声明式依赖的风格buildPhase执行idris --build ${ipkgName}.ipkg后接idrisBuildOptions经lib.escapeShellArgs转义checkPhase第 71–77 行先grep -q tests检查.ipkg是否声明了测试若有才执行idris --testpkg。也就是说没有测试声明的库会安静地跳过测试阶段而非报错installPhase第 79–96 行idris --install ${ipkgName}.ipkg --ibcsubdir $out/libs把.ibc与.lidris装进$out/libs这正是with-packages里$out/libs路径的由来接着IDRIS_DOC_PATH$out/doc idris --installdoc ... || true尽力生成文档失败不致命最后若.ipkg定义了executable 把可执行文件移入$out/bin构建输入buildInputs固定包含idris-with-packages即依赖闭包的包装器与gmppropagatedBuildInputs为idrisDeps prelude/base受noPrelude/noBase控制。这套阶段设计保证了依赖闭包即环境下游再把这个包作为别人的idrisDeps时它自带的$out/libs会继续被下一层的with-packagessymlink 聚合进去形成可静态推导的 Idris 依赖图。四、为idris命令注入构建选项build-idris-package除了源码与依赖还提供四个可选选项参数分别对应idris命令在不同阶段的调用idrisBuildOptions→idris --build构建阶段idrisTestOptions→idris --testpkg测试阶段idrisInstallOptions→idris --install安装阶段idrisDocOptions→idris --installdoc文档阶段例如要求构建阶段输出详细信息build-idris-package { idrisBuildOptions [ --log 1 --verbose ]; # ... 其余参数 }在 build-idris-package.nix 中这四个列表最终通过lib.escapeShellArgs拼接到对应 shell 行末尾第 67、74、82、84 行因此选项原样传递给idris命令取值合法性取决于 Idris 编译器本身。注意测试选项仅在.ipkg声明了tests时才会实际生效见 3.1 的 checkPhase 逻辑。五、边界与延伸阅读适用前提本文面向 Idris 1基于 Haskell 实现的编译器nixpkgs 中经haskellPackages.idris构建。仓库同时提供 Idris 2 支持入口是idris2Packages定义于 idris2/default.nix提供idris2、idris2Api、idris2Lsp、pack与buildIdris其withPackages/IDRIS2_PACKAGE_PATH策略与本文的 Idris 1 机制不同细节参见doc/languages-frameworks/idris2.section.md。库清单以仓库为准idrisPackages可用库随版本演进可用nix-env -qaPA nixpkgs.idrisPackages实时核对已弃维护的protobuf、descncrunch、sdl在作用域中被显式移除引用会抛错提示。文档与源码对照本文示例与 idris.section.md 逐节对应实现侧可继续深入 idris-modules/README.md 与 idris-modules/TODO.md 了解该模块的维护状态与遗留问题。【免费下载链接】nixpkgsNix Packages collection NixOS项目地址: https://gitcode.com/GitHub_Trending/ni/nixpkgs创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表