Linux kernelをninjaでビルドする
ninjaってなに
Ninja, a small build system with a focus on speed
make代替みたいなビルドシステムで, 小さくてスピードが速いのが特徴となっている. Chromiumとかで使われている様子.
Makefileを読めるわけではなく, ninja用のビルドファイルが必要.
kninja
GitHub - rabinv/kninja: Ninja build file generator for the Linux kernel
kninjaはLinux kernel用にkninjaのビルドファイルを生成してくれるスクリプト.
はやいの?
kninjaによるルール生成にはkninja.pyを使う. ルール生成のためにmakeしてから, “make -p"の情報からビルドファイルを生成する. したがって, 下のようにルール生成まで入れると1分ほどkninjaの方が遅くなる.
フルビルドの場合
$ make clean $ time make -j$(nproc) real 4m21.090s user 41m19.146s sys 2m48.689s $ make clean $ time python3.4 ~naota/src/kninja/kninja.py [INFO] Ensuring full build: make -j 12 … [INFO] Generating make database: make -p [INFO] Caching make database to .makedb [INFO] Parsing make database (2563191 lines) [INFO] Wrote build.ninja (3282 rules, 3255 build statements) [INFO] Wrote .ninja_deps (2597 targets, 1144879 deps) [INFO] Wrote .ninja_log (3282 commands) [INFO] Checking ninja status: ninja -d explain -n [INFO] All OK real 5m15.829s user 40m58.248s sys 2m59.674s
ビルド済
$ time make -j$(nproc) real 0m5.883s user 0m24.015s sys 0m9.905s $ time python3.4 ~naota/src/kninja/kninja.py real 1m4.807s user 1m12.236s sys 0m23.291s
ただ, これはルール生成をしているから遅いのであって, 一度ルールが作られてしまえばninjaの方が速い. たとえば, なんの変更もない時は以下のようにmakeだと6秒に対して, ninjaは1秒かからない. (ただこれはCHKとかCALLとかでビルドで関係ないチェックスクリプトが走っているのもあるか)
$ time make -j$(nproc) CHK include/config/kernel.release CHK include/generated/uapi/linux/version.h CHK include/generated/utsrelease.h CHK include/generated/timeconst.h CHK include/generated/bounds.h CHK include/generated/asm-offsets.h CALL scripts/checksyscalls.sh CHK include/generated/compile.h CHK kernel/config_data.h … real 0m6.005s user 0m24.038s sys 0m9.827s $ time ninja ninja: no work to do. real 0m0.204s user 0m0.152s sys 0m0.052s
1つのファイルを変更した場合は, makeで7秒, ninjaで3秒といったところ.
$ touch fs/btrfs/ctree.c; time make -j$(nproc) … real 0m7.129s user 0m27.334s sys 0m10.295s $ touch fs/btrfs/ctree.c; time ninja [3/3] ld -r -m elf_x86_64 -T ./scripts/module-common.lds --build-id -o fs/btrfs/btrfs.ko fs/btrfs/btrfs.o fs/btrfs/btrfs.mod.o ; true real 0m2.922s user 0m2.755s sys 0m0.222s
速くはなるので, inotifywaitでいじっているファイルを監視して, ninjaを走らせるループなどしておくとべんり. (while inotifywait fs/btrfs/*.c; do ninja ;done とか)
ただ.configやKconfigやらがupdateされるたびにルールを作り直す必要があるため, 全くLinux kernelを開発しませーん, ビルドしてインストールするだけです〜という人にはいらないツールになる.
compile DBを生成する
Emacsだとirony-mode, Atomだとlinter-clangやautocomplete-clangのように, clangを使ってCのsyntax checkや補完をしてくれるパッケージがある.
- GitHub - Sarcasm/irony-mode: A C/C++ minor mode for Emacs powered by libclang
- GitHub - AtomLinter/linter-clang: Lint C-based files using Clang.
- GitHub - yasuyuky/autocomplete-clang
これらを動かすにあたって, include pathやらdefineなどなどコンパイルオプションを適切に渡す必要がある. このへんのツールは"compile_commands.json"に書いてある, コンパイルオプションを読んでくれる.
ninjaには, このjsonファイルをdumpする機能がある. ninja -t compdb
で以降の引数で指定されたルールのjsonをはきだすので下のようなコマンドを実行すると全ルールとれる.
$ ninja -t compdb $(awk '/^rule /{print $2}' build.ninja) > compile_commands.json
ironyはなんやらうまくやってくれてる感じあるけど, gccだけが対応していてclangが対応していないoptionがあるとAtomの子たちはだいたいうまく動かない. よって, こんな感じで適当にargを削ってあげるといい.
DELETE_ARGS=( '-falign-jumps=1' '-falign-loops=1' '-fconserve-stack' '-fno-delete-null-pointer-checks' '-fno-var-tracking-assignments' '-mfentry' '-mno-fp-ret-in-387' '-mpreferred-stack-boundary=3' '-mskip-rax-setup' '--param=allow-store-data-races=0' '-DCC_HAVE_ASM_GOTO' '-Wno-frame-address' '-Wno-unused-but-set-variable' '-Werror=designated-init' ) script="" for x in ${DELETE_ARGS[@]};do script="s/$x//;${script}" done sed -i -e "${script}" compile_commands.json
compdbを使えばheader情報とかも追加できる
GitHub - Sarcasm/compdb: The compilation database Swiss army knife
いろいろ入れてこんな感じか
所感
なんだかんだEmacsのパッケージの方がCみたいな言語にはつよいね