[译文] 如何开始一个真正的 GTK 项目(第三部分:配置 Automake)

原文地址:Advanced GTK Techniques。


这篇教程中你将学会:

  • 生成指定那些文件参加编译的 Makefile 文件;
  • 检查程序所需库。

这篇文章是《如何开始一个真正的 GTK 项目》的一部分,如果你不想回看之前的章节,可以直接下载教学示例程序 app-skeleton1。你也可以从头开始。


现在我们把注意力放在 Automake 上,这是编译系统中另一个重要成员。我们配置好 Automake 后,就可以开始编写代码。

Make 是一个程序,它从文件 Makefile 中读取如何编译源代码的指令,然后将代码转换成可执行文件。Makefile 的内容取决于使用何种编译器,何种系统,以及很多其它事项。如果你希望实现在之前章节描述的所有标准 make target 内容,那么 Makefile 将会变得很长。

这便是引入 Automake 的原因。Automake 可以让这一切更加简洁、抽象,并且不受平台的限制。Automake 会寻找一个名为 Automake.am 的文件,将其转译为一个类似于 Makefile 的文件:Makefile.in。随后,在运行 configure 时,它会最终转化为 Makefile

创建 Makefile.am

我们首先完整拷贝一份 app-skeleton1 目录,将它命名为 app-skeleton2,或者直接重命名目录亦可。我们当前只需要在 Makefile.am 中添加一行:

# app-skeleton2/Makefile.am
SUBDIRS = src

SUBDIRS 变量告诉 Automake 需要在子目录 src 中查找另外一个 Makefile.am。最终生成的 Makefile 也会相应地在 src 中查找另外一个 Makefile

Make 的递归

Peter Miller 曾有一篇著名的文章给出了对编写递归 Makefile 的看法:《递归编写 Make 是有害的》。建议阅读一下这篇文章,也可以听取其中一些你认为有用的观点。他的实现方法并不比本文的方法简单多少。事实上,在实际应用中,大多数工程使用了递归式的 Make,所以至少你应当熟悉这种做法。

我们已经告知了 Automake 进入 src 目录,那么我们也应该在目录中准备一些让它能用得上的东西。创建一个 Makefile.am 是肯定的,此外还需要一些更重要的东西 —— 源代码文件。在下一章节我们才会开始正式的编程工作,现在可以先找个差不多的文件凑数。从 GTK 官方教程中拿来 “Hello World” 的例程不失为简单快捷的办法,我们不需要从网页中手动复制,只需要 cd src 后拷贝一份即可:

wget http://git.gnome.org/browse/gtk+/plain/examples/hello-world.c

现在我们在 src 中创建 Makefile.am,并在文件中输入以下内容:

#app-skeleton2/src/Makefile.am
bin_PROGRAMS = app-skeleton
app_skeleton_SOURCES = hello-world.c

Automake 的主要工作是设定变量,SUBDIRS 是其中一个案例。很多 Automake 变量的名称由两部分组成,举个例子,一个名为 something_PROGRAMS 的变量表示一个列表,包含了需要由 Automake 生成的可执行文件的名称。something 表示运行 make install 时程序的安装位置,所以 bin_PROGRAMS = app-skeleton 说明了 Automake 生成的 Makefile 将把 app-skeleton 安装在 /usr/local/bin 中(你也可以把 bin 换成其它名称,Makefile 可通过 configure 灵活定制,这一点可参阅之前的教程)。

顺着这种命名的思路,app_skeleton_SOURCES = hello-world.c 表示名为 app-skeleton 的程序编译依赖的源文件为 hello-world.c,也就是我们刚刚下载的文件(如果程序名包含了变量不允许的字符,Automake 将把它变为下划线)。

我们还需要让 configure 帮我们把刚才编写好的文件转换成一个新的 Makefile。我们把 configure.ac 中的 AC_CONFIG_FILES 替换成:

#app-skeleton2/configure.ac
AC_CONFIG_FILES([
    Makefile
    src/Makefile
])

最后,我们还需要指派 configure 去找一个合适的 C 编译器,在 AM_INIT_AUTOMAKE 后面加上一句 AC_PROG_CC 即可(CC 表示 C Compiler,Linux 中一般为为 gcc)。

现在,运行 autoreconf./configureconfigure 的输出将比之前的略长(你可以看到它在寻找一个 C 编译器),并且会有更多文件产生。到目前为止,一切运行良好。不过当你运行 make 时,会收到满屏的错误:所有的 GTK 函数都未定义,编译器也找不到 gtk/gtk.h 头文件。

引入 GTK 库

我们需要指定哪些库将被程序使用。这个工作在 configure.ac 中完成,然后 configure 会去查找这些库并将它们的变量放入 Makefile 中。借此机会,我们可以重新组织 configure.ac,还能学习一些新的宏。

现在将 configure.ac 分成四个部分:

  • 初始化:完成初始化编译系统的准备工作;

  • 工具箱:告诉 Autoconf 我们需要使用哪些工具。configure 会帮我们查找这些工具,如果没有找到便会报错;

  • 库:在此列出需要用到的库。同样地,如果 configure 没有找到,它就会报错;

  • 输出:输出上述检测的结果以供 make 使用。

注释

你可以在 configure.ac 或者 Makefile.am 中使用注释,在一行的开头输入 “#” 即可。configure.ac 中也可以为在一行开头输入 “dnl”(Delete until New Line)。二者的区别在于 “dnl” 会被 Autoconf 完全忽略,而 “#” 会被一同拷贝进 configure 文件中。当 configure 中出现错误时,注释有助于我们在 configure.ac 中快速定位引起错误的部分。

在“初始化”部分,输入:

# app-skeleton2/configure.ac
AC_INIT([App Skeleton], [2], [[email protected]])
AC_CONFIG_SRCDIR([src/hello-world.c])
AM_INIT_AUTOMAKE([-Wall foreign])
AM_SILENT_RULES([yes])

我们将版本号改为 2,此外还有两个新的宏:

  • AC_CONFIG_SRCDIR

    这是一个安全行检查,用于确认其所指定位置确有一个 hello-world.c。我们需要把它的参数设定为项目中一个独一无二的代码文件,这里我们写上目前唯一的代码文件。

  • AM_SILENT_RULES

    生成 Makefile 的过程通常产生非常长的信息,我们一般不需要看这么多,而且大量无用的信息刷屏可能会让你忽视夹在其中的警告和错误信息。AM_SILENT_RULES([yes]) 将屏蔽这些消息,只输出一些总结信息。一个真正的程序员可能会使用 AM_SILENT_RULES([no]),然后运行 ./configure --enable-silent-rules 来保证政治正确性

    如果你开启了静默模式,但还是希望能检查所有输出信息,可以使用 make V=1 命令(V 表示 Verbose)。

在“工具箱”部分,输入

# app-skeleton2/configure.ac
AC_PROG_CC
PKG_PROG_PKG_CONFIG

这里有一个新的宏 PKG_PROG_PKG_CONFIG,它会在项目中加入用于检查和引用库的工具 —— pkg-config。这个宏并非 AC_ 或者 AM_ 开头,而是 PKG_,说明这是一个由 pkg-config 提供的功能。

在“库”部分,我们用 pkg-config 引入我们需要的库:

# app-skeleton2/configure.ac
PKG_CHECK_MODULES([APP_SKELETON], [
    glib-2.0
    gtk+-3.0
])

PKG_CHECK_MODULES 的第一个参数 APP_SKELETON 为程序名,第二个参数列出了一系列 pkg-config 模块。一个模块代表了 pkg-config 可以识别的一个库。这个宏使用 pkg-config 查找计算机中的库,然后生成两个变量:APP_SKELETON_CFLAGS —— 包含了库的引用信息(类似于 gcc 中的 -I 参数);APP_SKELETON_LIBS —— 包含了库的链接信息(类似于 gcc 中的 -L-l 参数)。这些变量可用于 Makefile 的编辑。

这个宏表示我们将使用 glib-2.0gtk+-3.0 两个库。如果你不知道需要用到库的模块名,可以到 /usr/share/pkgconfig/usr/lib/pkgconfig 中查找以 .pc 结尾的文件(译者注:也可以在终端中输入 pkg-config --list-all 查看)。一些库并不能用 pkg-config 引入,而是需要其它的方法,我们在这里不做介绍。

最后的“输出”部分与之前相同:

# app-skeleton2/configure.ac
AC_CONFIG_FILES([
    Makefile
    src/Makefile
])
AC_OUTPUT

全部搞定后,我们在 src/Makefile.am 中使用由 pkg-config 生成的 APP_SKELETON_CFLAGSAPP_SKELETON_LIBS 变量:

# app-skeleton2/src/Makefile.am
AM_CFLAGS = $(APP_SKELETON_CFLAGS)
bin_PROGRAMS = app-skeleton
app_skeleton_SOURCES = hello-world.c
app_skeleton_LDADD = $(APP_SKELETON_LIBS)

AM_CFLAGS 中的定义将会应用到这个 Makefile.am 文件里的所有编译指令中。相反地,app_skeleton_LDADD 变量只会在链接 app-skeleton 程序时被使用。

译者注:原文作者使用了简便的引用库方法:将所有用到的库绑定到 APP_SKELETON 变量中。其实,更为稳妥的做法应该为将每个库分离对待,这样有利于模块的灵活调用。具体做法为修改 configure.ac 中的 PKG_CHECK_MODULES 宏:

#app-skeleton2/configure.ac
PKG_CHECK_MODULES([GLIB], [
    glib-2.0
])
PKG_CHECK_MODULES([GTK], [
    gtk+-3.0
])

然后修改 src/Makefile.am 中相关的变量:

# app-skeleton2/src/Makefile.am
bin_PROGRAMS = app-skeleton
app_skeleton_SOURCES = hello-world.c
app_skeleton_CFLAGS = \
    $(GLIB_CFLAGS)  \
    $(GTK_CFLAGS)
app_skeleton_LDADD = \
    $(GLIB_LIBS)    \
    $(GTK_LIBS)

在大型项目中往往会生成多个可执行文件或库文件,这样也利于分别为每个目标设定 cflagslibs 标签,更加清晰易读。

现在我们再次编译,这次应该就能正常工作了。注意这次编译我们只需要输入 make 即可,Makefile (现在是 22 KB 了)会在检测到 configure.ac 被修改后自动执行 ./configure,同时也生成了新的自己。

你现在可以运行 app-skeleton 来看看程序的效果:一个包含了一个按钮控件的窗口,按钮上写着“Hello World”,按下之后会在终端打印“Hello World”并退出。

现在我们已经有了像模像样的编译系统,接下来还有一些基础工作等待着我们去完成:安装和翻译。


文章许可协议:Attribution-NonCommercial-ShareAlike 3.0 Unported

你可能感兴趣的:([译文] 如何开始一个真正的 GTK 项目(第三部分:配置 Automake))