From 7c3c253dff0e9297b3962f265ac966d6638ef55f Mon Sep 17 00:00:00 2001 From: gapul <92638132+gapul@users.noreply.github.com> Date: Sat, 29 Aug 2026 14:22:04 +0900 Subject: [PATCH 1/4] =?UTF-8?q?feat(nix):=20Ladybird=20/=20Servo=20?= =?UTF-8?q?=E3=81=AE=E3=83=93=E3=83=AB=E3=83=89=E7=94=A8=20devShell=20?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WebKit 以外のブラウザエンジンを母艦でビルドするための devShell を nix/shells/browser-engines.nix に宣言する。どちらも Xcode の clang と macOS SDK を参照するので darwin 限定。 Homebrew を使わないのは darwin.nix が cleanup = "uninstall" で brew を 管理しているため。各プロジェクトの bootstrap が入れたパッケージは次の rebuild で消えるので、必要なツールは nix 側から供給する。 実際に踏んだ罠を3つコメントとして残した: - nixpkgs の libtool は GNU 版を素の `libtool` として入れるため Apple の /usr/bin/libtool を覆い隠す。skia が `libtool -static` を呼んで落ちるので、 Homebrew と同じく GNU 側を glibtool に退避する shim を挟む - Ladybird の find_compiler.py は macOS 上の clang 21 を明示的に拒否するため、 mkShellNoCC で nix の clang を PATH に出さない - bindgen は libclang を直接叩くので macOS SDK の sysroot が渡らず `#include ` で死ぬ。BINDGEN_EXTRA_CLANG_ARGS で明示する 両方ともビルドと実ページの描画まで確認済み。 --- docs/CHEATSHEET.md | 29 ++++++++ nix/flake.nix | 48 +++++++----- nix/shells/browser-engines.nix | 129 +++++++++++++++++++++++++++++++++ 3 files changed, 186 insertions(+), 20 deletions(-) create mode 100644 nix/shells/browser-engines.nix diff --git a/docs/CHEATSHEET.md b/docs/CHEATSHEET.md index dbd83356..b3a3e4e4 100644 --- a/docs/CHEATSHEET.md +++ b/docs/CHEATSHEET.md @@ -271,6 +271,35 @@ nix-init --url https://github.com/nosarthur/gita # → 動く flake.nix が生成される ``` +### ブラウザエンジンのビルド(Ladybird / Servo) + +WebKit 以外のエンジンを母艦でビルドするための devShell。どちらも Xcode の clang と +macOS SDK を使うので darwin 限定。定義は `nix/shells/browser-engines.nix`。 + +```bash +# Ladybird(CMake + vcpkg) +nix develop ~/.dotfiles/nix#ladybird +cd ~/Developer/github.com/LadybirdBrowser/ladybird +python3 Meta/ladybird.py build +./Build/release/bin/Ladybird.app/Contents/MacOS/Ladybird https://example.com + +# Servo(mach + cargo) +nix develop ~/.dotfiles/nix#servo +cd ~/Developer/github.com/servo/servo +./mach build --release --media-stack dummy +./target/release/servoshell --headless --exit -o out.png https://example.com +``` + +踏みやすい罠: + +- `./mach bootstrap` は Homebrew を叩く唯一の経路なので実行しない。必要な cmake / + pkg-config は devShell 側にある。`MACH_USE_NIX` も立てない(mach が darwin で + 評価できない `shell.nix` に再突入する) +- Servo の macOS 版は GStreamer 公式 pkg を sudo でシステムに入れないと通らないため、 + 宣言管理の外に出したくなければ `--media-stack dummy`(動画再生なし) +- Ladybird の headless は 2026-08 時点の master で壊れている(Compositor プロセスが + 起動しないまま WebContent が接続を叩いて落ちる)。GUI は正常 + --- ## 🩺 探索 / トラブル diff --git a/nix/flake.nix b/nix/flake.nix index 7edc853b..0748ed1a 100644 --- a/nix/flake.nix +++ b/nix/flake.nix @@ -563,26 +563,34 @@ # are Mach-O binaries. Putting them in a Linux shell got them execve'd, xargs fell back # to /bin/sh, and dash reported a syntax error inside the ELF. They stay darwin-only; # the git hooks are a local-dev convenience and checks.pre-commit is darwin-only too. - devShells.default = systemPkgs.mkShell ( - { - buildInputs = [ - systemPkgs.shellcheck - systemPkgs.statix - systemPkgs.stylua - systemPkgs.taplo - systemPkgs.yq-go - systemPkgs.jq - systemPkgs.just - systemPkgs.python3 # scripts/gen-docs.py (doc generation block) - systemPkgs.bun - systemPkgs.check-jsonschema - systemPkgs.actionlint - systemPkgs.gitleaks # om ci's gitleaks custom step - systemPkgs.git # ci-lint / ci-gitleaks use git ls-files / rev-parse - ] - ++ lib.optionals isDarwinWorkstation preCommit.enabledPackages; - } - // lib.optionalAttrs isDarwinWorkstation { inherit (preCommit) shellHook; } + devShells = { + default = systemPkgs.mkShell ( + { + buildInputs = [ + systemPkgs.shellcheck + systemPkgs.statix + systemPkgs.stylua + systemPkgs.taplo + systemPkgs.yq-go + systemPkgs.jq + systemPkgs.just + systemPkgs.python3 # scripts/gen-docs.py (doc generation block) + systemPkgs.bun + systemPkgs.check-jsonschema + systemPkgs.actionlint + systemPkgs.gitleaks # om ci's gitleaks custom step + systemPkgs.git # ci-lint / ci-gitleaks use git ls-files / rev-parse + ] + ++ lib.optionals isDarwinWorkstation preCommit.enabledPackages; + } + // lib.optionalAttrs isDarwinWorkstation { inherit (preCommit) shellHook; } + ); + } + # Ladybird and Servo build with Xcode's clang against the macOS SDK, + # so those two shells only exist on darwin. They are entered by hand + # rather than built by CI: the engines take hours and tens of GB. + // lib.optionalAttrs isDarwinWorkstation ( + import ./shells/browser-engines.nix { pkgs = systemPkgs; } ); } // lib.optionalAttrs isDarwinWorkstation { diff --git a/nix/shells/browser-engines.nix b/nix/shells/browser-engines.nix new file mode 100644 index 00000000..f8c1635a --- /dev/null +++ b/nix/shells/browser-engines.nix @@ -0,0 +1,129 @@ +# Build environments for the two independent browser engines that can be built +# on this Mac: Ladybird (CMake + vcpkg) and Servo (mach + cargo). +# +# Neither project is packaged; these shells only assemble the toolchain their +# own build systems shell out to, so that nothing has to be installed through +# Homebrew. That matters here because darwin.nix runs Homebrew with +# `cleanup = "uninstall"`, which would remove anything a project's bootstrap +# script installed behind nix's back on the next rebuild. +# +# Both are darwin-only: they compile with Xcode's clang and reference the +# macOS SDK by absolute path. +{ pkgs }: + +let + inherit (pkgs) lib; + + # Both projects pin an exact Rust version in rust-toolchain.toml (Ladybird + # 1.96.1, Servo 1.97.1), and only rustup reads that file. Shipping nixpkgs' + # rustc would silently build with whatever version nixpkgs happens to carry. + rust = pkgs.rustup; + + # GNU libtool and Apple's /usr/bin/libtool are unrelated programs that share a + # name. nixpkgs installs GNU's as plain `libtool`, which shadows Apple's, and + # skia's build then fails on `libtool -static -o libwuffs.a ...` -- an + # invocation only Apple's archiver understands. Homebrew avoids the collision + # by exposing GNU libtool as `glibtool`, and both projects' macOS instructions + # assume that layout, so reproduce it: `libtool` stays Apple's, while GNU's + # tools keep the names autotools actually searches for. + libtoolShim = pkgs.runCommand "libtool-macos-layout" { } '' + mkdir -p $out/bin + ln -s /usr/bin/libtool $out/bin/libtool + ln -s ${pkgs.libtool}/bin/libtool $out/bin/glibtool + ln -s ${pkgs.libtool}/bin/libtoolize $out/bin/glibtoolize + ln -s ${pkgs.libtool}/bin/libtoolize $out/bin/libtoolize + ''; + + # autoreconf still needs GNU libtool's m4 macros, which the shim does not carry. + aclocalPath = lib.concatStringsSep ":" [ + "${pkgs.libtool}/share/aclocal" + "${pkgs.autoconf-archive}/share/aclocal" + "${pkgs.automake}/share/aclocal" + ]; + + commonTools = [ + pkgs.ninja + pkgs.nasm + pkgs.autoconf + pkgs.autoconf-archive + pkgs.automake + libtoolShim + pkgs.pkg-config + pkgs.ccache + pkgs.cmake + rust + ]; + + # Shared by both shells: keep every nix compiler out of PATH and build with + # Xcode's clang. See the per-shell notes for why each project needs this. + appleToolchainHook = '' + export CC=/usr/bin/clang + export CXX=/usr/bin/clang++ + export ACLOCAL_PATH="${aclocalPath}''${ACLOCAL_PATH:+:$ACLOCAL_PATH}" + export PATH="${libtoolShim}/bin:$PATH" + ''; +in +{ + # Ladybird: ./Meta/ladybird.py build + # + # mkShellNoCC is deliberate. Ladybird's Meta/Utils/find_compiler.py explicitly + # rejects clang 21 on macOS -- it links against LLVM's libc++ and then fails on + # std::__1::__hash_memory -- and prefers Xcode clang when it finds one. Putting + # a nix clang in PATH would offer it a compiler it refuses to use. + ladybird = pkgs.mkShellNoCC { + packages = commonTools ++ [ + pkgs.python3 + # vcpkg's bootstrap shells out to these three. + pkgs.zip + pkgs.unzip + pkgs.curl + ]; + + shellHook = appleToolchainHook + '' + echo "ladybird shell: cc=$(clang --version | head -1)" + echo " libtool=$(readlink -f "$(command -v libtool)")" + ''; + }; + + # Servo: ./mach build --release --media-stack dummy + # + # The repo ships its own shell.nix, but it cannot evaluate on darwin -- udev + # and the X11 stack sit in its unconditional buildInputs -- so this shell + # replaces it. + # + # Do NOT set MACH_USE_NIX: it makes ./mach re-exec itself into + # `nix-shell shell.nix`, which is exactly the file that cannot evaluate. + # Homebrew is only ever reached through `mach bootstrap`, so the rule is + # simply never to run that; its Brewfile asks for cmake and pkg-config, both + # of which this shell already provides. + # + # `--media-stack dummy` is the workable default on macOS: Servo accepts only + # the official GStreamer .pkg there, which is a sudo install into + # /Library/Frameworks and therefore outside declarative management. Without it + # the build fails at link time rather than falling back. + servo = pkgs.mkShellNoCC { + packages = commonTools ++ [ + pkgs.uv + # .python-version pins 3.11, so uv symlinks this interpreter instead of + # downloading a build of its own. + pkgs.python311 + pkgs.gnumake # mozjs needs GNU make; Apple ships make 3.81 + pkgs.m4 + pkgs.perl + pkgs.yasm + pkgs.openssl + ]; + + shellHook = appleToolchainHook + '' + # mozangle and mozjs run their headers through bindgen, which drives + # libclang directly instead of the clang driver. Nothing supplies the + # macOS SDK sysroot that way, and the parse dies on `#include `. + # Point bindgen at Xcode's own libclang, matching CC/CXX, and hand it the + # sysroot explicitly. + export SDKROOT="''${SDKROOT:-$(xcrun --show-sdk-path)}" + export LIBCLANG_PATH="$(xcode-select -p)/Toolchains/XcodeDefault.xctoolchain/usr/lib" + export BINDGEN_EXTRA_CLANG_ARGS="-isysroot $SDKROOT" + echo "servo shell: rust via rustup -- never run ./mach bootstrap (it calls brew)" + ''; + }; +} From 825b1d9ff2a5efdcac003125463dd424566cfe4d Mon Sep 17 00:00:00 2001 From: gapul <92638132+gapul@users.noreply.github.com> Date: Sat, 29 Aug 2026 16:32:53 +0900 Subject: [PATCH 2/4] =?UTF-8?q?fix(nix):=20ladybird=20=E3=82=B7=E3=82=A7?= =?UTF-8?q?=E3=83=AB=E3=81=8B=E3=82=89=20nix=20=E3=81=AE=20curl=20?= =?UTF-8?q?=E3=82=92=E5=A4=96=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vcpkg は curl と libpsl を自前でビルドするが、nix の curl を PATH に 置くとそちらが勝ち、liblagom-url が /nix/store の libpsl に、vcpkg の libcurl が /nix/store の nghttp2 にリンクされていた。 devShell を抜けるとその store path を参照するものが無くなるため、次の GC で消える。実際に消えて Ladybird が dyld の "Library not loaded" で 起動しなくなった。ダウンロードは /usr/bin/curl で足りる。 --- nix/shells/browser-engines.nix | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/nix/shells/browser-engines.nix b/nix/shells/browser-engines.nix index f8c1635a..6b16c058 100644 --- a/nix/shells/browser-engines.nix +++ b/nix/shells/browser-engines.nix @@ -73,10 +73,15 @@ in ladybird = pkgs.mkShellNoCC { packages = commonTools ++ [ pkgs.python3 - # vcpkg's bootstrap shells out to these three. + # vcpkg's bootstrap shells out to zip/unzip/tar; macOS supplies tar. + # + # Deliberately no pkgs.curl here. vcpkg builds its own curl and libpsl, but + # a nix curl on the search path wins, and liblagom-url ends up linked + # against a /nix/store libpsl. Nothing roots that path once the shell is + # gone, so the next GC deletes it and Ladybird stops launching with a dyld + # "Library not loaded" error. /usr/bin/curl covers the download step. pkgs.zip pkgs.unzip - pkgs.curl ]; shellHook = appleToolchainHook + '' From d7a16a5be0757cbf193ec0482bc0bbb99e4f1fc6 Mon Sep 17 00:00:00 2001 From: gapul <92638132+gapul@users.noreply.github.com> Date: Sat, 29 Aug 2026 17:15:17 +0900 Subject: [PATCH 3/4] =?UTF-8?q?docs(cheatsheet):=20=E3=83=96=E3=83=A9?= =?UTF-8?q?=E3=82=A6=E3=82=B6=E3=82=A8=E3=83=B3=E3=82=B8=E3=83=B3=E3=81=AE?= =?UTF-8?q?=E3=83=93=E3=83=AB=E3=83=89=E3=81=A7=E8=B8=8F=E3=82=93=E3=81=A0?= =?UTF-8?q?=E7=BD=A0=E3=82=92=E8=BF=BD=E8=A8=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit devShell に依存を足すと成果物が /nix/store を参照し、GC 後に起動不能に なる件と、重いビルドを macmini に投げる手順。 --- docs/CHEATSHEET.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/CHEATSHEET.md b/docs/CHEATSHEET.md index b3a3e4e4..614eee00 100644 --- a/docs/CHEATSHEET.md +++ b/docs/CHEATSHEET.md @@ -299,6 +299,12 @@ cd ~/Developer/github.com/servo/servo 宣言管理の外に出したくなければ `--media-stack dummy`(動画再生なし) - Ladybird の headless は 2026-08 時点の master で壊れている(Compositor プロセスが 起動しないまま WebContent が接続を叩いて落ちる)。GUI は正常 +- devShell に依存ライブラリを足さない。vcpkg が自前で建てるものと衝突すると nix 側が + 勝ち、成果物が `/nix/store` を参照する。その store path は誰も root していないので + 次の GC で消え、ある日突然 dyld の "Library not loaded" で起動しなくなる。疑ったら + `otool -L` で `/nix/store` 参照が無いことを確認する +- ビルドが重いときは macmini に投げる(10 コア / 24GB / 空きが多い)。home が両機とも + `/Users/gapul` なので、`Build/release` を同じパスに rsync すればそのまま動く --- From 203264f910f0f1fdc77cc509abe00c409a05a72d Mon Sep 17 00:00:00 2001 From: gapul <92638132+gapul@users.noreply.github.com> Date: Sat, 29 Aug 2026 17:29:38 +0900 Subject: [PATCH 4/4] =?UTF-8?q?docs(cheatsheet):=20Servo=20=E3=81=AE=20med?= =?UTF-8?q?ia=20=E6=9C=89=E5=8A=B9=E3=83=93=E3=83=AB=E3=83=89=E3=81=AF=20m?= =?UTF-8?q?acmini=20=E3=81=A7=E8=A1=8C=E3=81=86=E6=97=A8=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E8=A8=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GStreamer 公式 pkg は sudo でのシステム install になるため macmini 側に だけ入れた。mach package が dylib を .app に同梱するので、母艦は成果物を 受け取るだけでよい。 --- docs/CHEATSHEET.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/CHEATSHEET.md b/docs/CHEATSHEET.md index 614eee00..47180d96 100644 --- a/docs/CHEATSHEET.md +++ b/docs/CHEATSHEET.md @@ -295,8 +295,11 @@ cd ~/Developer/github.com/servo/servo - `./mach bootstrap` は Homebrew を叩く唯一の経路なので実行しない。必要な cmake / pkg-config は devShell 側にある。`MACH_USE_NIX` も立てない(mach が darwin で 評価できない `shell.nix` に再突入する) -- Servo の macOS 版は GStreamer 公式 pkg を sudo でシステムに入れないと通らないため、 - 宣言管理の外に出したくなければ `--media-stack dummy`(動画再生なし) +- Servo の音声/動画を有効にするには GStreamer 公式 pkg (本体 + devel の2つ) を sudo で + システムに入れる必要があり、これは宣言管理の外に出る。macmini 側にだけ入れてあるので、 + media 有効ビルドは macmini で行う。`./mach package` が GStreamer dylib を .app に + 同梱するので、母艦は Servo.app を受け取るだけでよく何も入れなくてよい。 + 入れずにビルドするなら `--media-stack dummy`(再生なし) - Ladybird の headless は 2026-08 時点の master で壊れている(Compositor プロセスが 起動しないまま WebContent が接続を叩いて落ちる)。GUI は正常 - devShell に依存ライブラリを足さない。vcpkg が自前で建てるものと衝突すると nix 側が