Makefile基础

什么是Makefile?

  • 一个工程中的源文件不计其数,其按类型、功能、模块分别放在若干个目录中,Makefile定义了一系列规则来制定,哪些文件需要先编译,哪些文件需要重新编译,如何进行链接等操作。
  • Makefile就是"自动化编译",告诉make命令如何编译和链接。
  • Makefile也是make命令的配置脚本,默认情况下GUN make会在当前目录下寻找文件,按照优先级依次寻找 GNUmakefile -> makefile -> Makefile。也可使用 make -f/-file “文件名" 手动指定Makefile文件

Makefile文件内容

  • 显示规则
    • 如何生成一个或多个目标文件
  • 隐晦规则
    • 依赖于Makefile自动推导,可以简略书写Makefile
  • 变量定义
    • 可以定义变量 一般为String,类似于“宏”,当Makefile被执行时,变量会扩展到相应的引用位置上
  • 文件指示
    • 一个Makefile引用另外一个Makefile。类似于C的include
    • 根据情况,指定Makefile有效部分。类似于C的预编译
    • 定义多行命令
  • 注释
    • 只有行注释“#”

Makefile的规则

target ... : prerequisites ...
    command
或
target ... : prerequisites ... ; command
  • target: 目标文件。可以是Object File,也可以是执行文件,还可以是标签(Label)。
    • 如果是多文件用空格隔开。
    • 也可使用通配符
  • prerequisites:依赖文件,即要生成那个target所需要的文件或其他target
  • command:make需要执行的命令
    如果命令太长可以使用“\”作为换行符进行换行

Makefile的规则就是告诉make 文件的依赖关系以及如何生成目标文件;

target的文件依赖prerequisite文件,生成规则在command中

如果prerequisite有一个以上的文件比target新,target会被认为是过时的需要重新生成,command将被执行,从而生成新的target。
示例

# 当前目录存在main.c、tool.c、tool.h三个文件
# 要生成一个main目标文件依赖于main.o tool.o
main: main.o tool.o
# 生成目标的命令
    gcc main.o tool.o -o main
# .PHONY:显式的指明clean是一个“伪目标”
.PHONY: clean
# clean: 标签,不会生成“clean”文件,这样的target称之为“伪目标”,伪目标的名字不能和文件名重复。clean一般放在文件最后
clean:
# -rm “-” 表示如果某些文件出现问题 跳过
    -rm main *.o

clean伪目标需要make显式调用make clean

makefile执行效果如下

makefile执行效果

Makefile是如何工作的

默认方式下,输入make命令后:

  • make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
  • 如果找到,它会找文件中第一个目标文件(target),并把这个target作为最终目标文件,如前面示例中的“main”。
  • 如果main文件不存在,或main所依赖的.o的修改时间要比main文件新,那么它会执行后面所定义的命令来生成main文件
  • 如果main所依赖的.o文件也存在,那么make会在当前文件中找目标为.o文件的依赖性,若找到则根据规则生成.o文件。
  • make再用.o文件声明make的终极任务,也就是可执行文件“main”。

make会层层找文件依赖关系,直到最终编译出第一个目标文件,如果在找的过程出现错误,make会退出并抛出错误。Make只管文件的依赖性,编译错误不会理会

Makef中变量的使用

​ 如果工程需要加入一个新的“.o”文件 Makefile需要修改多处,为了易维护,在Makefile中我们可以使用变量。比如,我们声明一个变量为objects,这样就可以方便的在Makefile中以$(objects)的方式使用这个变量了。

我们修改一下刚刚的Makefile文件

objects = main.o tool.o
main: $(objects)
    gcc $(objects) -o main
.PHONY: clean
clean:
    -rm main $(object)

引用其他的Makefile

在实际开发中我们会按类型将源文件会分成不同的模块,每个模块有一个单独的Makefile文件,那么编译时就需要引入其他模块的Makefile,在Makefile中可以使用include关键字引入其他模块

# 语法格式
include 
# 如果文件找不到,而你希望make时不理会那些无法读取的文件而继续执行,可以在include前加"-"
-include 

环境变量 MAKEFILES

​ 如果当前环境中定义了环境变量MAKEFILES,make会把这个变量中的值做一个类似于include的动作。这个变量中的值是其它的Makefile,用空格分隔。只是,它和include不同的是,从这个环境变量中引入的Makefile的“目标”不会起作用,如果环境变量中定义的文件发现错误,make也会不理。但是建议不要使用这个环境变量,因为只要这个变量一被定义,那么当你使用make时,所有的Makefile都会受到它的影响。

​ 也许有时候Makefile出现了奇怪的事,可以查看当前环境中有没有定义这个变量。

Makefile预定义变量

Makefile预定义变量

Makefile自动变量

Makefile自动变量

函数

# 不带参数
define FUNC
$(info echo "hello")
endef

# 调用 FUNC函数 输出hello
$(call FUNC)

# 带参数
define FUNC1
$(info echo $(1) $(2))  
endef

# 调用FUNC1函数 输出hello world
$(call FUNC1,hello,world)

make的工作流程

  1. 读入所有的Makefile。
  2. 读入被include的其他Makefile。
  3. 初始化文件中的变量。
  4. 推导隐晦规则,并分析所有规则。
  5. 为所有的目标文件创建依赖关系链。
  6. 根据依赖关系,决定哪些目标要重新生成。
  7. 执行生成命令。

上面的流程分两个阶段:1-5 为第一阶段、6、7为第二阶段

在第一阶段中如果定义的变量被使用了,make会把变量展开在使用的位置,但不完全展开,如果变量出现在依赖关系的规则中,只有依赖被使用的时候,变量才会被展开

你可能感兴趣的:(Makefile基础)