Bazel 7 での jni.h: No such file というエラーへの対応 (bazelisk --bisect)
初稿: 2023-12-24
小松弘幸 (@komatsuh:twitter, @komatsuh:bsky)
概要
Bazel のバージョンが 7 にあがったら、「jni.h が存在しない」というエラーがでました。結論としては Bazel 7 以降では --fat_apk_cpu
オプションを使うには --incompatible_enable_android_toolchain_resolution=false
の追加が必要でした。
原因の発見と解決に至るまでを記事にしました。Bazel をすでに利用している人向けの内容です。
Bazel 7 で jni.h: No such file or directory というエラーになる
GitHub Actions での CI で、Bazel による Android ビルドに失敗するようになりました。次のようなエラーが起きていました。
java/com/app/jni.cc:15:10: fatal error: jni.h: No such file or directory
15 | #include <jni.h>
| ^~~~~~~
compilation terminated.
コミットをロールバックしてもエラーになるため、GitHub Actions の環境が更新されたことが原因だと考え確認しました。
経験上 Bazel のバージョンが更新されるとこのようなエラーが起きることが多いので、Bazel 関連の変更を確認します。
GitHub Actions の環境の更新履歴を確認して、Bazel のバージョンが 7.0.0 へ更新されていることを確認しました。
Bazelisk による Bazel のバージョン指定
Bazelisk は複数のバージョンの Bazel を使い分けるためのツールです。
Bazel のバージョン 7.0.0 を指定してビルドする場合は次のようにします。
USE_BAZEL_VERSION=7.0.0 bazelisk build (target_name)
手元の環境でも Bazel 7.0.0 では同様のエラーになり、Bazel 6.0.0 ではビルドに成功することが確認できました。
Bazelisk --bisect による原因の特定
Bazel のバージョンによる問題であることは分かりましたので、実際の原因や解決方法を探ります。Bazel へのどの変更で問題が起きたのかを Bazel --bisect
によって調べます。
バイセクト (bisect) とは変更履歴の中から実際に問題が起きた変更を調べるための手法で、二分探索によって確認していきます。git にも同様の手法があります。
Bazelisk でのバイセクトは次のようにします。バージョン 6.0.0 から 7.0.0 の間でビルドの成否が変わる場所を見つけられます。
bazelisk --bisect=6.0.0..7.0.0 build (target_name)
あとは結果を確認すれば完了のはずでしたが、バイセクトが途中で止まってしまいました。
--- Succeeded at 78db9ae9a545a9586dbb02d7831f5302594e01cb
--- Testing with Bazel built at 05bea52ed3159aa5d15d967f5f56fc084a2b6c73, 6 commits remaining...
...
--- Testing with Bazel built at f7946d0107dd75b2f45bcc79b91c016d075a756d, 3 commits remaining...
2023/12/20 11:23:45 Using unreleased version at commit f7946d0107dd75b2f45bcc79b91c016d075a756d
2023/12/20 11:23:45 Downloading https://storage.googleapis.com/bazel-builds/artifacts/centos7/f7946d0107dd75b2f45bcc79b91c016d075a756d/bazel...
2023/12/20 11:23:45 could not run Bazel: could not download Bazel: failed to download bazel: failed to download bazel: HTTP GET https://storage.googleapis.com/bazel-builds/artifacts/centos7/f7946d0107dd75b2f45bcc79b91c016d075a756d/bazel failed with error 404
理由は調べていませんが、途中のコミットが取得できないようです。それでも 6 コミットまでは絞り込めました。ここからは、判明しているコミットを指定してみます。
bazelisk --bisect=78db9ae9a545a9586dbb02d7831f5302594e01cb..05bea52ed3159aa5d15d967f5f56fc084a2b6c73 build (target_name)
今回は期待通り、原因の変更を確認できました。もし同じエラーが起きたとしても、6 コミットであれば全部確認すればよいでしょう。
--- Failed at 05bea52ed3159aa5d15d967f5f56fc084a2b6c73
--- Bisect Result
first bad commit is https://github.com/bazelbuild/bazel/commit/05bea52ed3159aa5d15d967f5f56fc084a2b6c73
原因と解決方法
ビルドエラーの原因は次の変更であることが判明しました。
RELNOTES: Enable Platforms and Toolchains for Android. Android projects will need to stop passing the legacy flag
--fat_apk_cpu
, and instead use--android_platforms
using platforms defined with the@platforms//os:android
constraint. The https://github.com/bazelbuild/rules_android repository defines four standard Android platforms for projects that use those rules,@rules_android//:armeabi-v7a
,@rules_android//:arm64-v8a
,@rules_android//:x86
,@rules_android//:x86_64
.
変更内容にある通り incompatible_enable_android_toolchain_resolution
オプションのデフォルト値が true
に変わりました。そのために --fat_apk_cpu
オプションを指定しているビルドではエラーになるようです。
とりあえずの解決方法としては、incompatible_enable_android_toolchain_resolution
を false
に戻せばよさようです。
USE_BAZEL_VERSION=7.0.0 bazelisk build (target_name) --incompatible_enable_android_toolchain_resolution=false
上記コマンドでは Bazel 7 でも期待通りビルドできることが確認できました。(自分の環境では --fat_apk_cpu
オプションは .bazelrc
にデフォルトオプションとして記載されています)
なおコミットメッセージにもあるとおり、将来的には --fat_apk_cpu
の代わりに --android_platforms
の使用が推奨されています。
まとめ
- GitHub Actions で
jni.h: No such file or directory
というエラーが起きました。 - GitHub Actions のビルド環境が Bazel が更新されたことを確認しました。
- Bazelisk --bisect によって、原因となる変更を特定しました。
- 変更履歴から Bazel 7 で
--fat_apk_cpu
を使うには--incompatible_enable_android_toolchain_resolution=false
が必要だと分かりました。
具体例
エラー時の GitHub Actions
修正後の GitHub Actions
修正コミット
Discussion