Makefile语法[1/10]


前言

makefile关系到了整个工程的编译规则。会不会写makefile,从一个侧面说明了一个人是否具备完成大型工程的能力。
作为一个专业程序员,为了胜任软件架构师的职责,必须在makefile方面拿到8分左右,假设精通=10分。

本文以GNU make Version 4.3英文手册作为参考,先过一遍直译,然后加上自己的理解,最后打磨成舒畅、易懂、全面的中文教程。

参考:3.79版中文博客,陈皓博客跟我一起写 Makefile

最最后搞懂下列脚本,小试牛刀:

# common argument
# -----------------------------------------------------------------------------------------------
# to find tools
CASE_ROOT ?=
SW_ROOT   ?= 
#------------------------------------------------------------------------------------------------
# file to compile
GCC_FILE ?= *.c
#================================================================================================
# all action
all			: gcc
clear		: gcc_clear	
distclear	: gcc_distclear
#================================================================================================
# gcc compile
#------------------------------------------------------------------------------------------------
# fixed config
# some fixed argument for hr3tvc to gcc tools
export ARCH = mips64
export CONFIG_PRINT = 1
export KBUILD_MODE = KERNEL_MODE
export CONFIG_BOOT = 1
export CONFIG_REAL_DDR = 1
#------------------------------------------------------------------------------------------------
# some argument to gcc command
CFLAGS += -I$(CASE_ROOT)/common -I./
CFLAGS += -I$(CASE_ROOT)/head
CFLAGS += -I$(CASE_ROOT)/device_test
CFLAGS += -I$(CASE_ROOT)/trunk/include/frame
CFLAGS += -D__MIPS64__
#------------------------------------------------------------------------------------------------
SRCS_DIRNAMES=$(dir $(GCC_FILE))
SRCS_FILENAMES=$(notdir $(GCC_FILE))
SRCS_BASENAMES=$(basename $(SRCS_FILENAMES))
#SRCS_BASENAMES = $(basename $(shell ls $(GCC_FILE)))
#ELF_OBJS = $(SRCS_BASENAMES:%=%_$(KBUILD_MODE).vmem)
ELF_OBJS = $(SRCS_BASENAMES:%=ELF/%.elf)
HEX_OBJS = $(SRCS_BASENAMES:%=HEX/%.hex)
BIN_OBJS = $(SRCS_BASENAMES:%=BIN/%.bin)
VMEM_OBJS = $(SRCS_BASENAMES:%=VMEM/%.vmem)
#ELF_CHECK_OBJS = $(shell ls ELF/*.elf)
#------------------------------------------------------------------------------------------------
# action of gcc
gcc    		:gcc_obj_dir $(ELF_OBJS) $(BIN_OBJS) $(VMEM_OBJS) $(HEX_OBJS)

gcc_obj_dir	:
	$(Q)mkdir -p ELF HEX BIN VMEM LOG

.depend: ${SRCS_FILES}
	$(CC) -M $(CFLAGS) $(SRCS_FILES) > $@

sinclude .depend

include $(SW_ROOT)/Makefile.inc                       	# gcc tools
	
#gcc_check	:
#	@for case in $(ELF_CHECK_OBJS); do \
#		$(SIM) $(SIM_FLAGS) ./$$case & (sleep 1; killall -q $(SIM)) || echo; \
#	done
clean		:
	$(Q)rm -rf *.o ELF HEX BIN VMEM* LOG .depend

gcc_clear	:
	$(Q)rm -rf *.o

1. 什么是make

可执行文件由目标文件链接而成,目标文件由源文件编译而成。大型工程中往往有多个可执行文件,更有大量的源文件,在文件更新后影响了哪些依赖的目标?make只是一个解释器,或者理解成一个程序,它根据按照固定语法组成的Makefile文件,翻译文件依赖关系、鉴别更新时间、一键化生成最新的目标/目标集合。

1.1 使用手册

新手看概述和每章的前面几小结,有个大致的了解,实际遇到问题再查手册、或者网上搜。
熟悉其他版本make的老鸟,看看14、15章的新特性。
走马观花看个总结的人,看看9.7、4.8。

1.2 找到bug?

make --help看看命令怎么用,确有bug的联系GNU。

2. Makefiles简介

make命令执行时,需要一个 Makefile 文件,以告诉make命令需要怎么样的去编译和链接程序。

首先,我们用一个示例来说明Makefile的书写规则。以便给大家一个感兴认识。这个示例来源于GNU的make使用手册,在这个示例中,我们的工程有8个C文件,和3个头文件,我们要写一个Makefile来告诉make命令如何编译和链接这几个文件。我们的规则是:
1)如果这个工程没有编译过,那么我们的所有C文件都要编译并被链接。
2)如果这个工程的某几个C文件被修改,那么我们只编译被修改的C文件,并链接目标程序。
3)如果这个工程的头文件被改变了,那么我们需要编译引用了这几个头文件的C文件,并链接目标程序。
————————————————

2.1 Makefile的规则

一份makefile中包含许多“rules",除此以外还可以包含其他文本,“rules"的一般格式为:

target ... : prerequisites ...
	recipe
	...

其中target可以是可执行文件名、目标名,或者标签(例如clean)。标签用于标识下面的命令想要做的动作集合。
prerequisites是创建target的输入,或者说依赖。
recipe字面意是“秘方、菜谱”,是make要做的动作,可以是多条命令,命令可以在一行也可以多行,但是必须以Tab键开头
如果 target还没生成过,或者任意一个prerequisite的更新时间更近,则会根据recipe重新生成target

在source insight中可以设置makefile文件的Tab键可视化,便于与空格区别。
在这里插入图片描述

2.2 一个简单的Makefile例子

官方手册中给出一个例子,最终可执行文件是edit,工程包括3个头文件、8个源文件。直白的依赖关系和生成规则如下:

edit : main.o kbd.o command.o display.o \
       insert.o search.o files.o utils.o
	cc -o edit main.o kbd.o command.o display.o \
               insert.o search.o files.o utils.o

main.o : main.c defs.h
	cc -c main.c
kbd.o : kbd.c defs.h command.h
	cc -c kbd.c
command.o : command.c defs.h command.h
	cc -c command.c
display.o : display.c defs.h buffer.h
	cc -c display.c
insert.o : insert.c defs.h buffer.h
	cc -c insert.c
search.o : search.c defs.h buffer.h
	cc -c search.c
files.o : files.c defs.h buffer.h command.h
	cc -c files.c
utils.o : utils.c defs.h
	cc -c utils.c

clean :
	rm edit main.o kbd.o command.o display.o \
       insert.o search.o files.o utils.o

其中,\换行,下一行从哪开始都行,make实际上将他们解释为同一行;
所有recipe都是Tab键开头;
如上:执行make生成edit文件,执行make clean删除8个目标文件和一个可执行文件,清理现场。
(如果makefile文件有多个target,你在执行make时又不指定target,则默认生成第一个target)

2.3 make处理Makefile的过程

缺省地,make从第一个target(不以.开头的target)开始,作为默认目的地(default goal)。
本例中, make执行时从第一个target也就是edit这一行开始,发现没有edit,则找依赖是否满足。发现依赖也还不存在,则根据每个依赖的依赖生成edit的依赖,再生成edit。

2.4 使用变量简化makefile文件

下列语句将8个目标文件重复写了两遍,又浪费时间又容易出错。

edit : main.o kbd.o command.o display.o \
       insert.o search.o files.o utils.o
	cc -o edit main.o kbd.o command.o display.o \
               insert.o search.o files.o utils.o
clean :
	rm edit main.o kbd.o command.o display.o \
       insert.o search.o files.o utils.o

怎么定义变量、使用变量呢?如果都是字符,make解释器怎么认出来谁是变量,谁是文件名、命令、参数呢?
在make的世界里,define变量用字符,使用变量用$取值。改写后如下:

objects = main.o kbd.o command.o display.o insert.o search.o files.o utils.o
edit : $(objects)
	cc -o edit $(objects)
clean :
	rm edit $(objects)

2.5 让make自行推导recipes

GNU的make很强大,它可以自动推导文件以及文件依赖关系后面的菜谱,于是我们就没必要去在每一个[.o]文件后都写上类似的命令,因为,我们的make会自动识别,并自己推导命令。

只要make看到一个[.o]文件,它就会自动的把对应[.c]文件加在依赖关系中,并且根据[.c]生成[.o]的菜谱:cc -c whatever.c -o whatever.o也会被推导出来。根据make已经学会的种种隐含规则,于是,我们的makefile再也不用写得这么复杂。改写后如下:

objects = main.o kbd.o command.o display.o insert.o search.o files.o utils.o
edit : $(objects)
	cc -o edit $(objects)

main.o : defs.h
kbd.o : defs.h command.h
command.o : defs.h command.h
display.o : defs.h buffer.h
insert.o : defs.h buffer.h
search.o : defs.h buffer.h
files.o : defs.h buffer.h command.h
utils.o : defs.h

.PHONY : clean
clean :
	rm edit $(objects)

这里干嘛有个.PHONY?它告诉make,这个clean是个标签,不是一个实实在在的文件。这有什么用?想象一下,假设当前文件恰好有个文件,名字就叫clean,就是这么不巧,那make发现:目标clean文件已经存在,而且它也没有依赖,所以也不会需要被更新,那么就永远不会执行下面的命令。这真是讨厌的事,所以,用.PHONY来"欺骗"make解释器,强迫它乖乖听话做clean的动作。

2.6 另类风格的Makefile

上面的写法也挺麻烦的,make也支持根据prerequisite对target重组,但是这种风格不容易理解,挺另类的,一般人玩不转,个人觉得没必要这样秀技。

objects = main.o kbd.o command.o display.o insert.o search.o files.o utils.o
edit : $(objects)
	cc -o edit $(objects)

$(objects) : defs.h
kbd.o command.o files.o : command.h
display.o insert.o search.o files.o : buffer.h

.PHONY : clean
clean :
	rm edit $(objects)

2.7 清空目标文件的规则

比如上例中的标签clean,已经很直白了。注意,不要把clean写在最前面,不然它就成了default goal了。
另外,在rm命令前面加了一个减号-的意思就是,也许某些文件出现问题,但不要管,继续做后面的事。

.PHONY : clean
clean :
	-rm edit $(objects)

总结

上面就是一个makefile的概貌,也是makefile的基础,下面还有很多makefile的相关细节,准备好了吗?不看细节,许多makefile文件你还是看不懂的,这才刚开了一个头。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值