makefile项目总结
命令行的方式编译文件需要怎么做
- 一个命令构建最终目标文件
gcc $(所有源文件) -o $(目标文件)
- 拆分多个步骤编译
gcc -S 生成汇编 gcc -c 生成编译文件 gcc 生成链接文件
- 其他命令
-I 引入头 -L 导入库
编写一个最简单的makefile
- 目标的目录结构
pro1 ├── main.cpp └── makefile
- 命令行上怎么编译这个文件
# 编译前需要考虑是否清理掉之前的输出 rm -f pro1.out # 编译main.cpp 并生成 g++ -o pro1.out main.cpp
- 怎么把这些写到makefile文件呢
#定义一个最终输出目标 pro1.out: g++ -o pro1.out main.cpp #定义目标的清理规则 clean: rm -f pro1.out
- 执行编译命令
ggg@ggg-X550JX ~/test_demo/test_demo/pro1 $ make pro1.out g++ -o pro1.out main.cpp ggg@ggg-X550JX ~/test_demo/test_demo/pro1 $ ls main.cpp makefile pro1.out ggg@ggg-X550JX ~/test_demo/test_demo/pro1 $ make clean rm -f pro1.out ggg@ggg-X550JX ~/test_demo/test_demo/pro1 $ ls main.cpp makefile
make [目标名]
使用该命令则可调用makefile内的编译方法 - 当然makefile文件名称并不规定只有这一种写法
ggg@ggg-X550JX ~/test_demo/test_demo/pro1 $ mv makefile pro1.mk ggg@ggg-X550JX ~/test_demo/test_demo/pro1 $ make -f pro1.mk g++ -o pro1.out main.cpp ggg@ggg-X550JX ~/test_demo/test_demo/pro1 $ tree . ├── main.cpp ├── pro1.mk └── pro1.out 0 directories, 3 files
make -f [makefile文件名] [目标名]
同样可以使用这样的一种方式编译 - 如果make时不指明编译目标,会默认执行makefile内第一个编译目标
- 至此解决了一个基本问题,就是不用每次编译都输入编译命令
变量
- 设置变量
设置变量时候不需要引号,这里make进行直接字符替换
INC=../util
使用变量
g++ -o pro1.out main.cpp -I$(INC)
- 变量允许嵌套
比如现在使用一个公共头文件名称是util_pro1.h
PRO=pro1 pro1_INC=../util/util_pro1.h g++ -o pro1.out main.cpp -I$($(PRO)_INC)
- 变量追加
比如现在多了一个log.cpp需要和 main.cpp一块便于
file = main.cpp file += log.cpp g++ -o pro1.out main.cpp -I$($(PRO)_INC)
- 内置的几个变量
$@ 输入 $< 所有依赖 $^ 当前依赖
- include 导入参数文件
在make时可以使用外部文件进行参数的导入,这个文件里边既可以是变量,也可以是目标方法,但如果是目标方法。
但需要注意和主makefile的命名冲突.
include 在文件头则作用于makefilre整个文件
如果在末尾,则只能作用在文件的执行区域,既不能用在依赖上。
使用场景:本次有10个工程共用一个makefile,因此差异化的参数就不能体现在一个makefile里,
因此设计一个附加工程用来专门生成RES.dep文件,
在编译不同项目时RES.dep内参数一致,但内容不同,
因此主makefile只需倒入这些变量即可不做任何修改,提升通用性,
- 至此将变量提取出来,分离了编译过程和编译参数
常用函数
- 获取源文件
1.1 方法1
1.2 方法2source_path=$(shell find $(src_path) -maxdepth 3 -type d) source_file = ($foreach dir,$(source_path),$(wildcard *.c))
$(wildcard *.c */*.c)
- 去除路径信息,只保留源文件
$(notdir $(souerce_file))
- 文件过滤
3.1 取参数2与参数3的交集
3.2 取参数2对参数3差集$(filrter a.c b.c,a.c) #结果a.c
$(filrter-out a.c b.c,a.c) #结果a.c
- 字符串的替换
4.1 方法1
4.2 方法2files4=$(patsubst %.c,%.o,$(src))
4.3 部分替换file=$(sourcefile:%.s=%.o)
file=$(sourcefile:$(src_path)/%.s=%.o)
流程控制
- 两种依赖方法
1.1 链式依赖
1.2 顺序依赖a:b b:c
a: b c
- if 分支
ifeq (Y,$(diffmode)) 操作1 else ifeq(N,$(diffmode)) 操作2 endif
- 判断是否被定义
ifdef speaxial_path booter =hhh endif
自动推导.o编译
- 使用默认的编译指令
#设置编译器版本 CC=gcc #设置输出目标 OBJS=1.o 2.o 3.o all:$(OBJS) mv *.o ../build #这里注意只要写目标就行 1.o: 2.o: 3.o: clean: rm ../build/*.o
- 自定义编译指令
src=$(wildcard *.c dir/*.c) files4=$(src:%.c=%.o) #把依赖从all上像下依赖 all:$(files4) echo "sucess" #all 将会依赖到这里,如果输入*.c中带有路径,则*.o也带路径 %.o:%.c $(CC) -c $^ -o $@ #允许特例化 1.o:1.c $(CC) -c 1.c -o 1.o -lpthread
其他
- 指明伪目标
.PHONY:clean clean: rm -rf *.o
- 在编译选项中进行替换 只能使用patsubst,而在变量定义中既可以用patsubst也可以用xx:%.c=%.d
- 隐藏命令和忽略错误
#比如echo 如果不隐藏则会打印两次 @命令 隐藏 #针对一些可以预见的错误 -命令 忽略错误