Linux 之 MakeFile

前言

  初学者在Linux中编写代码的时候,都会了解到需要使用 gcc 1.c -o app 把 .c源文件 变成可执行文件。但是如果是一个由上百个.c文件构成的项目,我们还得一个个去变成可执行文件么?
  因此工程管理器应运而生。工程管理器是一个能够管理较多的文件,并且能根据文件时间自动检测出更新过的文件而减少编译的工作量,同时通过读入 Makefile 文件来执行大量的编译工作。

MakeFile基本介绍

MakeFile介绍

  Makefile是一种用于自动化构建和编译软件项目的工具。它通常用于管理大型项目中的源代码文件,以及定义项目的编译、链接和其他构建过程。它通常是使用文本编辑器编写的,其语法基于一种称为"make"的构建工具的规范。Makefile的语法相对简单,但可以非常灵活,可以根据项目的需要进行定制和扩展。
  总的来说,Makefile是工程管理器在软件项目中非常重要的一部分,它可以帮助开发人员自动化构建过程,提高开发效率,减少错误和提高可维护性。

MakeFile文件命名

  默认的情况下,make 命令会在当前目录下按顺序找寻文件名为“GNUmakefile”、“makefile”、“Makefile”的文件。在这三个文件名中,最好使用“Makefile”这个文件名,因为,这个文件名第一个字符为大写,这样有一种显目的感觉。最好不要用“GNUmakefile”,这个文件是 GNU 的 make 识别的。有另外一些 make 只对全小写的“makefile”文件名敏感,但是基本上来说,大多数的 make 都支持“makefile”和“Makefile”这两种默认文件名。
  寻找顺序:“GNUmakefile” > “makefile” > “Makefile(推荐)”
  但是你可以指定执行某一个的MakeFile,使用make的 “-f” 和 “–file” 参数即可。比如:make -f Make.Linux 或 make --file Make.AIX

Makefile编写规则

Target(目标) : prerequisites(依赖)
	Command(命令)

eg;
app:main.o fun.o
	gcc main.o fun.o -o app
main.o:main.c
	gcc -c main.c -o main.o -I ./inc
fun.o:fun.c
	gcc -c fun.c -o fun.o -I ./inc

#1.第一行即“app”为终极目标,下面的所有目标都是为了生成这个终极目标而编写
#2.第一行的规则没有先后顺序
#3.当时间不对时,需要将时间调整正确之后才能使用 make 命令。
#4.makefile根据时间信息判断是否执行编译(目标文件与最终生成文件进行时间对比)。
#5.每个规则中的目标,都可以是一个文件,也可以是一个标签,标签作为第一个会一直执行。标签不是实际的文件;
#6.每一个规则中的目标,不一定要有依赖。
#7.每一个规则,不一定非得有命令列表。
#8.每个规则中可以有多条命令规则,但是前面都得需要加 Tab 键。

MakeFile的执行步骤

1 ) 读入所有的 Makefile 文件。
2 ) 读入 include 的 Makefile 文件。
3 ) 初始化文件中的变量。
4 ) 推导隐晦规则,并分析所有规则。
5 ) 为所有的目标文件创建依赖关系链。
6 ) 根据依赖关系,决定哪些目标要重新生成。
7 ) 执行生成命令。
  
注:①-⑤步为第一个阶段,⑥ -⑦为第二个阶段。第一个阶段中,如果定义的变量被使用了,那么 make 会把其展开在使用的位置。但 make 并不会马上展开,make 使用的是拖延战术,如果变量出现在依赖关系的规则中,仅当这条依赖将要使用了,变量才会在其内部展开。

MakeFile

makefile组成元素

  Makefile 里主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示和注释。

makefile显示规则

  显式规则说明了如何生成一个或多的的目标文件。这是由 Makefile 的编写人员明显指出的。包括要生成的文件,文件的依赖文件,生成的命令。

makefile隐晦规则

  使用模式规则来定义一个隐含规则。一个模式规则就好像一个一般的规则,只是在规则中,目标的定义需要有“%”字符。“%”的表示一个或多个任意字符。在依赖目标中同样可以使用“%”,只是依赖目标中的“%”的取值,取决于其目标。
  有一点需要注意的是,“%”的展开发生在变量和函数的展开之后,变量和函数的展开发生在 make 载入 Makefile 时,而模式规则中的“%”则发生在运行时。

app:main.o fun.o
	gcc main.o fun.o -o app
%.o:%.c
	gcc -c  $< -o $@  -I ./inc
伪目标(标签与文件冲突问题)

  当同级目录下具有标签名一致文件时使用伪目标。为目标不是一个真正的目标,仅仅是为了执行其所有规则下面的命令,不应该让 make 来判断他是否存在,或者是否应该被生成。
规则:.PHONY:后面声明是 伪目标

clean:
	rm *.o app
#这样在执行make clean命令的时候就不会运行名为clean的makefile文件而发生冲突

makefile变量定义

  makefile中声明变量需要赋予初值,在使用的时候要在前面加上 $ 符号(最好同时使用 () 或者 {} 将变量名包裹)。如果你要是用 $ 字符,则需要用 $$ 来替代。变量可以使用在许多地方,如规则中的“目标”、“依赖”、“命令”以及新的变量中。
  Makefile 中定义的变量切皆为字符串。
eg: 单字符: $A

   多字符:$(AA) 或者 ${AA}

makefile中的运算符和特殊变量
#符号 =
#功能 延迟展开赋值
#例子 
B=$A
A=10
all:
	echo $B   #输出为10

#符号 :=
#功能 立即展开赋值
#例子 
B:=$A
A=10
all:
	echo $B    #无输出,因为B为空

#符号 ?=
#功能 条件赋值(经常出现在make传参)
#例子 
A=30
A?=10
all:
	echo $A    #输出为30

#符号 +=
#功能 追加赋值
#例子
A=30
A+=10
all:
	echo $A    #输出为30 10,因为是字符串

#符号 $@
#功能 代替目标名

#符号 $^
#功能 代替依赖

#符号 $<
#功能 依赖集合中的第一个

#符号 @
#功能 make 在执行命令前不要将命令显示在标准输出上

makefile文件指示

  在 Makefile 使用 include 关键字可以把别的 Makefile 包含进来,这很像 C 语言的#include,被包含的文件会原模原样的放在当前文件的包含位置。include 的语法是:
  include ***.mk
  如果没有找到或是不能读取文件,make会产生警告并且继续载入其他文件,当完成 makefile的读取,make 会再次寻找这些文件,如果还是不行,make 才会出现一条致命信息。你可以在 include 前加一个减号“-”,例如 -include filename 来让 make 不理那些文件。

makefile注释

  上面的例子中已经提及了,即  # + 文本

makefile条件判断

makefile条件判断格式

#语法:
ifxxx (arg1,arg2)
	#do true
else
	#do false
endif
其他形式
ifxxx "arg1" "arg2"
ifxxx 'arg1' 'arg2'
ifxxx "arg1" 'arg2'
ifxxx 'arg1' "arg2"

在这里插入图片描述
判断语句前面的空格不能是 tab 键。

ifxxx功能
ifeq判断参数是否相等,相等为 true,否则为 false
ifneq判断参数是否不相等,不相等为 true,否则为 false
ifdef判断变量是否有值,有值则为 ture,否则为 false
ifndef判断变量是否没有值,没有值则为 ture,否则为 false

makefile条件判断的三种形式

#判断语句不在规则中
B=30
C=20
ifeq ($B,$C)
	A=10
else
	A=20
endif
all:
	echo $A
	
#标签在判断语句中
B=30
C=30
ifeq ($B,$C)
all:
	echo $B
else
all:
	echo $C
endif

#判断语句在命令行中
B=30
C=20
all:
ifeq ($B,$C)
	echo $B
else
	echo $C
endif

makefile条件判断总结

1 ) 条件判断语句根据条件的值来决定 make 的执行。
2 ) 条件判断可以比较两个不同变量或者变量和常量。
3 ) 条件判断在预处理阶段有效,在执行阶段无效。
4 ) 条件判断不能控制规则中命令的执行过程。
5 ) 条件判断语句之前可以有空格,但是不能有 Tab 字符。
6 ) 在条件判断语句中不要使用自动变量(@,$@,$^ , $<)。
7 ) 一条完整的条件语句必须位于同一个 makefile 中。

makefile函数

wildcard  匹配当前目录文件

例子:
  src=$(wildcard ./src/*.c)
返回值:目录下的所有 .c 文件(内容包含路径)

notdir  取文件名

例子:
  allFileName==$(notdir $(src))
返回值:每一个文件的非目录部分(即文件名)

patsubst  模式替换

例子:
  file=$(patsubst %.h,%.o,$(allFileName))
返回值:把所有的文件名后缀从 .h 换成了 .o

filter-out  反过滤

例子:
  sources=file1.c file2.c file3.h file4.h
  src=$(filter-out %.h, $(sources))
返回值:过滤掉所有的 .h 文件

MakeFile多文件、多文件夹具体例子

在这里插入图片描述
目标:
  从 inc 文件夹中取出 .h 头文件 与 src1、src2 文件夹中的 .c 源文件一起参与编译,并且将 .o 文件生成到 obj 目录下。最后在当前目录下生成可执行文件 app 。在后续编译中可以自行选择过滤 .o 文件或者 .c 文件。

INC=./inc/
obj=./obj/
CC=gcc
mode="1"
m="1"
src1c=$(wildcard ./src1/*.c)
src2c=$(wildcard ./src2/*.c)

obj1=$(patsubst ./src1/%.c,./obj/%.o,$(src1c))
obj2=$(patsubst ./src2/%.c,./obj/%.o,$(src2c))
file1=$(filter-out ./obj/$(nf),$(obj1))
file2=$(filter-out ./obj/$(nf),$(obj2))

file3=$(filter-out ./src1/$(nf),$(src1c))
file4=$(filter-out ./src2/$(nf),$(src2c))
obj3=$(patsubst ./src1/%.c,./obj/%.o,$(file3))
obj4=$(patsubst ./src2/%.c,./obj/%.o,$(file4))

#如果m=1代表过滤.o 其他代表过滤.c

ifeq ($(m),$(mode))
app:$(file1) $(file2)
	$(CC) $^ -o $@
else
app:$(obj3) $(obj4)
	$(CC) $^ -o $@
endif

$(obj)%.o:./src1/%.c
	$(CC) -c $^ -o $@ -I $(INC)
$(obj)%.o:./src2/%.c
	$(CC) -c $^ -o $@ -I $(INC)
	
clean:
	rm ./obj/*.o
.PHONY:clean

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值