Linux C编程一站式学习 part2: C语言本质

Linux C编程一站式学习 (akaedu.github.io)

22. Makefile基础

学习 Makefiles
Alt

1.基本规则

欲更新目标,必须首先更新它的所有条件;
所有条件中只要有一个条件被更新了,目标也必须随之被更新。
“更新”:执行一遍规则中的命令列表,命令列表中的每条命令必须以一个Tab开头
对于Makefile中的每个以Tab开头的命令,make会创建一个Shell进程去执行它

#target ... : prerequisites
#	command1
#	command2
# …
main: main.o stack.o maze.o
	gcc main.o stack.o maze.o-o main
# main.o stack.o maze.o发生改变则更新可执行文件main
main.o: main.c main.h stack.h maze.h
	gcc -c main.c
# main.c main.h stack.h maze.h改变则编译main.c生成main.o
stack.o: stack.c stack.h main.h
	gcc -c stack.c
# stack.cstack.hmain.h改变则编译stack.c生成stack.o
maze.o:maze.c maze.hmain.h
	gcc -c maze.c
# maze.c maze.h main.h改变则编译maze.c生成maze.0
# 如果编译之后又对maze.c做了修改,又要把所有源文件编译一遍
# 即使main.c、stack.c和那些头文件都没有修改也要跟着重新编译。
# 一个大型的软件项目往往由上干个源文件组成,全部编译一遍需要几个小时
# 只改一个源文件就要求全部重新编译肯定是不合理的。

目标属于以下情况之一,就称为需要更新:
1.目标没有生成。
2.某个条件需要更新。
3.某个条件的修改时间比目标晚。
Alt
make执行的命令前面加了@字符,则不显示命令本身而只显示它的结果
通常make执行的命令如果出错(该命令的退出状态非0)就立刻终止,不再执行后续命令
但如果命令前面加了-号,即使这条命令出错,make也会继续执行后续命令

  • rm和mkdir都有可能出错,但这种错误是应该忽略的
    Alt
    约定俗成的目标名字有:
    all,执行主要的编译工作,通常用作缺省目标。
    install,执行编译后的安装工作,把可执行文件、配置文件、文档等分别拷到不同的安装目录。
    clean,删除编译生成的二进制文件。
    distclean,不仅删除编译生成的二进制文件,也删除其它生成的文件

2.隐含规则和模式规则

一个目标依赖的所有条件不一定非得写在一条规则中,也可以拆开写
此时只有一条规则允许有命令列表,其它规则应该没有命令列表
一个目标在Makefile中的所有规则都没有命令列表,make会尝试在内建的隐含规则(Implicit Rule)数据库(可以用make -p命令打印)中查找适用的规则
例如配置文件和格式转换后的文档,执行make distclean之后应该清除所有这些文件,只留下源文件

# default 
OUTPUT_OPTION = -o $@  
# default 
CC = cc  
# default 
COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c  
%.o: %.c  #模式规则(Pattern Rule)
#  commands to execute (built-in):         
$(COMPILE.c) $(OUTPUT_OPTION) $<

$(CFLAGS)展开是空,CPPFLAGSTARGET_ARCH也是如此。
这样$(COMPILE.c)展开应该是cc␣空␣空␣空␣-c,去掉所有的“空”得到cc␣␣␣␣-c
注意中间留下4个空格
所以%.o: %.c规则的命令$(COMPILE.c)␣$(OUTPUT_OPTION)␣$<
展开之后是cc␣␣␣␣-c␣-o␣$@␣$<
$@的取值为规则中的目标
$<的取值为规则中的第一个条件
翻译后如下!

main.o: main.c
  cc    -c -o main.o main.c

以条件为中心,Makefile还可以这么写(只要把所有的依赖关系都描述清楚了就行)

main: main.o stack.o maze.o
	gcc main.o stack.o maze.o -o main
main.o stack.o maze.o: main.h
main.o maze.o: maze.h
main.o stack.o: stack.h
clean:
	-rm main *.o
.PHONY: clean

多目标的规则,make会拆成几条单目标的规则来处理

target1 target2: prerequisite1 prerequisite2
	command $< -o $@

展开如下

target1: prerequisite1 prerequisite2
	command prerequisite1 -o target1

target2: prerequisite1 prerequisite2
	command prerequisite1 -o target2

3.变量

①CFLAGS定义成一些编译选项,例如-O、-g等
②CPPFLAGS定义成一些预处理选项,例如-D、-I等
③用=号定义变量的延迟展开特性
④希望make在遇到变量定义时立即展开,可以用:=运算符
⑤foo ?= \((bar)的意思是: 如果foo没有定义过,那么?=相当于=,定义foo的值是\)(bar),但不立即展开;
如果先前已经定义了foo,则什么也不做,不会给foo重新赋值
⑥+=运算符可以给变量追加值
case1

objects = main.o
objects += $(foo)

object是用=定义的,+=仍然保持=的特性,追加前先加空格,不立即展开,需要展开时展开
case2

objects := main.o
objects += $(foo)

object是用:=定义的,+=保持:=的特性,保留空格、立刻展开
常用的特殊变量有:
$@,表示规则中的目标。
$<,表示规则中的第一个条件。
$?,表示规则中所有比目标新的条件,组成一个列表,以空格分隔。
$^,表示规则中的所有条件,组成一个列表,以空格分隔。
用特殊变量的好处:
即使以后又往条件里添加了新的目标文件,编译命令也不需要修改,减少了出错的可能。
常用变量:
AR静态库打包命令的名字,缺省值是ar
ARFLAGS静态库打包命令的选项,缺省值是rv
AS汇编器的名字,缺省值是as
ASFLAGS汇编器的选项,没有定义。
CC C编译器的名字,缺省值是cc
CFLAGS C编译器的选项,没有定义。
CXX C++编译器的名字,缺省值是g++
CXXFLAGS C++编译器的选项,没有定义。
CPP C预处理器的名字,缺省值是$(CC) -E
CPPFLAGS C预处理器的选项,没有定义。
LD 链接器的名字,缺省值是ld
LDFLAGS 链接器的选项,没有定义。
TARGET_ARCH 和目标平台相关的命令行选项,没有定义。
OUTPUT_OPTION 输出的命令行选项,缺省值是-o $@
LINK.o 把.o文件链接在一起的命令行,缺省值是$(CC) $(LDFLAGS) $(TARGET_ARCH)
LINK.c 把.c文件链接在一起的命令行,缺省值是$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)
LINK.cc 把.cc文件(C++源文件)链接在一起的命令行,缺省值是$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)
COMPILE.c 编译.c文件的命令行,缺省值是$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
COMPILE.cc 编译.cc文件的命令行,缺省值是$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
RM 删除命令的名字,缺省值是rm -f

posted @ 2024-03-24 10:23  asandstar  阅读(7)  评论(0编辑  收藏  举报