首页 > 代码库 > 《GNU make项目管理》笔记--规则(3)

《GNU make项目管理》笔记--规则(3)

1.管理程序库

程序库(archive library)是一个特殊的文件,该文件内含其他被称为成员的文件。程序库可用来将相关的目标文件聚集成

较容易操作的单元,例如,C的标准程序库lib.a就包含了许多低级的C函数。因为程序库如此常见,所以make对它们的创建、

维护以及引用提供了特别的支持。程序库的建立及修改可通过ar程序来进行。

下面我们看一个例子,程序包结构如下:

根目录

|---makefile

|----|include|

|           |___myutil.h

|           |___myutil2.h

|___|src|

         |----myutil.c

         |----myutil2.c

         |__main.c

各文件内容如下:

main.c:
#include <stdio.h>
#include "myutil.h"
int main(void) {
        myprint();

        myprint2();
        return 0;
}


myutil.h:
void myprint();


myutil.c:
#include <stdio.h>
void myprint(void) {
        printf("this is myprint function.\n");
}


myutil2.h:
void myprint2();


myutil2.c:
#include <stdio.h>
void myprint2(void) {
        printf("this is myprint2 function.\n");
}


makefile:

VPATH=src include
CPPFLAGS=-I include
main:myutil.o myutil2.o
main.o:myutil.h myutil2.h
myutil.o:myutil.h
myutil2.o:myutil2.h

我们可以把把util类打包成myutil.a,该程序由两个文件组成:myutil.o和myutil2.o。我们可以使用ar命令来创建此程序库。

todd@todd-pc:~/makefile$ ar rv libutil.a myutil.o myutil2.o
ar: creating libutil.a
a - myutil.o
a - myutil2.o

选项r代表我们想要以制定的目标文件来替换程序里的成员;选项v代表ar必须详细地告诉,他做了哪些动作。rv选线给之后

的第一个参数时程序库的文件名,接着是遗传目标文件。执行ar命令之后所显示的信息里,你将会看到它以a来表示目标文件

已被加入程序库里了。

以r选项来使用ar命令,可让我们立即创建或更新一个程序库:

todd@todd-pc:~/makefile$ ar rv libutil.a myutil.o
r - myutil.o
todd@todd-pc:~/makefile$ ar rv libutil.a myutil2.o
r - myutil2.o

一个程序被链接到一个可执行文件的方法有好几种,最简单的方法就是在命令行直接制定该程序库,编译器或连接器将会

以文件的扩展名来判断命令行上特定的类型并做正确的事情:

gcc main.o libmyutil.a -o main

gcc将libmyutil.a视为程序库,并对他们搜索未定义的符号,在命令行上引用程序库的另一个方法就是使用-l选项:

gcc main.o -lmyutil -o main
/usr/bin/ld: cannot find -lmyutil
collect2: error: ld returned 1 exit status
-l选项可省略程序库文件名的前缀以及扩展名,当gcc看到-l想俺想时,就会在系统的标准程序库目录中搜索相应的程序库。

这样,程序员就不必知道程序库的确切位置,而且可以让其所使用的命令行更具有可移植性。若要变更编译器所使用的搜索

路径,可以使用-L选项制定所要搜索的目录以及搜索的次序:

gcc main.o  -L. -lmyutil -o main


2.创建与更新程序库

下面就是一个用来创建程序库的简单规则:

libcounter.a : counter.o lexer.o

        $(AR)  $(ARFLAGS) $@ $^

然而有一个问题,程序里所有的成员每次都会被替换掉,及时它们并没有被修改,不过我们可以做的更好:

libcounter.a : counter.o lexer.o

        $(AR)  $(ARFLAGS) $@ $?

在gnu make中,还可以使用能够如下符号来引用程序库里的成员:

libgraphics.a(bitblt.o):bitblt.o

        $(AR) $(ARFLAGS)  $@  $<


3.以程序库为必要条件

当程序库作为必要条件时,可以使用标准的文件名语法或-l语法来引用它们。使用文件名语法时:

xpong:$(OBJETS) /lib/x11/libX11.a /lib/X11/libXaw.a

        $(LINK) $^ -o $@

连接器将会读取命令行上所列出的程序库文件,以及按正常的方式来处理它们。使用-l语法时,必要条件并非真正的文件

名称:

xpong: $(OBJECTS) -lX11 -lXaw

        $(LINK) $^ -o $@

第一种语法将会忽略共享程序库病使用链接行上所指定的程序库,第二种语法会使得make优先选择共享程序库。

有一个小问题,如果makefile已经将程序库文件指定为工作目标,它就不能在必要条件里对该文件使用-l选项,举例来说:

count_words:count_words.o -lcounter -lfl

        $(CC) $^ -o $@

libcounter.a:libcounter.a(lexer.o) libcounter.a(counter.o)

运行make,将会显示如下错误:

no rule to make target ‘-lcounter’,needed by ‘count_words‘

这是因为make不会把-lcounter扩展成libcounter.a并去搜索工作目标,make只会去搜索程序库。


要让复杂程序的链接工作没有错误,可能需要使用一些手段,连接器会一次搜索命令行上所指定的程序库。所以,如果程序

库A使用了一个未定义的符号,例如open,而且该符号定义在程序库B中,那么你就必须爱链接命令行上于B之前指定A。否则

一旦连接器读进A并且看到未定义的符号open,再回头来读取B就太迟了,连接器并不会回头来读取前面的程序库。


一个比较相关的问题就是程序见的相互引用,假设程序库B现在引用了程序库A中所定义的符号,我们知道A必须放在B的前面,

不过现在B必须放在A的前面,这个问题的解决方案就是在B之前与之后使用A:-lA -lB -lA。例如:

xpong:xpong.o libui.a libdynamics.a libui.a -lX11

        $(CC) $^ -o $@

但是此时$^会被扩展成:xpong.o libui.a libdynamics.a /usr/lib/X11R6/libX11.a,因为$^会自动去掉重复的部分。为了解决这个

问题需要使用S+。

《GNU make项目管理》笔记--规则(3)