凄い
目次
pkg-config とは
開発というのは時に、外部のオープンソースに頼りながら進めていくことになります。
外部ライブラリを利用して開発する際、たいていの場合は人 (環境) によってインクルードディレクトリやライブラリディレクトリが異なってきます。
そのため、Makefile (やシェルスクリプト等) を使ってビルドする際にこれらディレクトリを直接パスで指定してしまうと、他の人の環境では同じ方法で make できなくなってしまうことになります。
pkg-config はそれを解消するためのツールです。とにかくすごい。
pkg-config の使い方
pkg-config は PKG_CONFIG_PATH という環境変数で指定されるパスにある *.pc ファイルを参照して、該当の include, lib ディレクトリを持ってきてくれます。(上の Wikipedia 等を読もう)
PKG_CONFIG_PATH は (恐らく) 自分で指定する必要がありますが、大抵は次の2つのうちどちらかを指定することになるかと思います。
- /usr/lib/pkgconfig
- /usr/local/lib/pkgconfig
上は所謂すべてのユーザー用、下はローカルユーザー用ってことになりますが、不安なら両方を指定してやってもいいのかもしれない?ログインの度に環境変数を指定し直すのはしんどいので、 ~/.bash_profile
(か、~/.bashrc
) に以下を追記しておくとよいかと思われます。
# ~/.bash_profile か ~/.bashrc export PKG_CONFIG_PATH="/usr/lib/pkgconfig" # 上の場合 export PKG_CONFIG_PATH="/usr/local/lib/pkgconfig" # 下の場合 export PKG_CONFIG_PATH="/usr/lib/pkgconfig:/usr/local/lib/pkgconfig" # 両方の場合
さて、PKG_CONFIG_PATH を通しても、そこに *.pc ファイルがないと pkg-config は動いてくれません。巨大なライブラリをインストールしていたらいつの間にか自動で生成されているかもしれませんが、そうでないことも稀によくあることが知られています。
次の節では、glib-2.0 に含まれる glib.h
を例に、pkg-config を使ってコンパイルする流れを追ってみることにしましょう。
glib-2.0 の glib.h を pkg-config で使ってみよう
この節では、glib-2.0 に含まれる glib.h を使った C 言語のコードを pkg-config を使ってコンパイルする方法を追ってみることにします。
[事前の注意] libc と glib と glibc は違うぞ!
そもそもこの記事を書こうと思った訳
タップで開く
そもそも私がこの記事を書こうと思った動機は、fcitx-imlist の ./confingure
をしようとした時に glib-2.0 が見つからないと怒られたからです。
configure: error: Package requirements (glib-2.0 >= 2.26) were not met: No package 'glib-2.0' found Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables GLIB_CFLAGS and GLIB_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details.
これを見て、
$ echo $PKG_CONFIG_PATH
をやってみたら何も出力されなかったので、ああそういうことかとなった次第。ここで同時に pkg-config について学び、ついでに glib-2.0 を pkg-config 経由で動かす方法を模索した、という訳でありまして、ついでにここにその記録を残しとけ、ってなったという訳であります。
(動機おわり)
そもそも GLib って何やねん
GLib is a general-purpose, portable utility library, which provides many useful data types, macros, type conversions, string utilities, file utilities, a mainloop abstraction, and so on.
https://docs.gtk.org/glib/ ←2023/06/14 アクセス.
GLib provides the core application building blocks for libraries and applications written in C. It provides the core object system used in GNOME, the main loop implementation, and a large set of utility functions for strings and common data structures.
https://wiki.gnome.org/Projects/GLib ←2023/06/14 アクセス.
だそうです。
glib-2.0 の導入と pkg-config の設定
glib-2.0 は次で手に入れられます。
$ sudo apt install libglib2.0-dev
以下では、PKG_CONFIG_PATH に /usr/lib/pkgconfig を指定した場合について述べます。
前節で PKG_CONFIG_PATH に指定した場所*1に新しく glib-2.0.pc
を作成して、次のように記述する。(sudo が必要になると思います) (Wikipedia のコピーです。)
/usr/lib/pkgconfig/glib-2.0.pc
prefix=/usr exec_prefix=${prefix} libdir=${exec_prefix}/lib includedir=${prefix}/include glib_genmarshal=glib-genmarshal gobject_query=gobject-query glib_mkenums=glib-mkenums Name: GLib Description: C Utility Library Version: 2.30.2 Libs: -L${libdir} -lglib-2.0 Libs.private: -lrt Cflags: -I${includedir}/glib-2.0 -I${libdir}/glib-2.0/include
何を言っているのかよく分かりませんが、ざっと概観をいうと、
指定すると言っています。
試しにちゃんと動くか確かめてみましょう。
--cflags
でコンパイラに渡すべきインクルードのオプションを出力できます。
$ pkg-config --cflags glib-2.0 -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include
同様に、--libs
でライブラリのオプションを出力できます。
$ pkg-config --libs glib-2.0 -lglib-2.0
同時に出力させるには --cflags
と --libs
を並べれば OK です。
$ pkg-config --cflags --libs glib-2.0 -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -lglib-2.0
glib.h を使ってみよう
次に適当な場所に次のような C 言語のファイルを用意します。(a.c
とします。) (またもやほぼ Wikipedia のコピーです。)
// a.c #include <glib.h> int main() { g_print("hello\n"); return 0; }
さて、これを gcc するのですが、glib.h の場所を -I
で指定したりするのではなく、pkg-config を使って次のようにします。
$ gcc $(pkg-config --cflags --libs glib-2.0) a.c
すると
In file included from /usr/include/glib-2.0/glib/galloca.h:32, from /usr/include/glib-2.0/glib.h:30, from a.c:1: /usr/include/glib-2.0/glib/gtypes.h:32:10: fatal error: glibconfig.h: そのようなファイルやディレクトリはありません 32 | #include <glibconfig.h> | ^~~~~~~~~~~~~~ compilation terminated.
はい、怒られましたね。日常茶飯事です。glibconfig.h がないと言われました。
先ほど glib-2.0.pc で指定したディレクトリを見てみても、確かにありません。
これがどこにあるかと言いますと、、、
/usr/lib/x86_64-linux-gnu/glib-2.0/include
です。なんでやねん!!って感じですが、確かにここには置いてあります。
なので、先ほどの glib-2.0.pc にこれを書き加えるか、include ディレクトリに glibconfig.h をコピペすると無理やり通せそうな気がします。
今回はとりあえず glib-2.0.pc を追記することにしました。
/usr/lib/pkgconfig/glib-2.0.pc
prefix=/usr exec_prefix=${prefix} libdir=${exec_prefix}/lib includedir=${prefix}/include aptdir=${libdir}/x86_64-linux-gnu glib_genmarshal=glib-genmarshal gobject_query=gobject-query glib_mkenums=glib-mkenums Name: GLib Description: C Utility Library Version: 2.30.2 Libs: -L${libdir} -lglib-2.0 Libs.private: -lrt Cflags: -I${includedir}/glib-2.0 -I${libdir}/glib-2.0/include -I${aptdir}/glib-2.0/include
実際やってみると
$ gcc $(pkg-config --cflags --libs glib-2.0) a.c
/home/linuxbrew/.linuxbrew/bin/ld: /tmp/ccP3SXxK.o: in function `main': a.c:(.text+0x15): undefined reference to `g_print' collect2: error: ld returned 1 exit status
。。。もう訳分かんない!
はい。すみません。ライブラリは後でリンクする必要があるんでした。何と初歩的な...
Wikipedia 間違えてんじゃん!!
という訳なので、ちゃんと順番を守って指定してあげて
$ gcc a.c -o a $(pkg-config --cflags --libs glib-2.0) $ ./a hello
よーやっと通った。。。。あー大変だった。
Makefile で pkg-config を使ってみよう
さて、pkg-config は Makefile と相性が良いです。
Makefile が何なのかという分かりやすい解説はインターネット上に無限に存在するので、詳細はそちらに譲るとして、これを使うと何がどうなるかだけを述べると、上で
$ gcc a.c -o a $(pkg-config --cflags --libs glib-2.0)
と打たなければならなかったものが、単に
$ make
と打つだけでコンパイルできるようになります。
。。。いや、Makefile の偉大さは本来そこではないのですが*2、まぁ、pkg-config の利用例としては格好の題材だと思ったので。
Makefile 速習
Makefile は、次のような基本構造の羅列で構成されます。
[作りたいもの]: [材料] (---Tab---)[作り方]
([作り方] の前の空白は必ず Tab キーで入力してください)
旨いカップ麺: 日清カップヌードル お湯 日清カップヌードルにお湯をかけて3分待つ お湯: 水 水を沸騰させる
みたいな感じになるでしょう。(←)
もし手元に日清カップヌードルがなければ、それを手に入れるための方法をさらに下に追記していくことになります。
何が言いたいのかというと、作りたいものがまず先にあって、それを作るにはこの材料が必要で、こう作れば良い、という要領で、「結果から逆算」していってる訳です。
この「結果から逆算」をすることによって、プログラムのビルドの依存関係を直観的に記述することが容易になります。これが Makefile の強みの一つでもあります。
Makefile の特長はこれだけではないのですが、、、
Makefile を作ってみる
。。。ひとまず、先ほど作った a.c と同じディレクトリに、次のような Makefile を作ってみましょう。
# Makefile INCLUDES = $(shell pkg-config --cflags --libs glib-2.0) CC = gcc PROGRAM = a SRC = a.c all: $(PROGRAM) $(PROGRAM): $(SRC) $(CC) $(SRC) -o $(PROGRAM) $(INCLUDES)
- 最初の4行で変数 (正確にはマクロといいます) を定義しています。
- all が早速特殊なタイプで、作り方がなく材料だけが指定されています。
- その次の
$(PROGRAM)
云々が本体です。上で定義したマクロがそのまま展開されて、今まで shell で打ってきた通りのことがここで実行されます。
詳しくは他サイト様をみてね。
そして make してみます。するとどうでしょうか。
$ make gcc a.c -o a -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -lglib-2.0 $ ./a hello
はい。pkg-config がコンパイルに必要なインクルードやライブラリのオプションを自動で指定してくれ、ちゃんと実行できましたね。
まとめ
pkg-config を使いこなして充実したプログラミングライフを送ろう!