实现简单的Makefile工程管理器

Makefile
工程管理器能根据文件的时间发现更新过的文件而减少编译的工作量,避免仅仅修改数个文件而将工程中的所有文件重新编译的情况。
Makefile格式

target:dependency_files  //目标项:依赖项
	command              //tab键开头,command为编译命令

例如:

main.exe : main.o func.o
	gcc -o main.exe main.o func.o
main.o : main.c
	gcc -c main.c
func.o : func.c
	gcc -c func.c
.PHONY:clean
clean:
	rm -rf main.o func.o main.exe 

对于此Makefile文件,程序make处理的过程如下:make程序首先读到第一行的目标文件main.exe和它的两个依赖文件main.o和func.o。然后比较main.exe和main.o/func.o的产生时间。如果main.exe的时间更早,则执行gcc -o main.exe main.o func.o该命令,以产生新的main.exe。
在执行第二行的命令前,它会首先查看makefile中的其他定义,看有没有以第一行main.o和func.o为目标文件的依赖文件,如果有,则接着递归按照这两步骤执行下去。
make程序发现第三行中目标文件main.o依赖于main.c,则比较main.o和它的依赖文件main.c文件的更改时间,若main.o的时间比main.c的时间更早,说明main.c文件已经被重新修改了,此时就会执行第四条命令,重新编译main.c文件。若main.o的时间比main.c的时间更晚,说明main.c文件没有被修改,此时直接跳过第四行命令,接着进行判断。

.PHONY是Makefile文件的关键字,表示它后面列表中的目标均为伪目标,通常用在清理文件、强制重新编译等情况
.PHONY:clean
clean:
	rm -rf main.o func.o main.exe    //执行clean指令后,删除.o和.exe文件

下面通过make支持的变量定义、规则和内置函数,写出通用性较强的Makefile文件
用变量来代替一个文本字符串
定义变量的方法:
变量名:=变量值
使用变量名的方法$(变量名)
采用定义变量的方法,将之前的代码改为:

OBJS:=main.o func.o   //OBJS相当于main.o和func.o
ELF:main.exe          //ELF相当于main.exe
$(ELF):$(OBJS)
	gcc -o $(ELF) $(OBJS)
main.o : main.c
	gcc -c main.c
func.o : func.c
	gcc -c func.c
.PHONY:clean
clean:
	rm -rf $(ELF) $(OBJS)

变量分为:用户自定义变量,预定义变量(CFLAGS),自动变量,环境变量

自动变量:在使用的时候,自动用特定的值替换

$@ 当前规则的目标文件
$< 当前规则的第一个依赖文件
$^ 当前规则的所有依赖文件,以空格分隔

预定义变量:内部先定义好的变量,但是它的值是固定的,并且有些的值为空的
AS:汇编程序,默认为as
CC: c编译器默认为cc
CPP: c预编译器,默认为$(cc)-E
CXX: c++编译器,默认为g++
RM: 删除,默认rm -f
ARFLAGS: 库选项,无默认
CFLAGS: c编译器选项,无默认
根据内部变量,可以将Makefile改写为:

OBJS:=main.o func.o   //OBJS相当于main.o和func.o
ELF:main.exe          //ELF相当于main.exe
CC:=gcc
$(ELF):$(OBJS)
	$(CC) $^ -o $@
.PHONY:clean
clean:
	rm -rf $(ELF) $(OBJS)

规则分为:普通规则,隐含规则,模式规则
隐含规则:*.o文件自动依赖.c文件,所以可以省略main.o : main.c等
模式规则:通过匹配模式找字符串, %匹配1或多个任意字符串
%.o: %.c任何目标文件的依赖文件是与目标文件同名的并且扩展名为.c的文件

%.o:%.c  //模式通配
gcc -o $@ -c $^  

函数:

1、wildcard搜索当前目录下的文件名,展开成一列所有符合由其参数描述的文件名,文件间以空格间隔。SOURCES = $(wildcard *.c)把当前目录下所有’.c’文件存入变量 SOURCES 里。

2、字符串替换函数:$(patsubst要查找的子串,替换后的目标子串,源字符串)。将源字符串(以空格分隔)中的所有要查找的子串替换成目标子串。

SRCS:=$(wildcard *.c)                 //把当前目录下所有'.c'文件存入变量SRCS
OBJS:=$(patsubst %.c, %.o, $(SRCS))   //把SRCS中'.c' 替换为'.o' 。                                                                                                                                              
ELF:=main
CC:=gcc
CFLAGS:=-g -Wall
$(ELF):$(OBJS)
	gcc $^ -o $@
.PHONY:clean
clean:
	rm -rf $(OBJS) $(ELF)

同时编译出多个可执行二进制
采用for循环编译多个目标文件的Makefile写法

SRCS:=$(wildcard *.c)    //把当前目录下所有'.c'文件存入变量SRCS
ELFS:=$(SRCS:%.c=%)      //%.c=%,将SRCS所有.c文件的后缀去掉,直接对应可执行文件的名字
CC:=gcc
all:$(ELFS)     //因为最初的目标项只能是单个,依赖项可以是多个,我们将目标项变成依赖项,相当于进行了缓冲,如果需要得到依赖项,%的含义其实就是main1和main2,但%:%.c相当于循环,main1和main2各自独立执行的
%:%.c                                                                                                                                                                                  
$(CC) $< -o $@

有关于最后结尾部分的代码解释补充:

原文博客链接:https://blog.csdn.net/u012351051/article/details/88600562

main为终极目标,而其他的func1.o、func2.o…一方面是目标main的依赖对象,但是他们本身又是一个小目标,需要进行编译生成。那么问题就来了,如果我们的程序只有几个.c文件还好,如果有几百个、几千几万个呢,我还能这么一个的写编译吗?

很显然这是不可能的,而且Unix/linux也不允许我们这么愚蠢,那么我们就理所当然的想,是不是有类似于变成语言中的各种循环策略啊,比如for循环、do…while循环什么的,但是即便有这些循环,这些文件名字什么的都不一样啊,我们肯定不能用for什么的命令的,那么Unix/linux给我们提供了什么方法呢,答案是:

静态模式 + 自动化变量

这个静态模式就是一种自动编译模式,在这种模式下,我们可以容易的定义“多目标”规则,让我们的规则变得更加有弹性和灵活。它的语法如下:

< targets ....> : < target-pattern > : < prereq-patterns ...>
    <commands>
    .....

其中:

targets定义了一些列的目标文件,也就是多目标,可以有通配符,是目标的一个集合。
target-pattern 是targets的模式,也就是目标集模式
prereq-patterns 则是目标的“依赖”元素,
这么去说,可能还是比较拗口,不容易理解,我们还是把理论落地,举例一下吧:

我们把target-pattern 定义成 %.o 意思是我们的target集合都是以.o结尾。当然这里也可以使用通配符*,只不过%多用于Makefile,他们两个的区别,我们后面再讲。而我们的prereq-patterns则定义为%.c,这意思就是对 target-pattern中所形成的目标集进行二次定义,其计算方法是取target-pattern模式中的%代表部分(其实就是去掉.o后的文件名),并为其加上[.c]结尾,形成新的集合。代码如下:

$(OBJS) : %.o : %.c
    gcc -c $< -o $@

这两条命令的功能就是,大目标是OBJS,这个OBJS就是各种.o文件,然后%.o就是具体的解释,而%.c就是对应同样名字的.c文件,而下面的命令,结合了2个自动化变量。
$<表示依赖对象集中的第一个
$@ 则代表了目标集

所以这个功能就是要遍历所有的.c文件,对所有的.c文件进行编译,然后编译生成对应的.o文件。我们在实际编写程序时,targets是不需要的,可以简写如下:

%.o : %.c
    gcc -c $< -o $@

一点区别就是作者实现的是.c到.o。而我写的实现的是.c到可执行文件(test.c——test)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值