转载于: http://blog.sina.com.cn/s/blog_87c063060101ky45.html
1.关于GCC怎么样自动生成‘依赖’.网上有很多版本,有使用“正则法则”实现的,也有其他方法实现的.
笔者通过比较,觉得最简单的还是Linux内核里面的规则最简单.
2.在GCC的命令行加一条这样的语句就OK了.
-Wp,-MD,drivers/leds/.XXX.o.d (其中drivers/leds/.XXX.o.d为依赖文件,里面记录了很多依赖信息)
3.就这么简单,来举个例子.Linux内核的一条命令
cmd_drivers/leds/led-core.o := arm-linux-gcc -Wp,-MD,drivers/leds/.led-core.o.d
-nostdinc -isystem /work/tools/gcc-3.4.5-glibc-2.3.6/lib/gcc/arm-linux/3.4.5/include
-D__KERNEL__ -Iinclude -include include/linux/autoconf.h
-mlittle-endian
-Wall -Wundef -Wstrict-prototypes -Wno-trigraphs -fno-strict-aliasing
-fno-common -Os -marm -fno-omit-frame-pointer
-mapcs -mno-sched-prolog -mapcs-32 -mno-thumb-interwork
-D__LINUX_ARM_ARCH__=4 -march=armv4t -mtune=arm9tdmi -malignment-traps -msoft-float
-Uarm -fno-omit-frame-pointer -fno-optimize-sibling-calls
-g -Wdeclaration-after-statement
-D"KBUILD_STR(s)=\#s" -D"KBUILD_BASENAME=KBUILD_STR(led_core)"
-D"KBUILD_MODNAME=KBUILD_STR(led_core)"
-c -o drivers/leds/led-core.o drivers/leds/led-core.c
4.自己来举个例子吧!
①四个文件:def.h hello.c main.c Makefile
②四个文件的内容分别为
def.h:
#ifndef __DEF_H
#define __DEF_H
#define A 5
void hello();
#endif //__DEF_H
hello.c:
#include<stdio.h>
#include"def.h"
void hello()
{
printf("hello world!\n");
printf("A = %d\n",A);
}
main.c:
#include<stdio.h>
#include"def.h"
int main(void)
{
hello();
return 0;
}
Makefile:
PHONY := test
CROSS_COMPILE =
CC = $(CROSS_COMPILE)gcc
CFLAGS := -Wall -Werror -O2 -g
LDFLAGS =
test: hello.o main.o
$(CC) $(LDFLAGS) -o $@ $^
%.o : %.c
$(CC) $(CFLAGS) -c -o $@ $<
clean:
rm *.o test .*.d
③现象
在终端输入命令:make
gcc -Wall -Werror -O2 -g -c -o hello.o hello.c
gcc -Wall -Werror -O2 -g -c -o main.o main.c
gcc -o test hello.o main.o
运行:./test
④来修改一个文件,把def.h当中的 #define A 5 改为 #define A 10 然后再make一下.
make: `test' is up to date.
⑥来修改一下Makefile
PHONY := test
CROSS_COMPILE =
CC = $(CROSS_COMPILE)gcc
CFLAGS := -Wall -Werror -O2 -g
LDFLAGS =
test: hello.o main.o
$(CC) $(LDFLAGS) -o $@ $^
dep_file = .$@.d (特别注意:这里只能用 = ,也就是递归展开 ,而不能用 := ,进行直接展开)
<读者可以修改观察一下现象,就可以知道 递归展开 = 与 直接展开 := 的区别了,可以感性认识一下二者的区别.>
%.o : %.c
$(CC) $(CFLAGS) -Wp,-MD,$(dep_file) -c -o $@ $<
clean:
rm *.o test .*.d
再来 make 一下:
提示: make: `test' is up to date.
那么,make clean 之后,再来make看看:
提示信息:
gcc -Wall -Werror -O2 -g -Wp,-MD,.hello.o.d -c -o hello.o hello.c
gcc -Wall -Werror -O2 -g -Wp,-MD,.main.o.d -c -o main.o main.c
gcc -o test hello.o main.o
执行一下看看:./test
hello world!
A = 10
用ls -al 命令看看: 确实生成了“依赖文件”
.hello.o.d
.main.o.d
⑦再一次修改def.h试试:
把def.h当中的 #define A 10 改为 #define A 20 然后再make一下.
make: `test' is up to date.
明明已经生成依赖文件了,为什么make还会提示,说没有更新呢?(后面我们用:时间轴更新 来说明)
笔者也折腾了一阵,后来发现,是“依赖文件”没有"被包含"进Makefile,导致make的“时间轴”没有更新.
⑧再来修改Makefile:
在Makefile里面加这么一句: include .*.o.d 表示包含所以的 .d文件(依赖文件).
注意:有的需要写成这种形式 -include .*.o.d
再一次make:
提示信息:
gcc -Wall -Werror -O2 -g -Wp,-MD,.hello.o.d -c -o hello.o hello.c
gcc -Wall -Werror -O2 -g -Wp,-MD,.main.o.d -c -o main.o main.c
gcc -o test hello.o main.o
果然成功了.
再来执行一下: ./test
hello world!
A = 20
5.总结
在Makefile中,“依赖关系”也是一个比较重要的规则,尤其是“自动生成依赖”.这里笔者举了个简单的例子加以说明.对于Linux,尤其是这种内容,讲起来会比较抽象,尽量自己多去举几个例子,去实践实践,结果很快就可以出来了,也可以加深理解!
6.补充
关于,在Makefile中, include 与 -include 的区别
通过试验,笔者得出 include 与 -include 存在着用法上的区别,而且内核的Makefile当中,也同时出现过这两种情况.
在上个实验当中, 用 include.*.o.d 出现了如下错误提示:
Makefile:31: .*.o.d: No such file or directory
make: *** No rule to make target `.*.o.d'. Stop.
而用 -include .*.o.d 却解决了问题
在网上找了一篇文章:http://blog.csdn.net/xiaozhi_su/article/details/4202779
指示符“include”、“-include”和“sinclude”
如果指示符“include”指定的文件不是以斜线开始(绝对路径,如/usr/src/Makefile...),而且当前目录下也不存在此文件;make将根据文件名试图在以下几个目录下查找:
首先,查找使用命令行选项“-I”或者“--include-dir”指定的目录,如果找到指定的文件,则使用这个文件;
否则,继续依此搜索以下几个目录(如果其存在):“/usr/gnu/include”、“/usr/local/include”和“/usr/include”。
当在这些目录下都没有找到“include”指定的文件时,make将会提示一个包含文件未找到的告警提示,但是不会立刻退出。而是继续处理Makefile的后续内容。当完成读取整个Makefile后,make将试图使用规则来创建通过指示符“include”指定的但未找到的文件,当不能创建它时(没有创建这个文件的规则),make将提示致命错误并退出。会输出类似如下错误提示:
Makefile:错误的行数:未找到文件名:提示信息(No such file or directory)
Make: *** No rule to make target ‘<filename>’. Stop
通常我们在Makefile中可使用“-include”来代替“include”,来忽略由于包含文件不存在或者无法创建时的错误提示(“-”的意思是告诉make,忽略此操作的错误。make继续执行)。像下边那样:
-include FILENAMES...
使用这种方式时,当所要包含的文件不存在时不会有错误提示、make也不会退出;除此之外,和第一种方式效果相同。以下是这两种方式的比较:
使用“include FILENAMES...”,make程序处理时,如果“FILENAMES”列表中的任何一个文件不能正常读取而且不存在一个创建此文件的规则时make程序将会提示错误并退出。
使用“-include FILENAMES...”的情况是,当所包含的文件不存在或者不存在一个规则去创建它,make程序会继续执行,只有真正由于不能正确完成终极目标的重建时(某些必需的目标无法在当前已读取的makefile文件内容中找到正确的重建规则),才会提示致命错误并退出。
为了和其它的make程序进行兼容。也可以使用“sinclude”来代替“-include”(GNU所支持的方式)。