From cb013071ed35de12b1886ee6fa948953e8ec113c Mon Sep 17 00:00:00 2001 From: Dessera <1533653159@qq.com> Date: Sat, 14 Sep 2024 17:03:51 +0800 Subject: [PATCH 1/2] =?UTF-8?q?chore:=20=E6=B7=BB=E5=8A=A0Nix-01-=E5=8C=85?= =?UTF-8?q?=E4=B8=8EFlake=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加了Nix-01-包与Flake的文档,介绍了Nix和Flake的基本概念和用法,该文档尚未完成。 删除了NixOS-01不变即完美的文档 --- ...42Nix-01-\345\214\205\344\270\216Flake.md" | 45 ++++++ ...30\345\215\263\345\256\214\347\276\216.md" | 148 ------------------ 2 files changed, 45 insertions(+), 148 deletions(-) create mode 100644 "docs/Linux/\345\210\235\346\216\242Nix-01-\345\214\205\344\270\216Flake.md" delete mode 100644 "docs/Linux/\345\210\235\346\216\242NixOS-01\344\270\215\345\217\230\345\215\263\345\256\214\347\276\216.md" diff --git "a/docs/Linux/\345\210\235\346\216\242Nix-01-\345\214\205\344\270\216Flake.md" "b/docs/Linux/\345\210\235\346\216\242Nix-01-\345\214\205\344\270\216Flake.md" new file mode 100644 index 0000000..e42a909 --- /dev/null +++ "b/docs/Linux/\345\210\235\346\216\242Nix-01-\345\214\205\344\270\216Flake.md" @@ -0,0 +1,45 @@ +--- +title: 初探Nix-01-包与Flake +createTime: 2024/09/14 15:36:04 +permalink: /article/szm6xukr/ +--- + +笔者认为,要熟练掌握某项技术或工具,就需要深入地亲自体会它,强迫自己进入“探索-碰壁-再探索”这一良性循环。学习某一语言如此,学习使用某个工具亦如。 + +以笔者本人为例,笔者以大一的某次比赛为契机接触了Linux,并花费进一年的时间扎根于Linux系统的使用,从Ubuntu到Manjaro,再到ArchLinux,最后深入NixOS,这个过程为笔者带来了大量的实践经验。 + +笔者举上面的例子旨在说明,学习Nix的过程十分艰难,我们如果认定学习Ubuntu的难度不高,学习Arch的难度较高,那么学习Nix的难度就是非常高。因为它不像Arch那样有完整的文档和成熟的社区支持,Nix的文档是灾难性的——它分布及广、信息量低且几乎没有中文支持。 + +为了解决一些共性问题和分享一些见解,笔者计划编写新的系列文章,本文是该系列文章的第一篇。 + +## 所以,什么是Nix? + +事实上,上文我们将Nix与其他Linux发行版并列的做法是错误的,严格意义上说,Nix可以是一门编程语言,也可以是一个通用Linux包管理器,但它实际上不是发行版本身。而基于Nix包管理器的Linux发行版被称为NixOS。 + +> 事实上,社区还维护了一个基于Nix的发行版,叫做[NixNG](https://github.com/nix-community/NixNG),这里不做讨论 + +Nix是一个“函数式”的软件包管理器,它以配置文件的方式管理系统中所有的软件和其配置,这样的好处是,其完全遵守函数式编程中纯函数的理念,能够保证同样的配置文件能够产出完全相同的配置结果。 + +当然,Nix的神秘魅力并不能用上面那句“假大空”的宣传语概括,只有我们深入了解它,我们才能体会到它带给我们的各种便利。 + +## Flake + +> 到现在为止,Flake在事实上仍然是一个实验性项目,但鉴于其使用的广泛程度,本文不会介绍旧的配置方法,而是直接使用Flake + +Flake是Nix生态最重要的组成部分,它是大多数Nix项目的基石。 + +Flake本质上是一个函数,它接受其他Flakes作为输入,并返回一个巨大的结构,其返回内容可以是一个软件包、一个开发环境、一个系统配置等等。 + +举个例子,我的项目是一个软件`my-tool`,那么我就可以使用Flake将我的软件分发给其他Nix使用者,其他人拿到我的软件包,只需要运行`nix build .`就可以构建我的软件,如果他们想要使用我的软件,只要将我的Flake加入到他们系统的Flake中,就可以在整个系统中安装我的软件。 + +要创建一个Flake,需要在任意项目的根目录初始化一份`flake.nix`,下面是一个简单的Flake: + +```nix +{ + description = "A base nix flake template"; + + inputs = { + + }; +} +``` diff --git "a/docs/Linux/\345\210\235\346\216\242NixOS-01\344\270\215\345\217\230\345\215\263\345\256\214\347\276\216.md" "b/docs/Linux/\345\210\235\346\216\242NixOS-01\344\270\215\345\217\230\345\215\263\345\256\214\347\276\216.md" deleted file mode 100644 index 4335b31..0000000 --- "a/docs/Linux/\345\210\235\346\216\242NixOS-01\344\270\215\345\217\230\345\215\263\345\256\214\347\276\216.md" +++ /dev/null @@ -1,148 +0,0 @@ ---- -title: 初探NixOS-01不变即完美 -tags: -- Linux -- NixOS -- Flake -createTime: 2024/05/01 15:16:01 -permalink: /article/9f12aw72/ ---- - -很长一段时间,我的工作环境都搭建在 Archlinux 上,不得不承认,Archlinux 是极其优秀的发行版,它的优秀体现在高度可定制性、完整的社区支持和丰富的软件源。这一切都很美好,直到我遇到了 NixOS。 - -NixOS 是一个特殊的 Linux 发行版,它基于一个名为 Nix 的 **不可变**包管理器,允许用户通过编写`nix`配置文件来构建可复现的操作系统。只需要写一份配置文件,然后在任何平台上都可以通过一些简单的命令重新构建出操作系统。听起来是一个很美好的愿景,不是吗?这正是 NixOS 的强大之处。 - -但凡事都有两面性,对于国内用户来说,NixOS 的学习门槛非常高,主要原因有以下几点: - -- 资料的分散和不完整 -- 资料的汉化程度不高 - -由于以上种种问题,通向 NixOS 之路困难重重。 - -本文的目的是记录我在使用 NixOS 时遇到的种种问题和它们的解决方法。 - -## 伊始 - 关于 NixOS 的安装 - -和其他的发行版一样,你需要下载 NixOS 的镜像文件,并制作启动媒介。前往该页面下载 NixOS 的镜像文件 [NixOS下载](https://nixos.org/download/),注意,我们要下载的是 NixOS 而不是 Nix。 - -在下载页面,我们有两种选择——图形化ISO和最简ISO,图形化ISO顾名思义,它可以用图形的方式进行系统安装,但缺点是可定制性很差;而最简ISO则可完全定义我们的安装流程。简单来说,如果你不想使用`ext4`或者想要深度定制一些内容,我更推荐使用最简ISO。 - -下载好镜像,我们将其制作为USB启动器(我使用的是Ventoy),从USB启动并进入镜像,我们就进入了 NixOS 的安装程序(或者说 LiveCD)。 - -详细的安装过程可以参考[NixOS CN](https://nixos-cn.org/)。 - -## Nix 配置文件 - -NixOS 采用名为 Nix 的函数式语言作为配置语言,在传统模式下,系统配置的根文件为`/etc/nixos/configuration.nix`,在 Flake 模式下,入口可以是任何位置。 - -下面是一个传统的`configuration.nix`配置: - -```nix -# 配置函数参数 -{ config, lib, pkgs, ... }: - -{ - imports = [ - # 导入子模块 - # 硬件配置文件 - ./hardware-configuration.nix - ]; - - # 各种配置项 - - system.stateVersion = "24.05"; -} -``` - -配置 NixOS 的过程,就是修改这个文件,然后重新生成系统的过程。 - -例如,我们可以添加以下配置项以应用`grub2`: - -```nix -boot.loader = { - grub = { - enable = true; - device = "nodev"; - efiSupport = true; - }; - efi = { - canTouchEfiVariables = true; - efiSysMountPoint = "/boot"; - }; -}; -``` - -## Flake - -传统的`configuration.nix`模式会从`nix-channel`配置的源中下载需要的包,这种模式实际上无法保证每次下载到的包是完全相同的,即无法保证可复现性。 - -为了解决这一问题,NixOS 引入了 Flake,简单来说,Flake 将软件源统一为了`input`变量,并将该变量传递给名为`output`的函数(将软件源的配置也变成了声明式)。同时,引入了现代构建系统的`lock`文件来锁定软件版本,保证每一次构建获得的软件版本相同。 - -Flake 配置的入口是`flake.nix`,下面是一个系统配置的入口: - -```nix -{ - # 工程的描述 - description = "Dessera's NixOS configuration"; - - # 输入源配置 - inputs = { - nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; - }; - - # 输出函数定义 - outputs = { self, nixpkgs, ... }@inputs: { - nixosConfigurations.nixos = nixpkgs.lib.nixosSystem { - system = "x86_64-linux"; - modules = [ - # 将 configuration无缝迁移到flake配置 - ./configuration.nix - ]; - }; - }; -} -``` - -## 配置参考 - -要查看`nix`模块的所有配置选项,可以参考[Nix Options](https://search.nixos.org/options) - -要查看`pkgs`中包含的包,可以参考[Nix Packages](https://search.nixos.org/packages) - -## 不变性实现 - -NixOS 依靠上述的配置文件不仅*统一*了系统级的大部分配置文件,而且为它的函数式模式提供了保障。我们实际上是编写了一个巨大的系统配置函数,并规定了它的输入,通过这种方式生成的系统因为输入和函数本身的不变性和无状态性,天然就保证了可复现性。 - -NixOS 中,我们可以通过以下命令生成新的系统,我们称之为**世代**: - -```shell -sudo nixos-rebuild switch -``` - -等待一段时间后,如果命令没有错误输出,我们便成功生成了新的世代,并切换到了新的系统。 - -> 没错,NixOS 的重新生成大部分情况下可以热重载。 -> -> 如果不希望直接切换到新的系统,你可以将`switch`换为`boot`,它将在下次重启时切换到新的系统。 - -我们在配置文件中声明的任何内容,都将被下载到`/nix/store`中,包或者配置文件以`${digest}-${name}`的方式存储在该路径下,例如,下载的`clang`可能存在于: - -```shell -/nix/store/afxm7yvnadvv9a3vcrhzjvnmfhdgbfc0-clang-18.1.4 -``` - -该路径是一个只读文件系统,也就是所,我们不能在运行时更改该目录下的任何内容(除非通过nix),要清理该目录下没有使用的包,可以使用: - -```shell -nix-collect-garbage -d -# 或者 -nix store gc -``` - -在系统启动时,Nix 会根据当前选择的系统,将该目录下的内容动态链接至`/run/current-system/`中,并基于此启动系统。 - -例如,我们实际的`sddm`主题目录位于: - -```shell -/run/current-system/sw/share/sddm/themes -``` From 861ac2dd535dd02044be3c584208c3c49930c5c5 Mon Sep 17 00:00:00 2001 From: Dessera <1533653159@qq.com> Date: Tue, 17 Sep 2024 20:03:55 +0800 Subject: [PATCH 2/2] =?UTF-8?q?chore:=20=E6=9B=B4=E6=96=B0Nix-01-=E5=8C=85?= =?UTF-8?q?=E4=B8=8EFlake=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...42Nix-01-\345\214\205\344\270\216Flake.md" | 165 +++++++++++++++++- 1 file changed, 163 insertions(+), 2 deletions(-) diff --git "a/docs/Linux/\345\210\235\346\216\242Nix-01-\345\214\205\344\270\216Flake.md" "b/docs/Linux/\345\210\235\346\216\242Nix-01-\345\214\205\344\270\216Flake.md" index e42a909..9a71597 100644 --- "a/docs/Linux/\345\210\235\346\216\242Nix-01-\345\214\205\344\270\216Flake.md" +++ "b/docs/Linux/\345\210\235\346\216\242Nix-01-\345\214\205\344\270\216Flake.md" @@ -36,10 +36,171 @@ Flake本质上是一个函数,它接受其他Flakes作为输入,并返回一 ```nix { - description = "A base nix flake template"; + description = "A very basic flake"; inputs = { - + nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; + }; + + outputs = { self, nixpkgs }: { + + packages.x86_64-linux.hello = nixpkgs.legacyPackages.x86_64-linux.hello; + + packages.x86_64-linux.default = self.packages.x86_64-linux.hello; + + }; +} + +``` + +这是一个最基础的`Hello World`配置,会导出一个名为`hello`的程序,我们主要关注其`inputs`和`outputs`两项。 + +`inputs`是`outputs`的参数,它的来源是其他的Flakes,在这里,我们通过引用`nixos/nixpkgs`来导入Nix官方软件源,`outputs`就是我们想要导出的内容,`packages`下的内容意为导出软件包,这里,我们将软件源中的`hello`导出。 + +接着,我们运行`nix run .`就可以运行该软件包: + +```shell +❯ nix run . +世界你好! +``` + +我们也可以使用`nix build .`来构建该软件包 + +```shell +❯ nix build . +❯ ls +flake.lock flake.nix result +``` + +在`result/bin`中,我们就可以看到构建的软件: + +```shell +❯ ls result/bin/ +hello +``` + +我们也可以使用`nix develop .`进入这个包的开发环境: + +```shell +❯ nix develop . + +[your_name@your_pc:/path/to/project]$ +``` + +到这,有些朋友可能发现了,在Nix中,只要我们定义好了我们的软件包,那么我们就可以一键完成其构建过程、构建环境和开发环境。而更可怕的是,这才是Flake的冰山一角。 + +除却`packages`之外,我们还有`devShells`、`nixosConfigurations`等等配置项,足够支撑Flake进行几乎**任何**项目的开发。 + +## 使用Flake进行开发 + +要使用Flake,首先要进行一次系统配置,在没有启动Flake之前,NixOS的系统配置存在于`/etc/nixos`中,我们需要在配置文件中加入下面的内容: + +```nix +nix.settings = { + experimental-features = [ "nix-command" "flakes" ]; +}; + +programs.direnv.enable = true; +``` + +然后使用`sudo nixos-rebuild switch`切换系统配置,使用以上的配置,我们启用了Flake实验功能,并下载了`direnv`。 + +接下来,我们来到我们的项目文件夹下,例如,我的项目文件夹在`/data/project/my-tool`,我们先编写一个最简单的C程序: + +```c +// my-tool.c +#include + +int main() { + printf("hello world!\n"); + return 0; +} +``` + +接下来,我们准备该程序的打包文件: + +```nix +# default.nix +{ stdenv }: +stdenv.mkDerivation { + name = "my-tool"; + src = ./.; + + buildPhase = '' + $CC -o my-tool my-tool.c + ''; + + installPhase = '' + mkdir -p $out/bin + cp my-tool $out/bin + ''; +} +``` + +该文件是一个通用Nix包格式,能被`pkgs`中的`callPackage`函数调用,在这段逻辑中,我们引入了标准环境`stdenv`,其中包含`gcc`和相应的C标准库。 + +在`buildPhase`中,我们定义了该包的构建逻辑,这里使用`$CC`引用`gcc`。 + +在`installPhase`中,我们定义该包的安装逻辑,我们使用`$out`引用导出路径,先创建`bin`文件夹,然后将构建好的程序复制到指定位置。 + +最后,我们创建`flake.nix`,将该包暴露出去: + +```nix +{ + description = "A flake that builds a simple my-tool"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + }; + + outputs = { self, nixpkgs }: + { + packages.x86_64-linux.my-tool = nixpkgs.legacyPackages.x86_64-linux.callPackage ./default.nix {}; + packages.x86_64-linux.default = self.packages.x86_64-linux.my-tool; }; } ``` + +这里的写法和上文的`hello`几乎一致,值得一提的是,因为包是我们自己写的,所以要使用`callPackage`函数将其转化为正确的包格式。 + +现在,我们可以使用`nix build`、`nix run`和`nix develop`,其效果和上文基本一致。 + +```shell +❯ nix run . +hello world! +``` + +我们还可以使用上面安装的`direnv`,它可以让我们一进入该目录,就进入包的开发环境,首先编写`.envrc`: + +```shell +# .envrc +use flake +``` + +然后,在项目目录下运行`direnv allow`: + +```shell +❯ direnv allow +direnv: loading /data/projects/my-tool/.envrc +direnv: using flake +direnv: nix-direnv: Renewed cache +direnv: export +AR +AS +CC +CONFIG_SHELL +CXX +HOST_PATH +IN_NIX_SHELL +LD +NIX_BINTOOLS +NIX_BINTOOLS_WRAPPER_TARGET_HOST_x86_64_unknown_linux_gnu +NIX_BUILD_CORES +NIX_CC +NIX_CC_WRAPPER_TARGET_HOST_x86_64_unknown_linux_gnu +NIX_CFLAGS_COMPILE +NIX_ENFORCE_NO_NATIVE +NIX_HARDENING_ENABLE +NIX_LDFLAGS +NIX_STORE +NM +OBJCOPY +OBJDUMP +RANLIB +READELF +SIZE +SOURCE_DATE_EPOCH +STRINGS +STRIP +__structuredAttrs +buildInputs +buildPhase +builder +cmakeFlags +configureFlags +depsBuildBuild +depsBuildBuildPropagated +depsBuildTarget +depsBuildTargetPropagated +depsHostHost +depsHostHostPropagated +depsTargetTarget +depsTargetTargetPropagated +doCheck +doInstallCheck +dontAddDisableDepTrack +installPhase +mesonFlags +name +nativeBuildInputs +out +outputs +patches +propagatedBuildInputs +propagatedNativeBuildInputs +shell +src +stdenv +strictDeps +system ~PATH ~XDG_DATA_DIRS +``` + +接下来,我们就可以使用这个开发环境安装的所有软件,查看一下`gcc`: + +```shell +❯ gcc -v +使用内建 specs。 +COLLECT_GCC=/nix/store/x8rg4vhgd20i8vzykm1196f9qdb8klhh-gcc-13.3.0/bin/gcc +COLLECT_LTO_WRAPPER=/nix/store/x8rg4vhgd20i8vzykm1196f9qdb8klhh-gcc-13.3.0/libexec/gcc/x86_64-unknown-linux-gnu/13.3.0/lto-wrapper +目标:x86_64-unknown-linux-gnu +配置为:../gcc-13.3.0/configure --prefix=/nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-gcc-13.3.0 --with-gmp-include=/nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-gmp-6.3.0-dev/include --with-gmp-lib=/nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-gmp-6.3.0/lib --with-mpfr-include=/nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-mpfr-4.2.1-dev/include --with-mpfr-lib=/nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-mpfr-4.2.1/lib --with-mpc=/nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-libmpc-1.3.1 --with-native-system-header-dir=/nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-glibc-2.39-52-dev/include --with-build-sysroot=/ --with-gxx-include-dir=/nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-gcc-13.3.0/include/c++/13.3.0/ --program-prefix= --enable-lto --disable-libstdcxx-pch --without-included-gettext --with-system-zlib --enable-static --enable-languages=c,c++ --disable-multilib --enable-plugin --disable-libcc1 --with-isl=/nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-isl-0.20 --disable-bootstrap --build=x86_64-unknown-linux-gnu --host=x86_64-unknown-linux-gnu --target=x86_64-unknown-linux-gnu +线程模型:posix +支持的 LTO 压缩算法:zlib +gcc 版本 13.3.0 (GCC) +``` + +## 未完待续 + +本文我们简单了解了Flake如何构建软件,之后的文章我们会探讨Flake构建系统、以及高级软件包构建流程。