强曰为道

与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

第四章 包定义详解

第四章:包定义详解

4.1 包定义的结构

每个 Guix 包都用一个 S-expression(S 表达式)来定义,它是纯粹的 Scheme 代码。理解包定义是成为 Guix 高级用户的关键。

4.1.1 最小包定义示例

(package
  (name "hello")
  (version "2.12")
  (source (origin
            (method url-fetch)
            (uri (string-append "mirror://gnu/hello/hello-"
                                version ".tar.gz"))
            (sha256
             (base32
              "0ssi1wpaf7plaswqqjwigppsg5f漖qyvr2as4wd1g"))))
  (build-system gnu-build-system)
  (home-page "https://www.gnu.org/software/hello/")
  (synopsis "Hello, GNU world: An example GNU package")
  (description "GNU Hello prints a friendly message.")
  (license license:gpl3+))

4.1.2 包定义字段详解

字段类型必填说明
name字符串包名称(小写,用连字符分隔)
version字符串版本号
sourceorigin源码来源
build-system构建系统构建方式(如 gnu-build-system
inputs列表构建时和运行时依赖
native-inputs列表仅构建时依赖(不会出现在运行环境中)
propagated-inputs列表传播依赖(安装此包会自动安装这些依赖)
arguments列表传递给构建系统的参数
home-page字符串项目主页
synopsis字符串简短描述(一行)
description字符串详细描述
licenselicense 对象软件许可证
native-search-paths列表原生搜索路径
replacementpackage替代包(安全修复)

4.2 Scheme 语法基础

如果你不熟悉 Scheme/Lisp,以下是理解包定义所需的最低限度知识。

4.2.1 S-expression 基础

;; 注释以分号开头

;; 函数调用:(函数名 参数1 参数2 ...)
(string-append "hello" " " "world")  ; => "hello world"
(+ 1 2 3)                            ; => 6

;; 定义变量
(define name "vim")
(define version "9.0")

;; 列表
(list "git" "vim" "htop")

;; 引用(quote):不求值
'(a b c)  ; => (a b c)

;; 函数定义
(define (greet name)
  (string-append "Hello, " name "!"))

(greet "Guix")  ; => "Hello, Guix!"

4.2.2 常用 Scheme 构造

;; 条件表达式
(if (> 3 2)
    "yes"
    "no")  ; => "yes"

;; let 绑定
(let ((x 10)
      (y 20))
  (+ x y))  ; => 30

;; 字符串操作
(string-append "foo" "-" "bar")  ; => "foo-bar"
(string-length "hello")          ; => 5

;; 路径操作(Guix 内置)
(string-append "/gnu/store/" name "-" version)

4.2.3 Guix 特有的语法

;; use-modules:导入模块(类似 Python 的 import)
(use-modules (gnu packages vim)
             (gnu packages python)
             (guix gexp))

;; define-public:定义公共包(可被其他模块引用)
(define-public my-package
  (package ...))

;; origin:描述源码来源
(origin
  (method url-fetch)
  (uri "https://example.com/foo-1.0.tar.gz")
  (sha256 (base32 "0abc...")))

;; 查看源码哈希
;; $ guix download https://example.com/foo-1.0.tar.gz
;; 0abc...

4.3 源码来源(Origin)

4.3.1 常用获取方式

方法说明用途
url-fetch从 URL 下载官方发布包
git-fetchGit 克隆开发版本、GitHub 项目
hg-fetchMercurial 克隆使用 Mercurial 的项目
svn-fetchSVN 检出使用 SVN 的项目
patch-origin对已有 origin 打补丁修补漏洞

4.3.2 URL 下载

(source (origin
          (method url-fetch)
          (uri (string-append "https://example.com/"
                              name "-" version ".tar.gz"))
          (sha256
           (base32 "1abc..."))))

获取 sha256 哈希值:

# 使用 guix download 自动计算并显示哈希
guix download https://example.com/foo-1.0.tar.gz
# 输出:
# 1abc... (base32 hash)
# /gnu/store/...-foo-1.0.tar.gz

4.3.3 Git 克隆

(source (origin
          (method git-fetch)
          (uri (git-reference
                (url "https://github.com/user/project")
                (commit "v1.0.0")))
          (sha256
           (base32 "0xyz..."))))
# 获取 Git 源码的哈希
guix download --git-fetch --commit=v1.0.0 \
  https://github.com/user/project

4.3.4 文件列表和补丁

(source (origin
          (method url-fetch)
          (uri "https://example.com/foo-1.0.tar.gz")
          (sha256 (base32 "1abc..."))
          (patches (search-patches "foo-fix-build.patch"
                                   "foo-security-fix.patch"))
          (modules '((guix build utils)))
          (snippet '(begin
                      (delete-file "configure")
                      (copy-file "configure.ac" "configure")))))

4.4 构建系统(Build System)

构建系统决定了如何编译和安装软件包。

4.4.1 常用构建系统

构建系统说明典型项目
gnu-build-systemGNU Autotools(./configure && make && make install)大多数 C 项目
cmake-build-systemCMakeC++ 项目
cargo-build-systemRust CargoRust 项目
python-build-systemPython setuptools/pipPython 包
perl-build-systemPerl ExtUtils::MakeMakerPerl 模块
meson-build-systemMesonGNOME 生态项目
go-build-systemGo modulesGo 项目
haskell-build-systemHaskell Cabal/StackHaskell 项目
dune-build-systemOCaml DuneOCaml 项目
r-build-systemRR 包
texlive-build-systemTeX LiveLaTeX 宏包
trivial-build-system无特殊构建过程纯脚本、数据包

4.4.2 GNU Build System

最常用的构建系统,对应经典的 ./configure && make && make install 流程:

(package
  (name "example")
  ;; ...
  (build-system gnu-build-system)
  (arguments
    (list
      #:configure-flags #~(list "--disable-static")
      #:make-flags #~(list "CC=gcc")
      #:phases
      #~(modify-phases %standard-phases
          (add-after 'unpack 'fix-paths
            (lambda* (#:key inputs #:allow-other-keys)
              (substitute* "src/main.c"
                (("/usr/local")
                 (assoc-ref outputs "out"))))))))
  ;; ...)

4.4.3 构建系统参数(Arguments)

arguments 字段可以定制构建行为:

(arguments
  (list
    ;; 跳过测试(某些包的测试在沙箱中无法运行)
    #:tests? #f

    ;; 自定义 configure 参数
    #:configure-flags #~(list "--with-feature"
                              (string-append "--prefix=" #$output))

    ;; 自定义 make 参数
    #:make-flags #~(list (string-append "CC=" #$(cc-for-target)))

    ;; 修改构建阶段
    #:phases
    #~(modify-phases %standard-phases
        ;; 在 unpack 阶段之后执行
        (add-after 'unpack 'patch-source
          (lambda* (#:key inputs #:allow-other-keys)
            (substitute* "Makefile"
              (("/usr/bin/env python3")
               (which "python3")))))

        ;; 替换 install 阶段
        (replace 'install
          (lambda* (#:key outputs #:allow-other-keys)
            (let ((out (assoc-ref outputs "out")))
              (install-file "myapp" (string-append out "/bin"))))))))

4.4.4 构建阶段

GNU Build System 的标准阶段(phases):

阶段说明
unpack解压源码
patch-usr-bin-file修复 /usr/bin/file 引用
patch-source-shebangs修补脚本的 shebang 行
configure运行 ./configure
build运行 make
check运行 make check(测试)
install运行 make install
patch-shebangs修补安装后脚本的 shebang
strip剥离调试符号

4.5 依赖管理

4.5.1 依赖类型

Guix 区分三种依赖:

类型字段说明
普通依赖inputs构建和运行时都需要
原生依赖native-inputs仅构建时需要(如构建工具)
传播依赖propagated-inputs安装此包时自动引入
(package
  ;; ...
  ;; 运行时依赖
  (inputs (list ncurses glib gtk))

  ;; 仅构建时依赖
  (native-inputs
    (list pkg-config
          autoconf
          automake
          python))  ; Python 仅在构建脚本中使用

  ;; 传播依赖:安装此包的用户也会得到这些包
  (propagated-inputs
    (list python-setuptools)))  ; Python 包通常需要 setuptools

4.5.2 包的输出(Outputs)

一个包可以有多个输出:

(package
  (name "openssl")
  ;; ...
  (outputs '("out"    ; 运行时库和命令
             "doc"    ; 文档
             "dev"))) ; 头文件和 pkg-config

使用时可以只安装需要的输出:

# 只安装运行时库
guix install openssl

# 安装开发头文件
guix install openssl:dev

4.5.3 跨编译支持

(arguments
  (list
    #:make-flags
    #~(list (string-append "CC=" #$(cc-for-target)))))

cc-for-target 会自动在交叉编译时使用正确的工具链。


4.6 版本管理

4.6.1 版本号约定

Guix 遵循 GNU 版本号规范:

major.minor.patch

在包定义中,版本号通常与上游发布保持一致:

(package
  (name "example")
  (version "1.2.3")
  ;; source 自动使用 version 字段
  (source (origin
            (method url-fetch)
            (uri (string-append "https://example.com/example-"
                                version ".tar.gz"))
            (sha256 (base32 "0abc..."))))
  ;; ...)

4.6.2 预发布版本

;; 开发版本
(version "1.2.3-rc1")

;; Git 快照
(version (git-version "1.2.3" revision commit))
;; 生成类似 "1.2.3-1.abc1234" 的版本号

4.6.3 查看包的版本历史

# 查看通道中某个包的所有版本
guix show vim

# 查看特定包在 store 中的多个版本
guix package --list-installed | grep vim

4.7 完整包定义示例

示例一:简单的 C 程序

(define-public my-c-tool
  (package
    (name "my-c-tool")
    (version "1.0.0")
    (source (origin
              (method url-fetch)
              (uri (string-append "https://example.com/my-c-tool-"
                                  version ".tar.gz"))
              (sha256
               (base32 "0abc123..."))))
    (build-system gnu-build-system)
    (native-inputs (list pkg-config))
    (inputs (list zlib libpng))
    (home-page "https://example.com/my-c-tool")
    (synopsis "A simple C tool for image processing")
    (description "My-C-Tool provides fast image compression
and decompression using zlib and libpng.")
    (license license:gpl3+)))

示例二:Python 包

(define-public python-mylib
  (package
    (name "python-mylib")
    (version "2.1.0")
    (source (origin
              (method url-fetch)
              (uri (pypi-uri "mylib" version))
              (sha256
               (base32 "0def456..."))))
    (build-system python-build-system)
    (propagated-inputs
      (list python-numpy python-scipy))
    (native-inputs
      (list python-pytest))
    (home-page "https://pypi.org/project/mylib/")
    (synopsis "Scientific computing library")
    (description "MyLib provides numerical methods for
scientific computing applications.")
    (license license:bsd-3)))

💡 提示:对于 Python 包,pypi-uri 辅助函数可以自动生成 PyPI 下载 URL。

示例三:Rust 包

(define-public my-rust-cli
  (package
    (name "my-rust-cli")
    (version "0.5.0")
    (source (origin
              (method url-fetch)
              (uri (crate-uri "my-rust-cli" version))
              (sha256
               (base32 "0ghi789..."))))
    (build-system cargo-build-system)
    (arguments
      (list
        #:cargo-inputs
        `(("rust-clap" ,rust-clap-4)
          ("rust-serde" ,rust-serde-1)
          ("rust-tokio" ,rust-tokio-1))))
    (home-page "https://github.com/user/my-rust-cli")
    (synopsis "Fast command-line tool written in Rust")
    (description "A blazing-fast CLI tool for data processing.")
    (license license:expat)))

💡 提示:对于 Rust 包,使用 crate-uri 自动生成 crates.io 下载 URL,并使用 #:cargo-inputs 声明 Rust 依赖。


4.8 包定义的测试

4.8.1 本地构建测试

# 构建包(不安装)
guix build my-package

# 构建并查看完整日志
guix build my-package -v 3

# 仅运行测试阶段
guix build my-package --with-tests

# 跳过测试
guix build my-package --without-tests

4.8.2 使用 guix lint 检查

# 对包定义进行静态检查
guix lint my-package

# 检查内容包括:
# - 描述格式
# - 许可证是否正确
# - 主页是否可达
# - 版本是否有更新
# - 是否有已知安全漏洞

4.8.3 提交前检查清单

# 1. 编译通过
guix build my-package

# 2. Lint 检查
guix lint my-package

# 3. 风格检查
guix style my-package

# 4. 检查是否已有同名包
guix search ^my-package$

4.9 本地包开发工作流

4.9.1 使用 GUIX_PACKAGE_PATH

# 设置本地包定义路径
export GUIX_PACKAGE_PATH="$HOME/src/guix-packages:$GUIX_PACKAGE_PATH"

# 在该目录下创建模块结构
mkdir -p $HOME/src/guix-packages/myproject
;; $HOME/src/guix-packages/myproject/packages.scm
(define-module (myproject packages)
  #:use-module (guix packages)
  #:use-module (guix build-system gnu)
  #:use-module (guix gexp)
  #:use-module (guix licenses)
  #:use-module (guix download))

(define-public my-tool
  (package
    (name "my-tool")
    (version "1.0")
    (source (origin
              (method url-fetch)
              (uri (string-append "https://example.com/my-tool-"
                                  version ".tar.gz"))
              (sha256 (base32 "0abc..."))))
    (build-system gnu-build-system)
    (home-page "https://example.com")
    (synopsis "My tool")
    (description "A useful tool.")
    (license gpl3+)))
# 现在可以直接安装
guix install my-tool

# 或构建
guix build my-tool

4.9.2 使用 -L 参数

# 临时添加包定义路径
guix build -L ~/src/guix-packages my-tool

4.10 包定义字段速查表

场景字段/方法
添加 C 库依赖(inputs (list zlib))
添加构建工具(native-inputs (list cmake pkg-config))
Python 传播依赖(propagated-inputs (list python-numpy))
跳过测试(arguments (list #:tests? #f))
自定义编译选项(arguments (list #:configure-flags #~(list "--opt"))))
修改构建阶段#~(modify-phases %standard-phases ...)
禁用并行构建(arguments (list #:parallel-build? #f))
设置环境变量(arguments (list #:make-flags #~(list "CC=gcc")))

4.11 总结

本章深入讲解了 Guix 包定义的内部结构:

  1. 包定义结构——所有字段的含义和用法
  2. Scheme 语法——理解包定义所需的最低限度知识
  3. 源码来源——URL 下载、Git 克隆等多种方式
  4. 构建系统——GNU、CMake、Cargo 等构建系统
  5. 依赖管理——三种依赖类型和多输出支持
  6. 完整示例——C、Python、Rust 包的实际定义

下一章我们将学习通道(Channels)的管理机制。


扩展阅读