马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
Makefile
学习视频:1、野火的底子入门篇-第32讲 Makefile三要素_哔哩哔哩_bilibili
2、b站视频04 一个稍复杂的Makefile_哔哩哔哩_bilibili
学习资料:第2个视频对应的Make/make.md · 无穷十三年/CPP - 码云 - 开源中国
ch0_Makefile简介
Makefile是什么?
通过make工具和Makefile去管理必要编译的文件
make和Makefile是什么关系?
- make工具:找出修改过的文化,根据依靠关系,找出受影响的干系文件,末了按照规则单独编译这些文件。
- Makeflie文件:记载依靠关系和编译规则
makefile总览
ch1_Makefile三要素
Makefile三要素是什么?
目标、依靠、命令
怎么形貌三要素?
目标:依靠的文件大概是其他目标
命令1
命令2
…
命令可以多行,但是前面必须加tap字符
实行演示
- targeta:targetb targetc
- echo "targeta"
- targetb:
- echo "targetb"
- targetc:
- echo "tarfetc"
-
- mzj@ubuntu:~/Makefile$ make
- echo "targetb"
- targetb
- echo "targetc"
- targetc
- echo "targeta"
- targeta
- mzj@ubuntu:~/Makefile$ make targetb
- echo "targetb"
- targetb
- mzj@ubuntu:~/Makefile$ make targetc
- echo "targetc"
- targetc
复制代码 .PHONY:目标
必要注意当文件路径下有跟目标雷同的文件名时,想要make不被影响,应该在Makefile文件的开头加上.PHONY:目标名
缘故原由:
- .PHONY反面跟的目标都被称为伪目标
- 也就是说我们 make 命令反面跟的参数如果出如今.PHONY 界说的伪目标中,那就直接在Makefile中就实行伪目标的依靠和命令。不管Makefile同级目次下是否有该伪目标同名的文件,纵然有也不会产生辩论。
- 另一个就是进步实行makefile时的服从。
- .PHONY:targetb
- targeta:targetb targetc
- echo "targeta"
- targetb:
- echo "targetb"
- targetc:
- echo "targetc"
- mzj@ubuntu:~/Makefile$ ls
- Makefile targetb targetc
- mzj@ubuntu:~/Makefile$ make targetc
- make: “targetc”已是最新。
- mzj@ubuntu:~/Makefile$ make targetb
- echo "targetb"
- targetb
复制代码 ch2_Makefile变量
体系变量
- Makefile_test文件
- .PHONY:all
- all:
- echo "$(CC)"
- echo "$(AS)"
- echo "$(MAKE)"
- echo "$(MAKE)"
- mzj@ubuntu:~/Makefile$ make -f Makefile_test
- echo "cc"
- cc
- echo "as"
- as
- echo "make"
- make
- echo "make"
- make
复制代码 这里的make -f 指定Makefile文件名字,针对这个这个文件,大概有很多Makefile_test
自界说变量
- =:耽误赋值,实行到这个变量时才赋值
- Makefile_test1文件
- A = 123
- B = $(A)
- A = 456
- .PHONY:all
- all:
- echo "$(B)"
-
- mzj@ubuntu:~/Makefile$ make -f Makefile_test1
- Makefile_test1:7: *** 遗漏分隔符 (null)。 停止。
- !!!!!!!这里是tap建被vscode换成4个空格了,需要设置!!!!!!!
- #这里是执行到$(B)时,B才执行B = $(A) 456
- mzj@ubuntu:~/Makefile$ make -f Makefile_test1
- echo "456"
- 456
复制代码 - :=:为立即赋值,与通例思路一样
- A = 123
- B := $(A)
- A = 456
- .PHONY:all
- all:
- echo "$(B)"
- mzj@ubuntu:~/Makefile$ make -f Makefile_test1
- echo "123"
- 123
复制代码 - ?=:空赋值,只有变量为空时,赋值才有用,反面再赋值没有用,像是一次初始化
- A ?= 123
- A ?= 456
- .PHONY:all
- all:
- echo "$(A)"
-
- mzj@ubuntu:~/Makefile$ make -f Makefile_test1
- echo "123"
- 123
复制代码 - +=:追加赋值,不覆盖前面的,在前面的值底子上在尾部继承追加一些值
- A ?= 123
- A += 456
- .PHONY:all
- all:
- echo "$(A)"
-
-
- mzj@ubuntu:~/Makefile$ make -f Makefile_test1
- echo "123 456"
- 123 456
复制代码 自动化变量
- $<:第一个依靠文件first_tar
- $^:全部的依靠文件first_tar second_tar
- $@:目标all
- all:first_tar second_tar
- echo "$<"
- echo "$^"
- echo "$@"
- first_tar:
- second_tar:
- mzj@ubuntu:~/Makefile$ make -f Makefile_test1
- echo "first_tar"
- first_tar
- echo "first_tar second_tar"
- first_tar second_tar
- echo "all"
- all
复制代码 Makefile中另有别的自动化变量,此处仅列出方便以后利用到的时间举行查阅,见下表。
符号意义$@匹配目标文件$%与 @ 类 似 , 但 @雷同,但 @雷同,但%仅匹配“库”范例的目标文件$<依靠中的第一个目标文件$^全部的依靠目标,如果依靠中有重复的,只保存一份$+全部的依靠目标,纵然依靠中有重复的也原样保存$?全部比目标要新的依靠目标利用变量编译C文件
- CC = gcc
- TARGET=mp3
- OBJS=main.o mp3.o
- $(TARGET):$(OBJS)
- $(CC) $^ -o $@
- main.o:
- $(CC) -c main.c -o main.o
- mp3.o:
- $(CC) -c mp3.c -o mp3.o
- .PHONY:clean
- clean:
- rm $(TARGET)
复制代码 ch3_Makefile模式匹配和默认规则
- %:匹配恣意多个非空字符
- .PHONY:all
- %:
- @echo "$@"
- @echo "$^"
- @echo "$<"
- # @ 符号的作用是抑制命令本身的输出,只显示命令执行的结果。
- mzj@ubuntu:~/Makefile$ make
- make: *** 无目标。 停止。
- mzj@ubuntu:~/Makefile$ make 123
- 123
复制代码 @符号的作用是克制命令本身的输出,只表现命令实行的效果。
命令行输入给了%这个位置
再看这个模板:这里的.c文件到.o文件的情势根本上一样的,就可以利用模式匹配%去取代
- CC = gcc
- TARGET=mp3
- OBJS=main.o mp3.o
- $(TARGET):$(OBJS)
- $(CC) $^ -o $@
- main.o:main.c
- $(CC) -c main.c -o main.o
- mp3.o:mp3.c
- $(CC) -c mp3.c -o mp3.o
- |
- |
- |
- %.o:%.c
- $(CC) -c $< -o $@
- .PHONY:clean
- clean:
- rm $(TARGET)
复制代码 这里的%.o:包罗了全部的.o文件
- 默认规则:.o文件默认利用.c文件举行编译,以是可以直接不管.o文件,下面即可
- CC = gcc
- TARGET=mp3
- OBJS=main.o mp3.o
- $(TARGET):$(OBJS)
- $(CC) $^ -o $@
- .PHONY:clean
- clean:
- rm $(TARGET) *.o
复制代码 - 默认规则另有很多:后续增补
ch4_Makefile条件分支
- ifeq(var1,var2)
- ...
- else
- ...
- endif
- ifneq(var1,var2)
- ...
- else
- ...
- endif
复制代码 ifeq:两个条件相当
ifneq:两个条件不相当
内里必要加上
搭配?=举行举例
- ARCH ?= x86
- ifeq($(ARCH),x86)
- CC = gcc
- else
- CC = arm-linux-gnueabihf-gcc
- endif
- TARGET=mp3
- OBJS=main.o mp3.o
- $(TARGET):$(OBJS)
- $(CC) $^ -o $@
- .PHONY:clean
- clean:
- rm $(TARGET) *.o
-
-
- mzj@ubuntu:~/Makefile$ make
- 那么,CC = gcc
- mzj@ubuntu:~/Makefile$ make ARCH=arm
- 由于空赋值,命令行中就给ARCH赋值了,CC = arm-linux-gnueabihf-gcc
复制代码 ch5_Makefile常用函数
很多看官方手册
这里挑四个常用的
patsubst
作用,例如把一堆.c文件名转成.o文件名
有两种方法:objects = foo.o bar.o baz.o
- $(objects:.c=.o)
- $(patsubst %.o,%.c,$(objects))
- 第一种看起来,更直观,更简便
- $(patsubst %.c,%.o,x.c.c bar.c)
复制代码 把字串“x.c.c bar.c”中以.c 结尾的单词更换成以.o 结尾的字符。函数的返回效果是“x.c.o bar.o”
- $(patsubst %.c, build_dir/%.o, hello_main.c )
- #函数的输出为:
- build_dir/hello_main.o
- #执行如下函数
- $(patsubst %.c, build_dir/%.o, hello_main.xxx )
- #由于hello_main.xxx不符合匹配规则"%.c",所以函数没有输出
复制代码 例如:
我们存在一个代表全部.o 文件的变量。界说为
- objects = foo.o bar.o baz.o
复制代码 为了得到这些.o 文件所对应的.c 源文件。我们可以利用以下两种方式的恣意一个:
- $(objects:.o=.c)
- $(patsubst %.o,%.c,$(objects))
复制代码 notdir
作用,把路径去了,只保存文件名
示例:
- $(notdir src/foo.c hacks)
- foo.c hacks
复制代码 返回值为:这里的src/被去掉了,只留下foo.c hacks。
wildcard
作用:列出当前目次下全部符合模式“PATTERN”格式的文件名。
“PATTERN”利用shell可辨认的通配符,包括“?”(单字符)、“*”(多字符)等。
示例:
- $(wildcard *.c)
- #在sources目录下有hello_func.c、hello_main.c、test.c文件
- #执行如下函数
- $(wildcard sources/*.c)
- #函数的输出为:
- sources/hello_func.c sources/hello_main.c sources/test.c
复制代码 返回值为当前目次下全部.c源文件列表
foreach
我们来看一个例子,实现了将变量“files”赋值为目次“dirs”下全部文件列表:
- dirs := a b c d
- files := $(foreach dir,$(dirs),$(wildcard $(dir)/*))
复制代码 例子中,表达式第一次实行时将睁开为
$(wildcard a/*);第二次实行时将睁开为$(wildcard b/*);第三次睁开为$(wildcard c/*);….;以此类推。以是此函数所实现的功能就和一下语句等价:
- files := $(wildcard a/* b/* c/* d/*)
复制代码 ch6_多级布局工程的Makefile
接下来我们利用上面三个函数修改我们的Makefile,以顺应包罗多级目次的工程,修改后的内容如下所示。
- #定义变量
- #ARCH默认为x86,使用gcc编译器,
- #否则使用arm编译器
- ARCH ?= x86
- TARGET = hello_main
- #存放中间文件的路径
- BUILD_DIR = build_$(ARCH)
- #存放源文件的文件夹
- SRC_DIR = sources
- #存放头文件的文件夹
- INC_DIR = includes .
- #源文件
- SRCS = $(wildcard $(SRC_DIR)/*.c)
- #目标文件(*.o)
- OBJS = $(patsubst %.c, $(BUILD_DIR)/%.o, $(notdir $(SRCS)))
- #头文件
- DEPS = $(wildcard $(INC_DIR)/*.h)
- #指定头文件的路径
- CFLAGS = $(patsubst %, -I%, $(INC_DIR))
- #根据输入的ARCH变量来选择编译器
- #ARCH=x86,使用gcc
- #ARCH=arm,使用arm-gcc
- ifeq ($(ARCH),x86)
- CC = gcc
- else
- CC = arm-linux-gnueabihf-gcc
- endif
- #目标文件
- $(BUILD_DIR)/$(TARGET): $(OBJS)
- $(CC) -o $@ $^ $(CFLAGS)
- #*.o文件的生成规则
- $(BUILD_DIR)/%.o: $(SRC_DIR)/%.c $(DEPS)
- #创建一个编译目录,用于存放过程文件
- #命令前带“@”,表示不在终端上输出
- @mkdir -p $(BUILD_DIR)
- $(CC) -c -o $@ $< $(CFLAGS)
- #伪目标
- .PHONY: clean cleanall
- #按架构删除
- clean:
- rm -rf $(BUILD_DIR)
- #全部删除
- cleanall:
- rm -rf build_x86 build_arm
复制代码 ch7_同一项目中有多个Makefile文件
包罗其他makefile文件
利用include指令可以读入其他makefile文件的内容,效果就如同在include的位置用对应的文件内容更换一样。
- include mkf1 mkf2 # 可以引入多个文件,用空格隔开
- include *.mk # 可以用通配符,表示引入所有以.mk结尾的文件
复制代码 如果找不到对应文件,则会报错,如果要忽略错误,可以在include前加-
应用实例:自动天生依靠
- objs = block.o command.o input.o main.o scene.o test.o
- sudoku: $(objs)
- g++ $(objs) -o sudoku
- include $(objs:%.o=%.d)
- %.d: %.cpp
- @-rm $@
- $(CXX) -MM $< > [email protected]
- @sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < [email protected] > $@
- @-rm [email protected]
- %.o : %.cpp
- g++ -c $<
- @echo $^
复制代码 嵌套make
如果将一个大项目分为很多小项目,则可以利用嵌套(递归)利用make。具体做法为,写一个总的Makefile,然后在每个子项目中都写一个Makefile,在总Makefile中举行调用。
例如,可以把sudoku项目中除main.cpp,test.cpp外的其他cpp存为一个子项目,编译为一个库文件,main.cpp test.cpp为另一个子项目,编译为.o然后链接库文件成可实行文件:
库文件Makefile
- vpath %.h ../include
- CXXFLAGS += -I../include -fexec-charset=GBK -finput-charset=UTF-8
- cpps := $(wildcard *.cpp)
- objs := $(cpps:%.cpp=%.o)
- libsudoku.a: $(objs)
- ar rcs $@ $^
- $(objs): %.o : %.cpp %.h
复制代码 main.cpp test.cpp的Makefile
- CXXFLAGS += -I../include -fexec-charset=GBK -finput-charset=UTF-8
- vpath %.h ../include
- vpath %.a ../lib
- ../sudoku: main.o test.o -lsudoku
- $(CXX) -o $@ $^
复制代码 总的Makefile
- .PHONY: all clean
- all: subsrc
- subsrc: sublib
- $(MAKE) -C src
- sublib:
- $(MAKE) -C lib
- clean:
- -rm *.exe src/*.o lib/*.o lib/*.a
复制代码 此中
这一指令会自动进入subdir文件夹然后实行make。
可以通过export指令向子项目标make转达变量。
- export var # 传递var
- export # 传递所有变量
- unexport # 取消传递
复制代码 |