在介绍 pkg-config 之前,先讲一个我的经历。
有一次我想用 libgtk 库在 ubuntu 上实现一个简单的图形界面,就像下面代码
#include
int main(int argc, char *argv[])
{
GtkWidget *window;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_widget_show(window);
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
gtk_main();
return 0;
}
写个 Makefile 进行编译
gtk:
gcc gtk.c -o gtk.out -lgtk
run:
./gtk.out
想的简单,使用 gtk 库就引用下库 -lgtk
就好了,结果,编译报错
gcc gtk.c -o gtk.out -lgtk
gtk.c:1:10: fatal error: gtk/gtk.h: 没有那个文件或目录
1 | #include <gtk/gtk.h>
| ^~~~~~~~~~~
compilation terminated.
make: *** [Makefile:4:gtk] 错误 1
没有 gtk.h
,那就查找该文件在哪,
$ locate gtk.h
/usr/include/gtk-2.0/gtk/gtk.h
添加下头文件路径
gtk:
gcc gtk.c -o gtk.out -lgtk \
-I /usr/include/gtk-2.0
run:
./gtk.out
编译,结果又报错
$ make
gcc gtk.c -o gtk.out -lgtk \
-I /usr/include/gtk-2.0
In file included from /usr/include/gtk-2.0/gdk/gdk.h:32,
from /usr/include/gtk-2.0/gtk/gtk.h:32,
from gtk.c:1:
/usr/include/gtk-2.0/gdk/gdkapplaunchcontext.h:30:10: fatal error: gio/gio.h: 没有那个文件或目录
30 | #include <gio/gio.h>
| ^~~~~~~~~~~
compilation terminated.
make: *** [Makefile:4:gtk] 错误 1
就这样,头文件路径我一直加到如下,
gtk:
gcc gtk.c -o gtk.out -lgtk \
-I /usr/include/gtk-2.0 \
-I /usr/include/glib-2.0 \
-I /usr/lib/x86_64-linux-gnu/glib-2.0/include \
-I /usr/lib/x86_64-linux-gnu/gtk-2.0/include \
-I /usr/include/cairo \
-I /usr/include/pango-1.0 \
-I /usr/include/harfbuzz/ \
-I /usr/include/gdk-pixbuf-2.0 \
-I /usr/include/atk-1.0
run:
./gtk.out
结果报 找不到 -lgtk
,崩溃
后来,找到了 pkg-config
这个绝招,只要一行就解决了我上面的问题
gtk:
gcc gtk.c -o gtk.out `pkg-config --cflags --libs gtk+-2.0`
run:
./gtk.out
编译成功,运行效果如下
回过头再来看下 pkg-config
作用
pkg-config 是一个 linux 下的命令,用于获得某一个库/模块的所有编译相关的信息。
例子
$
pkg-config --cflags --libs gtk+-2.0
-pthread -I/usr/include/gtk-2.0 -I/usr/lib/x86_64-linux-gnu/gtk-2.0/include -I/usr/include/pango-1.0 -I/usr/include/atk-1.0 -I/usr/include/gdk-pixbuf-2.0 -I/usr/include/libmount -I/usr/include/blkid -I/usr/include/pango-1.0 -I/usr/include/fribidi -I/usr/include/cairo -I/usr/include/pixman-1 -I/usr/include/harfbuzz -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/include/uuid -I/usr/include/freetype2 -I/usr/include/libpng16 -lgtk-x11-2.0 -lgdk-x11-2.0 -lpangocairo-1.0 -latk-1.0 -lcairo -lgdk_pixbuf-2.0 -lgio-2.0 -lpangoft2-1.0 -lpango-1.0 -lgobject-2.0 -lglib-2.0 -lharfbuzz -lfontconfig -lfreetype
从上面的例子,可以看出,pkg-config 给出了 gtk+-2.0
的头文件和库的所有信息!
这有什么好处?
所有用 gtk+-2.0
的其他程序,在编译时,只需要写 pkg-config --cflags --libs gtk+-2.0
,而不需要自己去找 gtk+-2.0
的头文件在哪里,要链接的库在哪里!省时省力!
如果你写了一个库,不管是静态的还是动态的,要提供给第三方使用,那除了给人家库/头文件,最好也写一个 pkg-config 文件,这样别人使用就方便很多,不用自己再手动写依赖了你哪些库,只需要敲一个 pkg-config --libs --cflags [YOUR_LIB]
。
很简单,有 2 种路径:
第一种:取系统的 /usr/lib
下的所有 *.pc
文件。
第二种:PKG_CONFIG_PATH
环境变量所指向的路径下的所有 *.pc
文件。
这些 pc 文件什么时候有的?都是在你安装某个库/模块的时候,添加的。比如你往系统安装libcrypt-dev 时,就会在 /usr/lib/x86_64-linux-gnu/pkgconfig/ 目录下,放一个 libcrypt.pc。
那么,pc文件到底写了什么?
打开看看就知道啦。比如 libcrypt.pc
$ cat /usr/lib/x86_64-linux-gnu/pkgconfig/libcrypt.pc
#############################################
##### Pkg-Config file for libxcrypt #####
#############################################
prefix=/usr
exec_prefix=${prefix}
libdir=/usr/lib/x86_64-linux-gnu
includedir=${prefix}/include
Name: libxcrypt
Version: 4.4.10
Description: Extended crypt library for DES, MD5, Blowfish and others
Libs: -L${libdir} -lcrypt
Cflags: -I${includedir}
一目了然,就是存了所有 libcrypt 的头文件/库的路径信息。
所有参数,可以通过 pkg-config –help 来查看。
但我觉得其实就 3 个参数有用。
pkg-config --cflags [NAME]
,查看头文件信息,如
$
pkg-config --cflags gtk+-2.0
-pthread -I/usr/include/gtk-2.0 -I/usr/lib/x86_64-linux-gnu/gtk-2.0/include -I/usr/include/pango-1.0 -I/usr/include/atk-1.0 -I/usr/include/gdk-pixbuf-2.0 -I/usr/include/libmount -I/usr/include/blkid -I/usr/include/pango-1.0 -I/usr/include/fribidi -I/usr/include/cairo -I/usr/include/pixman-1 -I/usr/include/harfbuzz -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/include/uuid -I/usr/include/freetype2 -I/usr/include/libpng16
pkg-config --libs [NAME]
,查看库信息,如
pkg-config --libs gtk+-2.0
-lgtk-x11-2.0 -lgdk-x11-2.0 -lpangocairo-1.0 -latk-1.0 -lcairo -lgdk_pixbuf-2.0 -lgio-2.0 -lpangoft2-1.0 -lpango-1.0 -lgobject-2.0 -lglib-2.0 -lharfbuzz -lfontconfig -lfreetype
pkg-config --list-all
。查看 pkg-config 的所有模块信息,如
$ pkg-config --list-all
xf86driproto XF86DRIProto - XF86DRI extension headers
libelf libelf - elfutils libelf library to read and write ELF files
cairo-ps cairo-ps - PostScript surface backend for cairo graphics library
ncursesw ncursesw - ncurses 6.2 library
libcrypto OpenSSL-libcrypto - OpenSSL cryptography library
libnfnetlink libnfnetlink - Low-level netfilter netlink communication library
libffi libffi - Library supporting Foreign Function Interfaces
python-2.7 Python - Python library
...
如上文所说,有 2 种方式。
/usr/lib/…
默认路径下。PKG_CONFIG_PATH
环境变量里。比如,你可以在 ~/.bashrc
的文件末尾添加:
PKG_CONFIG_PATH=$PKG_CONFIG_PATH:xxx/pkgconfig/
export PKG_CONFIG_PATH
那么,pkg-config 就会到 xxx/pkgconfig/
寻找 *.pc
文件。
我猜你想问,那我这个pc文件何时生效呢?
答案是,如果是 /usr/lib
下,立马生效!!!如果在环境变量里,只要先 source ~/.bashrc
一下,让环境变量生成,也立马生效。
并不需要什么 pkg-config update 啥命令,让其更新信息。
其实每次你执行 pkg-config,都会去遍历所有的 *.pc
文件。
其实很简单,只需要拿别人的 pc 文件改一改就行了。不过我还是提一下吧。
pc 文件的所有参数:
Name: 该模块的名字,比如你的 pc 名字是 xxxx.pc,那么名字最好也是 xxxx。
Description: 模块的简单描述。上文 pkg-config --list-all 命令出来的结果,每个名字后面就是 description。
URL: 用户可以通过该 URL 获得更多信息,或者下载信息。也是辅助的,可要可不要。
Version: 版本号。
Requires: 该模块有木有依赖于其他模块。一般没有。
Requires.private: 该模块有木有依赖于其他模块,并且还不需要第三方知道的。一般也没有。
Conflicts: 有没有和别的模块冲突。常用于版本冲突。比如,Conflicts: bar < 1.2.3,表示和 bar 模块的 1.2.3 以下的版本有冲突。
Cflags: 这个就很重要了。pkg-config 的参数 --cflags 就指向这里。主要用于写本模块的头文件的路径。
Libs: 也很重要,pkg-config 的参数 --libs 就指向这里。主要用于写本模块的库/依赖库的路径。
Libs.private: 本模块依赖的库,但不需要第三方知道。
其实必须写的只有 5 个。Name、Description、Version、Cflags、Libs。
pkg-config 详解