mirror of
https://github.com/ryan4yin/nixos-and-flakes-book.git
synced 2025-03-01 08:41:17 +01:00
feat: the nix language
This commit is contained in:
parent
c6b61ab558
commit
99a07b8b28
@ -1,6 +1,5 @@
|
||||
# Nix 语言入门
|
||||
|
||||
|
||||
Nix 语言是 Nix 包管理器的基础,要想玩得转 NixOS 与 Nix Flakes,享受到它们带来的诸多好处,就必须学会这门语言。
|
||||
|
||||
Nix 是一门比较简单的函数式语言,在已有一定编程基础的情况下,过一遍这些语法用时应该在 2 个小时以内,本文假设你具有一定编程基础(也就是说写得不会很细)。
|
||||
@ -18,7 +17,7 @@ Nix 是一门比较简单的函数式语言,在已有一定编程基础的情
|
||||
|
||||
先把语法过一遍,有个大概的印象就行,后面需要用到时再根据右侧目录回来复习。
|
||||
|
||||
## 1. 基础数据类型一览 {#basic-data-types}
|
||||
## 基础数据类型一览 {#basic-data-types}
|
||||
|
||||
下面通过一个 attribute set (这类似 json 或者其他语言中的 map/dict)来简要说明所有基础数据类型:
|
||||
|
||||
@ -52,7 +51,7 @@ Nix 是一门比较简单的函数式语言,在已有一定编程基础的情
|
||||
bool -> bool
|
||||
```
|
||||
|
||||
## 2. let ... in ... {#let-in}
|
||||
## let ... in ... {#let-in}
|
||||
|
||||
Nix 的 `let ... in ...` 语法被称作「let 表达式」或者「let 绑定」,它用于创建临时使用的局部变量:
|
||||
|
||||
@ -65,7 +64,24 @@ a + a # 结果是 2
|
||||
|
||||
let 表达式中的变量只能在 `in` 之后的表达式中使用,理解成临时变量就行。
|
||||
|
||||
## 3. attribute set 说明 {#attribute-set}
|
||||
## if...then...else... {#if-then-else}
|
||||
|
||||
if...then...else... 用于条件判断,它是一个有返回值的表达式,语法如下:
|
||||
|
||||
```nix
|
||||
if 3 > 4 then "yes" else "no" # 结果为 "no"
|
||||
```
|
||||
|
||||
也可以与 let...in... 一起使用:
|
||||
|
||||
```nix
|
||||
let
|
||||
x = 3;
|
||||
in
|
||||
if x > 4 then "yes" else "no" # 结果为 "no"
|
||||
```
|
||||
|
||||
## attribute set 说明 {#attribute-set}
|
||||
|
||||
花括号 `{}` 用于创建 attribute set,也就是 key-value 对的集合,类似于 JSON 中的对象。
|
||||
|
||||
@ -123,7 +139,7 @@ a?b # 结果是 true,因为 a.b 这个属性确实存在
|
||||
|
||||
has attribute 操作符在 nixpkgs 库中常被用于检测处理 `args?system` 等参数,以 `(args?system)` 或 `(! args?system)` 的形式作为函数参数使用(叹号表示对 bool 值取反,是常见 bool 值运算符)。
|
||||
|
||||
## 4. with 语句 {#with-statement}
|
||||
## with 语句 {#with-statement}
|
||||
|
||||
with 语句的语法如下:
|
||||
|
||||
@ -144,7 +160,7 @@ in
|
||||
with a; [ x y z ] # 结果是 [ 1 2 3 ], 等价于 [ a.x a.y a.z ]
|
||||
```
|
||||
|
||||
## 5. 继承 inherit ... {#inherit}
|
||||
## 继承 inherit ... {#inherit}
|
||||
|
||||
`inherit` 语句用于从 attribute set 中继承成员,同样是一个简化代码的语法糖,比如:
|
||||
|
||||
@ -173,7 +189,7 @@ in
|
||||
} # 结果是 { x = 1; y = 2; }
|
||||
```
|
||||
|
||||
## 6. ${ ... } 字符串插值 {#string-interpolation}
|
||||
## ${ ... } 字符串插值 {#string-interpolation}
|
||||
|
||||
`${ ... }` 用于字符串插值,懂点编程的应该都很容易理解这个,比如:
|
||||
|
||||
@ -184,11 +200,11 @@ in
|
||||
"the value of a is ${a}" # 结果是 "the value of a is 1"
|
||||
```
|
||||
|
||||
## 7. 文件系统路径 {#file-system-path}
|
||||
## 文件系统路径 {#file-system-path}
|
||||
|
||||
Nix 中不带引号的字符串会被解析为文件系统路径,路径的语法与 Unix 系统相同。
|
||||
|
||||
## 8. 搜索路径 {#search-path}
|
||||
## 搜索路径 {#search-path}
|
||||
|
||||
> 请不要使用这个功能,它会导致不可预期的行为。
|
||||
|
||||
@ -198,7 +214,7 @@ Nix 会在看到 `<nixpkgs>` 这类三角括号语法时,会在 `NIX_PATH` 环
|
||||
|
||||
在这里做个介绍,只是为了让你在看到别人使用类似的语法时不至于抓瞎。
|
||||
|
||||
## 9. 多行字符串 {#multi-line-string}
|
||||
## 多行字符串 {#multi-line-string}
|
||||
|
||||
多行字符串的语法为 `''`,比如:
|
||||
|
||||
@ -210,7 +226,7 @@ Nix 会在看到 `<nixpkgs>` 这类三角括号语法时,会在 `NIX_PATH` 环
|
||||
''
|
||||
```
|
||||
|
||||
## 10. 函数 {#nix-function}
|
||||
## 函数 {#nix-function}
|
||||
|
||||
函数的声明语法为:
|
||||
|
||||
@ -297,7 +313,7 @@ pkgs.lib.strings.toUpper "search paths considered harmful" # 结果是 "SEARCH
|
||||
|
||||
可以通过 [Nixpkgs Library Functions - Nixpkgs Manual](https://nixos.org/manual/nixpkgs/stable/#sec-functions-library) 查看 lib 函数包的详细内容。
|
||||
|
||||
## 11. 不纯(Impurities) {#impurities}
|
||||
## 不纯(Impurities) {#impurities}
|
||||
|
||||
Nix 语言本身是纯函数式的,是纯的,「纯」是指它就跟数学中的函数一样,同样的输入永远得到同样的输出。
|
||||
|
||||
@ -307,7 +323,7 @@ Nix 有两种构建输入,一种是从文件系统路径等输入源中读取
|
||||
|
||||
> Nix 中的搜索路径与 `builtins.currentSystem` 也是不纯的,但是这两个功能都不建议使用,所以这里略过了。
|
||||
|
||||
## 12. Fetchers {#fetchers}
|
||||
## Fetchers {#fetchers}
|
||||
|
||||
构建输入除了直接来自文件系统路径之外,还可以通过 Fetchers 来获取,Fetcher 是一种特殊的函数,它的输入是一个 attribute set,输出是 Nix Store 中的一个系统路径。
|
||||
|
||||
@ -328,34 +344,68 @@ builtins.fetchTarball "https://github.com/NixOS/nix/archive/7c3ab5751568a0bc6343
|
||||
# result example(auto unzip the tarball) => "/nix/store/d59llm96vgis5fy231x6m7nrijs0ww36-source"
|
||||
```
|
||||
|
||||
## 13. Derivations {#derivations}
|
||||
## Derivations {#derivations}
|
||||
|
||||
一个构建动作的 Nix 语言描述被称做一个 Derivation,它描述了如何构建一个软件包,它的构建结果是一个 Store Object.
|
||||
Derivation 描述了如何构建一个软件包,是一个软件包构建流程的 Nix 语言描述,它声明了构建时需要有哪些依赖项、需要什么构建工具链、要设置哪些环境变量、哪些构建参数、先干啥后干啥等等。
|
||||
|
||||
Derivation 的构建结果是一个 Store Object,其中包含了软件包的所有二进制程序、配置文件等等内容。
|
||||
Store Object 的存放路径格式为 `/nix/store/<hash>-<name>`,其中 `<hash>` 是构建结果的 hash 值,`<name>` 是它的名字。路径 hash 值确保了每个构建结果都是唯一的,因此可以多版本共存,而且不会出现依赖冲突的问题。
|
||||
|
||||
`/nix/store` 被称为 Store,存放所有的 Store Objects,这个路径被设置为只读,只有 Nix 本身才能修改这个路径下的内容,以保证系统的可复现性。
|
||||
`/nix/store` 是一个特殊的文件路径,它被称为 Store,存放所有的 Store Objects,这个路径被设置为只读,只有 Nix 本身才能修改这个路径下的内容,以保证系统的可复现性。
|
||||
|
||||
在 Nix 语言的最底层,一个构建任务就是使用 builtins 中的不纯函数 `derivation` 创建的,我们实际使用的 `stdenv.mkDerivation` 就是它的一个 wrapper,屏蔽了底层的细节,简化了用法。
|
||||
Derivation 实质上只是一个 attribute set,Nix 底层会使用内置函数 `builtins.derivation` 将这个 attribute set 构建为一个 Store Object。
|
||||
我们实际编写 Derivation 时,通常使用的是 `stdenv.mkDerivation`,它只前述内置函数 `builtins.derivation` 的 Nix 语言 wrapper,屏蔽了底层的细节,简化了用法。
|
||||
|
||||
## 14. if...then...else... {#if-then-else}
|
||||
|
||||
if...then...else... 用于条件判断,它是一个有返回值的表达式,语法如下:
|
||||
一个简单的 Derivation 如下,它声明了一个名为 hello 的应用程序(摘抄自 [nixpkgs/pkgs/hello](https://github.com/NixOS/nixpkgs/blob/master/pkgs/applications/misc/hello/default.nix)):
|
||||
|
||||
```nix
|
||||
if 3 > 4 then "yes" else "no" # 结果为 "no"
|
||||
{ callPackage
|
||||
, lib
|
||||
, stdenv
|
||||
, fetchurl
|
||||
, nixos
|
||||
, testers
|
||||
, hello
|
||||
}:
|
||||
|
||||
stdenv.mkDerivation (finalAttrs: {
|
||||
pname = "hello";
|
||||
version = "2.12.1";
|
||||
|
||||
src = fetchurl {
|
||||
url = "mirror://gnu/hello/hello-${finalAttrs.version}.tar.gz";
|
||||
sha256 = "sha256-jZkUKv2SV28wsM18tCqNxoCZmLxdYH2Idh9RLibH2yA=";
|
||||
};
|
||||
|
||||
doCheck = true;
|
||||
|
||||
passthru.tests = {
|
||||
version = testers.testVersion { package = hello; };
|
||||
|
||||
invariant-under-noXlibs =
|
||||
testers.testEqualDerivation
|
||||
"hello must not be rebuilt when environment.noXlibs is set."
|
||||
hello
|
||||
(nixos { environment.noXlibs = true; }).pkgs.hello;
|
||||
};
|
||||
|
||||
passthru.tests.run = callPackage ./test.nix { hello = finalAttrs.finalPackage; };
|
||||
|
||||
meta = with lib; {
|
||||
description = "A program that produces a familiar, friendly greeting";
|
||||
longDescription = ''
|
||||
GNU Hello is a program that prints "Hello, world!" when you run it.
|
||||
It is fully customizable.
|
||||
'';
|
||||
homepage = "https://www.gnu.org/software/hello/manual/";
|
||||
changelog = "https://git.savannah.gnu.org/cgit/hello.git/plain/NEWS?h=v${finalAttrs.version}";
|
||||
license = licenses.gpl3Plus;
|
||||
maintainers = [ maintainers.eelco ];
|
||||
platforms = platforms.all;
|
||||
};
|
||||
})
|
||||
```
|
||||
|
||||
也可以与 let...in... 一起使用:
|
||||
|
||||
```nix
|
||||
let
|
||||
x = 3;
|
||||
in
|
||||
if x > 4 then "yes" else "no" # 结果为 "no"
|
||||
```
|
||||
|
||||
|
||||
## 参考
|
||||
|
||||
- [Nix language basics - nix.dev](https://nix.dev/tutorials/first-steps/nix-language)
|
||||
|
Loading…
Reference in New Issue
Block a user