makefile总结

[复制链接]
发表于 2025-9-22 03:05:52 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
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字符
实行演示

  1. targeta:targetb targetc
  2.         echo "targeta"
  3. targetb:
  4.         echo "targetb"
  5. targetc:
  6.         echo "tarfetc"
  7.        
  8. mzj@ubuntu:~/Makefile$ make
  9. echo "targetb"
  10. targetb
  11. echo "targetc"
  12. targetc
  13. echo "targeta"
  14. targeta
  15. mzj@ubuntu:~/Makefile$ make targetb
  16. echo "targetb"
  17. targetb
  18. mzj@ubuntu:~/Makefile$ make targetc
  19. echo "targetc"
  20. targetc
复制代码

.PHONY:目标

必要注意当文件路径下有跟目标雷同的文件名时,想要make不被影响,应该在Makefile文件的开头加上.PHONY:目标名
缘故原由:


  • .PHONY反面跟的目标都被称为伪目标
  • 也就是说我们 make 命令反面跟的参数如果出如今.PHONY 界说的伪目标中,那就直接在Makefile中就实行伪目标的依靠和命令。不管Makefile同级目次下是否有该伪目标同名的文件,纵然有也不会产生辩论。
  • 另一个就是进步实行makefile时的服从。
  1. .PHONY:targetb
  2. targeta:targetb targetc
  3.         echo "targeta"
  4. targetb:
  5.         echo "targetb"
  6. targetc:
  7.         echo "targetc"
  8. mzj@ubuntu:~/Makefile$ ls
  9. Makefile  targetb  targetc
  10. mzj@ubuntu:~/Makefile$ make targetc
  11. make: “targetc”已是最新。
  12. mzj@ubuntu:~/Makefile$ make targetb
  13. echo "targetb"
  14. targetb
复制代码
ch2_Makefile变量

体系变量

  1. Makefile_test文件
  2. .PHONY:all
  3. all:
  4.         echo "$(CC)"
  5.         echo "$(AS)"
  6.         echo "$(MAKE)"
  7.         echo "$(MAKE)"
  8. mzj@ubuntu:~/Makefile$ make -f Makefile_test
  9. echo "cc"
  10. cc
  11. echo "as"
  12. as
  13. echo "make"
  14. make
  15. echo "make"
  16. make
复制代码
  这里的make -f 指定Makefile文件名字,针对这个这个文件,大概有很多Makefile_test
  自界说变量



  • =:耽误赋值,实行到这个变量时才赋值
    1. Makefile_test1文件
    2. A = 123
    3. B = $(A)
    4. A = 456
    5. .PHONY:all
    6. all:
    7.         echo "$(B)"
    8.        
    9. mzj@ubuntu:~/Makefile$ make -f Makefile_test1
    10. Makefile_test1:7: *** 遗漏分隔符 (null)。 停止。
    11. !!!!!!!这里是tap建被vscode换成4个空格了,需要设置!!!!!!!
    12. #这里是执行到$(B)时,B才执行B = $(A) 456
    13. mzj@ubuntu:~/Makefile$ make -f Makefile_test1
    14. echo "456"
    15. 456
    复制代码
  • :=:为立即赋值,与通例思路一样
    1. A = 123
    2. B := $(A)
    3. A = 456
    4. .PHONY:all
    5. all:
    6.         echo "$(B)"
    7. mzj@ubuntu:~/Makefile$ make -f Makefile_test1
    8. echo "123"
    9. 123
    复制代码
  • ?=:空赋值,只有变量为空时,赋值才有用,反面再赋值没有用,像是一次初始化
    1. A ?= 123
    2. A ?= 456
    3. .PHONY:all
    4. all:
    5.         echo "$(A)"
    6.        
    7. mzj@ubuntu:~/Makefile$ make -f Makefile_test1
    8. echo "123"
    9. 123
    复制代码
  • +=:追加赋值,不覆盖前面的,在前面的值底子上在尾部继承追加一些值
    1. A ?= 123
    2. A += 456
    3. .PHONY:all
    4. all:
    5.         echo "$(A)"
    6.        
    7.        
    8. mzj@ubuntu:~/Makefile$ make -f Makefile_test1
    9. echo "123 456"
    10. 123 456
    复制代码
自动化变量



  • $<:第一个依靠文件first_tar
  • $^:全部的依靠文件first_tar second_tar
  • $@:目标all
  1. all:first_tar second_tar
  2.         echo "$<"
  3.         echo "$^"
  4.         echo "$@"
  5. first_tar:
  6. second_tar:
  7. mzj@ubuntu:~/Makefile$ make -f Makefile_test1
  8. echo "first_tar"
  9. first_tar
  10. echo "first_tar second_tar"
  11. first_tar second_tar
  12. echo "all"
  13. all
复制代码
Makefile中另有别的自动化变量,此处仅列出方便以后利用到的时间举行查阅,见下表。
符号意义$@匹配目标文件$%与                                             @                               类                               似                               ,                               但                                      @雷同,但                        @雷同,但%仅匹配“库”范例的目标文件$<依靠中的第一个目标文件$^全部的依靠目标,如果依靠中有重复的,只保存一份$+全部的依靠目标,纵然依靠中有重复的也原样保存$?全部比目标要新的依靠目标利用变量编译C文件

  1. CC = gcc
  2. TARGET=mp3
  3. OBJS=main.o mp3.o
  4. $(TARGET):$(OBJS)
  5.         $(CC) $^ -o $@
  6. main.o:
  7.         $(CC) -c main.c -o main.o
  8. mp3.o:
  9.         $(CC) -c mp3.c -o mp3.o
  10. .PHONY:clean
  11. clean:
  12.         rm $(TARGET)
复制代码
ch3_Makefile模式匹配和默认规则



  • %:匹配恣意多个非空字符
    1. .PHONY:all
    2. %:
    3.         @echo "$@"
    4.         @echo "$^"
    5.         @echo "$<"
    6. # @ 符号的作用是抑制命令本身的输出,只显示命令执行的结果。
    7. mzj@ubuntu:~/Makefile$ make
    8. make: *** 无目标。 停止。
    9. mzj@ubuntu:~/Makefile$ make 123
    10. 123
    复制代码
          @符号的作用是克制命令本身的输出,只表现命令实行的效果。
        命令行输入给了%这个位置
        再看这个模板:这里的.c文件到.o文件的情势根本上一样的,就可以利用模式匹配%去取代
    1. CC = gcc
    2. TARGET=mp3
    3. OBJS=main.o mp3.o
    4. $(TARGET):$(OBJS)
    5.         $(CC) $^ -o $@
    6. main.o:main.c
    7.         $(CC) -c main.c -o main.o
    8. mp3.o:mp3.c
    9.         $(CC) -c mp3.c -o mp3.o
    10.                         |
    11.                         |
    12.                         |
    13. %.o:%.c
    14.         $(CC) -c $< -o $@
    15. .PHONY:clean
    16. clean:
    17.         rm $(TARGET)
    复制代码
          这里的%.o:包罗了全部的.o文件
  • 默认规则:.o文件默认利用.c文件举行编译,以是可以直接不管.o文件,下面即可
    1. CC = gcc
    2. TARGET=mp3
    3. OBJS=main.o mp3.o
    4. $(TARGET):$(OBJS)
    5.         $(CC) $^ -o $@
    6. .PHONY:clean
    7. clean:
    8.         rm $(TARGET) *.o
    复制代码
  • 默认规则另有很多:后续增补
ch4_Makefile条件分支

  1. ifeq(var1,var2)
  2.         ...
  3. else
  4.         ...
  5. endif
  6. ifneq(var1,var2)
  7.         ...
  8. else
  9.         ...
  10. endif
复制代码
  ifeq:两个条件相当
  ifneq:两个条件不相当
  内里必要加上
  搭配?=举行举例
  1. ARCH ?= x86
  2. ifeq($(ARCH),x86)
  3.         CC = gcc
  4. else
  5.         CC = arm-linux-gnueabihf-gcc
  6. endif
  7. TARGET=mp3
  8. OBJS=main.o mp3.o
  9. $(TARGET):$(OBJS)
  10.         $(CC) $^ -o $@
  11. .PHONY:clean
  12. clean:
  13.         rm $(TARGET) *.o
  14.        
  15.        
  16. mzj@ubuntu:~/Makefile$ make
  17. 那么,CC = gcc
  18. mzj@ubuntu:~/Makefile$ make ARCH=arm
  19. 由于空赋值,命令行中就给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))
  • 第一种看起来,更直观,更简便
  1. $(patsubst %.c,%.o,x.c.c bar.c)
复制代码
把字串“x.c.c bar.c”中以.c 结尾的单词更换成以.o 结尾的字符。函数的返回效果是“x.c.o bar.o”
  1. $(patsubst %.c, build_dir/%.o, hello_main.c )
  2. #函数的输出为:
  3. build_dir/hello_main.o
  4. #执行如下函数
  5. $(patsubst %.c, build_dir/%.o, hello_main.xxx )
  6. #由于hello_main.xxx不符合匹配规则"%.c",所以函数没有输出
复制代码
例如:
我们存在一个代表全部.o 文件的变量。界说为
  1. objects = foo.o bar.o baz.o
复制代码
为了得到这些.o 文件所对应的.c 源文件。我们可以利用以下两种方式的恣意一个:
  1. $(objects:.o=.c)
  2. $(patsubst %.o,%.c,$(objects))
复制代码
notdir

   作用,把路径去了,只保存文件名
  示例:
  1. $(notdir src/foo.c hacks)
  2. foo.c hacks
复制代码
返回值为:这里的src/被去掉了,只留下foo.c hacks。
wildcard

   作用:列出当前目次下全部符合模式“PATTERN”格式的文件名。
  “PATTERN”利用shell可辨认的通配符,包括“?”(单字符)、“*”(多字符)等。
  示例:
  1. $(wildcard *.c)
  2. #在sources目录下有hello_func.c、hello_main.c、test.c文件
  3. #执行如下函数
  4. $(wildcard sources/*.c)
  5. #函数的输出为:
  6. sources/hello_func.c sources/hello_main.c sources/test.c
复制代码
返回值为当前目次下全部.c源文件列表
foreach

我们来看一个例子,实现了将变量“files”赋值为目次“dirs”下全部文件列表:
  1. dirs := a b c d
  2. files := $(foreach dir,$(dirs),$(wildcard $(dir)/*))
复制代码
例子中,表达式第一次实行时将睁开为
$(wildcard a/*);第二次实行时将睁开为$(wildcard b/*);第三次睁开为$(wildcard c/*);….;以此类推。以是此函数所实现的功能就和一下语句等价:
  1. files := $(wildcard a/* b/* c/* d/*)
复制代码
ch6_多级布局工程的Makefile

接下来我们利用上面三个函数修改我们的Makefile,以顺应包罗多级目次的工程,修改后的内容如下所示。

  1. #定义变量
  2. #ARCH默认为x86,使用gcc编译器,
  3. #否则使用arm编译器
  4. ARCH ?= x86
  5. TARGET = hello_main
  6. #存放中间文件的路径
  7. BUILD_DIR = build_$(ARCH)
  8. #存放源文件的文件夹
  9. SRC_DIR = sources
  10. #存放头文件的文件夹
  11. INC_DIR = includes .
  12. #源文件
  13. SRCS = $(wildcard $(SRC_DIR)/*.c)
  14. #目标文件(*.o)
  15. OBJS = $(patsubst %.c, $(BUILD_DIR)/%.o, $(notdir $(SRCS)))
  16. #头文件
  17. DEPS = $(wildcard $(INC_DIR)/*.h)
  18. #指定头文件的路径
  19. CFLAGS = $(patsubst %, -I%, $(INC_DIR))
  20. #根据输入的ARCH变量来选择编译器
  21. #ARCH=x86,使用gcc
  22. #ARCH=arm,使用arm-gcc
  23. ifeq ($(ARCH),x86)
  24. CC = gcc
  25. else
  26. CC = arm-linux-gnueabihf-gcc
  27. endif
  28. #目标文件
  29. $(BUILD_DIR)/$(TARGET): $(OBJS)
  30.    $(CC) -o $@ $^ $(CFLAGS)
  31. #*.o文件的生成规则
  32. $(BUILD_DIR)/%.o: $(SRC_DIR)/%.c $(DEPS)
  33. #创建一个编译目录,用于存放过程文件
  34. #命令前带“@”,表示不在终端上输出
  35. @mkdir -p $(BUILD_DIR)
  36. $(CC) -c -o $@ $< $(CFLAGS)
  37. #伪目标
  38. .PHONY: clean cleanall
  39. #按架构删除
  40. clean:
  41.    rm -rf $(BUILD_DIR)
  42. #全部删除
  43. cleanall:
  44.    rm -rf build_x86 build_arm
复制代码
ch7_同一项目中有多个Makefile文件

包罗其他makefile文件

利用include指令可以读入其他makefile文件的内容,效果就如同在include的位置用对应的文件内容更换一样。
  1. include mkf1 mkf2 # 可以引入多个文件,用空格隔开
  2. include *.mk    # 可以用通配符,表示引入所有以.mk结尾的文件
复制代码
如果找不到对应文件,则会报错,如果要忽略错误,可以在include前加-
  1. -include mkf1 mkf2
复制代码
应用实例:自动天生依靠
  1. objs = block.o command.o input.o main.o scene.o test.o
  2. sudoku: $(objs)
  3.     g++ $(objs) -o sudoku
  4. include $(objs:%.o=%.d)
  5. %.d: %.cpp
  6.     @-rm $@
  7.     $(CXX) -MM  $< > [email protected]
  8.     @sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < [email protected] > $@
  9.     @-rm [email protected]
  10. %.o : %.cpp
  11.     g++ -c $<
  12.     @echo $^
复制代码
嵌套make

如果将一个大项目分为很多小项目,则可以利用嵌套(递归)利用make。具体做法为,写一个总的Makefile,然后在每个子项目中都写一个Makefile,在总Makefile中举行调用。
例如,可以把sudoku项目中除main.cpp,test.cpp外的其他cpp存为一个子项目,编译为一个库文件,main.cpp test.cpp为另一个子项目,编译为.o然后链接库文件成可实行文件:
库文件Makefile
  1. vpath %.h ../include
  2. CXXFLAGS += -I../include -fexec-charset=GBK -finput-charset=UTF-8
  3. cpps := $(wildcard *.cpp)
  4. objs := $(cpps:%.cpp=%.o)
  5. libsudoku.a: $(objs)
  6.     ar rcs $@ $^
  7. $(objs): %.o : %.cpp %.h
复制代码
main.cpp test.cpp的Makefile
  1. CXXFLAGS += -I../include -fexec-charset=GBK -finput-charset=UTF-8
  2. vpath %.h ../include
  3. vpath %.a ../lib
  4. ../sudoku: main.o test.o -lsudoku
  5.     $(CXX) -o $@ $^
复制代码
总的Makefile
  1. .PHONY: all clean
  2. all: subsrc
  3. subsrc: sublib
  4.     $(MAKE) -C src
  5. sublib:
  6.     $(MAKE) -C lib
  7. clean:
  8.     -rm *.exe src/*.o lib/*.o lib/*.a
复制代码
此中
  1. $(MAKE) -C subdir
复制代码
这一指令会自动进入subdir文件夹然后实行make。
可以通过export指令向子项目标make转达变量。
  1. export var  # 传递var
  2. export         # 传递所有变量
  3. unexport    # 取消传递
复制代码
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表