前:上級者のための Hello World! ~C/C++ プログラミング~
はじめに
これは C/C++ に関する記事の二つ目である。今回はライブラリの作成者が作るべきツールについて書く。今回はそれぞれのツールについては深入りしない。
Make
C/C++ のコンパイル方法は前回みたように長かった。また、ファイルやライブラリも増えていくとコンパイルに時間がかかり大変だ。そこで、コンパイルを自動化するために Make がうまれた。我々が Makefile というテキストファイルにコンパイル方法を書き込んでおくと、勝手にコンパイルがおこなわれるのである。
書き方
前回作ったバイナリファイルを全て消しておこう。
$ rm lib/* ./hello*
Makefile の基本的な書き方は下の通りである。<コマンド> の左は必ず Tab でなくてはならない。空白だと動かない。<ターゲット> とは通常、生成されるファイルのことである。
<ターゲット>: <依存ファイル>
<コマンド>
<依存ファイル> と <コマンド> は 0 個でも 100 個でもいい。
実行するときは下のようにする。
$ make <ターゲット>
Make ではまず <ターゲット> のところが呼ばれる。そして依存ファイルを再帰的にたどっていって、もし前回の Make と違っていればコマンドを実行する。下にフローチャートを書いた。
ライブラリの作成
前回のライブラリを生成する Makefile を下に書く。静的ライブラリと共有ライブラリをどちらも作る。先に言っておくが、これはスマートな書き方ではない。これを lib ディレクトリに入ってそこに置いておく。
$ cd lib
$ touch Makefile
.PHONY: all
all: libhello.a libhello.so
libhello.a: hello.o
ar r libhello.a hello.o
libhello.so: hello.o
gcc hello.o -o libhello.so -shared
hello.o: ../src/hello.c ../include/hello.h
gcc -c ../src/hello.c -I../include
ここで 1 行目の .PHONY: all をみてほしい。これは「all というのがファイルではない」という意味だ。これで下のように打つと、all の依存関係のファイルがすべて作られ、コマンドが実行される(この例だとコマンドはない)。
$ make all
次に、lib/libhello.so を生成するところを見てほしい。前回と違い lib/hello.o から共有ライブラリを作っている。Makefile では、.c などのソースコードからまず .o ファイルを作成する。こうすることでソースコードが変更されても、そのファイルだけをコンパイルしなおすことができる。
もう一度 make all を打ってみよう。
$ make all
make: Nothing to be done for 'all'.
これは依存ファイルがすべて編集されていないため、なにも起こらなかったのである。例えば include/hello.h の編集時刻を更新してみよう。
$ touch hello.h
$ make all
コマンドが再び実行されているのがわかるだろう。
Makefile から Makefile を呼ぶ
別のディレクトリから Makefile を呼ぶには、-C オプションを使う。
$ cd .. && touch include/hello.h
$ make all -C lib
hello_ws 直下に Makefile を書くと統合できる。
$ touch Makefile include/hello.h
.PHONY: all
all:
make all -C lib
$ make all
Makefile の書き方は以下に詳しい。
makeコマンドを使ってみよう #1 — MIRACLE LINUX サポート&テクノロジー | サイバートラスト株式会社
ついにッ!最強のMakefileが完成したぞッッッ!!! - Qiita
pkg-config
ライブラリの作成は自動化できたが、コンパイルの引数の問題は解決していない。例えば画像処理に使われるOpenCV では現在、ライブラリが50個以上ある。コンパイルのたびにライブラリを50個以上指定するさまを想像してほしい。 pkg-config はライブラリを使うときにコンパイラへ引数を渡す役割を負う。ライブラリの製作者が pc という拡張子のファイルを作成することで使用できる。
書き方
<ライブラリ>.pc ファイルの基本的な書き方は下のとおりである。
<オプション>: <出力>
また、.pc ファイルでは変数も使える(Makefile でも使える)。
<変数名>=<値>
変数を使いたいところに ${<変数名>} と書けば、そこが <値> で置き換わって使うことができる。
オプションは自由に決められないが、変数は自由に決められる。
.pc ファイルの作成
ではライブラリを使うための .pc ファイルを作ろう。 lib/pkgconfig の配下に hello.pc ファイルを作る。
$ mkdir lib/pkgconfig
$ touch lib/pkgconfig/hello.pc
1行目の <hello_wsへのパス> には pwd コマンドの出力をコピペして書き換えてほしい。
prefix=<hello_wsへのパス>
Name: HelloLib
Version: 1.0
Description: Hello World! を出力するライブラリ
Libs: -L${prefix}/lib -lhello
Cflags: -I${prefix}/include
Name, Version, Description がないとエラーが出る。適当に書いておこう。
このままでは pkg-config が .pc ファイルを探せないので、環境変数 PKG_CONFIG_PATH に lib/pkgconfig を指定しておこう。
$ export PKG_CONFIG_PATH=$PWD/lib/pkgconfig:$PKG_CONFIG_PATH
pkg-config はオプションに応じて文字列を出力するだけのコマンドである。下のコマンドをそれぞれ実行してみよう。オプションの最初の文字が小文字になっていることに注意してほしい。
$ pkg-config --libs hello
$ pkg-config --cflags hello
オプションはまとめることができる。コンパイルしてみよう。$() は先に () 内のコマンドを実行して文字列を置換する。
$ gcc main.c -o hello $(pkg-config --cflags --libs hello)
$ ./hello
下のエラーが出るときはライブラリへのパスを指定していないためなので、 export LD_LIBRARY_PATH=$PWD/lib:$LD_LIBRARY_PATH を実行してほしい。(ここを参照)
./hello: error while loading shared libraries: libhello.so: cannot open shared object file: No such file or directory
