“C语言” 读书札记(六)之[Linux下C语言编程环境Make命令和Makefile]

介绍

一般程序都是由多个源文件编译链接而成的,这些源文件的处理步骤通常由Makefile文件管理。
用途

make工具用来进行协调的工具,可以根据程序模块的修改情况重新编译链接目标代码,以保证目标代码总是由它的最新模块组成。

准备:

要使用make,准备Makefile的文件(也可以准备其他文件如GNUMakefile或makefile,推荐使用Makefile),它描述了软件包中各个文件之间的关系,提供了更新每个文件的命令。

方便使用:
当一个适当的Makefile存在时,每次改变某些源文件,用简单的shell命令(make),将足以完成所有必需的重新编译。

原理:
make 程序利用Makefile的数据和每个文件最新一次更改的时间来确定哪些文件需要更新;对每个需要更新的文件,make程序使用Makefile中定义的命令来更新它。

格式:

复制代码

#用“井”号表明注释。
target(要生成的文件): dependencies(被依赖的文件)
#命令前面用的是“tab”而非空格。误用空格是初学者容易犯的错误!
命令1
命令2
命令3
.
.
.
命令n
#可以使用“\”表示续行。注意,“\”之后不能有空格!

复制代码

target通常是我们要生成的文件的名字,摆放的顺序不重要,但第一个target是默认的target。当make不带参数时,自动执行第一个target。target也可以是要求make完成的动作,执行这种target后并不能得到和target同名的文件,因此,也称为伪target(phony target)。
dependencies是生成target所需的文件名列表。依赖可以为空,常用的“clean”target就常常没有依赖,只有命令。
命令可以是任何一个shell能运行的命令。

案例:

比如生成exe文件,它由2个目标代码某块组成,分别为module1.o和module2.o

module1.h文件

int module1 = 1;

module1.c文件

#include "module1.h"

void print1(){
printf("var module1:%d\n", module1);
}

module2.h文件

int module2 = 2;

module2.c文件
复制代码

#include <stdio.h>
#include "module2.h"

void print2(){
printf("var module2:%d\n", module2);
}

int main(){
print1();
print2();
return 0;
}

复制代码
编译
Makefile文件
复制代码

exe:module2.o module1.o
gcc -g module1.o module2.o -o exe
module2.o:module2.h module2.c
gcc -g -c module2.c
module1.o:module1.h module1.c
gcc -g -c module1.c
clean:
rm -f exe *.o

复制代码

截图

规则

在编写完源程序文件后,从中生成需要的Makefile规则。

最基本的编写规则的方法是从最终的源程序文件开始一个一个的查看源码文件,把它们要生成的目标文件做为目标,而C语言源码文件和源码文件包含的头文件作为依赖文件生成规则。

目标和条件之间的关系是:欲更新目标,必须首先更新它的所有条件;所有条件中只要有一个条件被更新了,目标也必须随之被更新。所谓“更新”就是执行一遍规则中的命令列表,命令列表中的每条命令必须以一个Tab开头,注意不能是空格,Makefile的格式不像C语言的缩进那么随意,对于Makefile中的每个以Tab开头的命令,make会创建一个Shell进程去执行它。

Makefile中的变量

Makefile中变量就像一个环境变量。事实上环境变量在make中也被解释成make的变量。

作用:
保存文件名列表。——可以方便地加入新的目标文件而且不易出错。
保存编译器参数。——在很多源代码编译时,gcc需要很长的参数选项,在很多情况下,所有的编译命令使用一组相同的选项,如果把这组选项使用一个变量表示,那么可以把这个变量放在所有引用编译器的地方。(当要改变选项的时候,只需改变一次这个变量内容即可)

语法:

变量:“变量”指的是用一个字符串代替另一个字符串的功能。在makefile中可以使用“=”号来定义变量,使用“$(变量名)”来使用变量;还可以用“:=”追加变量的内容。习惯上,变量名使用大写。

定义:
变量名=字符串
使用:
$(变量名)
追加:
变量名:=字符串

案例:进化上面的Makefile

复制代码

OBJS=module1.o module2.o
C=-c
CC=gcc
exe:$(OBJS)
$(CC) -g $(OBJS) -o exe
module2.o:module2.h module2.c
$(CC) -g $(C) module2.c
module1.o:module1.h module1.c
$(CC) -g $(C) module1.c
clean:
rm -f exe *.o

复制代码

伪目标

  首先要明确,并不是所有的目标文件都对应于磁盘文件,有的目标文件的存在只是为了形成一条规则,从而使make完成特定的工作,并不生成新的目标文件,这样的目标称为伪目标。——如上面Makefile中的clean。常用的还有all。

放例子:
复制代码

all:exe1 exe2
exe1:exe1.c exe1.h
gcc exe1.c -o exe1
exe2:exe2.c exe2.h
gcc exe2.c -o exe2
clean:
rm -f exe*

复制代码

其中的all和clean都为伪目标。伪目标文件是不存在的。注意上面例子中第一条规则下的命令行为空,make不会执行任何动作,只是检查依赖文件的更新情况, 扫描剩下的几条规则并执行相应的编译命令生成可执行文件。

条件语句

条件语句可以将一个变量与其他变量的值进行比较,或将一个变量与一个字符串常量相比较。——这样就可以根据变量的值执行或忽略Makefile文件中的一部分脚本。

注意:条件语句用于控制make时间看见的Makefile文件部分,而不能用于执行时控制shell命令。

条件语句3条指令:ifeq, else 和endif

放样例:

ifeq($(VAR), 1)
gcc -o exe1 module
else
gcc -o exe2 module
endif

调试make

我们在很多语言中都要用到调试,那么我们自然对make也要想到调试了。

make的调试很简单,只需通过-d选项可是make在执行命令时打印调试信息。
这些信息包括:
1、make重新编译时需要检查的文件
2、哪些文件被比较以及比较的结果
3、需要重新生成的文件
4、make将要使用的隐含规则
5、make实际执行的隐含规则和命令

放截图样例(一部分)

参考文献
  
make - 维基百科,自由的百科全书
第 22 章 Makefile基础

总结
  
想要熟练地掌握make工具,必需通过不断的练习并参考其他Makefile的例子。

我会在后期的开发学习中不断完善这篇博文。

推荐

分类: C Language
标签: C Language

发表回复