【Linux篇】 在Linux中如何自动化构建项目之 Makefile的使用

Makefile一文详解

  • 一 . 什么是Makefile
    • Makefile的文件名
    • 为什么要学会使用Makefile
  • 二 . make指令的运行规则
  • 三 . 如何编写一个简单的Makefile
    • 1> Makefile文件的基本框架
    • 2> 一个简单示例
    • 3> 文件修改时间的比较
    • 4> 如何一键删除自动化编译所形成的多个目标文件
    • 5> 伪目标的声明
    • 6> 稍微复杂一点点的示例
    • 7> Makefile中的隐晦规则 -- 自动化推导
    • 8> Makefile中变量的定义
    • 9> Makefile中的注释
    • 10> Makefile中的自动化变量

一 . 什么是Makefile

Makefile的文件名

  • 默认有以下三种:
    • GNUmakefile
    • Makefile
    • makefile
  • 当然你还可以使用指定Makefile的文件名, 例如:
    • Make.Linux (非默认Makefile文件名)
    • Make.type(非默认Makefile文件名)
  • 不过当你设置的Makefile文件的名称不为默认名称时, 运行相应的make命令需要携带参数指明具体的文件名, 例如
    • make -f Make.Linux // 编译Make.Linux中的第一个目标文件
    • make -f Make.Linux clean // 执行Make.Linux中的 clean

为什么要学会使用Makefile

     Makefile它是一个文件, 文件中的内容描述了如何在Linux系统下 
C/C++工程的编译规则
     Makefile为我们所带来的好处便是  --- 自动化编译, 当我们为大
型项目编写好对应的makefile文件后, 我们只需要一个指令 make, 整个
工程就会进行自动化编译, 最终按照我们的预期生成最终目标文件
      Makefile文件定义了一系列规则, 指明了文件的编译顺序, 文件与
文件之间的依赖关系以及是否需要重新编译等

总而言之, 我们使用makefile的原因便是, 它可以极大的简化文件的编译过程。

二 . make指令的运行规则

当我们为工程编写了一个Makefile文件, 然后通过make指令便可以进行工程的自动化编译 那么make运行的规则是什么呢?

  • 如果这个工程未被编译过, 那么我们编译并连接makefile中指明的所有C/Cpp文件
  • 如果这个工程已被编译过, 但是某几个C文件被修改, 那么我们只编译被修改过的文件, 并链接目标程序
  • 如果该工程的头文件被修改, 那么我们需要编译所有引用了该头文件的C/Cpp文件,并链接目标程序。

三 . 如何编写一个简单的Makefile

    好的,现在我们知道了make指令的运行规则。 那么我们要如何编写一个
Makefile文件呢? 
    请看下文: 

1> Makefile文件的基本框架

target : prerequisites   
    command

// target -- 目标文件, 是我们在整个自动化编译过程中想要得到的文件
// prerequisites -- 先决条件 , 也就是生成target目标文件的前提条件,在这里我们把它叫做依赖文件, 依赖文件是生成目标文件的前提条件。
// command -- 指令, 它是可以在shell命令行中运行的linux命令, 在这里我们将其称为 依赖关系 。 也就是目标文件与依赖文件之间的依赖关系。 它描述了如何通过依赖文件去生成目标文件 
// 另外的 command指令前面的空白是 Tab键形成的(必须是Tab, 这是规定)

// 简单来说 , target - 目标文件它依赖于一个或多个 prerequisites中的文件, 也就是依赖文件, command中定义了生成规则

2> 一个简单示例

下面来个示例 :
        假设现在我们要编写一个C程序, 它有一个头文件, 两个.c文件
 分别是 test.h   test.c   main.c
        那我们所要编写的Makefile文件如下:

mybin : main.c test.c test.h
    gcc -o mybin main.c test.c test.h

    // 在这里 mybin是要生成的目标文件
    // main.c test.c test.h 是生成目标文件所依赖的文件
    // gcc -o .. 这一行是依赖关系, 也就是生成目标文件要执行的命令
    
// 当我们在shell命令行提示符后 运行 make指令, 它就会自动化编译并且生成一个名叫 mybin的可执行文件

3> 文件修改时间的比较

当我们编写好一个Makefile文件时, 使用指令make就可以自动化编译并生成最终目标文件。
如果我们对其中一个源文件进行了修改, 再次运行make指令。 那么 , make又是怎么知道是否要生成这个目标文件呢 ?

Makefile的显式规则:

  • 在我们使用make指令后, Makefile通过比较目标文件与依赖文件的最近修改时间来确定是否要执行make指令,生成新的目标文件
  • 文件的时间属性中包含三个时间
    • access – 最近访问时间
    • modify – 文件内容最近修改时间
    • change – 文件属性最近修改时间
  • Makefile就是通过比较 modify 文件内容的最近修改时间, 如果目标文件的modify最新, 那么就不执行make指令; 反之 , 就执行make指令生成新的目标文件是在原目标文件的基础上进行修改)

4> 如何一键删除自动化编译所形成的多个目标文件

在日常的程序设计中, 编译大型程序会生成多个目标文件, 当我们不需要这些目标文件时,肯定想要一个指令自动删除多个目标文件。
Makefile支持这样的操作

clean:
	command

// clean -- 它并不是一个目标文件, 而是一个标签。 不具备相应的依赖文件
// command -- 其中定义了我们所要执行的shell指令, 用于删除一个或多个目标文件
    
mybin : main.c test.c test.h
    gcc -o mybin main.c test.c test.h

clean:
	rm mybin 

// 例如上述的示例中, 我们添加clean这样的内容
// 当我们在shell的命令行提示符中输入 make clean, 它就会自动执行 clean对应的 rm mybin , 进而删除目标文件mybin

5> 伪目标的声明

如果你去连续多次运行make 或者make clean, 那么它一定会出现如下的提示

[Jin_Nian@VM-4-8-centos test01_dir]$ make clean
rm *.c *.h
rm: cannot remove ‘*.c’: No such file or directory
rm: cannot remove ‘*.h’: No such file or directory
make: *** [clean] Error 1

// 该提示告诉我们, 我们所要删除的文件并不存在 (或是所要创建的文件已存在 或是最新)

那我们如何在连续多次执行同一条make命令时, 忽略掉这样的提示呢
这就需要我们去声明一个伪目标了

通过特殊标记 .PHONY声明 “伪目标”

  • 伪目标并不是一个文件,只是一个标签 。
  • 伪目标具有总是被执行的特性
  • .PHONY :name – 显式的声明一个伪目标
  • 伪目标可以不需要依赖文件
  • 伪目标也可以作为依赖文件

6> 稍微复杂一点点的示例

mybin : main.o test.o   # mybin -- 默认为最终目标文件
	gcc -o mybin $@ $^  # $@ -- 目标文件; $^ -- 依赖文件 ($@ $^这俩是自动变量)
	
main.o : main.c test.h  # main.o -- 中间文件(也是目标文件)
	gcc -c $@ $^
test.o : test.c test.h  # test.o -- 中间文件(也是目标文件)
	gcc -c $@ $^
	
.PHONY : clean      // .PHONY声明的伪目标
clean :  // clean 是个 标签 , 该标签下有我们想要执行的shell命令
	rm mybin main.o test.o

7> Makefile中的隐晦规则 – 自动化推导

GNU中的make很强大, 依据它的智能性, 它可以自动推导目标文件的依赖文件以及它们之间的依赖关系

make自动推导:

  • make看到一个 [.o] 目标文件, 它会自动的把[.c]文件加入到依赖文件中
  • 并且 会自动的推导出 .o 与 .c文件之间的依赖关系
    • gcc -c file.o -o file.c (或是gcc -c -o file.o file.c)
  • 也可以根据 gcc -c file.c 推导出 gcc -c -o file.o file.c

注意 : 在g++中这种自动推导部分不适用

有了自动推导, 我们可以编写更加简洁的Makefile文件了

mybin : main.o test.o  
	gcc -o mybin $@ $^ 
	
main.o :    # 自动推导出 main.o : main.c
            #          	gcc -c -o main.o main.c  
test.o :    # 自动推导出 test.o : test.c
	        #           	gcc -c -o test.o test.c
.PHONY : clean     
clean : 
	rm mybin main.o test.o


8> Makefile中变量的定义

Makefile中还存在对于变量的定义:

  • 所定义的变量必须进行初始化
    • 为命令定义变量, 例如 cc = gcc , -o = flag
    • 为目标文件定义变量, 例如 target = mybin
    • 为依赖文件定义变量, 例如 object = *.o
  • 如何使用变量 , 假设定义了如下变量
    cc = gcc
    flag = -o
    tar = mybin
    obj = main.o
  • 使用方式如下 :
    • $(tar) : $(obj)
    • cc $(flag) $(tar) $(obj) # 前面少了Tab键
    • 所有的变量都要通过 $来使用 ,
    • 加了括号只是为了便于区分, 避免出现错误
    • 变量 cc 表示使用gcc 还是 g++ 来编译文件, 该变量可以直接使用, 也可以通过 $ 来使用

9> Makefile中的注释

  • Makefile中只有行注释, 和UNIX中的shell脚本一样, 其注释是用 # 字符声明的
  • 如果我们要在Makefile中使用 # 的字面含义时, 需要 反斜杠 \ 对#进行转义 , 也就是 #
  • 最后需要再次强调的一点是 , 在Makefile中的commad(任意的shell命令) 都要以 Tab键在开头留出空白

10> Makefile中的自动化变量

这里只介绍两个

  • $@ – 代表目标文件
  • $^ – 代表依赖文件

参考如下 :
浅显易懂 Makefile 入门
跟我一起写Makefile – 陈皓老师

你可能感兴趣的:(Linux笔记,linux,c++,自动化)