既然该软件已正确配置,所剩的就是编译了。这个阶段通常比较简单,并且不会出现什么严重的问题。
自由软件社区中编译源代码最得宠的工具是 make。它具有两个优点:
编译源代码的各个步骤常存储于 Makefile 或 GNUMakefile 文件中。当调用 make 时,它会从当前目录读取该文件(如果该文件存在的话)。如果它不存在,可以通过 make 的 -f 选项指定其他文件。
make 按照依赖性进行操作,因此为了编译某个二进制文件(“目标”)需要依次执行几个步骤(“依赖”)。例如,(假定)要创建 glloq 这个二进制文件,必需编译并链接 main.o 和 init.o 这两个目标文件(编译过程的中间文件)。而这两个目标文件同样也是目标,它们分别依赖于各自相应的源文件。
本文仅能对纷繁芜杂的 make 用法作一简略介绍。更详尽的文档,请参考使用 Make 管理项目,第二版,O'Reilly, 和 著。
$ make gcc -c glloq.c -o glloq.o gcc -c init.c -o init.o gcc -c main.c -o main.o gcc -lgtk -lgdk -lglib -lXext -lX11 -lm glloq.o init.o main.o -o glloq |
好,二进制文件已经正确编译完成。我们可以进入下一个阶段 -- 安装该发行版的文件了(二进制文件、数据文件等)。参见“安装”一节。
如果您曾好奇地查看过 Makefile 文件,您会发现一些已知的命令(rm、mv、cp 等),以及一些诸如 $(CFLAGS) 的奇怪字符串。
它们是变量,通常它们在 Makefile 文件开始处被设定。然后,在它们出现的地方会被相应的值所取代。当您需要一直使用同样的编译选项时,这会非常有用。
例如,要使用 make all 在屏幕上显示字符串“foo”:
TEST = foo all: echo $(TEST) |
LD:该程序用以确保编译的最后一个阶段(参见“编译的四个步骤”一节)。默认为 ld。
CFLAGS:它将在编译的第一个阶段为编译器提供额外的参数。其中有:
$(CC) $(CFLAGS) -c foo.c -o foo.o |
glloq.c:16: decl.h: No such file or directory
编译器不能找到对应的头文件。不过,在软件配置阶段就可能已经发现该错误了。解决方法是:
请检查该头文件确实已经存在于以下某个目录中:/usr/include、/usr/local/include、/usr/X11R6/include 或它们的某个子目录。如果没有,请在整个磁盘上查找它(可用 find 或 locate)。如果还是没有,请检查您是否已经安装了该头文件对应的库了。您可以在 find 和 locate 命令各自的手册页面中分别找到它们的示例。
如果它在 /usr/local/include 或 /usr/X11R6/include 目录中,您有时可能需要为您的编译器添加额外的参数。用您常用的文本编辑器(Emacs、Vi 等)打开对应的 Makefile (请注意,您得打开编译出错的那个目录中的文件[33])。查找到出错的那一行,并紧接着调用编译器(gcc,有时是 $(CC))的地方添加字符串 -I<路径>(其中<路径>是包含该头文件的路径)。如果您不清楚要把该选项加到哪里,请把它添加到文件开始处 CFLAGS=<什么什么的> 后面或是 CC=<什么什么的> 后面。
如果还是不起作用,请向您周围的高手求助,或向自由软件社区求助(参见“技术支持”一节)。
glloq.c:28: `struct foo' undeclared (first use this function)
结构几乎是所有程序都会使用的一种特殊类型。系统在头文件中定义了许多。这表示该问题很可能是由于找不到头文件,或误用头文件造成。解决该问题的正确步骤是:
试着查一查出问题的结构是否已由程序或系统定义。比如用 grep 命令查看该结构是否已经在某个头文件中定义了。
$ find . -name '*.h'| xargs grep 'struct foo' | less |
在屏幕上可能会出现许多行(例如,每一次某个函数使用该类型的结构定义一个实例)。如果它们存在,找到头文件中 grep 指出的那一行。
struct foo { <结构内容> }; |
如果没有,在系统头文件中(通常位于 /usr/include、/usr/X11R6/include 或 /usr/local/include)再次查找。不过这一次,您得添加 #include <<文件名>.h>。
如果该结构还是不存在,请试着找找它应该在哪个库(即保存许多函数的一个软件包)中定义(请查看 INSTALL 或 README 文件以确定该程序使用哪些库以及它们的版本)。如果该程序需要的版本不是您系统上安装的那一个,您就需要更新该库了。
如果依然不起作用,请检查该程序是否真能在您的架构上运行(某些程序还没有移植到 UNIX® 系统上)。并请检查您是否已经为您的架构正确配置了该程序了(比如在执行 configure 的时候)。
这个问题解决起来较为复杂。因为编译器常会在真正出错的地方之后报错。有时候,它仅仅是由于某个数据类型未定义。如果您碰上如下的错误信息:
main.c:1: parse error before `glloq_t main.c:1: warning: data definition has no type or storage class |
这个问题解决起来较为简单:磁盘上已经没有足够的空间从源文件生成二进制文件了。您可以通过释放安装目录所在分区的一些空间来解决(删除临时文件或源文件,卸载某些您已经不用的程序)。如果您解压到 /tmp 而不是 /usr/local/src 目录中,它会阻止对 /tmp 分区不必要的填充。请检查磁盘上是否有 core 文件[34]。如果有,请删除它们;如果它们属于别的用户,请让他们删除这些文件。
/usr/bin/ld: cannot open -lglloq: No such file or directory
这表示 ld 程序(在编译的最后阶段由 gcc 调用)无法找到某个库。为了包含某个库,ld 将会搜索文件名由 -l<库> 选项指定的库文件。相应文件为 lib<库>.so。如果 ld 找不到,它将给出一条错误信息。要解决该问题,请如下操作:
请用 locate 命令检查该文件是否在硬盘上。通常,图形库在 /usr/X11R6/lib 目录中。例如:
$ locate libglloq |
如果上述查找没有结果,请使用 find 命令查找(比如 find /usr -name "libglloq.so*")。如果您还是找不到,您就需要安装它了。
一旦找到了该库,请检查它是否能被 ld 访问:/etc/ld.so.conf 文件指定寻找这些库文件的目录。把该库的路径添加到该文件末尾(您可能需要重启您的计算机以使改动起作用)。您也可以把该目录添加到环境变量 LD_LIBRARY_PATH 中。例如,如果要添加 /usr/X11R6/lib 目录,请键入:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/X11R6/lib |
如果还是不行,请用 file 命令检查该库文件是否为一个可执行文件(或 ELF)。如果它是一个符号链接,请检查该链接完好且没有指向不存在的文件(例如,可用 nm libglloq.so 检查)。而且,该库文件的权限可能是错误的(例如,如果您不是 root 且该库文件不允许读)。
glloq.c(.text+0x34): undefined reference to `glloq_init'
该问题是由于在编译的最后阶段某个符号找不到。通常,这是因为某个库出问题了。可能的问题有:
首先,请查找该符号是否应该在某个库文件中。比如,如果该符号以 gtk 开头,它就应该属于 gtk 库。如果这个库能够容易地找到(frobnicate_foobar),您可以用 nm 命令列出该库中的符号。例如,
$ nm libglloq.so 0000000000109df0 d glloq_message_func 000000000010a984 b glloq_msg 0000000000008a58 t glloq_nearest_pow 0000000000109dd8 d glloq_free_list 0000000000109cf8 d glloq_mem_chunk |
使用 nm 时添加 -o 选项让您分别在不同行中显示库中的名称,从而使搜索变得更为简单。假定我们要搜索符号 bulgroz_max,您可以:
$ nm /usr/lib/lib*.so | grep bulgroz_max $ nm /usr/X11R6/lib/lib*.so | grep bulgroz_max $ nm /usr/local/lib/lib*.so | grep bulgroz_max /usr/local/lib/libfrobnicate.so:000000000004d848 T bulgroz_max |
好极了!该符号 bulgroz_max 定义于 frobnicate 库中(其名称前有一个大写字符 T)。然后,您只需要编辑 Makefile 文件以在编译命令行中添加字符串 -lfrobnicate:请将其添加于定义 LDFLAGS 或 LFGLAGS (或至少 CC)的那一行的末尾,或是在创建相应二进制文件的那一行处。
该发行版的目标文件没有全部正确链接。缺少了定义该函数的文件。请键入 nm -o *.o 以查看应该是哪个文件,然后将相应的 .o 文件添加到对应的编译命令行上。
出错的函数或变量可能并不存在。请试试删除它:编辑出问题的源文件(它的名字会出现于出错信息的开始处)。这是没有办法的办法了,而且它将导致程序执行混乱,以及会在启动时出现segfault(段错误),等错误。
在不同的阶段,编译器需要临时工作空间。如果它不能申请到这些空间的话,它将出错。因此,您需要清理分区,不过请尽量小心,因为删除某些文件会导致某些正在执行的程序(X 服务器、管道等)挂起。您必需能够清楚知道您在做什么!如果 /tmp 所在的分区并不仅仅包含该目录(比如根目录),请搜索并删除 core 文件。
在您的系统上,这常常只是一个时间上的问题。make 确实需要了解计算机时间和它检查的文件的时间。它比较这两个时间,并根据结果确定某个目标是否已过时。
某些日期问题可能导致 make 不停地编译(或不停地递归编译某个子目录)。在这种情况下,touch (其作用是将有问题的文件的时间设定为当前时间)通常会解决该问题。
$ touch * |
$ find . | xargs touch |