make常用选项
# make 默认在当前目录中寻找GUNmakefile,makefile,Makefile的文件作为make的输入文件
# -f 可以指定默认的输入文件名,如: -f MyMakefile
# -v 显示make版本号
# -n 只输出命令,但不执行,一般用于测试
# -s 只执行命令,但不显示具体命令,与在命令中使用@作用一样
# -w 显示执行前执行后的路径
# -C dir 指定 makefile 所在的目录
make [-f file][options][target]
gcc/g++ 编译流程
预处理
gcc -E main.cpp > main.ii
编译
# 输出汇编文件 main.s
gcc -S main.ii
汇编
# 输出二进制文件 main.o(或main.obj)
gcc -c main.s
链接
# -o 重命名,默认输出a.out
# -lstdc++ 表示链接c++库
# -lc 默认链接c库
gcc main.o -lstdc++ -o main
Makefile 基本语法
# 这是一个注释
# 命令前是一个tab键
# 目标:一般指要编译的目标,也可以是执行的动作
# 依赖可以有多个
# 依赖先于当前目标执行
# 多条命令时,每一行是一个命令
目标:依赖
命令
示例一
# make 默认执行第一个目标
# make b 表示执行b目标
a:
echo "hello a"
b:
echo "hello b"
示例二
# 使用 @ 不显示指令本身
# 运行 make 指令,先显示 hello b,然后显示 hello a
a:b
@echo "hello a"
b:
@echo "hello b"
Makefile 中的变量和常量
系统变量
变量名 | 说明 |
---|---|
$* | 不包括扩展名的目标文件的名称 |
$+ | 所有的依赖文件,以空格分隔 |
$< | 表示规则中的第一个条件 |
$? | 所有时间戳比目标文件晚的依赖文件,以空格分隔 |
$@ | 目标文件的完整名称 |
$^ | 所有不重复的依赖文件,以空格分隔 |
$% | 如果目标是归档成员,则该变量表示目标的归档成员名称 |
系统常量
可以使用 make -p
查看
常量名 | 说明 |
---|---|
AS | 汇编程序的名称,默认为 as |
CC | C 编译器名称,默认为 cc |
CPP | C 预编译器名称,默认为 cc -E |
CXX | C++ 编译器名称,默认为 g++ |
RM | 文件删除程序别名,默认为 rm -f |
AR | 静态库打包命令名,默认为 ar |
LDFLAGS | 指定库文件路径 |
LIBS | 指定库文件 |
MAKE | make 指令 |
自定义变量
定义
变量名=变量值 或者 变量名:=变量值
使用
$(变量名)或者${变量名}
示例
# = 赋值为终值,后面赋值可以影响前面赋值,取最后一次出现的值
OBJ=demo.o add.o sub.o multi.o
# := 赋值只受前面赋值的影响
TARGET:=demo
$(TARGET):$(OBJ)
$(CXX) $^ -o $@.exe
demo.o:demo.cpp
$(CXX) -c $^ -o $@
add.o:add.cpp
$(CXX) -c $^ -o $@
sub.o:sub.cpp
$(CXX) -c $^ -o $@
multi.o:multi.cpp
$(CXX) -c $^
.PHONY:clean show
clean:
@$(RM) *.o $(TARGET).exe
show:
@echo $(AS)
@echo $(CC)
@echo $(CPP)
@echo $(CXX)
@echo $(RM)
Makefile 中的伪目标和模式匹配
伪目标
作用
声明目标为伪目标之后, makefile 将不会判断目标是否存在或该目标是否需要更新
用法
# .PHONY 固定名称
.PHONY:目标
模式匹配
指令 | 说明 |
---|---|
%.o:%.cpp | .o 依赖于对应的 .cpp |
wildcard | $(wildcard ./*.cpp) 获取当前目录下所有的 .cpp 文件 |
patsubst | ( p a t s u b s t (patsubst %.cpp,%.o, (patsubst(wildcard ./*.cpp) ) 将当前目录下 .cpp 文件名替换成对应的 .o 文件名 |
示例
OBJ=$(patsubst %.cpp,%.o,$(wildcard ./*.cpp))
TARGET=demo
$(TARGET):$(OBJ)
$(CXX) $^ -o $@.exe
# 根据 OBJ 中的 .o 找到对应的 .cpp
%.o:%.cpp
$(CXX) -c $^ -o $@
.PHONY:clean show
clean:
@$(RM) *.o $(TARGET).exe
show:
@echo $(AS)
@echo $(CC)
@echo $(CPP)
@echo $(CXX)
@echo $(RM)
@echo $(wildcard ./*.cpp)
@echo $(patsubst %.cpp,%.o,$(wildcard ./*.cpp))
Makefile 中编译动态链接库
不会把代码直接编译到二进制文件中,而是在运行时才加载,只需维护一个地址
库以 .dll 和 .so 为结尾
选项 | 说明 |
---|---|
-fPIC | 大写的i,产生位置无关的代码 |
-shared | 共享 |
-l | 小写L,指定动态库 |
-I | 大写i,指定头文件目录,默认当前目录 |
-L | 手动指定库文件搜索目录,默认只链接共享目录 |
示例
# 生成链接库的名称规则是 lib+目标明称.dll
g++ -shared -fPIC add.cpp -o libadd.dll
# -ladd 指定动态库 libadd.dll
# -L 指定动态库路径
# main.cpp 紧跟 g++之后
g++ main.cpp -ladd -L./ -o main.exe
编写 Makefile
# 小写的L
LIBS=-ladd -lsub
# -L 可以有多个, -Wl,R 可以有多个
# -Wl,-rpath=可以让链接器在运行时找到该库,小写的L
LDFLAGS=-L./ -L./01 -Wl,-rpath=./ -Wl,-rpath=./01
main:libadd.dll libsub.dll
# main.cpp 紧跟 g++之后
$(CXX) main.cpp $(LIBS) $(LDFLAGS) -o $@.exe
libadd.dll:add.cpp
$(CXX) -fPIC -shared $^ -o $@
libsub.dll:sub.cpp
$(CXX) -fPIC -shared $^ -o $@
注意
windows 下是以 .dll 结尾, linux 下是以 .so 结尾
Makefile 中编译静态链接库
将代码直接编译到目标文件中,编译完成之后静态库可以删除
windows下使用 .lib 后缀, linux 下使用 .a 后缀
示例
g++ -c sub.cpp -o sub.o
# 将 .o 文件打包为 .a 文件,格式不能改变
ar -r libsub.a sub.o
# main.cpp 紧跟 g++ 之后
g++ main.cpp -lsub -L./ -o main.exe
# 使用 objdump -DC 验证 main.exe 中有 sub.cpp内容
objdump -DC main.exe > main.txt
编写 Makefile
OBJ=add.a sub.a multi.a
TARGET=main
LIBS:=-ladd -lsub -lmulti
LDFLAGS:=-L./
$(TARGET):$(OBJ)
# main.cpp 紧跟 g++之后
$(CXX) main.cpp $(LIBS) $(LDFLAGS) -o $@
%.a:%.o
$(AR) -r lib$@ $^
%.o:%.cpp
$(CXX) -c $^ -o $@
.PHONY:clean
clean:
@$(RM) $(TARGET) *.o *.a *.lib
Makefile 抽取公共部分
# 使用 include 关键字
# 用法 include makefile路径
include ../makefile
Makefile 中使用shell命令
# $(shell command)
a:=$(shell ls ./)
.PHONY:show
show:
@echo $(a)
# make show
Makefile 中的条件判断
指令 | 说明 |
---|---|
ifeq | 判断是否相等,相等返回 true, 不等返回 false |
ifneq | 判断是否不相等,不相等返回 true, 相等返回 false |
ifdef | 判断变量是否存在,存在返回 true,不存在返回 false |
ifndef | 判断变量是否不存在,不存在返回 true,存在返回 false |
示例
a:=123
res:=
res1:=
# ifeq 与 () 有个空格
ifeq ($(a), 123)
res:="相等"
else#没有elseif
res:="不相等"
endif#结束
ifdef res1
res1:=8
else#可以没有 else
res1:=9
endif
# 传参: make 变量名=值
# make f=123
show:
@echo $(res)
@echo $(res1)
@echo f=$(f) b=$(b)
Makefile 中的循环
不是所有 make
都支持,可以使用 shell
替换
TARGETS:=a b c
# $(foreach v,变量,$(v))
TARGET:=$(foreach v,$(TARGET),-l$(v))
show:
@echo $(TARGET)
Makefile 中自定义函数
# define 函数名
define fun
# 函数名
@echo $(0)
# 参数1,参数2
@echo $(1) $(2)
@echo "hello makefile"
# 结束
endef
# 调用: $(call 函数名,参数1,参数2,...)
show:
@$(call fun)