NixのbuildをOpenTelemetryを使ってtraceする
概要
Nixのbuildは依存関係が複雑でどのビルドにどの程度の時間がかかっているかを把握するのは難しい。
そこで OpenTelemetry を使ってbuildやcacheの取得にかかった時間を可視化することを試みる。
NixはC++で書かれているので GitHub - open-telemetry/opentelemetry-cpp: The OpenTelemetry C++ Client を使ってNix自体にpatchを当てることでtraceの機能を実現することもできるが
- Observabilityを上げる上でコード変更を伴わない方がversionの変更に対応しやすい
- C++を書きたくない
といった理由から、今回はNixのsource codeを変更せずに可観測性を上げることを目標とする。ちなみに後者の理由が9割である。
実装方針(build)
Nixのoptionに preBuildHook
と postBuildHook
というものがある。これはbuildの前と後で任意のscriptを実行させることができる。
buildの開始と終了がわかればtrace情報が作成できるので、これらを使っていく。
ちなみに、検証した結果hookは以下のような仕様になっている
pre-build-hook
は$1
にderivationの名前
が$2
にderivationの名前.chroot
が入った状態で呼ばれる。post-build-hook
はDRV_PATH
OUT_PATHS
といった環境変数が入った状態で呼ばれる。- pre-build-hook は sandbox optionが有効になっていないと呼ばれない
また、shell scriptからtrace情報を送信するためのツールとして otel-cli というものがあり、今回はこれで送信する。
pre-build-hookとpost-build-hookの間で情報を受け渡す手段が用意されていないので、 pre-build-hookで $TMPDIR/$drv
に開始時刻を書き込んで、 post-build-hookで otel-cliからtrace情報を送る方針にする。
自分の認識の限りではあるビルドの中で同じderivationがビルドされることはないのでうまくいくはず。
雑に試す場合は以下のようにする
pre-build-hook
drv=$1 name=${drv#/nix/store/} $path/bin/date +%s.%N > /tmp/$name"
post-build-hook
drv=$DRV_PATH name=${drv#/nix/store/} ${pkgs.otel-cli}/bin/otel-cli span \ --start $($path/bin/cat /tmp/$name) \ --end $($path/bin/date +%s.%N) \ --config $OTEL_CLI_CONFIG \ -n $name \ -s nix-build \ --fail
dateコマンドやotel-cliへのコマンドは当然絶対PATHで用意すること
helloコマンドをビルドした感じは以下の様な感じ
実装方針(cache)
上記のtraceはbuildの時間しか計測されず、cacheを取得している時間は含まれていない
また、cacheの取得はbuildではないので hookを使うことができない
しかしcacheをhttpで取得している場合はproxyを使うことでtrace情報を取得できる可能性がある
今回はEnvoy proxyを使ってcacheのtrace情報を取得する。
envoyのtraceに関しては公式のドキュメントがあるので、以下を読む。
ドキュメントによると、traceの設定をした上で traceparent
headerをつければ送ってくれるようだ
ただ、cacheの取得のリクエストに対して traceparent
headerにtrace idを乗せた上で送る方法を考える必要がある。
nix側のコードを見た感じではそのような設定はないので、今回は traceparent
headerを載せるenvoy proxyとtraceの送信を行うproxyの2段構成でやる。
つまり 、nix -> (traceparent headerをつけるproxy:8081) -> (cache.nixos.orgにrequestを通しながらtraceの送信を行うproxy:8080)
といった感じだ。
trace id を envoyのconfigに対して直接書き込でおけば比較的簡単に1段目のproxyは実装することができるが、今回はビルドごとにidを発行したい。
なので、envoyのconfigを動的に変更する xDS configuration API を使う。
xDSはistioとかを使っていると比較的に簡単に使うことができるのだが流石に大袈裟な仕組みになってしまうので、今回はファイルシステムの変更を検知して設定を書き換えてくれるDynamic from filesystemを使う
Configuration: Dynamic from filesystem — envoy 1.27.0-dev-f12afb documentation
ファイルシステムの変更検知にどのsyscallを使っているのか知らないが mvコマンド相当を使わないといけないらしい。
雑にやる場合、以下の様になる。
export ENVOY_LDS_FILE_TMP="$TMPDIR/tmp.json" jq --arg traceparent "$TRACEPARENT" \ '.resources[0].filter_chains[0].filters[0].typed_config.route_config.request_headers_to_add[0]={"header": {"key": "traceparent","value": $traceparent}}' \ ${./lds.json} > "$ENVOY_LDS_FILE_TMP" rm "$ENVOY_LDS_FILE" mv "$ENVOY_LDS_FILE_TMP" "$ENVOY_LDS_FILE" sleep 1 nix build "$1" \ --option substituters "http://localhost:8081?priority=20" \ --no-link \ -L
sleep 1を挟んでいるのはファイルの変更検知後にenvoyの設定変更を待つためである
実行した感じは以下の様になる
tracing-nix-build
今回試しやすい様にある程度再現しやすい形でCodeをpushしておいた
ただ、現段階では動かすのにテクがいるのでそれを書いておく。
まず、上記のスクリーンショットにも使っているがjaegerを動かす必要があるが、jaeger自体がかなり複雑なので提供されているdocker imageを使うのが良いだろう。
上記のrepositoryで言うと example ディレクトリの中で docker-compose up
で立ち上げてもらう。
opentelemetry collectorが 4317で立ち上がるので、example/envoy.yaml
と exapmle/config.json
の中にある 192.168.64.1
を そのアドレス(127.0.0.1など)に書き換えてもらう。
これは私の環境ではdocker自体はmacos上で動かしておりUTMを使ってnixos上でenvoyと今回のtracing-nix-buildを実行しているので、上記の様な設定になっている。
また、/tmp/envoy/lds.yaml
に空ファイルを用意しておかないとenvoyが立ち上がらないので用意しておく必要がある。この値は ENVOY_LDS_FILE
で変更することができる。
まとめると
nix develop cd example export OTEL_CLI_CONFIG_FILE=$PWD/config.json export ENVOY_LDS_FILE=/tmp/envoy/lds.yaml docker-compose up -d vi envoy.yaml vi config.json touch $ENVOY_LDS_FILE envoy -c envoy.yaml & tracing-nix-build ".#hello"
の様になる
Nixの日本語コミュがあるので、動かない人は以下で聞いてください
今後の方針
上記の実装方法ではenvoyの設定を書き換えながらやるので複数のビルドを同時に扱うことができない。
なので、netrc-file
に localhost:8081に対する認証情報をtrace idを埋め込むようにして、1段目のproxyで認証情報からtraceparent headerを組み立てると理屈上はenvoyの設定を動的に変更せずにtrace idをビルドごとに送ることができる可能性がある
また、ZeroCodeChangesではできないことも多いので、詳細なメタデータを送るために C++ でtracingの実装を追加するのも面白いかもしれない。
例
nixosのtoplevelをビルドした例。途中でcacheのfetchに失敗している(httpの分割requestが失敗している雰囲気)
cacheの取得を進めた上で max-jobsとcoreを8指定でした例。並行で実行されている様子がわかる
第5回しろさとTT反省会
前回は以下
前回からのざっくり変更点
- zwiftのTT tune up プランを終えた
- Rotorクランクになった
- フロントシングルになった
- パワーメータ導入
- クランク長が170→165
- ボトムブラケット新しくなった
トレーニング
TT tune upを終えた
このプランは8週間のインターバル系?的なトレーニングで、週を追うごとに微妙に負荷が上がっており折り返しの5週目あたりからはグッとしんどくなる
特に最後の方のPower Intervalsマジでキツかった
このプラン自体は必須のメニューだけで週5日のトレーニングが必要なので、時間の捻出と肉体の回復の両方の意味で自分にはまぁまぁ大変だった
FTPが合っていれば回復が追いくぐらいの設定におそらくなっておりなんとか継続できている。これが例えば自分で適当に組んだトレーニングの場合は同程度の間隔でトレーニングするのは無理だろう
また、上のプランをやっていない期間は大体、週4〜5程度のお好みトレーニングもしくはレースをした。
お好みトレーニングは気分に左右されるので適切な負荷をかけにくいし、レースはオールアウトしがちなので継続性に難があると感じた
FTP変化
今回の大会付近のFTPは以下
RAMP TEST→238w
zwiftpower 推定値→208w
どんどんRAMP TESTと20分TTの値が乖離していく
目標設定値
今回の目標は本来パワーメータで試走した結果のW数から出す予定だった。
が、しかしパワーメータをインプレできたのは大会がある週になってしまい、一切まともに試走できていない状態で大会を迎えることになってしまう。
My new gear... pic.twitter.com/QcywGaWzAa
— hiroqn (@hiroqn) 2023年3月16日
こんなtweetをしている場合ではない
最終的に180Wぐらいで漕いだ場合、流石に34km/hは出るだろうしポジション維持できれば35km/hぐらい出るのでは?という雑思考で目標値を34~35に決定する
本番
前回は前泊だったが今回は当日の朝出発にして、受付1時間前には着く予定だった
しかし、事故渋滞でエントリー締切の時間に会場の門をくぐることになってしまう(一敗)
駐車場でチャリの準備をしていたが、焦りのあまり計測チップを結束バンドでフォークとスポークごと結んでしまいチャリを担いで走る人間が発生してしまう(二敗)
出走前に最終のカロリーを腹に入れる予定だったのだが、そんな余裕もなく出走し途中でお腹が減る(三敗)
タイム1:32:45で32.992km/hだった
と言うわけで全然目標未達でダメだった。前回より2分程度は早くなったが自分の中でベストを尽くせなかったので、かなり悔やまれる。
ちなみに、他にもトラブルはあった
走っている途中の8周目あたりで気づいたのだがライトがハンドルについていた(四敗)
帰ってから気づいたが、前日に微妙にいじったせいでクランクの取り付けに失敗しておりチェーンがついてない状態で一切回らない状態だった(五敗)
データ
パワー
心拍
データから見る反省
心拍数的には最初の2周が飛ばし過ぎていた感じがする。
お腹が減ったのもあるが6〜7周目あたりで足の疲労を招いてしまい、心肺は余裕だがペダルがもう回らない状態になっていた
あと、パワーを可視化してみて改めて思ったのが出力が安定していない
もうちょっと実走を増やしてパワーを安定させる感覚を掴まなければいけないなってなった。zwiftではそれができないので
余談:パワーメータの取り付けがなぜギリギリになってしまったか
実は1月あたりから余裕を持って準備を開始したのだがトラブル続きでこのような事態になった
パワーメータ取り付けに当たって、自分の股下の長さ的に165cmのクランクの方が良さそうと言う考えになりクランク長の変更が容易なRotorにする意思決定をした
また、自分の使い方的にもフロントシングルでいいと思ったのでナローワイドのチェーンリングとRotorの INSPIDER DMを海外通販で発注した
しかし、注文したチェーンリングは通販の画像では4armsだったのに到着したものは5armsと言うトラブルが発生
仕方なく4armsのチェーンリングを注文するも、どうやら途中で紛失になった様子
そして届かぬまま2週間前になり、今回はパワメつけて走る目標を何とか達成させるためにSIGEYIというRotor互換で5arms対応のパワメを購入する
ギリギリで届いたので装着し当日へ
Rotor INSPIDER DMが余ってしまったな...
ちなみにチェーンリングはクレームを入れたところ返金されたので使わなさそうならパワメは売るか
SQLiteでLoadable Extensionsを静的リンクする
about
この記事は Run-Time Loadable Extensions で書かれている Statically Linking A Run-Time Loadable Extension
をやる。
SQLiteのversionは3.40以降
Loadable Extension とは
Loadable ExtensionはSQLiteに関数とか何やかんやを足すことができる機能である。
基本的な使用方法としてはsqlite cliから .load ライブラリへのpath
を使うとloadすることができる。
今回、Loadable Extension を作ることはせずに既存のextensionを使っていく。
そもそもの発端としては sqlite内でbase64のencode/decodeしたかったので以下を使う。
なんでsqlite内でbase64のencode/decodeをしたかったのか、という点は直接聞いてもらえれば。
ビルド編
実は最近はLoadable Extensionの仕様が変わっており、上記のcodeは現代的な書き方ではないので変更していく
変更し終わったコードは以下。
nix build .#sqlite3-base64
でビルドできるはず。
macの場合、 export DYLD_LIBRARY_PATH=$(nix build .#sqlite3-base64 --print-out-paths --no-link)/lib
のようにしてあげれば
sqlite cli 内で.load libbasesixtyfour
で loadできるようになる。
勿論、 .load 絶対PATH
でも良い
以下、変更点解説など
変更点: #include <sqlite3ext.h>
拡張用のheaderになっている
変更点: sqlite3_basesixtyfour_init
ちょっと未確認 だが、.load libxxxx.so
で呼び出した際に、 sqlite3_xxxx_init
が呼び出されるようだが、xxxxに数字が含まれるとうまくいかなかったので名前を変えている。
libファイルの名前の制約のように感じる・
変更点: SQLITE_EXTENSION_INIT*
静的リンクする場合に備えたmacroで SQLITE_CORE
が定義されているかどうかで挙動が変わるようになっている
静的リンクに向けて
実はsqlite 3.40までは既存のコードに変更を加えずに素直に静的ビルドするのが難しい
ここで良い話と悪い話がある。
良い話は上記のcommitでビルド時にフラグを追加するだけでビルドできるようになる。
悪い話は上記のcommitでbase64の機能が追加されたので、実は上のextensionをloadしなくても良くなった。
つまり sqlite3でbase64を使いたいだけの人間は そのうちリリースされる予定のsqlite 3.41を使えば良い
今回はモチベーションを失いつつも静的リンクに向けてやっていく
静的リンクビルド解説
元々どういった手法でビルドすれば良いかを明確に解説したドキュメントはないのだが、
SQLite Forum: Decimal extension in amalgamation?
SQLite Forum: Latest trunk/extensions & UDF's on tap
を見ると、とにかく shell.c
に拡張用のcを結合しつつ SQLITE_EXTRA_INIT
に init用の関数を指定しろ、と言うふうに読める。
ちなみに SQLiteは amalgamationという全てのソースコードを1ファイルにまとめる操作を Tcl という言語を使いつつやっているのだが
「とんでもないコードが出てますよ今」
という印象を受ける
そんな操作をしているので cat ext.c >> shell.c
すれば良いという雰囲気になっている。
しかし、直近は SQLITE_EXTRA_INIT
無くなったようで、上記commitを見ると, SQLITE_SHELL_EXTFUNCS
や SQLITE_SHELL_EXTSRC
や以下のmacroが追加されている。
#define SHELL_SUB_MACRO(base, variant) base ## _ ## variant #define SHELL_SUBMACRO(base, variant) SHELL_SUB_MACRO(base, variant) /* Let custom-included extensions get their ..._init() called. * The WHATEVER_INIT( db, pzErrorMsg, pApi ) macro should cause * the extension's sqlite3_*_init( db, pzErrorMsg, pApi ) * inititialization routine to be called. */ { int irc = SHELL_SUBMACRO(SQLITE_SHELL_EXTFUNCS, INIT)(p->db); /* Let custom-included extensions expose their functionality. * The WHATEVER_EXPOSE( db, pzErrorMsg ) macro should cause * the SQL functions, virtual tables, collating sequences or * VFS's implemented by the extension to be registered. */ if( irc==SQLITE_OK || irc==SQLITE_OK_LOAD_PERMANENTLY ){ SHELL_SUBMACRO(SQLITE_SHELL_EXTFUNCS, EXPOSE)(p->db, 0); } #undef SHELL_SUB_MACRO #undef SHELL_SUBMACRO } #endif
これは目を細めて凝視すると、
#define BASESF_INIT(db) sqlite3_basesixtyfour_init(db, 0, 0) #define BASESF_EXPOSE(db, pzErr)
という initを呼ぶ macroを定義しつつ SQLITE_SHELL_EXTFUNCS=BASESF
と SQLITE_SHELL_EXTSRC=拡張関数.cへのpath
のフラグを渡せばい良い、ということがおぼろげながら浮かんでくる。
あとはやるだけなので以下のnixファイルからビルドする。
sqlite.overrideAttrs (final: prev: { src = pkgs.fetchurl { url = "https://sqlite.org/snapshot/sqlite-snapshot-202302052029.tar.gz"; sha256 = "sha256-MNjujQGJ/QlKs/3gUGna2G6Fmc/gAZ4qxmXflWH5DH0="; }; buildInputs = prev.buildInputs ++ [ pkgs.libb64 ]; NIX_CFLAGS_COMPILE = prev.NIX_CFLAGS_COMPILE + " -DSQLITE_CORE=1 -lb64 -isystem ${pkgs.libb64}/include/b64 -DSQLITE_SHELL_EXTSRC=${sqlite3-base64}/sqlite3_base64.c -DSQLITE_SHELL_EXTFUNCS=BASESF"; }))
実行結果
❯ sqlite3 SQLite version 3.41.0 2023-02-05 20:29:10 Enter ".help" for usage hints. Connected to a transient in-memory database. Use ".open FILENAME" to reopen on a persistent database. sqlite> SELECT BASE64(X'010203'); AQID sqlite> SELECT HEX(BLOBFROMBASE64('AQID')); 010203 sqlite>
よかったですね
SAIYO JOHO
第4回しろさとTT反省会
これは何?
チャリを買ったので3ヶ月練習して、しろさとTTというチャリを漕ぐ会に参加してきて思ったことを書く
参加は50kmの部門。
参加するまで
なんか世間ではzwiftという家でチャリを漕ぐやつが流行ってるらしいじゃね〜か、というインサイトを今年の夏頃に得て
チャリを...漕ぐカァ...という気持ちが高まり諸々を買い、せっかくなのでトレーニングして上のレースに出る意思決定をした。
zwiftを買ってよかったのはFTPというチャリ界の階級みたいなものに基づいて科学的な根拠のあるトレーニングができるので、根性論的なやる意味をいちいち考えさせられるような運動をさせられないのが良い
というか学生時代の部活の体力トレーニングって人間ごとにメニューを組む分けではなかったので、無意味で自己満足を育ててやっている感を出してるだけだなって思ってしまった
ちなみにFTPというのは1時間漕ぎ続けることができる限界のパワー(単位はワット)になる
練習など
練習メニューを組む上でとりあえずFTPを測らないといけないので、なんか測り方が色々あってその中でシュッと測れる RAMP testで測った
RAMP testはアスリート以外は基本的に高く出ることが多いらしい
どのみち練習メニューをこなした際にターゲットの心拍数ゾーンが設定されており、それより高く出ていればFTPの値が間違っていると考えることができるので下げたらいいと思う。
ちなみにやってみた結果は以下のようになった
8月:152w 9月:184w 11月:214w
練習メニューは目的に合わせてzwiftの初心者向けのトレーニングメニューであるFTP Builderというの一旦2ヶ月やった
途中、コロナっぽい風邪に罹って1週間休んだが概ねメニューは完遂した。
本番前
申し込みの時点では51kmあたり大体30km/hの1時間40分ぐらいを目標にしていたが
事前にzwiftや路上で漕いでみた感じでは 普通の姿勢で31km/h エアロポジションで32km/h ぐらいはいけるかなという感じだったので、 時速33km/hの1時間32分あたりを目標にした。
元々、クレアチンを取っていたが一旦飲まない期間を置いてから本番1週間前にクレアチンローディングをした。
ケトジェニックで体を慣らせば有酸素能力が一時的に上がるのでは?と思い本番2週間前から1週間ぐらいにやってみたが、科学的根拠に足る文献が無かったから今のなしで、ってなった
ただ糖質オフすると明らかに練習の負荷が高まってその後糖質をとるとめちゃ楽にこげる!ってなるから気分上の効果はあるかもしれない
本番&振り返り
チャリの会は初だったので若干不安だったがなんとかなった。
結果は1h34m30sだった
練習で50kmの距離をエアロポジション取り続けることは無かったので不安だったがずっと取り続けれたのはよかった。
心拍数のavgは166で1時間半のTTの場合はおそらくL4領域の心拍数に収まっていたらいいと思うので、そこらへんのコントロールはうまく行った説はある。
後半の足の前の筋肉がかなり"キてしまった"ので、尻で漕いでた感じがある。足がきてしまったのも一定のパワーで漕げていないことからスピードを維持しようとして不安定な出力をしてしまった感じがある。 あと、1時間半通しのトレーニングをあまりしていなかった。
zwiftで今漕いでいるパワー(ワット)が出るが、持っているチャリにはつけていないので、練習通りのパワーコントロールができていないという課題がある。
ケイデンスは安定はしてた気もするような、してないような?
気づき
エアロポジションの研究はしたいが、ハンドルまわりを変えないといけないし計測しにくいので難しいなって思った
パワーメータは流石に欲しいかもしれない。エンジニアとして「推測するな計測せよ」というのは大事にしないといけないので、計測できる環境をまず作るか
来年の3月に第5回があるのでそれも出る。
最近はNix on raspberry pi zero をやった
イントロ
raspberry pi zeroをちょっと前に2枚買ったのが特に用途がない
強いて用があるとすれば、この前買ったserial通信経由でデータが取れるco2計測器がLinuxの方が読みやすそうだなってぐらい。 これはこのco2計測器が悪いわけではなく、macにマウントされるserial device?がちょっといまいちな挙動をしているからだ。
というわけで 何かと開発する上で便利なので、Raspberry Pi OSにnixをいれていく。
余談だが、rasipbian OSはRaspberry Pi OSに名前が変わっていた。
install
Nixをいれるのは適当にやってもらっていいが、以下の行に注意する必要がある
nix/install.in at master · NixOS/nix · GitHub
"$(uname -s).$(uname -m)"
の実行結果が Linux.armv6l_linux
になることを期待しているが、 少なくとも自分の環境では Linux.armv6l
になる
これは Linux.armv6l_linux
が返ってくる環境が世の中にあるのかわからないのでバグなのかわからないが、armv6lのRaspberry Pi OSにNixを入れようとしている人間がzeroに近いことを演繹させてくれる有力な証拠である。
ちなみに NixOS on raspberry pi zeroをやっている人間はGitHubを見た感じでは何人かいることが観測できたので、そのうちやると思う。
build on pi
普通の環境では(ここでいう普通の環境とはflake-utilsのdefaultSystemsとか)「はい!あとはプログラムを書いて終わり!」となるが、ここはarmv6l、一切のcacheが降ってこない砂漠地帯、nix-shellが要求するbashInteractiveですらgccのビルドから始まってしまう。
というわけで gccとstdenvのビルドをやっていく。
まぁ時間さえあれば終わるでしょうと思っていてビルドを走らせていたわけだが、途中で止まってしまう。
どうやらメモリが足りなくなってOOMで落ちたっぽいエラーを観測した。足りないのは時間ではなくメモリ。
正直 パイゼロは500Mしかメモリがない、がしかし「20年前のPCでも500Mあったし流石にgccはビルドできるのは?」という気持ちも捨てがたい。そして「SD上にswapを増やしても大変な遅さになってしまうのでは?」と思いパイゼロ上でビルドするのはやめた。
なのでここからはmacでビルドしてラズパイに送る戦略をとる。
build on mac
ところでDockerDesktopという便利なものがある。何が便利かというと、マルチCPUアーキテクチャサポートがある
つまり armv6lのlinuxが豊富なメモリと屈強なCPUを積んだmacで動いてくれるわけだ。
今回は arm32v6/alpine
Docker Hub にNixをインストールする戦略をとる。
余談だが NixのcrossCompile機能を使ってやるという手法もあるのだが、derivationが異なってしまう恐れを直感的に感じ取り今回はやめた。 具体的には crossCompileでビルドしたgccを使って ラズパイ上で他のものをビルドすること際に気を使う必要がある可能性を感じた、これは試していないからわからない
話を戻し、まずは bashをビルドしていく。ここで必要なのは時間と忍耐力で、具体的には一晩の時間とmacのファンの音を気にしない心だ。
ここで 一旦 ラズパイの方に できた bashを送る。 nix copy -s --to $ビルドされたbashのpath
。
送る際に because it lacks a valid signature
と言われると思うので、nix.confに require-sigs = false
を足して nix-daemonを再起動させる。
おそらく動くと思うし、bashをbuildしようとしてもbuildされずに先程送ったpathが返ってくると思う。もしビルドが始まった場合同一のderivationをビルドしようとしていない可能性が高いと思う。
あとはstdenvをビルドしていく。
あとはビルドするだけと思いきや、ここでstdenvの依存の中に含まれるpcreのtest phaseでコケてしまう。
「そんなわけがないだろう」という気持ちになるが、手元にはすでにコンパイル済みのgccがいるのでラズパイに送りさえすればビルドできるので試してみたところ pcreはビルドできてしまった。
ビルドできてしまったこと自体も「そんなわけがないだろう」という気持ちになったが、pcreがビルドされていれば後はmacのdocker側に持って来ればいいだけなので nix copy --from
で持ってくる。もはやキャッチボールだ。
stdenvとbashInteractiveをラズパイ側に送りつけ、 nix-shell
できることを確認する。
よかったですね
何故こういったことをやっていたのか忘れてしまったが、おおよそ、私の目的はビルドでプログラムの実行は副作用でしかないので、よかったですね。
pcre debug編
なぜpcreのテストが失敗したのか調査していくと pcregrepの -L オプションで失敗しているようだった。
pcreのソースコードを読んでいくとreaddirの挙動がおかしいように感じたので、疑似的なプログラムを書いて試してみると確かに動かない。
比較するために
の三つの環境で疑似プロを動かしたところ armv6lでだけ動かない。
最初はoverlayfsあたりが悪さをしているのでは?と考えたがどうやら違うらしい。
そもそもマルチCPUアーキテクチャが qemuを使っているので、そこら辺のワードと共に探っていくと以下のIssueに行き着いた。
これは...ダメですね...
テーマは「情報収集について」
RSS
自分の場合、日常的なInputとして使っているのはRSS(Feedly)で大体以下のRSSかな
ほんとは他にもフォローしているブログがあったのだが、更新されなくなってしまった
まぁはてブの人気エントリは1ヶ月で2000ぐらいあったりするので大体それ見とけばいいとなっている 基本的にタイトルは全て追っていて、気になったり知らないことに関して書かれた記事は読むという感じ
Twitterもソースとして当然使っているが、とはいえメジャーな技術の記事は上に含まれているような気もする。
なのでニッチな技術に関してはその第一人者をfollowしておくとかで有用、例えばNixなら Domen Kožar (@domenkozar) | Twitter とか
たまに海外記事のまとめをReplyで翻訳し始める人間もいるので、そう言った場合に有用。
AWS
他にはAWS ニュースレターとかもAWSのアップデートを追う上ではまぁまぁ有効、最近は新サービス開始時にTokyo regionもおま国されないのが嬉しい
nixpkgs
nixpkgsのcommit logとか適当に見ているだけでかなりの情報がある、これは流石に全部見れんが
GitHub
遭遇したIssueはとりあえずSubscribeしとくと便利、あまりfollow機能は使っていない
Web
あと、なんか普通に仕事してたりTwitter見ていたら、知らないことって結構出てくるので納得いくまで調べることが多い
shell.nixとdirenvでプロジェクトごとに補完をいい感じにする
概要
HERPでは shell.nix
をgit repositoryのrootに置いて依存関係を解決しているが、PATH
がprojectごとに設定されるだけなので 例えば kubectl
の補完が有効になるわけではない。
その課題を解決するために shell.nix
にzshの補完もうまく済ませるワザップを今回は書く。
zsh completion について
zsh の completionの仕組みをざっくり書くと、 FPATHをいい感じにして compinit
すれば FPATH
から解決されるcompletion用のscriptが実行され補完される
手法
FPATH用のderivation
まず、completionを集めたderivationを作る。
buildEnv
というderivationの特定のディレクトリの集合体を作る関数があるのでそれを使う
FPATH = "${nixpkgs.buildEnv { name = "zsh-comp"; paths = buildInputs; pathsToLink = "/share/zsh"; }}/share/zsh/site-functions";
FPATHの設定
上記で作った FPATH
を $HOME/.zshrc
で追加された FPATH
に足せばいいのだが
安易な方法として shell.nix
にFPATHを足してしまうと、そのまま上書きされて既存の補完の全てを失ってしまう
なので、 project_FPATH
のような環境変数に一時的に保存する。
このまま nix-shell --run zsh
のように実行する場合は, ZDOTDIR
をshell.nix中で上書きするなどのコードをを足せば問題ないのだが、
いちいち実行するのが面倒なので direnvを使っている場合は工夫が必要になる。
use nix
path_add FPATH $project_FPATH
以下の理由でこのコードは動きそうに見えて動かない(人によっては動く)
direnvは .envrc
の評価をbash上で行っており、評価した結果の差分を既存のshellに足すような実装になっている。(コレは echo $0
してあげるとわかる)
実はzshの FPATH
は exportされていない変数なので (コレは echo ${(t)FPATH}
でわかる、コレをlocal変数と言っていいのかはわからない) bash上で評価した際に存在していない変数として扱われFPATHが上書きされる
なので direnvの評価が始まる前、つまり ~/.zshrc
などで export FPATH
(typeset
でも可) しておく必要がある
export FPATH
してない人間向け対応
世の中には export FPATH
していない人間もいるので、そう言った人が projectに入って direnv allow
した瞬間に壊れて cd
するのも困るのは不親切という考え方もあるので対応をする。(考え方によっては学習機会を奪っている。人間は困らないと進化しないので)
上記で書いたように 評価がbash上で行っているので export -p
した中に FPATH
が含まれていなければ export FPATH
されていないことがわかる
よって以下のようになる
use nix if export -p|grep "declare -x FPATH" ; then path_add FPATH $project_FPATH fi
compinitの設定
あとは compinit するだけなのだが毎回 compinit
していてはダルい(ダルい)
なので direnv がhookで評価した後にFPATHが書き変わっていれば compinit
するようなhookを作ってあげれば良い
export COMPINIT_DIFF="" _chpwd_compinit() { if [ -n "$IN_NIX_SHELL" -a "$COMPINIT_DIFF" != "$DIRENV_DIFF" ]; then compinit -u COMPINIT_DIFF="$DIRENV_DIFF" echo "compinited !" fi } if [[ -z ''${precmd_functions[(r)_chpwd_compinit]} ]]; then precmd_functions=( ''${precmd_functions[@]} _chpwd_compinit ) fi if [[ -z ''${chpwd_functions[(r)_chpwd_compinit]} ]]; then chpwd_functions=( ''${chpwd_functions[@]} _chpwd_compinit ) fi
FPATH
の diffを取ると言ったが色々考えた結果,nix-shell中でdirenv が実行されたときにcompinitされればいいので上記のようにした。(compinit以外のこともしたくなるかもしれない)
蛇足として一応書いておくが autoload compinit
は他でするように
終わりに
nix-shell
と direnv
の組み合わせは環境を揃える手法として有名だが、実は補完まで揃えることができる。
各個人のzshrcに追記する必要はあるがオススメできる手法である。
余談
HERPのSREチームはnixを使って各種ツールの環境を揃えている。