1.makefile编写的三要素
在学习编写makefile文件以前,我们先来看makefile编写的三要素。
1.makefile编写的三要素:
三要素:
1.目标
2.依赖
3.规则命令
现在有hello.c源文件如下,hello.c位于~/练习/practice/下,
head.h位于~/练习/include/下。
hello.c如下:
1 #include <stdio.h>
2 #include <head.h>
3
4 int returnMess(int val)
5 {
6 return val;
7 }
8
9 int main()
10 {
11 int a;
12 #ifdef DEBUG
13 printf("HelloWorld");
14 #endif
15 printf("%d\n",returnMess(100));
16 return 0;
17 }
2.示例
我们来编写一个简单的makefile文件:
[zhihua@localhost practice]$vim makefile
在makefile文件下编辑如下内容:
1 app:hello.o
2 gcc -o app hello.o
3 hello.o:hello.c
4 gcc -c -I ../include hello.c
然后我们来make一下:
make结果.png
然后呢就生成了可执行程序,如下
目标.png
我们借用上诉示例来解释一下三要素:
注意:makefile的隐含规则:make时默认处理第一个目标。比如在上诉makefile中目标有两个。
2.变量的使用和常用函数的使用
我们发现,当改变源程序比如增加.c文件或者删除某个.c文件,上诉的makefile文件就需要做出大的改动。所以呢我们有必要学习变量的使用和常用函数的使用来对makefile做出改进。
1.变量的使用
为了简述变量的使用,我还是以上面的示例来说明。现在我们来举例说明使用变量的例子:
1 #自定义变量,变量名为var
2 var = hello.o
3 #变量名的使用:$(变量名)
4 app:$(var)
5 gcc -o app hello.o
6 hello.o:hello.c
7 gcc -c -I ../include hello.c
规则中使用的变量:下面这些变量只能在规则命令中使用,并且用于模式匹配规则。关于使用例子在后文中的makefile编写中有介绍。
1.$@:代表目标
2.$^:代表全部依赖
3.$<:代表第一个依赖
4.$?:代表第一个变化的依赖
2.函数的使用
1.wildcard函数:可以进行文件匹配,比如匹配makefile当前所在目录下的所有后缀名为.c的源文件。
2.patsubst函数:内容的替换。
为了简述wildcard函数的用法,我们举个例子
~/练习/include/目录下包含有add.h,sub.h头文件。
~/练习/practice/目录下有example.c,和add.c,sub.c实现文件。内容分别如下:
add.c如下:
1#include <add.h>
2 int add(int a,int b)
3 {
4 return a+b;
5 }
sub.c如下:
1#include <sub.h>
2 int sub(int a,int b)
3 {
4 return a - b;
5 }
example.c如下
1 #include <add.h>
2 #include <sub.h>
3 #include <stdio.h>
4
5 int main()
6 {
7 printf("%d,%d"add(100,100),sub(500,300));
8 return 0;
9 }
现在我们再编写makefile,[zhihua@localhost practice]$ vim makefile
其中使用了wildcard函数,来匹配与makefile文件同一目录下的所有后缀名为.c的文件.patsubst函数来将.c文件替换成.o文件。使用了一些变量以便编写不与源文件依赖的makefile文件。makefile文件内容如下:
1 #获取与makefile文件同级目录下的所有后缀名为.c的文件
2 sourceFile = $(wildcard *.c)
3 #获取所有的后缀名为.o的文件
4 objFiles1 = $(patsubst %.c, %.o ,$(sourceFile))
5 result:$(objFiles1)
6 gcc -o result -I ../include $(objFiles1)
7 %.o:%.c
#变量的使用,$^,$@
8 gcc -c $^ -o $@ -I ../include
9 test:
10 #输出变量sourceFile和objFiles1的值
11 echo $(sourceFile)
12 echo $(objFiles1)
在上诉makefile文件中,为了验证wildcard函数使用和patsubst函数是否成功,我们输出sourceFile和objFiles1的值。期间新增了一个目标test。
9 test:
10 #输出变量sourceFile和objFiles1的值
11 echo $(sourceFile)
12 echo $(objFiles1)
然后我们在make的时候指定目标
[zhihua@localhost practice]$ make test
其结果为:
echo example.c add.c sub.c
example.c add.c sub.c
echo example.o add.o sub.o
example.o add.o sub.o
表明这两个函数的使用成功.
现在我们来使用makefile文件来编译如下,这样可以生成可执行文件:
[zhihua@localhost practice]$ make
make的结果如下:
gcc -c example.c -o example.o -I ../include
gcc -c add.c -o add.o -I ../include
gcc -c sub.c -o sub.o -I ../include
gcc -o result -I ../include example.o add.o sub.o
最后运行可执行文件:
[zhihua@localhost practice]$ ./result
200,200[zhihua@localhost practice]$
表明makefile文件编写成功.
对上述makefile文件的改进:
我们发现如果没有对源文件访问或者修改,每次在需要重新编译前都要手动删除中间产生的二进制文件然后才能编译。就像这样:
make: “result”是最新的。
我们可以在makefile文件中新增目标clean如下所示:
13 #定义伪目标,防止当前makefile文件所在目录下有与此目标名相同的文件造成歧义
14 .PHONY:clean
15 clean:
16 @rm -f *.o
17 @rm -f result
然后需要重新编译前就make clean
,删除后缀名为.o的二进制文件,然后再编译make
.注意如果不想输出这样的结果:
rm -f *.o
可以在规则前加上@符号。
现在我们来再举个例子。请按照下图的源程序编写makefile文件:
[zhihua@localhost day1]$ ls -lrt
总用量 16
-rw-rw-r--. 1 zhihua zhihua 114 9月 11 20:04 main.c
-rw-rw-r--. 1 zhihua zhihua 89 9月 11 20:05 world.c
-rw-rw-r--. 1 zhihua zhihua 87 9月 11 20:16 hello.c
-rw-rw-r--. 1 zhihua zhihua 438 9月 11 20:21 makefile
[zhihua@localhost day1]$ ls -lrt ../include
总用量 8
-rw-rw-r--. 1 zhihua zhihua 79 9月 11 20:20 hello.h
-rw-rw-r--. 1 zhihua zhihua 80 9月 11 20:20 world.h
makefile如下:
1 #定义变量获取所有的与此makefile文件同目录下的源文件
2 sourceFile = $(wildcard *.c)
3 #定义变量获取所有的二进制文件
4 objFiles = $(patsubst %.c,%.o,$(sourceFile))
5
6 #第一个目标
7 app:$(objFiles)
8 gcc -o app -I ~/练习/practice/include $(objFiles)
9
10 #第二个目标
11 %.o:%.c
12 gcc -I ~/练习/practice/include -c $^ -o $@
13
14 #第三个目标,作为清理,方便再次编译
15 clean:
16 @rm -f *.o
17 @rm -f app
编译运行结果如下:
[zhihua@localhost day1]$ make
gcc -I ~/练习/practice/include -c hello.c -o hello.o
gcc -I ~/练习/practice/include -c world.c -o world.o
gcc -I ~/练习/practice/include -c main.c -o main.o
gcc -o app -I ~/练习/practice/include hello.o world.o main.o
[zhihua@localhost day1]$ ./app
HelloWorld
3.GDB调试
因为下面内容都是以~/练习/include/目录下的example.c文件来示例的,故附上example.c文件内容:
1 #include <stdio.h>
2
3 int main(int argc,const char* args[])
4 {
5 int a = 100;
6 float b = 200.0;
7 printf("HelloWorld\n");
8 printf("%f\n",a + b);
9 int c = argc;
10 const char* str1 = args[1];
11 const char* str2 = args[2];
12 printf("%d%s%s\n",c,str1,str2);
13 printf("%s\n",args[0]);
14 return 0;
15 }
1.启动gdb这个调试程序前需要做的事情
1.-g:在编译成目标可执行程序时加上-g选项,就会在生成目标可执行程序文件中加入调试信息。
[zhihua@localhost include]$ gcc -o result.exe -g example.c
2.启动GDB:gdb 可执行程序名
[zhihua@localhost include]$ gdb result.exe
2.gdb调试程序的一些方法
1.在gdb调试程序里运行可执行程序:输入run或者r即可,即r[un]
(gdb) run
##或者
(gdb) r
2.start:开启分步调试,停留在main函数
(gdb) start
Temporary breakpoint 1 at 0x40056c: file example.c, line 5.
Starting program: /home/zhihua/练习/include/result.exe
Temporary breakpoint 1, main (argc=1, args=0x7fffffffe4f8) at example.c:5
3.n[xet]:执行下一步指令
4.s[tep]:执行下一条指令,可以进入函数内部,但是库函数不能进入。
5.设置主函数启动的参数:set args xxx1 xxx2,当然了这个等同于run xxx1 xxx2。run xxx1 xxx2也可以设置程序启动的参数。也等同于set args[1] = "xxx1" set args[2] = "xxx2";args[0]的值默认是可执行程序名。
6.显示源文件代码
1.l[ist]:查看主函数的源码。默认只显示主函数所在文件的十行,需要显示更多的话敲回车。
2.l[ist] xxx.c:行号:查看某个文件从指定的行号开始显示十行
7.设置断点调试:
1.指定行号加断点:b[reak] 行号,默认在主函数所在文件的行号
2.指定函数名加断点:b[reak] 函数名
3.指定文件对应的行:b[reak] xxx.c:行号
4.设置条件断点:b[reak] 行号 条件
8.查看已经设置好的断点信息:i[nfo] b[reak],这样得到number
9.删除断点:d[el] number
10.跳到下一断点:c[ontinue]
11.p[rint] 变量名:打印变量的值
12.ptype 变量名:打印变量的类型
13.跟踪某个变量的值:display 变量名.
14.取消跟踪某个变量:undisplay number,查看number info display。
3.退出gdb这个调试程序
1.q[uit]:退出gdb调试程序
举个例子:
4.gdb跟踪core文件
1.查看core文件的大小:
[zhihua@localhost include]$ ulimit -c
0
2.设置生成core的大小
##设置core文件大小1024
[zhihua@localhost include]$ ulimit -c 1024
##设置core文件大小无限制
[zhihua@localhost include]$ ulimit -c unlimited
3.设置core文件的格式
4.查看core文件:gdb 可执行文件名 core
网友评论