Makefile

Makefile

1.什么是Makefile

一个工程中的源文件不计其数,其按照类型、功能、模块分别存放在若干个不同的目录中。Makefile文件定义了一系列的 规则 来指定哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至进行更为复杂的功能操作,因为 Makefile 就像一个脚本一样,也可以执行操作系统的命令。

Makefile 的优点就是 “自动化编译”,一旦写好,只需要一个 make 命令,整个工程完全自动编译,这样就极大的提高了软件开发的效率。

make 是一个命令工具,是一个解释 Makefile文件中指令的命令工具。一般来说,大多数的 IDE 都有这个命令,比如 Delphi 的 make ,Visual C++ 的 nmake ,Linux 下的 GNU make

2.Makefile文件命名和规则

文件命名:

  • makefile 或者 Makefile

Makefile 规则:

  • 一个 Makefile 文件中可以有 一个或者多个规则
目标... : 依赖...
	命令(shell命令)
	...
  • 目标:最终要生成的文件(伪目标除外);
  • 依赖:生成目标所需的文件或者目标;
  • 命令:通过执行命令对依赖操作生成目标(命令前必须以 Tab 缩进);

Makefile 中其他的规则都是为第一条规则服务的。

示例

Makefile_第1张图片
在任意一个目录下,创建如下几个文件。

add.c

#include "head.h"

int add(int a,int b){
    return a + b;
}

div.c

#include "head.h"

double div(int a,int b){
    return a * 1.0 / b;
}

head.h

#include 

int add(int a,int b);

int sub(int a,int b);

int mul(int a,int b);

double div(int a,int b);

main.c

#include 

#include "head.h"

int main(){
    int a = 10 , b = 20;
    printf("a = %d , b = %d\n",a,b);
    printf("a + b = %d\n",add(a,b));
    printf("a - b = %d\n",sub(a,b));
    printf("a * b = %d\n",mul(a,b));
    printf("a / b = %.2lf\n",div(a,b));
    printf("xixixiixixixi\n");

    return 0;
}

mul.c

#include "head.h"

int mul(int a,int b){
    return a * b;
}

sub.c

#include "head.h"

int sub(int a,int b){
    return a - b;
}

makefile

app:add.c sub.c mul.c div.c main.c
	gcc add.c sub.c mul.c div.c main.c -o app

然后执行 make 命令,再执行可执行文件。

Makefile_第2张图片

3.工作原理

命令在执行之前,需要先检查规则中的依赖是否存在。

  • 如果存在,则执行命令;
  • 如果不存在,就向下检查其他规则,检查是否存在一个规则是用来生成这个依赖的。如果找到了,就执行该规则中的命令;

检测更新,在执行规则中的命令时,会比较目标和依赖文件的时间。

  • 如果依赖的时间 比 目标的时间晚,需要重新生成目标;
  • 如果依赖的时间 比 目标的时间早,目标不需要更新,对应规则中的命令不需要执行;

Makefile

#app 依赖于 add.o sub.o mul.o div.o main.o 这些文件
#由于这些文件还不存在,所以向下检查其他规则
#比如首先检测到 add.o 发现它不存在,就向下找到生成 add.o 的规则 并执行命令
#其他的也是类似,直到得到所有的依赖文件 最终生成目标文件 app
app:add.o sub.o mul.o div.o main.o
	gcc add.o sub.o mul.o div.o main.o -o app

add.o:add.c 
	gcc -c add.c -o add.o 

sub.o:sub.c 
	gcc -c sub.c -o sub.o 

mul.o:mul.c 
	gcc -c mul.c -o mul.o 

div.o:div.c 
	gcc -c div.c -o div.o 

main.o:main.c 
	gcc -c main.c -o main.o 

Makefile_第3张图片
我们推荐写成这样的形式

因为这样写的话,如果我们修改了其中的部分文件,那么我们也只需要重新编译这些被修改过的文件,而不是所有文件。

示例

比如在 main 函数最后,添加一句输出 hello makefile 的语句,再编译。

Makefile_第4张图片
再次执行 make 命令。

Makefile_第5张图片

我们发现这次只重新编译了 main.c 文件,这样的形式就提高了编译的效率。之前的那种形式就算修改了一个文件,也会重新编译所有文件。

4.变量

自定义变量

  • 变量名=变量值,var=hello

预定义变量

  • AR:归档维护程序的名称,默认值为 ar
  • CC:C编译器的名称,默认值为 cc
  • CXX:C++编译器的名称,默认值为 g++
  • $@:目标的完整名称;
  • $<:第一个依赖文件的名称;
  • $^:所有的依赖文件;

获取变量的值

  • $(变量名)

示例

将工作原理中的示例,全部用变量替换。

Makefile

src=add.o sub.o mul.o div.o main.o
target=app

$(target):$(src)
	$(cc) $(src) -o $(target)

add.o:add.c 
	$(cc) -c add.c -o add.o 

sub.o:sub.c 
	$(cc) -c sub.c -o sub.o 

mul.o:mul.c 
	$(cc) -c mul.c -o mul.o 

div.o:div.c 
	$(cc) -c div.c -o div.o 

main.o:main.c 
	$(cc) -c main.c -o main.o 

Makefile_第6张图片

5.模式匹配

Makefile 中我们也可以使用 模式匹配

%.o : %.c

  • %:通配符,匹配一个字符串;
  • 两个 % 匹配的是同一个字符串;

例如:

%.o:%.c
	gcc -c $< -o $@

示例

把变量中的示例使用模式匹配。

src=add.o sub.o mul.o div.o main.o
target=app

$(target):$(src)
	$(CC) $(src) -o $(target)

%.o:%.c 
	$(CC) -c $< -o $@

Makefile_第7张图片

6.函数

$(wildcard PATTERN...)

  • 功能:获取指定目录下指定类型的文件列表;
  • 参数:PATTERN 指的是 某一个或者多个目录下的对应的某种类型的文件,如果有多个目录,一般使用 空格间隔
  • 返回:得到的若干个文件的文件列表,文件名之间使用 空格间隔

例如:

$(wildcard *.c ./sub/*.c)
#返回 a.c b.c c.c d.c e.c ... 当前目录下 以及 当前目录的 sub 子目录下的 .c文件 

$(patsubst , , )

  • 功能:查找 中的单词(单词以 空格,Tab 或者 回车 ,换行 分隔)是否符合模式 ,如果匹配的话则以 替换;
  • 可以包括通配符 % ,表示任意长度的字串。如果 中也包含 %,那么这个 % 将是 中那个 % 所代表的子串;
  • 返回:函数返回被替换后的字符串;

例如:

$(patsubst %.c , %.o , x.c bar.c)
#返回为 x.o bar.o

示例

使用函数来实现。

src=$(wildcard *.c)
objs=$(patsubst %.c,%.o,$(src))

target=app

$(target):$(objs)
	$(CC) $(objs) -o $(target)

%.o:%.c
	$(CC) -c $< -o $@

Makefile_第8张图片

我们可以添加 clean ,使用 make clean 删除目标文件。

clean:
	rm $(objs) -f

Makefile_第9张图片

需要注意的是,如果我们此时在 当前目录下创建一个 clean 文件,make clean 文件就会失效。

Makefile_第10张图片
此时我们需要将 clean 设置生成伪文件。

.PHONY:clean
clean:
	rm $(objs) -f

Makefile_第11张图片
此时就执行成功了。

你可能感兴趣的:(Linux,makefile,linux)