首页 > 代码库 > 《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)