Makefile
Make 官方文档
https://www.gnu.org/software/make/manual/make.html
Makefile语法
语法简单介绍
注释
#注释代码
显式规则
目标:依赖
[TAB]指令
目标:一般是指要编译的目标,也可以是一个动作。
依赖:指执行当前慕白所要依赖的先项,包括其他目标,某个文件或者库等。一个目标可以有多个依赖,依次执行
命令:该目标下要执行的指令,可以多条,也可以没有
伪目标
.PHONY:
clean:
变量 A
- A= 替换
- A+= 追加
- A:= 恒等于(常量,不能追加)
- $(A) 取值
隐含规则
%.c %.o 任意的.c文件,任意的.o文件
# 需要.o文件则都通过.c 产生 %.o:%.c gcc -c %.c -o %.o
*.c *.o 所有的.c文件,所有的.o文件
区别是%是当需要寻找构造规则时,找到依赖文件进行构造。*不知道目标的名字,系统目录下所有后缀为.c的文件,然后遍历目录中的文件,是不是匹配,然后进行构造
Make命令
make [-f file] [options] [target]
make 默认在当前目录中寻找GUNmakefile、makefile、Makefile的文件作为make的输入文件
-f 指定输入文件
-v 显示版本号
-n 只输出命令,不执行
-s 只执行命令,不输出具体命令,或者使用@进行抑制输出
-w 显示执行前执行后的路径
-C dir 指定构造目录
没有指定目标时,默认使用第一个目标
如果指定,则执行对应的命令。
变量
系统变量
$@:目标的名字
$^:构造所需文件列表所有文件的名字
$<:构造所需文件列表的第一个文件的名字
$?:构造所需文件列表中更新过的文件
$*:不包括扩展名的目标文件名称
$+:所有的依赖文件,以空格分隔
$%:如果目标是归档成员,则该变量表示目标的归档成员名称
系统常量(make -p 查看)
AS 汇编程序的名称
CC C编译器的名称
CPP C与编译器名称
RM 文件按删除程序别名
自定义变量
定义:变量名=变量值
使用:$(变量民)/${变量名}
伪目标和模式匹配
.PHONY:clean
声明目标为伪目标之后,make将不会判断目标是否存在或该目标是否需要更新。
当touch clean时,不会出现问题
%.o:%.cpp .o依赖于对应的.cpp
wildcard $(wildcard ./*.cpp) #获取当前目录下所有的.cpp文件
patsubst $(patsubst %.cpp, %.o, ./*.cpp) #将对应的cpp文件名替换为.o文件名
OBJ=$(patsubst %.cpp, %.o, $(wildcard ./*.cpp)) #将目录下的所有cpp文件转为.o的目标文件
模式匹配
%目标:%依赖
条件判断
ifeq #判断是否相等,相等返回true,不相等返回false
ifneq
ifdef #判断变量是否存在,如果存在则返回true,不存在返回false
ifndef
else
endif
#没有elseif的写法,只能写嵌套
ABC=y
ifeq ($(ABC),y) #ifeq后面必须要有一个空格,ifneq也一样
DEF=y
else
ifeq ($(ABC),123)
DEF=123
else
DEF=y
endif
endif
传参
可通过传参写入变量
make Moliam=12_years_old all
all:
@echo $(Moliam)
输出结果为
12_years_old
循环
只有一个循环 foreach
可以在循环中逐个修改值
只有GUN make支持,其他版本中的make,可以用shell中的循环来实现
TAGGET=a b c d
FILE=$(foreach v,$(TARGET),$v.txt)
all:
echo $(foreach v,$(TARGET),$v)#v为被展开的变量
echo $(FILE)
自定义函数
自定义函数不是真的函数,本质上是多行命令,可以有传参,没有返回值
define FUN1
echo $(0) #函数名
echo $(1) $(2) #传参第一个和第二个
echo fun1
endif
all:
$(call FUNC1,abc,$(Moliam))#传参在后面以逗号隔开
Make运行流程
当依赖的文件比目标的文件更新时,会再次执行。保证目标是用最新的依赖生成的。
第一次完全编译,后面只编译最新的代码(部分编译)。
Makefile 中编译动态链接库
动态链接库:不会把代码编译到二进制文件中,而是在运行时才去加载,所以只需要维护一个地址。
- -fPIC 产生位置无关的代码
- -shared 共享
- -l(小L)指定动态库
- -I(大i)指定头文件目录,默认当前目录
- -L 手动指定库文件搜索目录,默认只链接共享目录
动态:运行时才去加载,动态加载
链接:库文件和二进制程序分离,用某种特殊手段维护两者的关系。
库: 库文件 .dll .so
好处是程序可以和库文件分离,可以分别发版,然后库文件可以被多处共享。
gcc -shared -fPIC a.c -o liba.so # 生成动态库
gcc -la -L./ b.c -o b# 依赖动态库
linux默认动态库路径配置文件
/etc/ld.so.conf
/etc/ld.so.conf.d/*.conf
Makefile 中编译静态链接库
静态链接库:会把库中的代码编译到二进制文件中,当程序编译完成后,该库文件可以删除。动态链接库不行,动态链接库必须和程序同时部署,还要保证程序能加载得到库文件。
与动态库相比,静态库可以不用部署(已经加载到程序内部),运行速度更快(因为不用去加载),但是会导致程序体积更大,并且库中内容更新,需要重新编 译生成程序。
Makefile 通用部分做公共头文件
include 目录
调用其他位置的Makefile相关文件,进行相关操作
在一个目录中做通用部分如clean,对象和目标等。然后在其他部分的makefile中,可以直接进行包含,而减少代码重复的工作量。
=和:=
Makefile中,是先展开所有变量,再调用指令
A=123
B=$(A)
A=456
输出B的值,也是456
Y=123
Y=$(Y) #此处给变量Y赋值,会报错,错误原因为不能引用自己
Y:=$(Y)111 #此处会输出123111
= 取用最终值,不管变量调用写在赋值前还是赋值后。写在前还是写在后
:= 只受当前行以及之前的代码影响,而不会受后面的赋值影响。
调用shell命令
A:=$(shell ls)
B:=$(shell pwd)
C:=$(shell if [! -f abc];then touch abc;fi) #如果文件abc不存在则进行创建
$(shell cmd) #cmd为具体shell脚本/命令,可用变量直接等于命令返回结果
嵌套调用和多项目同时编译
all:
make -C dir1 -f Makefile all
make -C dir2 all
clean:
make -C dir1 clean
make -C dir2 clean
调用dir1
,dir2
中的make all和clean
简略写如下:
.PHONY:dir1 dir2 clean
DIR=dir1 dir2
all:$(DIR)
$(DIR):
make -C $@
clean:
# $$dir表示展开shell中的变量
$(shell for dir in $(DIR);do make -C $$dir clean;done)
Make install
- 将源文件编译成二进制可执行文件
- 创建目录,将可执行文件拷贝到指定目录
- 加全局可执行的路径
- 加全局启停脚本
- 重置编译环境,删除无关文件
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!