make工具
make工具:找出修改过的文件,根据依赖关系,找出受影响的相关文件,然后单独编译这些文件
Makefile文件:记录依赖关系和编译规则
Makefile三要素:目标、依赖、命令
目标:依赖文件或者其他文件
< tab > 命令1
< tab > 命令2
< tab > 命令3
使用make命令之后,到当前目录寻找Makefile文件,然后找到第一个要执行的目标,由于targeta依赖于targetb和targetc,所以先去执行b、c目标对应的命令,等到targeta依赖目标命令执行完之后才能执行targeta
make + 目标:直接执行某一个目标的命令
make targetb或者make targetc
伪目标
.PHONY:指定伪目标
如果在我当前路径下有了targetc文件,如果这个文件没有改动过,执行make targetc就不会更新,不会去执行Makefile里的命令
若想要去执行targetc目标,需要将targetc设置成伪目标
在Makefile开头加上.PHONY:targetc
Makefile 变量、模式匹配
变量
=:延时赋值
#当变量被调用时才会被赋值
:= #立即赋值
?= #空赋值
#当变量为空的时候赋值才有效
+= #追加赋值
#真追加,不是求和,字符串追加
截图错了,不是{ }, 是()
自动化变量
$< #Makefile第一个依赖文件,
$^ #Makefile全部的依赖文件
$@ #目标文件
模式匹配
%:匹配任意多个非空字符,此时的文件名叫Makefile_1
运行命令:如图所示
这有个知识点:make后直接加要执行的目标文件的前提是,必须是Makefile文件(默认为Makefile),放把文件名改了之后,就要先加上Makefile文件在写要执行的目标文件,通配符做目标,结果打印目标,传什么打什么。
项目编译方法
将所有的.c文件写好 -o指定编译后可执行文件的名称
引入Makefile对工程管理
首先目标文件mp3目标文件就是mp3
mp3这个文件依赖于main.c和MP3.c 文件
根据Makefile三要素 :目标依赖和命令
最后写命令:生成MP3文件的命令
但是这种写法让目标文件依赖于.c 文件,每次所有的.c文件都会重新编译,应该让他们依赖于一个重定向文件.o文件,再次编译生成5个文件,main.c mp3.c main.o mp3.o mp3,MP3不直接依赖于main.c和MP3.c,从一个整体拆分成多个部分。
当更改main.c时,只需要重现编译main.c文件即可,MP3.c则不受影响,优化了编译的时间
优化工程Makefile文件
另外还可以用通配符进一步优化,提高可移植性
或者
Makefile条件分支
ifeq(var1, var2) #如果1 == 2 就去执行它下边的语句
……
else
……
endif
还有ifneq(var1, var2), 和上边用法相反
以上面的Makefile举例:
ARCH ?= x86
ifeq ($(ARCH),x86) #注意看!!!ifeq后边有空格,没有不对
cc=gcc
else
cc=arm-linux-gnueabihf-gcc
endif
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
%.o:%.c
# $(cc) -c %.c -o %.o
$(cc) -c $< -o $@
.PHONY:clean
clean:
rm mp3 *.o *.o
在使用make工具时,直接输入make则,空赋值语句成立,使用GCC编译工具
如果写成 make ARCH=123。此时else成立。使用ARM-GCC工具链
常用函数
- patsubst
- notdir
- wildcard
- foreach
使用Makefile_function验证举例:
- patsubst
$(patsubst 模式1, 模式2, 文本) ——模式替换函数
#举例:
$(patsubst %.c, %.o, x.c.c bar.c)
#最后的文本部分是两个.c结尾的文件名,当和模式1匹配的时候
#发现模式1也是.c结尾的文件,这两个单词都是能匹配这个模式的,当匹配上之后,这两个单词的模式就会被替换成新的模式——模式2
#最后就变成了x.c.o bar.o
.PHONY:all
all:
echo "$(patsubst %.c, %.o, x.c.c bar.c)"
- notdir
$(notdir src/foot.c abc.c) ——去掉目录,只保留文件名
.PHONY:all
all:
echo "$(notdir src/foot.c abc.c)"
- wildcard
$(wildcard *.c) ——在当前目录下找到所有.c问后缀的文件名
.PHONY:all
all:
echo "$(wildcard *.c)"
- foreach
dirs := a b c d
$( foreach var, $(dirs), $(wildcard $(var)/*) ) ——遍历函数
#首先先看中间$(dir),每次取值赋给var
#var参与第三个表达式的运算
#总体是说首先取到a,var = a, 然后列出a目录下所有的文件
#然后是取到b……
.PHONY:all
dirs := a b c
all:
echo "$(foreach var, $(dirs), $(wildcard $(var) /*))"
完善之前的Makefile项目
将每个模块都放在自己的文件夹里,编译生成的文件放在一起
ARCH ?= x86
ifeq ($(ARCH),x86)
cc=gcc
else
cc=arm-linux-gnueabihf-gcc
endif
TARGET=mp3
#定义一个生成目录,用来存放生成文件的
BUILD_DIR=build
#源目录存放每个子模块文件夹的
SRC_DIR=moudle1 moudle2
#存放c文件的,得到c文件的所有路径
SOURCE=$(foreach var, $(SRC_DIR), $(wildcard $(var)/*.c))
#首先去掉了路径,然后模式匹配,所有的.c换成.o中间文件
OBJS=$(patsubst %.c, $(BUILD_DIR)/%.o, $(notdir $(SOURCE)))
#指定源文件的路径
VPATH=$(SRC_DIR)#在当前目录找不到文件的基础上,去指定目录去找
#mp3生成文件存放到build,包括最后所有的中间文件也要放到build里面
$(BUILD_DIR)/$(TARGET):$(OBJS)
$(cc) $^ -o $@
$(BUILD_DIR)/%.o:%.c | creat_build
$(cc) -c $< -o $@
.PHONY:clean creat_build
clean:
-rm -r $(BUILD_DIR)
creat_build:
mkdir -p $(BUILD_DIR)
添加头文件
首先Linux下的编译器会自动检测依赖文件的改动,如果变化,执行make命令,会重新去编译对应的文件,对于.h文件,对于调用它的c文件同样重要,它的更改也需要去做重新编译,所以在依赖.c文件的基础上,也要依赖.h文件,做如下更改:新建include文件夹,用来存放.h文件
ARCH ?= x86
ifeq ($(ARCH),x86)
cc=gcc
else
cc=arm-linux-gnueabihf-gcc
endif
TARGET=mp3
#定义一个生成目录,用来存放生成文件的
BUILD_DIR=build
#定义头文件路径
INC_DIR = include
#指定头文件路径 -I选项,可以使用模式匹配替换掉
CFLAGS = $(patsubst %, -I %, $(INC_DIR))
#遍历获取所有的.h文件
INCLUDES = $(foreach var, $(INC_DIR), $(wildcard $(var)/*.h))
#源目录存放每个子模块文件夹的
SRC_DIR=moudle1 moudle2
#存放c文件的,得到c文件的所有路径
SOURCE=$(foreach var, $(SRC_DIR), $(wildcard $(var)/*.c))
#首先去掉了路径,然后模式匹配,所有的.c换成.o中间文件
OBJS=$(patsubst %.c, $(BUILD_DIR)/%.o, $(notdir $(SOURCE)))
#指定源文件的路径
VPATH=$(SRC_DIR)#在当前目录找不到文件的基础上,去指定目录去找
#mp3生成文件存放到build,包括最后所有的中间文件也要放到build里面
$(BUILD_DIR)/$(TARGET):$(OBJS)
$(cc) $^ -o $@
#不仅依赖.c文件还要依赖.h 文件,有变化就会重新编译
$(BUILD_DIR)/%.o:%.c $(INCLUDES) | creat_build
$(cc) -c $< -o $@ $(CFLAGS)
.PHONY:clean creat_build
clean:
-rm -r $(BUILD_DIR)
creat_build:
mkdir -p $(BUILD_DIR)