当前位置: 首页 > news >正文

makefile总结

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 targetcecho "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 targetcecho "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:allall: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_tarecho "$<"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:cleanclean: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:cleanclean:rm $(TARGET)
    

    这里的%.o:包含了所有的.o文件

  • 默认规则:.o文件默认使用.c文件进行编译,所以可以直接不管.o文件,下面即可

    CC = gcc
    TARGET=mp3
    OBJS=main.o mp3.o$(TARGET):$(OBJS)$(CC) $^ -o $@.PHONY:cleanclean:rm $(TARGET) *.o
    
  • 默认规则还有很多:后续补充

ch4_Makefile条件分支

ifeq(var1,var2)...
else...
endififneq(var1,var2)...
else...
endif

ifeq:两个条件相等

ifneq:两个条件不相等

里面需要加上

搭配?=进行举例

ARCH ?= x86
ifeq($(ARCH),x86)CC = gcc
elseCC = arm-linux-gnueabihf-gcc
endif
TARGET=mp3
OBJS=main.o mp3.o$(TARGET):$(OBJS)$(CC) $^ -o $@.PHONY:cleanclean:rm $(TARGET) *.omzj@ubuntu:~/Makefile$ make 
那么,CC = gcc
mzj@ubuntu:~/Makefile$ make ARCH=arm
由于空赋值,命令行中就给ARCH赋值了,CC = arm-linux-gnueabihf-gcc

ch5_Makefile常用函数

很多看官方手册

这里挑四个常用的

  • patsubst

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前加-

-include mkf1 mkf2

应用实例:自动生成依赖

objs = block.o command.o input.o main.o scene.o test.osudoku: $(objs)g++ $(objs) -o sudokuinclude $(objs:%.o=%.d)%.d: %.cpp@-rm $@$(CXX) -MM  $< > $@.temp@sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.temp > $@@-rm $@.temp%.o : %.cppg++ -c $<@echo $^

嵌套make

如果将一个大项目分为许多小项目,则可以使用嵌套(递归)使用make。具体做法为,写一个总的Makefile,然后在每个子项目中都写一个Makefile,在总Makefile中进行调用。

例如,可以把sudoku项目中除main.cpp,test.cpp外的其他cpp存为一个子项目,编译为一个库文件,main.cpp test.cpp为另一个子项目,编译为.o然后链接库文件成可执行文件:

库文件Makefile

vpath %.h ../includeCXXFLAGS += -I../include -fexec-charset=GBK -finput-charset=UTF-8cpps := $(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 cleanall: subsrcsubsrc: sublib$(MAKE) -C srcsublib:$(MAKE) -C libclean:-rm *.exe src/*.o lib/*.o lib/*.a 

其中

$(MAKE) -C subdir

这一指令会自动进入subdir文件夹然后执行make。

可以通过export指令向子项目的make传递变量。

export var  # 传递var
export         # 传递所有变量
unexport    # 取消传递

相关文章:

  • 【C语言干货】回调函数
  • 优先队列、堆笔记(算法第四版)
  • Android完整开发环境搭建/Studio安装/NDK/本地Gradle下载配置/创建AVD/运行一个Android项目/常用插件
  • APP、游戏、网站被黑客攻击了怎么解决?
  • 机器学习之三:归纳学习
  • 通俗易懂一文讲透什么是 MCP?
  • EMQX​​ 默认安装后常用端口及其功能
  • Opnelayers:向某个方向平移指定的距离
  • C++初阶-模板初阶
  • 业务中台与数据中台:企业数字化转型的核心引擎
  • 【源码】【Java并发】【ConcurrentHashMap】适合中学体质的ConcurrentHashMap
  • 全球城市范围30米分辨率土地覆盖数据(1985-2020)
  • MCP协议:AI生态的统一标准
  • ppt章节页怎么做好看?ppt章节页模板
  • 京东商品详情数据爬取难度分析与解决方案
  • 线上线程池的调优与监控 - Java架构师面试实战
  • C++ 基础内容入门
  • 服务器ubuntu镜像磁盘空间怎么管理
  • Java学习--HashMap
  • Nacos简介—4.Nacos架构和原理二
  • 央行副行长谈美债和美元波动:单一市场、单一资产的变动,对外储影响总体有限
  • 夜读丨囿于厨房与爱
  • 神二十成功对接空间站
  • “70后”女博士张姿卸任国家国防科技工业局副局长
  • 王毅同伊朗外长阿拉格齐会谈
  • 中国空间站已在轨实施了200余项科学与应用项目