diff --git a/.envrc b/.envrc new file mode 100644 index 000000000..ed11f2b3d --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake ./tools/nix-dev-shell \ No newline at end of file diff --git a/.gitignore b/.gitignore index 3080ca0b8..d83944e00 100644 --- a/.gitignore +++ b/.gitignore @@ -21,14 +21,8 @@ compile_commands.json /logs/ *.log -# Nix dev files -/.envrc -/.direnv/ -/flake.nix -flake.lock -/default.nix - # initram *.cpio *.cpio.xz *.cpio* +.direnv/ diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 000000000..8dfcc59dd --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,2 @@ +_build/ +result diff --git a/docs/conf.py b/docs/conf.py index e6bcc1c6f..c1da2dbc9 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -88,6 +88,7 @@ "strikethrough", "substitution", "tasklist", + "linkify" ] # sphinx-multiversion 指定哪个分支为 lastest 版本 diff --git a/docs/dev-skills/nix-skills.md b/docs/dev-skills/nix-skills.md new file mode 100644 index 000000000..e04dbac24 --- /dev/null +++ b/docs/dev-skills/nix-skills.md @@ -0,0 +1,61 @@ +# Nix 技巧 + +## 同一终端内避免反复指定 `nix run .#start-x86_64` + +需要注意的是,仅推荐重复使用 qemu 启动脚本 `dragonos-run`,因为 rootfs 在 `nix shell` eval 完毕后生成的仅仅是最终 `rootfs.tar` 拷贝到磁盘文件的过程,而 `rootfs.tar` 需要重新调用 nix build/shell/run 命令来生成。你的所有对 nix 的修改都需要通过重新执行 nix 命令来进行 eval 与构建,因此 `nix shell .#rootfs-x86_64` 意义不大。 + +```shell +❯ nix shell .#start-x86_64 .#rootfs-x86_64 +❯ which dragonos-rootfs +/nix/store/rpsb6f76hxzcfblghfgch2jl8413w6m4-dragonos-rootfs/bin/dragonos-rootfs +❯ which dragonos-run +/nix/store/1sdnhsjihj2h3mkdx7x0v8zy9ldfijml-dragonos-run/bin/dragonos-run +❯ dragonos-run +# DragonOS QEMU Start +❯ make kernel # 重新构建内核 +❯ dragonos-run # 不涉及启动脚本和rootfs的更改,直接qemu启动 +``` + +## 查看可用构建产物 + +```shell +❯ nix flake show +warning: Git tree '/workspace' is dirty +git+file:///workspace +├───apps +│ └───x86_64-linux +│ ├───rootfs-riscv64: app: 构建 riscv64 rootfs 镜像 +│ ├───rootfs-x86_64: app: 构建 x86_64 rootfs 镜像 +│ ├───start-riscv64: app: 以 riscv64 启动DragonOS +│ └───start-x86_64: app: 以 x86_64 启动DragonOS +└───packages + └───x86_64-linux + ├───rootfs-riscv64: package 'build-rootfs-image' + ├───rootfs-x86_64: package 'build-rootfs-image' + ├───start-riscv64: package 'run-dragonos' + └───start-x86_64: package 'run-dragonos' + +user/apps/about ❯ nix flake show +git+file:///workspace?dir=user/apps/about +└───packages + ├───aarch64-darwin + │ └───default omitted (use '--all-systems' to show) + ├───aarch64-linux + │ └───default omitted (use '--all-systems' to show) + ├───x86_64-darwin + │ └───default omitted (use '--all-systems' to show) + └───x86_64-linux + └───default: package 'about-static-x86_64-unknown-linux-musl-0.1.0' + +tests/syscall/gvisor ❯ nix flake show +git+file:///workspace?dir=user/apps/tests/syscall/gvisor +└───packages + └───x86_64-linux + └───default: package 'gvisor-tests' +``` + +## 回收 nix 构建历史缓存 + +```shell +❯ nix store gc +``` diff --git a/docs/dev-skills/write-docs.md b/docs/dev-skills/write-docs.md new file mode 100644 index 000000000..7f8fa1ccf --- /dev/null +++ b/docs/dev-skills/write-docs.md @@ -0,0 +1,32 @@ +# 贡献文档 + +## 使用 nix + +### 实时构建预览文档 + +sphinx-autobuild 作为默认 nix run 目标,写文档只需要 + +```shell +cd docs +nix run +``` + +然后访问 8000 端口即可。 + +### 构建文档为 drv (sphinx-build) + +```shell +cd docs +nix build +``` + +### 预览构建好的文档 (python http-server) + +```shell +cd docs +nix run .#release +``` + +## 使用 pip Makefile 构建文档 + +TODO diff --git a/docs/flake.lock b/docs/flake.lock new file mode 100644 index 000000000..100021151 --- /dev/null +++ b/docs/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1765838191, + "narHash": "sha256-m5KWt1nOm76ILk/JSCxBM4MfK3rYY7Wq9/TZIIeGnT8=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "c6f52ebd45e5925c188d1a20119978aa4ffd5ef6", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-25.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/docs/flake.nix b/docs/flake.nix new file mode 100644 index 000000000..7976983c7 --- /dev/null +++ b/docs/flake.nix @@ -0,0 +1,127 @@ +{ + description = "Reproducible Sphinx Documentation Build"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, flake-utils }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + lib = pkgs.lib; + + # ---------------------------------------------------------------- + # 1. 定义精确的 Python 环境 + # ---------------------------------------------------------------- + # 这里显式列出依赖,确保无论在哪台机器,Sphinx 版本一致 + pythonEnv = pkgs.python3.withPackages (ps: with ps; [ + sphinx + sphinx-multiversion + sphinx-autobuild + # 在这里添加你的 theme 或 extension,例如: + sphinx-rtd-theme + myst-parser + sphinxcontrib-mermaid + linkify-it-py + ]); + + # ---------------------------------------------------------------- + # 2. 从 Flake 输入中提取 Git 信息 (Pure 方式) + # ---------------------------------------------------------------- + # Nix 在求值时知道当前的 revision,不需要在 build 时运行 git 命令 + gitHash = if (self ? rev) then self.rev else "dirty-dev"; + isDirty = if (self ? rev) then "0" else "1"; + + buildDir = "./_build"; + port = 8000; + + in + { + # ---------------------------------------------------------------- + # Packages: 纯净构建 (nix build) + # ---------------------------------------------------------------- + # 这种方式构建的是"当前代码的快照",完全不依赖 .git 目录 + packages.default = pkgs.stdenv.mkDerivation { + name = "sphinx-docs"; + src = ./.; + + buildInputs = [ pythonEnv ]; + + # 将 Nix 提取的 Git 信息注入环境变量 + # 这完全替代了 Makefile 中 $(shell git rev-parse) 的逻辑 + env = { + LANGUAGE = "zh_CN"; + CURRENT_GIT_COMMIT_HASH = gitHash; + CURRENT_GIT_COMMIT_DIRTY = isDirty; + SPHINXBUILD = "sphinx-build"; + }; + + # 直接定义构建逻辑,不再依赖 Makefile + buildPhase = '' + echo "Building documentation for commit: $CURRENT_GIT_COMMIT_HASH" + + # 使用 -W 将警告视为错误,确保构建质量 + sphinx-build -M html . ${buildDir} \ + -D language=$LANGUAGE \ + -w _build/warnings.log + ''; + + installPhase = '' + mkdir -p $out + cp -r ${buildDir}/* $out/ + ''; + }; + + # ---------------------------------------------------------------- + # Apps: 快速运行工具 (nix run) + # ---------------------------------------------------------------- + # 提供一个脚本来预览构建结果 + apps.release = flake-utils.lib.mkApp { + drv = let targetDir = self.packages.${system}.default; + in pkgs.writeShellApplication { + name = "preview-docs"; + runtimeInputs = [ pythonEnv ]; + text = '' + echo "Open at http://localhost:${toString port}/index.html" + python3 -m http.server --directory "${targetDir}/html" ${toString port} + ''; + }; + }; + app.develop = flake-utils.lib.mkApp { + drv = pkgs.writeShellApplication { + name = "sphinx-autobuild"; + runtimeInputs = [ pythonEnv ]; + text = '' + sphinx-autobuild . ${buildDir} --host 0.0.0.0 --port ${toString port} + ''; + }; + }; + # 设置默认 run 行为 + apps.default = self.app.${system}.develop; + + # ---------------------------------------------------------------- + # DevShell: 开发环境 (nix develop) + # ---------------------------------------------------------------- + # 用于开发和需要访问 .git 的操作(如 sphinx-multiversion) + devShells.default = pkgs.mkShell { + packages = [ pythonEnv pkgs.git pkgs.gnumake ]; + + shellHook = '' + export LANGUAGE="zh_CN" + export SPHINXBUILD="sphinx-build" + + # 在开发环境中,我们可以动态获取 git 状态 + export CURRENT_GIT_COMMIT_HASH=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown") + + echo "🚀 Sphinx Dev Environment Loaded" + echo "-----------------------------------" + echo "Run 'make html' for standard build" + echo "Run 'make html-multiversion' for versioned build (Requires .git)" + echo "Run 'nix build' for clean production build" + ''; + }; + } + ); +} diff --git a/docs/index.rst b/docs/index.rst index e7ee61b0a..f89e39910 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -25,6 +25,8 @@ introduction/index introduction/build_system + introduction/develop_nix + introduction/devcontainer introduction/mirrors .. toctree:: @@ -55,13 +57,21 @@ .. toctree:: :maxdepth: 1 :caption: 应用层 - + userland/appdev/index + userland/rootfs/index + +.. toctree:: + :maxdepth: 1 + :caption: 开发指南 + + dev-skills/nix-skills + dev-skills/write-docs .. toctree:: :maxdepth: 1 :caption: Q&A - + questions/index diff --git a/docs/introduction/devcontainer.md b/docs/introduction/devcontainer.md new file mode 100644 index 000000000..0e743ae43 --- /dev/null +++ b/docs/introduction/devcontainer.md @@ -0,0 +1,31 @@ +# 使用 devcontainer 开发 DragonOS + +本教程以 VSCode 为例,需要装有 Docker 的 Linux。 + +## 克隆仓库 + +```shell +git clone https://github.com/DragonOS-Community/DragonOS.git +code DragonOS +``` + +## 进入 devcontainer 环境 + +在 VSCode 右下角会有弹窗,选择 `Reopen in Container`。如果不可见,请根据下列步骤来进入: +- 下载 devcontainer 插件 +- `ctrl+shift+p` 打开 VSCode 命令面板 +- 输入 `devcontainer` 字样,会有 `Reopen in Container` 的选项,点击即会构建 devcontainer 环境 + +构建可能需要一些时间,尤其 msr 的插件在网络环境不好的情况下容易安装失败。 + +## 构建 DragonOS! + +直接输入 + +```shell +make run-nographic +``` + +等待构建,最后会自动进入 DragonOS qemu 环境。 + +需要退出qemu环境,请输入 `ctrl+a` 然后按 `x`。 diff --git a/docs/introduction/develop_nix.md b/docs/introduction/develop_nix.md new file mode 100644 index 000000000..4f4d04b58 --- /dev/null +++ b/docs/introduction/develop_nix.md @@ -0,0 +1,77 @@ +# 使用 nix 开发 DragonOS + +nix 的引入使得 DragonOS 的开发环境不再依赖手动维护的 `bootstrap.sh` 。现在任意发行版都可通过安装 nix 环境快速构建运行 DragonOS! + +## 安装 nix 并启用 flake 功能 + +参考 https://nixos.org/download/ 安装 Nix: The Nix package manager. (不是 NixOS !) + +参考 https://wiki.nixos.org/wiki/Flakes#Setup 启用 flakes 功能。 + +- 如果你想体验 nix 带来的声明式管理,又不想更改发行版,尝试 home-manager 并在其上配置启用 flakes、direnv +- 否则可以直接以 nix standalone 的方式安装 flakes,或者每次输入命令时添加 `--experimental-features 'nix-command flakes'` + +## 克隆仓库 + +DragonOS 现在在多个托管平台上都有仓库镜像 +- `https://github.com/DragonOS-Community/DragonOS.git` +- `https://atomgit.com/DragonOS-Community/DragonOS.git` +- `https://cnb.cool/DragonOS-Community/DragonOS.git` + +```shell +git clone https://atomgit.com/DragonOS-Community/DragonOS.git +cd DragonOS +``` + +## 激活内核编译环境 + +```shell +nix develop ./tools/nix-dev-shell +``` + +如果你配置了 `direnv`,首次进入仓库目录会提示需要执行 `direnv allow`,相当于自动进入了 `nix develop` 环境。 + +## 编译内核 + +做一些 dirty 的修复(TODO: 兼容性改进 or 不再使用 Makefile 构建) + +```shell +grep -rlZ '+nightly-2025-08-10' ./build-scripts | xargs -0 sed -i 's/+nightly-2025-08-10//g' +grep -rlZ '+nightly-2025-08-10' ./kernel | xargs -0 sed -i 's/+nightly-2025-08-10//g' +sed -i 's/CCPREFIX=x86_64-linux-gnu-/CCPREFIX=/g' kernel/env.mk +``` + +执行编译 + +```shell +make kernel +``` + +默认状态下,这会将内核 elf 编译到 `./bin/kernel/kernel.elf` + +## 构建 rootfs + +```shell +nix run .#rootfs.x86_64 +``` + +这会生成 `./bin/qemu-system-x86_64.img` + +## 启动内核 + +```shell +nix run .#start.x86_64 +``` + +现在你能看到你的终端载入 DragonOS 了 + +:::{note} +需要退出 DragonOS (QEMU)环境,请输入 `ctrl + a`,然后 `x` +::: + +## 更多 nix 命令用法及 nix script 维护 + +- `cd docs && nix run` 构建文档并启动一个 http 服务器 +- 如果存储空间告急,`nix store gc` 清理悬空的历史构建副本 +- 项目根目录下 `nix flake show` 查看可供构建的目标 +- 更多 diff --git a/docs/requirements.txt b/docs/requirements.txt index 16ab91616..018e06029 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -5,3 +5,4 @@ sphinxcontrib-mermaid==1.0.0 git+https://git.mirrors.dragonos.org.cn/DragonOS-Community/sphinx-multiversion.git@5858b75#egg=sphinx-multiversion openai~=1.79 tqdm~=4.65.0 +linkify-it-py diff --git a/docs/userland/appdev/index.rst b/docs/userland/appdev/index.rst index ccf82118c..4ba661a37 100644 --- a/docs/userland/appdev/index.rst +++ b/docs/userland/appdev/index.rst @@ -1,4 +1,4 @@ -应用程序开发文档 +用户态构建文档(DADK) =================================================== diff --git a/docs/userland/rootfs/add-your-own-app.md b/docs/userland/rootfs/add-your-own-app.md new file mode 100644 index 000000000..44c85de22 --- /dev/null +++ b/docs/userland/rootfs/add-your-own-app.md @@ -0,0 +1,109 @@ +# 添加程序/添加自定义程序! + +由于新的 userland 构建采用了 nix 来管理,添加程序变得非常的简单!下面我们自顶向下地讲解如何添加程序进 DragonOS 里跑 + +## 概念 + +nix 中,一个软件包是一个 derivation,所以只需要用 nix 将你的程序定义为一个 derivation,那么他就是一个可供“安装”的软件包。 +`nixpkgs` 也提供了许多原生的软件包,甚至有一套语法来帮助我们快速地指定静态编译/交叉编译版本的软件包,而不需要手动指定工具链。 +下面,我们先来看看如何快速添加一个 `nixpkgs` 软件包到 DragonOS 中 + +## 添加一个 nixpkgs 软件包 + +首先我们来看到 `user/apps/default.nix` 中定义引用 `nixpkgs` 软件包的部分 + +```{literalinclude} ../../../user/apps/default.nix +:language: nix +:lines: 34-43 +``` + +这里我们先简单将 `static` 和 `cross` 等理解为提供好的现成的软件包调用前缀(即,你在其他nix教程中见到的引用软件包时的 `pkgs` 前缀),他们会帮助我们处理好依赖、交叉编译和静态编译的麻烦事~ +- `cross` : 使用 GNU 动态链接的软件,需要交叉编译时自动处理 +- `cross-musl` : 使用 musl 动态链接的软件,同样自动处理交叉 +- `static` : 使用 musl 静态编译的软件,自动处理交叉 + +显然,在这里,我们注入的都是静态链接的软件包,如 `busybox` 与 `dropbear`。更多的软件包,可以通过 `nix search github:NixOS/nixpkgs/nixos-25.11 ` 或者 https://search.nixos.org/packages?channel=25.11 快速检索。 + +```shell +~ ❯ nix search github:NixOS/nixpkgs/nixos-25.11 dropbear +evaluation warning: darwin.iproute2mac has been renamed to iproute2mac +* legacyPackages.x86_64-linux.dropbear (2025.88) + Small footprint implementation of the SSH 2 protocol +evaluation warning: 'dockerfile-language-server-nodejs' has been renamed to 'dockerfile-language-server' +evaluation warning: beets-stable was aliased to beets, since upstream releases are frequent nowadays +evaluation warning: beets-unstable was aliased to beets, since upstream releases are frequent nowadays +evaluation warning: 'f3d' now build with egl support by default, so `f3d_egl` is deprecated, consider using 'f3d' instead. +evaluation warning: beets-stable was aliased to beets, since upstream releases are frequent nowadays +evaluation warning: beets-unstable was aliased to beets, since upstream releases are frequent nowadays +evaluation warning: 'f3d' now build with egl support by default, so `f3d_egl` is deprecated, consider using 'f3d' instead. +evaluation warning: 'hsa-amd-aqlprofile-bin' has been replaced by 'aqlprofile'. +evaluation warning: 'system' has been renamed to/replaced by 'stdenv.hostPlatform.system' +evaluation warning: 'ethersync' has been renamed to 'teamtype' +evaluation warning: Please replace 'pure-lua' with 'moonlight-nvim' as this name was an error +evaluation warning: windows.mingw_w64_pthreads is deprecated, windows.pthreads should be preferred + +~ took 28s ❯ +``` + +此处检索出来的是 `legacyPackages.x86_64-linux.dropbear`,说明至少这个软件包存在于 x86_64 上,直接 `cross.dropbear` 引用的就是这个软件包。而使用 `static.dropbear` 则会因为没有远程构建缓存而重新构建(但仍然省去自己手动配置的麻烦) + +## 添加一个自定义软件包 + +### C/C++ +上面还可以看到有 `(static.callPackage ./about {})` ,这个 about 软件包即是自定义构建的。我们来看看如何用 nix 取代了它的 Makefile: + +```{literalinclude} ../../../user/apps/about/default.nix +:language: nix +``` + +你还可以用 nix 工具来迁移目前 NixOS 上没有收录的软件包进来!(当然,这很少见,尤其是非 GUI 的软件) + +更多可以参考: +- https://book.divnix.com/ch06-01-simple-c-program.html +- https://ryantm.github.io/nixpkgs/stdenv/stdenv/ +- https://wiki.nixos.org/wiki/C + +### Rust +对于简单的 Rust 程序,直接使用 nix 提供的 rustPlatform.buildRustPackage 构建即可,参考 `user/apps/tests/syscall/gvisor/default.nix` + +```nix +{ lib, pkgs, fenix, system, installDir }: + +let + fenixPkgs = fenix.packages.${system}; + toolchain = fenixPkgs.combine (with fenixPkgs; [ + minimal.rustc + minimal.cargo + ]); + rustPlatform = pkgs.makeRustPlatform { + cargo = toolchain; + rustc = toolchain; + }; + + runner = rustPlatform.buildRustPackage { + pname = "gvisor-test-runner-bin"; + version = "0.1.0"; + + src = ./runner; + cargoLock = { + lockFile = ./runner/Cargo.lock; + }; + + # 你可以在这里选择不把binary装在bin目录下 + postInstall = '' + mkdir -p $out/${installDir} + if [ -f "$out/bin/runner" ]; then + mv "$out/bin/runner" "$out/${installDir}/gvisor-test-runner" + # Clean up empty bin directory if it exists, to avoid clutter in symlinkJoin + rmdir "$out/bin" || true + fi + ''; + }; + + ... +``` + +对于复杂的应用程序,以及交叉编译,可以参考 fenix 的几个例子: +- https://github.com/nix-community/fenix#examples + +TODO: Multiplatform Rust Application diff --git a/docs/userland/rootfs/diskgen.md b/docs/userland/rootfs/diskgen.md new file mode 100644 index 000000000..7a9ec77c9 --- /dev/null +++ b/docs/userland/rootfs/diskgen.md @@ -0,0 +1,34 @@ +# 从软件包到RootFS镜像 + +定义完软件包后,无需做更多修改,直接 `nix run .#rootfs-${target}` 即可构建 RootFS。 + +## 生成 Docker 镜像 + +我们利用 `pkgs.dockerTools.buildImage` 的展平和拷贝属性,将 `user/apps` 下的软件包,以及 `user/sysconfig` +中的配置文件复制到单层 overlayfs 中(Docker原理),`buildImage` 的终产物是一个可以被 `docker import` 的一个 +`tar.gz`。见 `rootfs-tar.nix` + +```{literalinclude} ../../../user/rootfs-tar.nix +:language: nix +``` + +## 解压 Docker tar 并生成文件系统镜像 + +原理:解压 docker 镜像,拿出第一层中的 `layer.tar`,然后使用 `guestfish` 导入到虚拟镜像文件中。见 `user/default.nix` + +```{literalinclude} ../../../user/default.nix +:language: shell +:lines: 23-112 +``` + +## nix script 的背后 + +生成 Docker 镜像时,Docker Image 是作为二进制产物缓存在 /nix/store 中的,因此多次构建可能会快速占用硬盘。 + +hint: 要想释放磁盘空间,执行 `nix store gc` + +但第二步:解压 Docker tar 到文件系统磁盘镜像文件,是以生成一个 shell script 并运行来实现的,生成的 `disk-image-${target}.img` 只会存在一个在 `bin` 目录下;同时从 `.tar.gz` 中解压出来的 `rootfs.tar` 也会存在于 `bin` 目录下,你可以自己解压看看里面都存了些啥。 + +TODO: 后续脚本允许分两步进行,中间允许注入自定义文件(除了直接在 sysconfig 中注入外) + +如果你想看看这个脚本长什么样,你可以在项目根目录执行 `nix build .#rootfs-${target} -o bin/result` 。 nix 会把 script 对应的 derivation 链接到 result 目录下,只需 `cat bin/result/bin/build-rootfs-image` 就能看到全部命令。 diff --git a/docs/userland/rootfs/index.rst b/docs/userland/rootfs/index.rst new file mode 100644 index 000000000..e8075f6a5 --- /dev/null +++ b/docs/userland/rootfs/index.rst @@ -0,0 +1,9 @@ +用户态构建文档(Nix) +=================================================== + +.. toctree:: + :maxdepth: 1 + :caption: 目录 + + diskgen + add-your-own-app diff --git a/flake.lock b/flake.lock new file mode 100644 index 000000000..0fc081fae --- /dev/null +++ b/flake.lock @@ -0,0 +1,100 @@ +{ + "nodes": { + "fenix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "rust-analyzer-src": "rust-analyzer-src" + }, + "locked": { + "lastModified": 1765435813, + "narHash": "sha256-C6tT7K1Lx6VsYw1BY5S3OavtapUvEnDQtmQB5DSgbCc=", + "owner": "nix-community", + "repo": "fenix", + "rev": "6399553b7a300c77e7f07342904eb696a5b6bf9d", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1765835352, + "narHash": "sha256-XswHlK/Qtjasvhd1nOa1e8MgZ8GS//jBoTqWtrS1Giw=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "a34fae9c08a15ad73f295041fec82323541400a9", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1765838191, + "narHash": "sha256-m5KWt1nOm76ILk/JSCxBM4MfK3rYY7Wq9/TZIIeGnT8=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "c6f52ebd45e5925c188d1a20119978aa4ffd5ef6", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-25.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "lastModified": 1765674936, + "narHash": "sha256-k00uTP4JNfmejrCLJOwdObYC9jHRrr/5M/a/8L2EIdo=", + "owner": "nix-community", + "repo": "nixpkgs.lib", + "rev": "2075416fcb47225d9b68ac469a5c4801a9c4dd85", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixpkgs.lib", + "type": "github" + } + }, + "root": { + "inputs": { + "fenix": "fenix", + "flake-parts": "flake-parts", + "nixpkgs": "nixpkgs" + } + }, + "rust-analyzer-src": { + "flake": false, + "locked": { + "lastModified": 1765400135, + "narHash": "sha256-D3+4hfNwUhG0fdCpDhOASLwEQ1jKuHi4mV72up4kLQM=", + "owner": "rust-lang", + "repo": "rust-analyzer", + "rev": "fface27171988b3d605ef45cf986c25533116f7e", + "type": "github" + }, + "original": { + "owner": "rust-lang", + "ref": "nightly", + "repo": "rust-analyzer", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 000000000..acc815d4c --- /dev/null +++ b/flake.nix @@ -0,0 +1,87 @@ +{ + description = "RootFS"; + + inputs = { + fenix = { + url = "github:nix-community/fenix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11"; + flake-parts.url = "github:hercules-ci/flake-parts"; + }; + + outputs = inputs@{ self, nixpkgs, fenix, flake-parts }: flake-parts.lib.mkFlake { inherit inputs; } { + systems = [ + "x86_64-linux" + ]; + perSystem = { self', inputs', system, ... }: + let + nixpkgs = inputs.nixpkgs; + fenix = inputs.fenix; + pkgs = nixpkgs.legacyPackages.${system}; + lib = pkgs.lib; + rootfsType = "ext4"; + buildDir = "./bin"; # Specifying temp file location + + testOpt = { + # 自动测试项目,指定内核启动环境变量参数 AUTO_TEST + autotest = "none"; + syscall = { + enable = true; + testDir = "/opt/gvisor"; + version = "20251218"; + }; + }; + + mkOutputs = target: + let + diskPath = "${buildDir}/disk-image-${target}.img"; + qemuScripts = import ./tools/qemu/default.nix { + inherit lib pkgs testOpt diskPath; + # QEMU 相关参数: + # 内核位置 + kernel = "${buildDir}/kernel/kernel.elf"; # TODO: make it a drv 用nix构建内核,避免指定相对目录 + # -s -S + debug = false; + }; + + startPkg = qemuScripts.${target}; + rootfsPkg = pkgs.callPackage ./user/default.nix { + inherit lib pkgs nixpkgs fenix system target testOpt rootfsType buildDir diskPath; + }; + in + { + apps = { + # start-${target} 的产物只是一个shell脚本,因此启动相关的参数,直接在上面修改即可, + # 脚本不占什么空间所以重复eval也没关系,并且最终产出的脚本可读性更好. + "start-${target}" = { + type = "app"; + program = "${startPkg}/bin/dragonos-run"; + meta.description = "以 ${target} 启动DragonOS"; + }; + # rootfs 中涉及到基于docker镜像的rootfs构建,修改了 user/ 下软件包相关内容后, + # rootfs 的docker镜像会重复构建,并且由于nix特性,副本会全部保留 + # 因此可能会占很多空间,如果要清理空间请执行 nix store gc + "rootfs-${target}" = { + type = "app"; + program = "${rootfsPkg}/bin/dragonos-rootfs"; + meta.description = "构建 ${target} rootfs 镜像"; + }; + }; + packages = { + "start-${target}" = startPkg; + "rootfs-${target}" = rootfsPkg; + }; + }; + + allOutputs = map mkOutputs [ "x86_64" "riscv64" ]; + merged = lib.foldl' (acc: elem: { + apps = acc.apps // elem.apps; + packages = acc.packages // elem.packages; + }) { apps = {}; packages = {}; } allOutputs; + + in { + inherit (merged) apps packages; + }; + }; +} diff --git a/tools/nix-dev-shell/flake.lock b/tools/nix-dev-shell/flake.lock new file mode 100644 index 000000000..3aed1fdef --- /dev/null +++ b/tools/nix-dev-shell/flake.lock @@ -0,0 +1,100 @@ +{ + "nodes": { + "fenix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "rust-analyzer-src": "rust-analyzer-src" + }, + "locked": { + "lastModified": 1765435813, + "narHash": "sha256-C6tT7K1Lx6VsYw1BY5S3OavtapUvEnDQtmQB5DSgbCc=", + "owner": "nix-community", + "repo": "fenix", + "rev": "6399553b7a300c77e7f07342904eb696a5b6bf9d", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1765838191, + "narHash": "sha256-m5KWt1nOm76ILk/JSCxBM4MfK3rYY7Wq9/TZIIeGnT8=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "c6f52ebd45e5925c188d1a20119978aa4ffd5ef6", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-25.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "fenix": "fenix", + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "rust-analyzer-src": { + "flake": false, + "locked": { + "lastModified": 1765400135, + "narHash": "sha256-D3+4hfNwUhG0fdCpDhOASLwEQ1jKuHi4mV72up4kLQM=", + "owner": "rust-lang", + "repo": "rust-analyzer", + "rev": "fface27171988b3d605ef45cf986c25533116f7e", + "type": "github" + }, + "original": { + "owner": "rust-lang", + "ref": "nightly", + "repo": "rust-analyzer", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/tools/nix-dev-shell/flake.nix b/tools/nix-dev-shell/flake.nix index b627a347f..df0abf852 100644 --- a/tools/nix-dev-shell/flake.nix +++ b/tools/nix-dev-shell/flake.nix @@ -1,18 +1,19 @@ { description = "dragonos-nix-dev"; inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11"; fenix.url = "github:nix-community/fenix"; fenix.inputs.nixpkgs.follows = "nixpkgs"; - utils.url = "github:numtide/flake-utils"; + flake-utils.url = "github:numtide/flake-utils"; }; - outputs = { self, nixpkgs, fenix, utils, ... } @ inputs: - utils.lib.eachDefaultSystem (system: + outputs = { self, nixpkgs, fenix, flake-utils, ... } @ inputs: + flake-utils.lib.eachDefaultSystem (system: let pkgs = nixpkgs.legacyPackages.${system}; - rusttoolchain = fenix.packages.${system}.fromToolchainFile{ + rust-toolchain = fenix.packages.${system}.fromToolchainFile{ file = ../../kernel/rust-toolchain.toml; + sha256 = "sha256-3JA9u08FrvsLdi5dGIsUeQZq3Tpn9RvWdkLus2+5cHs="; }; in { devShells.default = pkgs.mkShell { @@ -21,13 +22,13 @@ git llvm libclang - rusttoolchain + rust-toolchain + gcc ]; env = { - LIBCLANG_PATH = "${pkgs.libclang.lib}/lib"; - }; - + LIBCLANG_PATH = "${pkgs.libclang.lib}/lib"; + }; # Shell启动脚本 shellHook = '' diff --git a/tools/qemu/default.nix b/tools/qemu/default.nix new file mode 100644 index 000000000..7fc4daeed --- /dev/null +++ b/tools/qemu/default.nix @@ -0,0 +1,151 @@ +{ + lib, + pkgs, + diskPath, + kernel, + testOpt, + debug ? false +}: + +let + qemuFirmware = pkgs.callPackage ./qemu-firmware.nix {}; + + baseConfig = { + nographic = true; + memory = "512M"; + cores = "2"; + shmId = "dragonos-qemu-shm.ram"; + }; + + riscv-uboot = pkgs.pkgsCross.riscv64-embedded.buildUBoot { + defconfig = "qemu-riscv64_smode_defconfig"; + extraMeta.platforms = [ "riscv64-linux" ]; + filesToInstall = [ "u-boot.bin" ]; + }; + + # 3. 参数生成器 (Nix List -> Nix List) + mkQemuArgs = { arch, isNographic }: + let + baseArgs = [ + "-m" baseConfig.memory + "-smp" "${baseConfig.cores},cores=${baseConfig.cores},threads=1,sockets=1" + "-object" "memory-backend-file,size=${baseConfig.memory},id=${baseConfig.shmId},mem-path=/dev/shm/${baseConfig.shmId},share=on" + "-netdev" "user,id=hostnet0,hostfwd=tcp::12580-:12580" + "-device" "virtio-net-pci,vectors=5,netdev=hostnet0,id=net0" + "-usb" + "-device" "qemu-xhci,id=xhci,p2=8,p3=4" + "-D" "qemu.log" + + # Boot Order + "-boot" "order=d" + + "-rtc" "clock=host,base=localtime" + # Trace events + "-d" "cpu_reset,guest_errors,trace:virtio*,trace:e1000e_rx*,trace:e1000e_tx*,trace:e1000e_irq*" + "-trace" "fw_cfg*" + ] ++ lib.optionals debug [ + # GDB Stub + "-s" "-S" + ]; + nographicArgs = lib.optionals isNographic ([ + "--nographic" + "-serial" "chardev:mux" + "-monitor" "chardev:mux" + "-chardev" "stdio,id=mux,mux=on,signal=off,logfile=serial_opt.txt" + ] ++ (if arch == "riscv64" then [ + "-device" "virtio-serial-device" "-device" "virtconsole,chardev=mux" + ] else [ + "-device" "virtio-serial" "-device" "virtconsole,chardev=mux" + ])); + kernelCmdlinePart = if isNographic then "console=/dev/hvc0" else ""; + in { + flags = baseArgs ++ nographicArgs; + cmdlineExtra = kernelCmdlinePart; + }; + + # 4. 运行脚本生成器 + mkRunScript = { name, arch, isNographic, qemuBin }: + let + qemuConfig = mkQemuArgs { inherit arch isNographic; }; + qemuFlagsStr = lib.escapeShellArgs qemuConfig.flags; + + initProgram = if arch == "riscv64" then "/bin/riscv_rust_init" else "/bin/busybox init"; + + # Define static parts of arguments using Nix lists + commonArchArgs = if arch == "x86_64" then [ + "-machine" "q35,memory-backend=${baseConfig.shmId}" + "-cpu" "IvyBridge,apic,x2apic,+fpu,check,+vmx," + ] else [ + "-cpu" "sifive-u54" + ]; + + kernelPath = if arch == "x86_64" then kernel else "${riscv-uboot}/u-boot.bin"; + + diskArgs = if arch == "x86_64" then [ + "-device" "virtio-blk-pci,drive=disk" + "-device" "pci-bridge,chassis_nr=1,id=pci.1" + "-device" "pcie-root-port" + "-drive" "id=disk,file=${diskPath},if=none" + ] else [ + "-device" "virtio-blk-device,drive=disk" + "-drive" "id=disk,file=${diskPath},if=none" + ]; + + # Generate bash code for dynamic parts + archSpecificBash = if arch == "x86_64" then '' + if [ "$ACCEL" == "kvm" ]; then + ARCH_FLAGS+=( "-machine" "accel=kvm" "-enable-kvm" ) + else + ARCH_FLAGS+=( "-machine" "accel=tcg" ) + fi + '' else '' + ARCH_FLAGS+=( "-machine" "virt,accel=$ACCEL,memory-backend=${baseConfig.shmId}" ) + ''; + + in pkgs.writeScriptBin name '' + #!${pkgs.runtimeShell} + + if [ ! -d "bin" ]; then echo "Error: Please run from project root (bin/ missing)."; exit 1; fi + + ACCEL="tcg" + if [ -e /dev/kvm ] && [ -w /dev/kvm ]; then ACCEL="kvm"; fi + + cleanup() { sudo rm -f /dev/shm/${baseConfig.shmId}; } + trap cleanup EXIT + # FIXED: 既然用了 sudo 运行 qemu,这里创建 shm 也需要权限, + # 但实际上 qemu 会自己创建,这里只需要保证清理。 + # 原脚本是 rm -rf ... -> qemu -> rm -rf ... + + EXTRA_CMDLINE="${qemuConfig.cmdlineExtra}" + + # FIXED: 补全缺失的默认内核参数 AUTO_TEST 和 SYSCALL_TEST_DIR + FINAL_CMDLINE="init=${initProgram} AUTO_TEST=${testOpt.autotest} SYSCALL_TEST_DIR=${testOpt.syscall.testDir} $EXTRA_CMDLINE" + + ARCH_FLAGS=( ${lib.escapeShellArgs commonArchArgs} ) + ${archSpecificBash} + + BOOT_ARGS=( "-kernel" "${kernelPath}" "-append" "$FINAL_CMDLINE" ) + + DISK_ARGS=( ${lib.escapeShellArgs diskArgs} ) + + echo -e "================== DragonOS QEMU Command Preview ==================" + echo -e "Binary: sudo ${qemuBin}" + echo -e "Base Flags: ${qemuFlagsStr}" + echo -e "Arch Flags: ''${ARCH_FLAGS[*]}" + echo -e "Boot Args: ''${BOOT_ARGS[*]}" + echo -e "Disk Args: ''${DISK_ARGS[*]}" + echo -e "==================================================================" + echo "" + + # --- 3. 执行 --- + ${qemuBin} --version + sudo ${qemuBin} ${qemuFlagsStr} -L ${qemuFirmware} "''${ARCH_FLAGS[@]}" "''${BOOT_ARGS[@]}" "''${DISK_ARGS[@]}" "$@" + ''; + + script = lib.genAttrs [ "x86_64" "riscv64" ] (arch: mkRunScript { + name = "dragonos-run"; + inherit arch; + isNographic = if arch == "riscv64" then true else baseConfig.nographic; + qemuBin = "${pkgs.qemu_kvm}/bin/qemu-system-${arch}"; + }); +in script diff --git a/tools/qemu/qemu-firmware.nix b/tools/qemu/qemu-firmware.nix new file mode 100644 index 000000000..000eaa125 --- /dev/null +++ b/tools/qemu/qemu-firmware.nix @@ -0,0 +1,27 @@ +{ pkgs }: + +pkgs.stdenv.mkDerivation { + pname = "qemu-system-data"; + version = "10.1.3+ds-1"; + + src = pkgs.fetchurl { + url = "http://ftp.cn.debian.org/debian/pool/main/q/qemu/qemu-system-data_10.1.3+ds-1_all.deb"; + sha256 = "0nz3whjiw3qmp27mhidh8fk7gfkcvk2vk7z4pr6w3is9hd2g36sg"; + }; + + nativeBuildInputs = with pkgs; [ dpkg ]; + + unpackPhase = '' + dpkg-deb -x $src . + ''; + + installPhase = '' + mkdir -p $out + cp -r usr/share/qemu/* $out/ + ''; + + meta = with pkgs.lib; { + description = "QEMU firmware files from Debian package"; + platforms = platforms.all; + }; +} diff --git a/user/apps/about/default.nix b/user/apps/about/default.nix new file mode 100644 index 000000000..61a6233d6 --- /dev/null +++ b/user/apps/about/default.nix @@ -0,0 +1,23 @@ +{ stdenv }: + +stdenv.mkDerivation { + pname = "about"; + version = "0.1.0"; + + src = ./.; + + makeFlags = [ + "ARCH=x86_64" + "CROSS_COMPILE=${stdenv.cc.targetPrefix}" + ]; + + installPhase = '' + mkdir -p $out/bin + install -m755 about $out/bin/about.elf + ''; + + meta = { + description = "About utility for DragonOS"; + platforms = [ "x86_64-linux" ]; + }; +} diff --git a/user/apps/about/flake.lock b/user/apps/about/flake.lock new file mode 100644 index 000000000..6eab61193 --- /dev/null +++ b/user/apps/about/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1765838191, + "narHash": "sha256-m5KWt1nOm76ILk/JSCxBM4MfK3rYY7Wq9/TZIIeGnT8=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "c6f52ebd45e5925c188d1a20119978aa4ffd5ef6", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-25.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/user/apps/about/flake.nix b/user/apps/about/flake.nix new file mode 100644 index 000000000..081704eb8 --- /dev/null +++ b/user/apps/about/flake.nix @@ -0,0 +1,18 @@ +{ + description = "About utility for DragonOS"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, flake-utils }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = import nixpkgs { inherit system; }; + in + { + packages.default = pkgs.pkgsStatic.callPackage ./default.nix { }; + } + ); +} diff --git a/user/apps/default.nix b/user/apps/default.nix new file mode 100644 index 000000000..209ac841b --- /dev/null +++ b/user/apps/default.nix @@ -0,0 +1,54 @@ +{ + lib, + pkgs, + nixpkgs, + system, + target, + fenix, + testOpt +}: + +# Return a list of app derivations to be copied into the rootfs. +let + cross = if system == "x86_64-linux" && target == "x86_64" then pkgs + else if target == "riscv64" then pkgs.pkgsCross.riscv64 + else import nixpkgs { + localSystem = system; + crossSystem = if target == "x86_64" then "x86_64-unknown-linux-gnu" + else abort "Unsupported target: ${target}}"; + }; + + cross-musl = if system == "x86_64-linux" && target == "x86_64" then pkgs.pkgsMusl + else if target == "riscv64" then pkgs.pkgsCross.riscv64-musl + else import nixpkgs { + localSystem = system; + crossSystem = if target == "x86_64" then "x86_64-unknown-linux-musl" + else abort "Unsupported target: ${target}-musl}"; + }; + + static = if system == "x86_64-linux" && target == "x86_64" then pkgs.pkgsStatic + else if target == "riscv64" then import nixpkgs { + crossSystem = lib.systems.examples.riscv64-musl; + isStatic = true; + } else abort "Unsupported static target: ${target}"; + + gvisor-syscall-tests = (pkgs.callPackage ./tests/syscall/gvisor { + inherit fenix system; + installDir = testOpt.syscall.testDir; + version = testOpt.syscall.version; + }); +in [ + static.busybox + static.curl + static.dropbear + cross.glibc + + # Simple C utility + (static.callPackage ./about {}) + +] +++ lib.optionals (target == "x86_64" && testOpt.syscall.enable) [ + # gvisor test case only included on x86_64 + gvisor-syscall-tests + # TODO: Add debian libcxx deps or FHS +] diff --git a/user/apps/tests/syscall/gvisor/default.nix b/user/apps/tests/syscall/gvisor/default.nix new file mode 100644 index 000000000..3adee8777 --- /dev/null +++ b/user/apps/tests/syscall/gvisor/default.nix @@ -0,0 +1,85 @@ +{ lib, pkgs, fenix, system, installDir, version ? "20251218" }: + +let + fenixPkgs = fenix.packages.${system}; + toolchain = fenixPkgs.combine (with fenixPkgs; [ + minimal.rustc + minimal.cargo + ]); + rustPlatform = pkgs.makeRustPlatform { + cargo = toolchain; + rustc = toolchain; + }; + + testsArchive = pkgs.fetchurl { + url = "https://cnb.cool/DragonOS-Community/test-suites/-/releases/download/release_${version}/gvisor-syscalls-tests.tar.xz"; + sha256 = "sha256-JVCjDtqF9iNw6B4pXGP39gZRs6rEqtLsrroihraPqQE="; + }; + + # 1. Build the Rust runner separately + # This ensures that changes to test scripts or data don't trigger a Rust rebuild. + runner = rustPlatform.buildRustPackage { + pname = "gvisor-test-runner-bin"; + version = "0.1.0"; + + src = ./runner; + cargoLock = { + lockFile = ./runner/Cargo.lock; + }; + + # Move the binary to the expected install directory structure + postInstall = '' + mkdir -p $out/${installDir} + if [ -f "$out/bin/runner" ]; then + mv "$out/bin/runner" "$out/${installDir}/gvisor-test-runner" + # Clean up empty bin directory if it exists, to avoid clutter in symlinkJoin + rmdir "$out/bin" || true + fi + ''; + }; + + # 2. Prepare the test data, scripts, and patched binaries + # This derivation handles downloading, extracting, and patching the tests. + tests = pkgs.stdenv.mkDerivation { + pname = "gvisor-tests-data"; + version = "0.1.0"; + + # Use sourceByRegex to only depend on relevant files. + # This prevents rebuilds when files in ./runner change. + src = lib.sourceByRegex ./. [ + "^whitelist\.txt$" + "^blocklists" + "^blocklists/.*" + "^run_tests\.sh$" + ]; + + nativeBuildInputs = [ pkgs.autoPatchelfHook ]; + + buildInputs = [ pkgs.stdenv.cc.cc.lib ]; + + installPhase = '' + mkdir -p $out/${installDir} + + install -m644 whitelist.txt $out/${installDir}/ + cp -r blocklists $out/${installDir}/ + install -m755 run_tests.sh $out/${installDir}/ + + # Bundle tests archive for offline systems + mkdir -p $out/${installDir}/tests + tar -xf ${testsArchive} -C $out/${installDir}/tests --strip-components=1 + + runHook preInstall + find $out/${installDir}/tests -type f -name '*_test' -exec install -m755 {} $out/${installDir}/tests \; || true + runHook postInstall + ''; + }; + +in pkgs.symlinkJoin { + name = "gvisor-tests"; + paths = [ runner tests ]; + meta = with lib; { + description = "gVisor syscall test runner and scripts"; + platforms = platforms.linux; + license = licenses.mit; + }; +} diff --git a/user/apps/tests/syscall/gvisor/flake.lock b/user/apps/tests/syscall/gvisor/flake.lock new file mode 100644 index 000000000..f9684fdfa --- /dev/null +++ b/user/apps/tests/syscall/gvisor/flake.lock @@ -0,0 +1,99 @@ +{ + "nodes": { + "fenix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "rust-analyzer-src": "rust-analyzer-src" + }, + "locked": { + "lastModified": 1765435813, + "narHash": "sha256-C6tT7K1Lx6VsYw1BY5S3OavtapUvEnDQtmQB5DSgbCc=", + "owner": "nix-community", + "repo": "fenix", + "rev": "6399553b7a300c77e7f07342904eb696a5b6bf9d", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "id": "flake-utils", + "type": "indirect" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1765838191, + "narHash": "sha256-m5KWt1nOm76ILk/JSCxBM4MfK3rYY7Wq9/TZIIeGnT8=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "c6f52ebd45e5925c188d1a20119978aa4ffd5ef6", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-25.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "fenix": "fenix", + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "rust-analyzer-src": { + "flake": false, + "locked": { + "lastModified": 1765400135, + "narHash": "sha256-D3+4hfNwUhG0fdCpDhOASLwEQ1jKuHi4mV72up4kLQM=", + "owner": "rust-lang", + "repo": "rust-analyzer", + "rev": "fface27171988b3d605ef45cf986c25533116f7e", + "type": "github" + }, + "original": { + "owner": "rust-lang", + "ref": "nightly", + "repo": "rust-analyzer", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/user/apps/tests/syscall/gvisor/flake.nix b/user/apps/tests/syscall/gvisor/flake.nix new file mode 100644 index 000000000..fca05fa23 --- /dev/null +++ b/user/apps/tests/syscall/gvisor/flake.nix @@ -0,0 +1,23 @@ +{ + description = "gVisor syscall test runner and scripts"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11"; + fenix = { + url = "github:nix-community/fenix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = { self, nixpkgs, fenix, flake-utils }: + let + pkgs = import nixpkgs { inherit system; }; + installDir = "share/gvisor-tests"; + system = "x86_64-linux"; + in + { + packages.${system}.default = pkgs.callPackage ./default.nix { + inherit fenix system installDir; + }; + }; +} diff --git a/user/apps/tests/syscall/gvisor/runner/.gitignore b/user/apps/tests/syscall/gvisor/runner/.gitignore index 786a31746..9511f4a21 100644 --- a/user/apps/tests/syscall/gvisor/runner/.gitignore +++ b/user/apps/tests/syscall/gvisor/runner/.gitignore @@ -1,6 +1,5 @@ # Rust构建产物 /target/ -Cargo.lock # IDE文件 .vscode/ diff --git a/user/apps/tests/syscall/gvisor/runner/Cargo.lock b/user/apps/tests/syscall/gvisor/runner/Cargo.lock new file mode 100644 index 000000000..27e77bcd0 --- /dev/null +++ b/user/apps/tests/syscall/gvisor/runner/Cargo.lock @@ -0,0 +1,558 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys", +] + +[[package]] +name = "anyhow" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "cc" +version = "1.2.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c481bdbf0ed3b892f6f806287d72acd515b352a4ec27a208489b8c1bc839633a" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chrono" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "clap" +version = "4.5.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "env_logger" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" + +[[package]] +name = "gvisor-test-runner" +version = "0.1.0" +dependencies = [ + "anyhow", + "chrono", + "clap", + "env_logger", + "log", + "regex", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "humantime" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" + +[[package]] +name = "iana-time-zone" +version = "0.1.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "is-terminal" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "js-sys" +version = "0.3.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.177" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" + +[[package]] +name = "log" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "proc-macro2" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "wasm-bindgen" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] diff --git a/user/default.nix b/user/default.nix new file mode 100644 index 000000000..69b082ca9 --- /dev/null +++ b/user/default.nix @@ -0,0 +1,123 @@ +{ + lib, + pkgs, + nixpkgs, + system, + target, + fenix, + buildDir, + testOpt, + rootfsType ? "vfat", + diskPath, + partitionType ? "mbr" +}: + +let + image = import ./rootfs-tar.nix { inherit lib pkgs nixpkgs system target fenix testOpt; }; + + # 构建脚本 - 在bin/目录下构建 + buildScript = pkgs.writeShellApplication { + name = "dragonos-rootfs"; + runtimeInputs = [ pkgs.coreutils pkgs.gnutar pkgs.libguestfs-with-appliance pkgs.findutils ]; + text = '' + set -euo pipefail + + # Ensure build directory exists + mkdir -p "${buildDir}" + + OUTPUT_TAR="${buildDir}/rootfs.tar" + + echo "==> Generating rootfs" + + # 创建临时目录 + TEMP_DIR=$(mktemp -d) + trap 'chmod +w -R "$TEMP_DIR" && rm -rf "$TEMP_DIR"' EXIT + + # 提取 layer.tar (rootfs) + echo " Extracting rootfs layer..." + cd "$TEMP_DIR" + tar -xzf ${image} + + # 找到 layer.tar 并复制到 bin/ + LAYER_TAR=$(find . -name "layer.tar" | head -1) + if [ -z "$LAYER_TAR" ]; then + echo "Error: layer.tar not found in docker image" + exit 1 + fi + + cp "$LAYER_TAR" "$OLDPWD/$OUTPUT_TAR" + cd "$OLDPWD" + + TAR_SIZE=$(du -h "$OUTPUT_TAR" | cut -f1) + echo " ✓ rootfs.tar created ($TAR_SIZE)" + + # 如果是 vfat 文件系统,需要特殊处理:排除 /nix/store,解引用符号链接 + FINAL_TAR="$OUTPUT_TAR" + # shellcheck disable=SC2050 + if [ "${rootfsType}" = "vfat" ]; then + echo " Processing rootfs for vfat (excluding /nix/store, dereferencing symlinks)..." + + EXTRACT_DIR=$(mktemp -d) + FILTERED_TAR="${buildDir}/rootfs-filtered.tar" + + # 添加到清理列表 + trap 'chmod +w -R "$TEMP_DIR" "$EXTRACT_DIR" && rm -rf "$TEMP_DIR" "$EXTRACT_DIR"' EXIT + + # 解压原始 tar,排除 /nix/store + echo " Extracting and not filtering..." + # tar --exclude='nix' -xf "$OUTPUT_TAR" -C "$EXTRACT_DIR" + chmod +w -R "$TEMP_DIR" "$EXTRACT_DIR" + fakeroot tar --owner=0 --group=0 --numeric-owner --exclude='proc' --exclude='dev' \ + --exclude='sys' -xf "$OUTPUT_TAR" -C "$EXTRACT_DIR" # 当 RootFS 里包含这几个文件夹时会报错 + + # 重新打包,解引用符号链接和硬链接 + echo " Re-packing with dereferenced links..." + fakeroot tar --owner=0 --group=0 --numeric-owner --dereference --hard-dereference -cf "$FILTERED_TAR" -C "$EXTRACT_DIR" . + + FILTERED_SIZE=$(du -h "$FILTERED_TAR" | cut -f1) + echo " ✓ Re-packed rootfs.tar created ($FILTERED_SIZE)" + + FINAL_TAR="$FILTERED_TAR" + fi + + echo "==> Building disk image at ${diskPath}" + + export LIBGUESTFS_CACHEDIR=/tmp + export LIBGUESTFS_BACKEND=direct + + # 创建磁盘镜像并初始化文件系统 + echo " Creating disk image..." + TEMP_IMG="${diskPath}.tmp" + + # 计算所需磁盘大小:tar包大小 + 1G 缓冲空间 + TAR_SIZE_KB=$(du -k "$FINAL_TAR" | cut -f1) + DISK_SIZE_KB=$(( TAR_SIZE_KB + 1024 * 1024 )) + truncate -s "''${DISK_SIZE_KB}K" "$TEMP_IMG" + + # 使用 guestfish 创建分区并注入 tar + echo " Initializing disk and copying rootfs..." + guestfish -a "$TEMP_IMG" < Build complete!" + echo " Rootfs tar: $OUTPUT_TAR" + echo " Disk image: ${diskPath}" + ''; + }; + +in buildScript diff --git a/user/rootfs-tar.nix b/user/rootfs-tar.nix new file mode 100644 index 000000000..5e4911127 --- /dev/null +++ b/user/rootfs-tar.nix @@ -0,0 +1,24 @@ +{ lib, pkgs, nixpkgs, system, target, fenix, testOpt }: + +# 产物是一个可以生成 rootfs.tar 的脚本 +let + apps = import ./apps { inherit lib pkgs nixpkgs system target fenix testOpt; }; + + sys-config = pkgs.runCommand "sysconfig" { + src = ./sysconfig; + } '' + mkdir -p $out + cp -r $src/* $out/ + ''; + + # 使用 buildImage 创建 Docker 镜像(单层) + # 直接返回 dockerImage,解压逻辑在 default.nix 中处理 + dockerImage = pkgs.dockerTools.buildImage { + name = "busybox-rootfs"; + copyToRoot = [ + sys-config + ] ++ apps; + keepContentsDirlinks = false; + }; + +in dockerImage