前:上級者のための 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