sysrootって?
GCCに--sysroot=<dir>
というオプションがあります。僕がどこでこのオプションを知ったのか最早思い出せないのですが、組込みLinuxでのクロスコンパイルについて調べているときに、どこかで見かけたのだと思います。なんとなく、漠然と組込みLinux開発で使ったり使わなかったりしていましたが、ここらで一つ真面目に調査をし、記録を残しておこうと思います。
ただし、僕は英語についてはサッパリなので、GCCのリファレンスから正確な意味を汲み取りことができません。ですので、この記事は実践上での結果をまとめたものになるのでご注意を。
GCCのマニュアルから機能を推測する
gcc.gnu.org Directory-Optionsにsysroot
についての説明があります。うーん、わかったような、わからんような。
実際に試してみる
文章だけだとピンとこないので、実際に試してみましょう。通称のGCCとARM-GCCの二つで試してみます。
テスト環境
# OS
% lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 16.04.1 LTS
Release: 16.04
Codename: xenial
# ホストPC GCCのバージョン
% gcc --version
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609
# クロス GCCのインストール
% sudo apt-get install -y gcc-arm-linux-gnueabi
# クロス GCCのバージョン
% arm-linux-gnueabi-gcc --version
arm-linux-gnueabi-gcc (Ubuntu/Linaro 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609
--sysroot
には存在しないディレクトリを指定してもエラーにはならないので、とりあえず適当に/dammy
としておきます。
% gcc --sysroot=/dammy -o hello hello.c
hello.c:1:19: fatal error: stdio.h: そのようなファイルやディレクトリはありません
compilation terminated.
通常のGCC(以下GCC)では、stdio.h
のインクルードに失敗し、エラーになりました。ふむ。確かに標準のインクルードパスが変化しているようです。
続いてARM-GCCで同様に試してみます。
% arm-linux-gnueabi-gcc --sysroot=/dammy -o hello hello.c
あれ?コンパイルが通った!?さあ何やらわけがわからなくなってきました。こういう時は-v
オプションでGCCの内部動作を調査してみましょう。
% gcc -v --sysroot=/dammy -o hello hello.c
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper
Target: x86_64-linux-gnu
...
ignoring nonexistent directory "/dammy/usr/local/include/x86_64-linux-gnu"
ignoring nonexistent directory "/dammy/usr/local/include"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/5/../../../../x86_64-linux-gnu/include"
ignoring nonexistent directory "/dammy/usr/include/x86_64-linux-gnu"
ignoring nonexistent directory "/dammy/usr/include"
#include "..." search starts here:
#include <...> search starts here:
/usr/lib/gcc/x86_64-linux-gnu/5/include
/usr/lib/gcc/x86_64-linux-gnu/5/include-fixed
End of search list.
...
hello.c:1:19: fatal error: stdio.h: そのようなファイルやディレクトリはありません
compilation terminated.
ずらずらとでてくるので、無関係そうな所は除外しています。ignoring nonexistent directory "/dammy/xxx"
というあたりがそれっぽいですね。しかし、よく見ると/dammy
配下ではないディレクトリもインクルードサーチパスに含まれているようです。どういうことでしょうか。とりあえず、ARM-GCCも-v
で実行してみましょう。
% arm-linux-gnueabi-gcc -v --sysroot=/dammy -o hello hello.c
Using built-in specs.
COLLECT_GCC=arm-linux-gnueabi-gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc-cross/arm-linux-gnueabi/5/lto-wrapper
Target: arm-linux-gnueabi
ignoring nonexistent directory "/dammy/usr/local/include/arm-linux-gnueabi"
ignoring nonexistent directory "/dammy/usr/include/arm-linux-gnueabi"
ignoring nonexistent directory "/dammy/usr/include"
#include "..." search starts here:
#include <...> search starts here:
/usr/lib/gcc-cross/arm-linux-gnueabi/5/include
/usr/lib/gcc-cross/arm-linux-gnueabi/5/include-fixed
/usr/lib/gcc-cross/arm-linux-gnueabi/5/../../../../arm-linux-gnueabi/include
End of search list.
こちらもsysroot配下でないディレクトリがインクルードサーチパスに含まれていますね。
その中から探してみるとstdio.h
がありました。うーん・・・。
% cd /usr/lib/gcc-cross/arm-linux-gnueabi/5/../../../../arm-linux-gnueabi/include
% find . -name "stdio.h" | sed -e 's/^\.\///g'
bits/stdio.h
stdio.h
実行結果から振る舞いを推測する
/usr/local/include/
, /usr/include/
はsysroot
オプションの置き換え対象になっているが、/usr/lib
配下は対象ではないっぽいですね。
そして、何故かわかりませんがARM-GCCの方は、sysrootを指定しても上書きされない固定パスに標準ライブラリのヘッダが配置されているようです。
GCCの詳細については詳しくないので推測になりますが、これはARM-GCCだから、というよりは、ビルド時にたまたまこういう設定にしていたから、と考えておいた方がよさそうな気がします。
この記事では省略していますが、G++でsysrootを指定した場合は、GCCもARM-GCCも両方iostream
のインクルードに失敗しました。
この結果で重要なポイントは、sysrootを指定しても上書きされないパスが存在する、という部分ではないでしょうか。
で、何に使うの?
ターゲット側のルートファイルシステムをsysroot
に指定する、というのが想定される用途・・・なのかな?
しかし、一般的な組込みLinux環境ではルートファイルシステムにヘッダファイルを配置するという運用はほぼありえないので、あまり有用な使い道が思い浮かばない・・・。デフォルトサーチパスよりも-I
や、-L
で指定したディレクトリが優先されるので、少なくとも自分で開発しているアプリケーションをビルドするときには必要性を感じない。
RaspberryPiみたいな、PCのLinuxに近いユーザーランド環境のクロスコンパイルをしたい時は、便利なのかもしれない。
Buildrootで自分でルートファイルシステムを構築したときは、ヘッダファイルってどうなるんだろう。ヘッダファイルありのルートファイルシステムと、シュリンクされたルートファイルシステムの2つができあがったりするんだろうか。であれば、そんな時も役に立つのかも。時間があるときに試してみよう。
総括
動きはなんとなくわかったが、具体的な用途がイメージできない!まあ今回の案件では使わなくていいということはわかった。