博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
实现简单的Makefile工程管理器
阅读量:2432 次
发布时间:2019-05-10

本文共 4020 字,大约阅读时间需要 13 分钟。

Makefile

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

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

例如:

main.exe : main.o func.o	gcc -o main.exe main.o func.omain.o : main.c	gcc -c main.cfunc.o : func.c	gcc -c func.c.PHONY:cleanclean:	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:cleanclean:	rm -rf main.o func.o main.exe    //执行clean指令后,删除.o和.exe文件

下面通过make支持的变量定义、规则和内置函数,写出通用性较强的Makefile文件

用变量来代替一个文本字符串
定义变量的方法:
变量名:=变量值
使用变量名的方法$(变量名)
采用定义变量的方法,将之前的代码改为:

OBJS:=main.o func.o   //OBJS相当于main.o和func.oELF:main.exe          //ELF相当于main.exe$(ELF):$(OBJS)	gcc -o $(ELF) $(OBJS)main.o : main.c	gcc -c main.cfunc.o : func.c	gcc -c func.c.PHONY:cleanclean:	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.oELF:main.exe          //ELF相当于main.exeCC:=gcc$(ELF):$(OBJS)	$(CC) $^ -o $@.PHONY:cleanclean:	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'文件存入变量SRCSOBJS:=$(patsubst %.c, %.o, $(SRCS))   //把SRCS中'.c' 替换为'.o' 。                                                                                                                                              ELF:=mainCC:=gccCFLAGS:=-g -Wall$(ELF):$(OBJS)	gcc $^ -o $@.PHONY:cleanclean:	rm -rf $(OBJS) $(ELF)

同时编译出多个可执行二进制

采用for循环编译多个目标文件的Makefile写法

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

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

原文博客链接:

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

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

静态模式 + 自动化变量

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

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

其中:

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)

转载地址:http://exxmb.baihongyu.com/

你可能感兴趣的文章
ts:json串转换成数组
查看>>
String、StringBuffer和StringBuilder的区别
查看>>
java——职责链模式
查看>>
java_选择类排序——简单选择排序
查看>>
java_中介者模式
查看>>
java_备忘录模式
查看>>
多线程——背景了解
查看>>
power designer使Comment与Name相同.txt
查看>>
学习Spring 开发指南------基础语义
查看>>
Ajax-----简易聊天室实战
查看>>
IE下的图片空隙间距BUG和解决办法
查看>>
[pb]从excel导入数据到datawindow
查看>>
CSS Padding in Outlook 2007 and 2010
查看>>
有关内存的思考题
查看>>
What is the difference between gross sales and revenue?
查看>>
Dreamweaver默认打开后缀名为ftl的文件时
查看>>
LNMP一键安装
查看>>
几个分析函数的比较
查看>>
主流算法:
查看>>
RMI
查看>>