Makefile编程快速上手

 一、认识makefile

makefile是一个工程管理器,可以在makefile文件中给工程,定义一系列的规则来指定哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作。简而言之就是告诉系统如何去编译这个工程

其中我们使用makefile还有一个很重要的原因,就是make工程管理器根据时间戳,来自动发现更新过的文件从而减少编译的工作量

就这点文字说明可能体会不到这个make工程管理器减少编译的工作量,我举个例子说明一下,假如编译器编译一个源文件需要0.1s,假如你的工程有10个源文件,那个工程编译耗时1s,然后后面我们写的大型工程代码往往有成千上万个源文件,那么每次编译都需要100~1000s中,这里花费的时间就特别的多,后期代码出现bug,简单的修改一次,就得编译一次,不用make工程管理器的话,你的时间就都花在了编译代码上了。

make工程管理器是怎么通过时间戳来减少编译的工作量?我们应该了解一下程序编译的过程:预处理,编译,汇编,链接

预处理:添加头文件,展开宏定义,处理条件编译的命令,删除注释

编译:把预处理后的文件转换成特定的汇编代码(中间包含程序的语法查错)。

汇编:将上一步得到的汇编代码进一步生成机器执行代码,生成目标文件

链接:将上一步得到的目标文件及各种调用到的库文件建立链接,最终生成可执行文件。

工程中的所有源文件都得单独进行预处理、编译、汇编过程,最后在一起进行链接过程

make工程管理器,根据源文件和目标文件的时间戳来判断源文件是否修改过,若修改过,则重新生成对应的目标文件,然后将生成的目标文件重新链接得到新的可执行文件。这里没有修改的源文件则make工程管理器不会对其进行操作。

我们改了那个文件,工程管理器就只重新编译那个源文件,而不用编译工程中的所有源文件,大大的减小了编译的工作量。

 

二、makefile编程

不同产商的make各不相同,也有不同的语法,但其本质都是在“文件依赖性”上做文章,这里,我仅对GNU的make进行讲述。

 

2.1 makefile的文件名

默认情况下,我们在终端使用make命令,系统会自动在当前目录下按顺序寻找文件名为“ GUNmakefile ”,“ Makefile ”,“makefile”的文件,并且执行文件中的命令,也就是说我们只要将makefile文件设置为上面三个文件名中的一个即可。

 

2.2 格式及变量

makefile的格式如下:

target1 : dependent_file

(Tab)command 1
(Tab)command 2
    .
    .    
    .


目标1 : 依赖文件
(Tab)命令1
(Tab)命令2
(Tab)命令3	

目标2 : 依赖文件
(Tab)命令4
(Tab)命令5

目标3 : 依赖文件
(Tab)命令6

这个格式类似于一个函数,先写目标和依赖文件,依赖文件可为空,然后换行写命令,切记命令前一定要用一次Tab键,否则会出错。命令根据实际情况编写。

把最上面的目标1称为是终级目标。默认情况下,make命令执行结果的就是完成终级目标对应的命令。在执行这些命令前,先去查看相关的依赖文件是否存在。如果依赖文件不存在,先去执行其它目标,产生当前依赖文件。

 

2.2 变量

定义变量 :
               OBJ=1.o 2.o 3.o
       使用变量:${OBJ}
            
       直接展开:
               x := var1
               y := $(x) bar
               x := var2
       等价于:
               y = var1 bar
               x = var2


      条件赋值操作
             前面没有定义该变量,现在定义使用现在赋的值。如果前面已经定义了该变量,则使用之前的值,不改变
      例:
             IMMEDIATE ?= DEFERRED 

自定义变量:

$@目标名
$<第一个依赖目标. 如果依赖目标是多个, 逐个表示依赖目标
$^所有依赖目标的集合, 会去除重复的依赖目标
$?比目标新的依赖目标的集合
$%当目标是函数库文件时, 表示其中的目标文件名
$*这个是GNU make特有的, 其它的make不一定支持
$+所有依赖目标的集合, 不会去除重复的依赖目标

常用的为红色字体的前三个。

 

三、makefile编程

下面根据代码讲解一下makefile编程上的语法

头文件head.h

#ifndef _HEAD_H_
#define _HEAD_H_

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


void Test1(void);
void Test2(void);
void Test3(void);

#endif

主函数源文件main.c

#include "head.h"

int main(int argc, const char *argv[])
{
	Test1();
	Test2();
	Test3();
	
	printf("Test is end \n");

}

工程的的驱动源文件test1.c

#include "head.h"

void Test1(void)
{
	printf("Test1ing \n");
}

工程的的驱动源文件test2.c

#include "head.h"

void Test2(void)
{
	printf("Test2ing \n");
}

工程的的驱动源文件test2.c

#include "head.h"

void Test3(void)
{
	printf("Test3ing \n");
}

makefile文件

OBJ=main.o test1.o test2.o test3.o
GCC=gcc -c 

output : ${OBJ}
	gcc ${OBJ} -o output
	
main.o : main.c head.h
	${GCC} main.c -o main.o
	
test1.o : test1.c head.h
	${GCC} test1.c -o test1.o
	
test2.o : test2.c head.h
	${GCC} test2.c -o test2.o
	
test3.o : test3.c head.h
	${GCC} test3.c -o test3.o
	
.PHONY : clean
clean:
	rm *.o

这一种写法是最基本的写法,每个目标文件都由依赖文件编译所得。在这里有时候我们的头文件并不是在同一个位置,这里我们就需要我们自己利用gcc相关命令对头文件路径进行指定。变量的使用就是为了减少书写的工作量以及代码查看更直观。

OBJ=main.o test1.o test2.o test3.o
GCC=gcc -c 

output : ${OBJ}
	gcc $^ -o $@
	
main.o : main.c head.h
	${GCC} $< -o $@
	
test1.o : test1.c head.h
	${GCC} $< -o $@
	
test2.o : test2.c head.h
	${GCC} $< -o $@
	
test3.o : test3.c head.h
	${GCC} $< -o $@
	
.PHONY : clean
clean:
	rm *.o

这一种写法是利用自动变量来减少书写工作量。

还有一种是利用makefile的自动推到规则,

OBJ=main.o test1.o test2.o test3.o
GCC=gcc -c 

output : ${OBJ}
	gcc $^ -o $@
	
main.o : main.c head.h            
test1.o : test1.c head.h		
test2.o : test2.c head.h	
test3.o : test3.c head.h
	

.PHONY : clean
clean:
	rm *.o


//超级简写  把依赖关系也省略掉
OBJ=main.o test1.o test2.o test3.o
GCC=gcc -c 

output : ${OBJ}
	gcc $^ -o $@
	
	
.PHONY : clean
clean:
	rm *.o

只要make看到一个[.o]文件,它就会自动的把[.c]文件加在依赖关系中,如果make找到一个main.o,那么mian.c,就会是main.o的依赖文件。并且 cc -c main.c 也会被推导出来,于是,我们的makefile再也不用写得这么复杂。我们的是新的makefile又出炉了。

在makefile编程中依赖文件中注意要将头文件写上,不写的话,头文件修改,make工程管理器并不会自动检测头文件,在后面链接的时候会出现错误。

 

 仓促成文,不当之处,尚祈方家和读者批评指正。联系邮箱1772348223@qq.com

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值