最近我第一次开始使用 Mac。到目前为止我注意到的最大缺点是包管理比 Linux 差得多。有时我对 homebrew 感到沮丧,因为我觉得在安装新包时它花了太多时间升级,所以我想——也许我会试试nix包管理器!
nix 以令人困惑着称(它有自己的编程语言!),所以我一直在尝试找出如何以尽可能简单的方式使用 nix,并且不涉及管理任何配置文件或学习新的编程语言。到目前为止,这就是我的想法!我们将讨论如何:
像往常一样,我可能在这篇文章中弄错了一些东西,因为我对 nix 还是很陌生。我也仍然不确定我有多喜欢 nix——它非常令人困惑!但它帮助我编译了一些我以前很难编译的软件,而且总的来说,它的安装速度似乎比自制软件快。
nix有什么有趣的地方?
人们经常将 nix 描述为“声明式包管理”。我不太关心声明式包管理,所以这里有两点我很欣赏 nix:
- 它提供二进制包(托管在https://cache.nixos.org/ ),您可以快速下载和安装
- 对于没有二进制包的包,编译它们更容易
我认为 nix 擅长编译软件的原因是:
- 您可以同时安装同一个库或程序的多个版本(例如,您可以安装 2 个不同版本的 libc)。例如,我的计算机上现在有两个版本的节点,一个在
/nix/store/4ykq0lpvmskdlhrvz1j3kwslgc6c7pnv-nodejs-16.17.1
和一个在/nix/store/5y4bd2r99zhdbir95w5pf51bwfg37bwa-nodejs-18.9.1
。 - 当 nix 构建一个包时,它会单独构建它,仅使用您明确声明的特定版本的依赖项。因此,该包不存在秘密依赖于您系统上您不知道的另一个包的风险。不再与
LD_LIBRARY_PATH
战斗!
我将在这篇文章的后面给出几个例子,两次 nix 让我更容易编译软件。
我是如何开始使用 nix 的
这是我开始使用 nix 的方式:
- 安装尼克斯。我完全忘记了我是怎么做到的,但看起来有一个来自 zero-to-nix.com 的官方安装程序和一个非官方安装程序
- 将
~/.nix-profile/bin
放在我的路径上 - 使用
nix-env -iA nixpkgs.NAME
安装软件包 - 就是这样。
基本上这个想法是像对待brew install
或apt-get install
一样对待nix-env -iA
。
例如,如果我想安装fish
,我可以这样做:
nix-env -iA nixpkgs.fish
这似乎只是从https://cache.nixos.org下载一些二进制文件——非常简单。
有些人使用 nix 来安装他们的 Node、Python 和 Ruby 包,但我没有这样做——我只是像往常一样使用npm install
和pip install
。
我没有使用的一些 nix 功能
有很多我没有使用的 nix 功能/工具,但我会提到。我最初认为您必须使用这些功能才能使用 nix,因为我读过的大多数 nix 教程都在谈论它们。但你不必使用它们。
我不会深入讨论这些,因为我还没有真正使用过它们,而且那里有很多解释。
nix包在哪里定义的?
我认为主 nix 包存储库中的包在https://github.com/NixOS/nixpkgs/中定义
看起来您可以在https://search.nixos.org/packages上搜索包。我还没有想出如何像使用apt-cache search
一样在命令行上搜索包。
一切都安装了符号链接
nix 的主要设计选择之一是,所有包都没有一个单独的bin
,而是使用符号链接。例如,我机器上的~/.nix-profile/bin/fish
是/nix/store/afkwn6k8p8g97jiqgx9nd26503s35mgi-fish-3.5.1/bin/fish
的符号链接
我认为这意味着如果我安装了新版本的fish
但我不喜欢它,我可以通过更新符号链接轻松返回。 Nix 有很多管理这些符号链接的工具,我还没有完全理解它们。
卸载软件包不会删除它们
如果我像这样卸载一个 nix 包,它实际上并没有释放任何硬盘空间,它只是删除了符号链接。
$ nix-env --uninstall oil
我仍然不确定如何真正删除包——我运行了这样的垃圾收集,它似乎删除了一些东西:
$ nix-collect-garbage ... 85 store paths deleted, 74.90 MiB freed
但是我的系统上仍然有oil
/nix/store/8pjnk6jr54z77jiq5g2dbx8887dnxbda-oil-0.14.0
。
升级
看起来你可以像这样升级 nix 包:
nix-channel --update nix-env --upgrade
(类似于apt-get update && apt-get upgrade
)
我还没有真正升级任何东西。我认为如果升级出现问题,您可以回滚(因为在 nix 中一切都是不可变的!)
nix-env --rollback
下一个目标:定制paperjam
包装
安装现有软件包几个月后,我想使用 nix 为一个名为paperjam的程序制作一个自定义软件包,该程序尚未打包。
实际上,即使没有 nix,我也很难编译paperjam
,因为我系统上的libiconv
版本是错误的。我认为用 nix 编译它可能更容易,尽管我还不知道如何制作 nix 包。它实际上是!
但是弄清楚如何到达那里非常令人困惑,所以这里有一些关于我是如何做到的笔记。
如何构建示例包
在开始处理 mG paperjam
包之前,我想构建一个现有包示例,以确保我了解构建包的过程。我真的很难弄清楚如何做到这一点,但我在 Discord 中询问,有人向我解释了如何从https://github.com/NixOS/nixpkgs/获取工作包并构建它。所以这是这些说明:
第 1 步:从 github 上的nixpkgs下载一些任意包,例如dash
包:
wget https://raw.githubusercontent.com/NixOS/nixpkgs/47993510dcb7713a29591517cb6ce682cc40f0ca/pkgs/shells/dash/default.nix -O dash.nix
第 2 步:替换第一条语句 ( { lib , stdenv , buildPackages , autoreconfHook , pkg-config , fetchurl , fetchpatch , libedit , runCommand , dash }:
with with import <nixpkgs> {};
我不知道你为什么要这样做,但它有效。
第 3 步:运行nix-build dash.nix
这编译包
第 4 步:运行nix-env -i -f dash.nix
这会将包安装到我的~/.nix-profile
就这样!一旦我这样做了,我觉得我可以修改dash
包并制作我自己的包。
我是如何制作自己的包裹的
paperjam
有一个依赖项 ( libpaper
) 也没有打包,所以我需要先构建libpaper
。
这是libpaper.nix
。我基本上只是通过从nixpkgs存储库中的其他包复制和粘贴来编写这个。我的猜测是这里发生的事情是 nix 有一些默认的编译 C 包的规则(比如“运行make install
”),所以make install
是默认发生的,我不需要明确地配置它。
with import <nixpkgs> {}; stdenv.mkDerivation rec { pname = "libpaper"; version = "0.1"; src = fetchFromGitHub { owner = "naota"; repo = "libpaper"; rev = "51ca11ec543f2828672d15e4e77b92619b497ccd"; hash = "sha256-S1pzVQ/ceNsx0vGmzdDWw2TjPVLiRgzR4edFblWsekY="; }; buildInputs = [ ]; meta = with lib; { homepage = "https://github.com/naota/libpaper"; description = "libpaper"; platforms = platforms.unix; license = with licenses; [ bsd3 gpl2 ]; }; }
基本上这只是告诉 nix 如何从 GitHub 下载源代码。
我通过运行nix-build libpaper.nix
构建了它
接下来,我需要编译paperjam
。这是我写的 nix 包的链接。除了告诉它在哪里下载源代码之外,我需要做的主要事情是:
- 添加一些额外的构建依赖项(如
asciidoc
) - 为安装设置一些环境变量 (
installFlags = [ "PREFIX=$(out)" ];
) 以便它安装在正确的目录中而不是/usr/local/bin
中。
我想出了如何通过在 nixpkgs 存储库中运行rg PREFIX
来设置installFlags
我认为需要设置PREFIX
是很常见的,之前可能有人做过,我是对的。所以我只是从另一个包中复制并粘贴了该行。
然后我跑了:
nix-build paperjam.nix nix-env -i -f paperjam.nix
然后一切正常,我安装了paperjam
!万岁!
下一个目标:安装 5 年前的hugo
版本
现在我使用 Hugo 0.40 构建这个博客,从 2018 年开始。我不需要任何新功能,所以我觉得不需要升级。在 Linux 上这很简单:Hugo 的发布是一个静态二进制文件,所以我可以从发布页面下载 5 年前的二进制文件并运行它。简单的!
但是在这台 Mac 上我遇到了一些问题。 Mac 硬件在过去 5 年发生了变化,所以我下载的 Mac Hugo 二进制文件崩溃了。当我尝试使用go build
从源代码构建它时,这也不起作用,因为 Go 构建规范在过去 5 年中也发生了变化。
我通过在 Linux docker 容器中运行 Hugo 来解决这个问题,但我不喜欢这样:它有点慢而且感觉很傻。编译一个 Go 程序应该不难!
尼克斯来救援!这是我用 nix 安装旧版本的 hugo 所做的。
使用 nix 安装 hugo 0.40
我想安装 hugo 0.40 并将其作为hugo-0.40
放入我的 PATH 中。我是这样做的。我以一种奇怪的方式做到了这一点( Searching and installing old versions of Nix packages描述了一种可能更正常的方法),但它奏效了。
第 1 步:搜索 nixpkgs 存储库以找到 Hugo 0.40
我在这里找到了.nix
文件https://github.com/NixOS/nixpkgs/blob/17b2ef2/pkgs/applications/misc/hugo/default.nix
第 2 步:下载该文件并构建它
我下载了那个文件(以及同一目录中另一个名为deps.nix
的文件),将第一行替换为with import <nixpkgs> {};
,并使用nix-build hugo.nix
构建它。
这几乎无需任何更改即可工作,但我必须进行两项更改:
- 出于某种原因
with stdenv.lib
替换为with lib
。 - 将包重命名为
hugo040
这样它就不会与我安装的其他版本的hugo
冲突
第 3 步:将hugo
重命名为hugo-0.40
我写了一个小的安装后脚本来重命名 hugo 二进制文件。
postInstall = '' mv $out/bin/hugo $out/bin/hugo-0.40 '';
我想出了如何通过在 nixpkgs 存储库中运行rg 'mv '
并复制和修改看起来相关的内容来运行它。
第 4 步:安装它
我通过运行nix-env -i -f hugo.nix
安装到我的~/.nix-profile/bin
中。
这一切都有效!我将最终的.nix
文件放入我自己的个人nixpkgs 存储库中,以便以后可以根据需要再次使用它。
可复制的构建并不神奇
我认为这里值得注意的是这个hugo.nix
文件并不神奇——我今天可以轻松安装 Hugo 的原因是 5 年前有人致力于以可重现的方式打包那个版本的 Hugo。
就这样!
安装paperjam
和这个已有 5 年历史的 Hugo 版本出奇地轻松,实际上比没有 nix 的情况下编译它要容易得多,因为 nix 使我更容易使用正确版本的libiconv
编译paperjam
包,而且因为有人 5几年前就已经麻烦地列出了 Hugo 的确切依赖项。
我没有任何计划让 nix 变得更复杂(而且我仍然很可能会对它感到沮丧并回到自制软件!),但我们会看到会发生什么!我发现以简单的方式开始,然后在我觉得需要时开始使用更多功能,而不是一下子采用一大堆复杂的东西要容易得多。
我可能不会在 Linux 上使用 nix——我一直对apt
(在基于 Debian 的发行版上)和pacman
(在基于 Arch 的发行版上)很满意,而且它们不会那么令人困惑。但在 Mac 上,这似乎是值得的。我们拭目以待!
原文: https://jvns.ca/blog/2023/02/28/some-notes-on-using-nix/