CMakeでクロスコンパイルするときのtoolchain fileの書き方

CMakeでクロスコンパイルする場合、まず出てくるのが toolchain file です。

普通のビルドでは、CMakeがホスト環境を見てコンパイラを探します。一方でクロスコンパイルでは、ビルドする環境と実行する環境が違います。そのため、どのターゲット向けに、どのコンパイラでビルドするのかを先に渡す必要があります。

CMake公式の toolchains ドキュメントでも、クロスコンパイルでは toolchain file を指定する流れになっています。

https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling

sysrootまで含めた設定は CMakeのクロスコンパイルでsysrootを設定する理由 に分けています。まずはこの記事で toolchain file の最小構成だけ見ます。

最小のtoolchain file

Linuxホスト上で、ARM向けLinuxバイナリを作る例です。

# toolchains/arm-linux.cmake
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)

set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc)
set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++)

ひとまず見るのはこの4つです。

変数 内容
CMAKE_SYSTEM_NAME ターゲットOS
CMAKE_SYSTEM_PROCESSOR ターゲットCPU
CMAKE_C_COMPILER Cコンパイラ
CMAKE_CXX_COMPILER C++コンパイラ

CMAKE_SYSTEM_NAME はターゲット側のOSを表します。

https://cmake.org/cmake/help/latest/variable/CMAKE_SYSTEM_NAME.html

ホストがLinuxでも、ターゲットが別環境ならここでターゲット側を指定します。

CMake実行時に指定する

toolchain file は configure 時に指定します。

cmake -S . -B build-arm \
  -DCMAKE_TOOLCHAIN_FILE=toolchains/arm-linux.cmake

または、CMake 3.21以降なら --toolchain でも指定できます。

cmake -S . -B build-arm \
  --toolchain toolchains/arm-linux.cmake

toolchain file はCMakeの初期段階で読み込まれるため、ビルドディレクトリを作った後に差し替えるとキャッシュと噛み合わないことがあります。

切り替えるなら、別のbuildディレクトリを使う方が分かりやすいです。

cmake -S . -B build-native
cmake -S . -B build-arm --toolchain toolchains/arm-linux.cmake

同じ設定を何度も使う場合は、CMakePresets.json にtoolchain fileを持たせる方法もあります。preset側の基本は CMakePresets.jsonで最初に設定しておくべきこと にまとめています。

CMakeLists.txt側は普通に書く

CMakeLists.txt 側は、できるだけ普通のCMakeとして書きます。

cmake_minimum_required(VERSION 3.20)
project(MyApp LANGUAGES C CXX)

add_executable(my_app
  src/main.cpp
)

ターゲット環境の違いは toolchain file に寄せます。

CMakeLists.txtarm-linux-gnueabihf-g++ のようなコンパイラ名を直接書き始めると、ホストビルドや別ターゲットへの切り替えがしづらくなります。

toolchain file内で避けたい変数

公式ドキュメントでは、toolchain file内で CMAKE_SOURCE_DIRCMAKE_BINARY_DIR を使うのは避けた方がよいとされています。

https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling

toolchain file は try_compile() などの中でも読み込まれることがあり、そのときに source/binary の意味が変わるためです。

パスを組み立てたい場合は、CMAKE_CURRENT_LIST_DIR を使うのが扱いやすいです。

set(toolchain_dir ${CMAKE_CURRENT_LIST_DIR})
set(CMAKE_C_COMPILER ${toolchain_dir}/../bin/arm-linux-gnueabihf-gcc)
set(CMAKE_CXX_COMPILER ${toolchain_dir}/../bin/arm-linux-gnueabihf-g++)

まず確認すること

クロスコンパイルが動かないときは、まずこのあたりを確認します。

  • toolchain fileをconfigure時に指定しているか
  • buildディレクトリを使い回していないか
  • CMAKE_SYSTEM_NAME がターゲットOSになっているか
  • CMAKE_CXX_COMPILER が実際に存在するか
  • コンパイラ単体で実行できるか

コンパイラのパスが間違っている状態でCMake側を見ても、だいたい遠回りになります。

コンパイラ指定までは通るのに、ヘッダーやライブラリの探索で失敗する場合は CMakeのクロスコンパイルでsysrootを設定する理由CMakeのクロスコンパイルでfind_packageがホストを見に行くときの設定 を見ると原因を分けやすくなります。

まとめ

CMakeでクロスコンパイルする最初の設定は、toolchain fileに寄せます。

  • CMAKE_SYSTEM_NAME でターゲットOSを指定する
  • CMAKE_SYSTEM_PROCESSOR でターゲットCPUを指定する
  • CMAKE_C_COMPILER / CMAKE_CXX_COMPILER でクロスコンパイラを指定する
  • configure時に --toolchain または CMAKE_TOOLCHAIN_FILE で渡す
  • buildディレクトリはターゲットごとに分ける

まずはこの形で、ネイティブビルドとクロスビルドを分離しておくのが扱いやすいです。

次に見る内容としては、ターゲット環境のヘッダーやライブラリを扱う sysrootの設定 が近いです。

参考