C/C++ + autotools/cmake 編
- 主にCやC++で書かれたソースコードのビルド方法
- C, C++は昔から開発言語の主流であり, 歴史も深い
- そのためビルドの慣習もいくつかあり, 古い方式と新しい方式が混在している
- ビルドするためにはそのソフト用のビルド手順を読むのが基本である
- ... と述べた上で, ほとんどのソフトが従っている, よくあるパターンをいくつか説明しておく
configure; make; make install
基本
- tarballで入手したソースツリーには多くの場合,
configureという名前のファイル (実体はシェルスクリプト) が含まれる - そのソフトを 本番用ディレクトリ (
/usr/localなど)にインストールする場合 (今はこちらはやらない) の よくある手順は以下
# 注: 以下は本番ディレクトリにインストールするとき; 今はこちらは使わない
$ cd ソースツリーのトップディレクトリ
$ ./configure
$ make
$ sudo make install
./configureは- その環境で無事ビルドが可能かをチェックする --- 例えばある関数を使うのであればそれが使えるか (環境に必要なライブラリがインストールされているか) をチェックする
config.hを, (config.h.inを元に) 生成する . ここにはconfigureで判明した環境固有の情報 --- この関数が使えるとか使えないとか --- が書き込まれており, 多くのファイルから#include "config.h"で取り込まれるMakefileを (Makefile.inを元に) 生成する. 次のmakeコマンドで使われる
makeはMakefileに書かれた命令を読み込んで実際のビルド (コンパイルやリンク) を行う. 普通はここで,gcc,g++などのコマンドが実行されるmake installは最終的な生成物 --- 実行可能ファイル (いわゆるコマンド) やライブラリ --- を所定の場所 (コマンドは/usr/local/bin, ライブラリは/usr/local/libが典型) にコピーする. それらのディレクトリに書き込み権限がないといけないのでここだけsudo(root権限つき) で実行するのが普通
開発・デバッグ用の configure
ここで我々が行う開発・デバッグ用の configureでは
- インストール先ディレクトリの指定
- デバッグオプションの指定
- 最適化なしオプションの指定 を行うので, 我々がよく使うテンプレートは以下である
# 開発・デバッグ用のビルド・インストール
$ cd ソースツリーのトップディレクトリ
$ mkdir inst # インストール先ディレクトリを作る
$ ./configure --prefix=ソースツリーのトップディレクトリ/inst CFLAGS="-O0 -g3" CXXFLAGS="-O0 -g3"
$ make
$ make install
- ポイントは
configureに与えた以下のオプション--prefix=dir : インストール先のディレクトリをdirにするCFLAGS="-O0 -g3": Cコンパイラ (gccやclang) を起動するときのオプションを指定するCXXFLAGS="-O0 -g3": C++コンパイラ (g++やclang++) を起動するときのオプションを指定する
--prefixで指定したディレクトリなどは, 生成されるMakefileやconfig.hの中に焼き込まれ,makeコマンド, ひいては生成されるプログラムの動作に影響を与える- 注意:
configureスクリプトはソフトごとに異なる- すべてのソフトに共通した
configureという唯一無二のコマンドがあるわけではない - これは
configureの目的 --- そのソフトをビルドするのに必要なものが環境に備わっているかをチェックし, 環境の違いをconfig.hやMakefileに書き込む --- を考えれば当然
- すべてのソフトに共通した
- 一方で
configure自身, ツールで自動生成されるものであり, ほとんどのソフトに共通の動作がある--prefixなどのオプションはほぼ全てのconfigureが受け付けているCFLAGS=, CXXFLAGS=なども概ね受け付けている. が, 必ずそうだと信じ込まないほうが良い (確かめ方は以下)
- 自分が手にした
configureが何をサポートしているかは,
$ ./configure --help
で確かめられる
make
./configure後にmakeコマンドを実行するとgcc,g++などが (普通は数多く) 実行され, うまくすれば成功するmakeコマンドは一般的にはどのようなコマンドが実行されるかを表示してくれるので,CFLAGSやCXXFLAGSで指定したオプションが渡っているかに注目せよ- しかし, コマンドラインを垂れ流すのが汚いと思うからなのか, それを隠す
Makefileもある - そのような場合にはそれを露出させるオプションがないかを探ることに価値がある
- 典型的には
$ make VERBOSE=1
$ make V=1
make install
--prefixで指定したディレクトリ下に最終生成物をインストールする--prefix=D としたのであれば D/bin, D/lib, などのディレクトリができ, コマンドは D/binの下に生成されているはず
練習
- GNU awk のtarballをダウンロードし, configure; make; make install でビルド, インストールせよ
--prefixの指定CFLAGSの指定 を忘れずに, gccのコマンドラインにCFLAGSが渡っていることを目視せよ
autotools
- 上記で tarball には
configureが含まれておりその場合は述べた手順でビルドができる (ことが多い) と述べた - 一方
git cloneしてできたディレクトリはさにあらずで,configureを生成するというステップから行う想定である場合が多い - それは,
configure自身が非常にややこしいスクリプトであって, 手動で書くものではなく, 別のファイル (configure.ac) からツール (autoconf) を使って生成しており, git のレポジトリは通常, ツールで生成されたものは含まないよう管理されているという方針による - 実は tarball に同梱されているファイルには
configure以外にもそのような, ツールで生成されているもの (Makefile.inなど) があり, 生成方法もややこしい - 処世術としては,
configureが含まれておらず代わりにconfigure.acが含まれていたら,- 以下のコマンドでもろもろのファイルを生成する
$ autoreconf -i
configure, Makefile.inが生成されていることを確認
autorefonfを初めて実行した人は多くの場合, エラーになるだろう- 多くの場合以下の3つのツールをインストールすれば解決する
- autoconf
- automake
- libtoolize
$ sudo apt install autoconf automake libtoolize
-
その後の手順は前節 (
configure; make; make install) で述べたものと同じ -
configure.ac, Makefile.am, config.h.in
-
autoreconf -i-> configure, Makefile.in, libtool -
./configure-> Makefile config.h
CMake
基本
-
configure(autotools) があまりにもややこしいからか, それに変わるものとして CMake があり, 最近はそれが多い印象 -
makeと似た名前なのでmakeの代替のように見えるがそれは誤解で, あくまでconfigureのステップ (Makefileを生成するまで) の代替であり, 実際のコンパイルはmakeで行う -
CMakeでもconfigureでもどちらでもビルドできるようになっているプロジェクトもある
-
CMake でのビルドを想定しているソースツリーでは, (普通はトップレベルに)
CMakeLists.txtというファイルが存在している -
最低限, configureとの以下のような対応を覚えれば足りる
| configure | CMake |
|---|---|
--prefix=D | -DCMAKE_INSTALL_PREFIX=D |
CFLAGS="-O0 -g3" | -DCMAKE_C_FLAGS="-O0 -g3" |
CXXFLAGS="-O0 -g3" | -DCMAKE_CXX_FLAGS="-O0 -g3" |
-
cmakeには-DCMAKE_BUILD_TYPE=Debugというオプションを指定するだけで, 普通は "-g" フラグを指定してくれるが,"-O0 -g3"を指定したければ信用しないほうが良い (きちんと gcc などのコマンドラインを目視する) -
CMake は慣例として, 「生成物でソースツリーを汚さない (ソースと生成物は別ディレクトリ)」という方針が貫かれており, build途中のファイルが全て入ったディレクトリ (e.g.,
build) を作ってそこでcmakeもmakeも実行するのが普通である -
なお
cmakeが生成するMakefileはコマンドラインを表示しない (まるでそうすることが美しいと思っているかのような, 進捗表示だけを行う) が,make VERBOSE=1とすることで表示してくれるので開発をしたいならそれを推奨する -
以上より, 開発・デバッグ用の典型的なビルド手順は以下
$ cd ソースツリーのトップディレクトリ
$ mkdir build inst # 生成物置き場(build), 最終青果物置き場(inst) を作成
$ cd build
$ cmake -DCMAKE_INSTALL_PREFIX=ソースツリーのトップディレクトリ/inst -DCMAKE_C_FLAGS="-O0 -g3" -DCMAKE_CXX_FLAGS="-O0 -g3" ..
$ make VERBOSE=1
$ make install
-
注: cmake の最後の引数 (
..) はCMakeLists.txtの存在するディレクトリを指定している -
cmake では一般に,
-D変数=値という形でオプションを指定するが, おびただしいオプションがあり名前も長い上, どのようなオプションがあるのかがわかりにくい -
cmakeを実行すると変数の設定が
CMakeCache.txtというファイルから読み込まれるとともに, 施した設定が同じファイルに書かれるので, 以下のようなやり方が実践的である
$ cmake ..
$ # CMakeCahce.txt をエディタで変更する (CMAKE_C_FLAGS, CMAKE_CXX_FLAGS, CMAKE_INSTALL_PREFIX ほか)
$ cmake .. # もう一度cmake .. を実行
$ make VERBOSE=1
$ make install
練習
- CMakeでビルドすることを想定した小さなプロジェクトとして fastfetch を開発・デバッグ用にビルド, インストールして実行してみよ
- 適切にオプション (
CMAKE_C_FLAGS,CMAKE_CXX_FLAGS,CMAKE_INSTALL_PREFIX) を設定すること make VERBOSE=1をつけてコンパイラのコマンドラインを目視し,"-O0 -g3"が行き渡っていることを確認すること
コードを修正した後の再ビルド
- ソースコードの一部を修正してもう一度ビルドする場合は
makeとmake installをやり直せば良い
$ make
$ make install
- つまり
./configureやcmakeの再実行は通常は不要 makeはビルドを完了するのに必要なコマンド (修正があったファイルやそれに影響を受けるファイルのコンパイル) だけを実行してくれるので初回のビルドに比べて普通は高速に終わる- しかし人生においては, 時として全てをやり直したくなるときもある
- その場合は
make cleanとする
$ make clean
$ make
$ make install
configure の再実行
./configureを実行するとconfig.cacheというファイルが生成される- 以降の実行ではこれを参照して一部のコマンド実行をスキップする
- このせいで環境を修正したにもかかわらずエラーがで続けることがあるので, 万全を期したければ
config.cacheを消す
$ rm config.cache
$ ./configure
- もっとも
./configureからやり直す, その際に万全を期したいのであれば tarball の展開からやり直すのが一番確実
cmake の再実行
cmakeを実行するとCMakeCache.txtというファイルが生成されるcmake実行中にエラーがでて, 環境を修正したにもかかわらずエラーがで続ける場合,CMakeCache.txtを消してから再実行する
$ rm CMakeCache.txt
$ cmake ..
- この場合も, 初期状態に戻すことに万全を期したければ, ソースツリーごと一旦消去してやり直すのも一考する
configure/cmake時のエラー対策
./configureやcmake時にはエラーや警告が出ることがある- その環境ではビルドができない, ということを早めに検出して, その環境の何が問題なのかを指摘することが主な役割である
- 典型的なエラー
- ツール (コマンド) がない. 例えば gcc/g++ などのC/C++コンパイラがない
- ライブラリがない. 典型的にはそのライブラリがあれば存在しているはずのヘッダファイル (
xxxx.h) や共有ライブラリファイル (libxxx.so) がない
- それらの多くは必要なソフトをパッケージ管理ソフト (Ubuntuであれば
apt) でインストールすることで解決する. 例えばgccであれば,
$ sudo apt install gcc
- 入れるべきパッケージ名を調べる完璧な方法はない (OS (Linux? Mac? BSD?) が違ったり, 同じOSでもディストリビューション (Ubuntu? Arch? Fedora?) が異なればそれによってもパッケージ名が異なる可能性があるためある程度致し方ない)
make時のエラー (コンパイルエラー) 対策
configure/cmakeは成功したがmakeの最中にエラーがおきたらそれはじっくりと腰を据えた調査が必要になる- しっかりと,
- どのディレクトリで実行したどのコマンドがエラーを起こしているのか (だからそれを表示させることが重要!)を見極める
- 同じエラーを手動で再現する
- その上でエラーメッセージやその場所を見てじっくり原因を探る のが重要
- エラーの理由についてはありとあらゆる可能性があるとしか言えないが, おそらく他の環境 (OS, ディストリビューションやそのバージョン) ではコンパイル成功したものだから, 非常に初歩的なエラーということは稀
- 依存しているライブラリが更新されてインタフェースが変更された (ソースコードは少し前のバージョンを前提に書かれている), みたいなことが典型である
必要なパッケージ名を探り当てるいくつかの方法
./configure/cmake時によく遭遇するエラーは, 環境に必要なソフト (コマンド, ライブラリ) が入っていないことに起因するものが多い- 典型的な調査・解決方法
コマンドがないと言われた場合
- コマンド名=パッケージ名となっていることは結構多い
$ g++
g++: command not found
$ sudo apt install g++
- Ubuntuのデスクトップ環境では, シェルにコマンドを打ち込んでそのコマンドがない場合, 必要なパッケージ名を教えてくれる
$ g++
Command 'g++' not found, but can be installed with:
sudo apt install g++
- 教えてくれない場合,
command-not-foundというパッケージをインストールすると教えてくれるようになる
$ sudo apt install command-not-found
$ sudo apt update
- このあと改めて再ログインが必要になるかもしれない
ライブラリ (必要な関数が入ったパッケージ) がないと言われた場合
- Ubuntuのライブラリのパッケージ名は
libXXX-devという名前のパッケージ名になっていることが多い - 例えばライブラリ
gmpがないと言われたら,
$ sudo apt install libgmp-dev
をやってみる価値はある. ただしやや当てずっぽうがすぎるという嫌いはある
汎用的なパッケージ検索方法1 apt sesarch
- Ubuntu であれば
apt search キーワードでそのキーワードがパッケージ名や記述に含まれるパッケージを列挙してくれるので, あるパッケージ名が存在することやその簡単な説明を確認できるため,apt installを試みる前に試すことを推奨する
$ apt search libgmp-dev
$ apt search libgmp-dev | less # ページめくりで表示
$ apt search libgmp-dev | grep -A1 -B1 arithmetic # arithmeticが出現する行とその前後1行を表示
汎用的なパッケージ検索方法2 apt-file sesarch
- Ubuntu には
apt-fileというコマンドがありファイル名を与えるとそれが含まれるパッケージ名を教えてくれる
$ apt-file search gmp.h
$ apt-file search /gmp.h # igmp.h などが引っかかってほしくない場合
apt-fileをインストール, セットアップする手順は以下
$ sudo apt apt-file
$ sudo apt-file update