makefile教程(1)

makefile教程

文章目录

  • makefile教程
    • makefile是什么:
    • 使用makefile的好处:
    • 关于程序的编译和链接:
    • makefile内核
    • makefile的使用规则:
      • 显示规则
      • make是如何工作的
      • makefile中使用变量(变量定义)
        • **变量的使用**
          • 一般是变量 = 依赖,首先先把变量声明好并且需要赋予初值
          • 调用变量时使用 $ (OBJ),如果要是使用真实的“$ ”字符,那么需要使用“$$”来表示
          • 可以是变量 = 变量
          • 可以是 :=操作符 来使用变量
          • 可以是 ?=
          • 变量的高级用法
          • 追加变量值
          • override指示符
          • 多行变量
          • 目标变量
          • 模式变量
      • makefile自动推导(隐式规则)
      • 引用其余的makefile(文件指示)
    • 通配符的使用
    • 文件搜寻
    • 伪目标
      • 什么是伪目标
      • 伪目标的使用要求
    • 多目标 涉及到函数后续
    • 静态模式
    • 自动生成依赖性
    • 书写命令
    • 嵌套执行make
    • 定义命令包

makefile是什么:

makefile是用户自行完成的IDE(integrated development environment集成开发环境)程序,与传统的操作系统下的编译不同,makefile可以通过用户自行安排,决定文件的编译顺序,决定哪些文件需要编译,哪些文件需要重复编译等各种复杂的操作

使用makefile的好处:

可自由编译的好处,能够指定文件编译,将繁杂的编译方法集成到一个makefile文件中,然后使用make进行一键编译,极大的提高了开发效率

关于程序的编译和链接:

关于gcc编译成可执行文件的命令,以及对应命令下生成的文件后缀

命令 文件
gcc -E 预处理(宏展开) *.i
gcc -S 编译(生成汇编代码) *.s
gcc -c 汇编(生成机器码) *.o(unix下)
gcc -o 链接(把大量的.o文件合成生成可执行文件) (主要链接的是函数和全局变量)对于a.out文件的重命名 可执行文件
  • 链接 link

把大量的.o文件合并生成可执行文件

主要链接的就是函数和全局变量,而存放函数和全局变量的地方通常称为库文件 .lib文件

在unix下也叫做.a文件

  • 编译

在编译时,编译器只会管你程序的语法是否正确,函数和变量是否已经声明,函数如果没有声明也没关系,只会报警告,而在链接这一步,链接器会在所有的.o文件中寻找这个函数,如果没有找到,就报链接错误码

makefile内核

所有的makefile的实际都是:编译+链接

只要记住编译+链接就行

  1. 如果这个工程没有被编译过,那么所有的.c文件都要编译并被链接
  2. 如果这个工程已经编译,但是修改了.c文件,我们需要编译修改的.c文件,并连接目标程序
  3. 如果这个工程已经编译,但是修改了.h文件,我们需要编译修改的.h文件,并连接目标程序

makefile的使用规则:

一共有五大原则:显示规则、隐式规则、变量定义、文件指示、注释说明

显示规则

目标…:依赖…

命令

  • 目标:可以是个文件(object文件或者是可执行文件),也可以是个标签label

  • 依赖:要生成target目标的所需要文件或者是目标

  • 命令:就是使用make之后所要执行的命令(shell命令)

当依赖中的文件比目标中的文件要新的话,就必须执行命令中的内容,这就是makefile的核心

makefile教程(1)_第1张图片

makefile教程(1)_第2张图片

其中“ \ ”表示换行符

clean不是一个文件,而是一个动作名,我们要使用clean,就需要make clean。而且clean后面也不需要出现依赖。

所以我们也可以在makefile中定义不用的编译或者与编译无关的命令,比如程序的打包,程序的备份等

怎么使得make时会判断哪些是不需要makefile的命令呢,例如clean这个动作写入makefile时当我们使用make时不会自动执行,其中的原理在哪

这就谈到make是如何工作的

make是如何工作的

  1. make会首先在当前文件夹中搜寻makefile或者是Makefile文件
  2. 读入被include的其他makefile
  3. 初始化定义的变量
  4. 为所有的目标建立依赖关系链
  5. 顺序执行第一个target,并且把target作为最终的目标文件,也就是一切都是为了第一个target
  6. make会比较依赖和目标的文件时间戳,如果依赖比目标的文件时间新,则会重新生成target文件
  7. 根据最终目标文件的依赖,首先会查找是否存在这些依赖,如果不存在则会按照是否makefile中存在依赖文件的生成方式,有的话就会执行下去,没有的话就直接退出报错
  8. 为什么要单独对clean进行make,是因为:

第一个target并没有直接或间接利用clean

clean之后没有存在依赖

如果我们改变了“command.h”,那么,kdb.o、command.o 和 files.o

都会被重编译,并且,edit 会被重新链接。

makefile中使用变量(变量定义)

  • 变量的定义
  1. 就好比C语言中宏的作用,当我们在添加或者修改依赖时,至少要修改三遍以上,所以干脆用个宏,修改一处就可以,一般宏设置为 “OBJ、OBJECT、OBJS ”
  • 变量的使用
  1. 一般是变量 = 依赖,首先先把变量声明好并且需要赋予初值

直接使用=表示,在变量的值在使用时变量才会递归展开

例如:

VAR1 = foo

VAR2 = $(VAR1) bar

VAR1 = baz

则最终输出VAR2的值为:baz bar而不是foo bar,因为VAR1在VAR2被展开时的值是最后一次赋值的 “baz”。

  1. 调用变量时使用 $ (OBJ),如果要是使用真实的“$ ”字符,那么需要使用“$$”来表示
  2. 可以是变量 = 变量

first = $(second)

second = $(third)

third = forth

all :

echo $(first)

即打印出来的值就是forth

  1. 可以是 :=操作符 来使用变量

只是简单展开,也就是顺序展开,但是=是递归展开

x := first

y := $(x) second

x := third

则y的值为first second 、 x的值为third

但是不可以倒推比如:

x := $(y) second

y := first

则x的值不能是first second而是second

  1. 可以是 ?=

?= 表示这个变量如果以前被定义过,那这个变量什么也不做,如果以前没有被定义过,那这个变量就赋值为等号后面的值

First ?= bar

  1. 变量的高级用法

变量值的替换

$(var:a=b),意思就是将var变量中所有a替换成为b

eg1:

first := 1.c 2.c 3.c

second := $(first:.c = .o)

eg2:静态模式下的变量替换

first := 1.c 2.c 3.c

second := $(first:%.c = %.o)

把变量的值再当成一个变量

eg:

x = y

y = z

a := $ ($ (x))

$ (x)的值为y,$ (y)的值为z,所以$(a)的值为z

  1. 追加变量值

使用+=操作符来给变量追加值

如果变量之前没有定义过,+=就会自动变成=

如果前一次变量的定义赋值是 :=,那么+=就会以 :=作为其赋值符

  1. override指示符

通常,当我们make时的装有命令行参数的变量在makefile中的赋值就会被忽略。如果我们想在makefile中设置这类参数的值,我们就可以使用 “override”指示符

override < variable> =< value>

override < variable> :=< value>

override < variable> +=< value>

  1. 多行变量

还有一种设置变量值的方法就是使用define,可以去定义多个变量,多个变量分开可以使用换行,结束时使用endef,和命令包的方法一样。如果你的一行开头没有用[tab],就会当成变量,否则就会当成命令

define first

second

third

endef

  1. 目标变量

一般来说,在makefile中定义的变量为全局变量,在整个文件中通用,但是例如 $<这种自动化变量则称为规则型变量

当然我们可以为某个目标自己设置局部变量,他可以与全局变量同名因为此目标只作用于局部

< target…> : < variable assignment>

< target…> : override < variable-assignment>

< variable-assignment>表示赋值表达式:=、+=、?=、:=

pro : CFLAGS = -g

pro : 1.o 2.o 3.o

cc $(CFLAGS) 1.o 2.o 3.o

表示只要在pro这个目标引发的规则下CFLAGS都为-g

  1. 模式变量

我们由目标变量可知,目标也可以成为一个变量。模式变量的好处就是我们可以给定一个模式,然后按照这个模式 我们可以将所有的变量定义在符合这个模式下的目标中

例如make的模式一般至少含有一个%,所以我们可以在%的模式下给所有目标定义目标变量

%.o : CFLAGS = -o

< pattern…> : < variable assignment>

< pattern…> : override < variable-assignment>

  • 自动化变量

$< 和 $@称为自动化变量

$@表示目标文件

$<表示依赖文件

makefile自动推导(隐式规则)

一般来说make会自动推导出依赖文件,例如我们的main.o文件就是由main.c得到的,但我们没必要去写:

main.o:main.c defs.h

​ cc -c main.c

直接用 main.o:defs.h

也只是.o对于.c,其余依赖也要补充

makefile教程(1)_第3张图片

引用其余的makefile(文件指示)

  • 使用include关键字

include + Makefilename(路径或者通配符)

路径如果没有设置,将会选择是当前文件夹下的makefile

“如果 make 执行时,有“-I”或“–include-dir”参数,那么 make 就会在这个参数所指定”

这个后面再说

  • 环境变量MAKRFILES

如果在当前环境中使用了环境变量MAKEFILES

那么make时会把这个变量MAKEFILES中的值做一个inlcude的动作

但是这个环境变量中的值如果是其他的makefile文件,则会用空格分开

这个变量中的target值在make时也不会起作用,依赖文件发生错误也不会管

通配符的使用

如果要使用一系列的类似的文件,一般使用通配符(* ,?,[…])来概括:

如果文件名中有通配符,我们可以使用 “\ *”来表示真正的“ * ”字符

object = *.o 表示的就是 *.o文件,而没有展开

object = $(wildcard *.o) 就可以得到一个展开后的变量合集

文件搜寻

  • 特殊变量 VPATH

VPATH表示目标或者依赖文件的寻找目录

比如:VPATH = src:…/header

表示目标或者依赖文件寻找的目录为src 或者上一层的header目录

其中:表示目录的分隔

  • 关键字 vpath

可以指定某个文件在哪个目录下寻找:vpath < pattern模式> < directories>

vpath %.h …/header

表示所有的.h文件都可以在上一层的header目录下找到

vpath %.h

表示符合模式%.h的文件目录

vpath

表示清除所有已被设置好了的文件搜索目录

当然目录的优先级最高的还是当前目录下的

连续使用vpath关键字来指定不同的搜索方式:

vpath %.c first

vpath % second

vpath %.c third

其表示“.c”结尾的文件,先在“first”目录,然后是“second”,最后是“third”目录

这里second比较特殊,什么文件都可以在second中搜索,比较浪费时间

可以通过改写成:

vpath %.c first:third

vpath % second

而上面的语句则表示“.c”结尾的文件,先在“first”目录,然后是“third”目录,最后才是“second”目录

伪目标

什么是伪目标

伪目标不是一个文件,所以无法使用依赖关系,我们必须使用一个特殊的标签来注明,使用时也得使用make+标签来表示

伪目标的使用要求

  • 不能与其他目标名重复,我们可以使用.PHONY来指明这是个伪目标

.PHONY:clean

  • 总是被执行的

伪目标同样可作为默认目标,就可以放在开头

all : prog1 prog2 prog3

.PHONY : all

由于伪目标总是被执行的,all作为一个伪目标,如果说prog1更新了,或者是prog3更新了,all也会更新

  • 伪目标也可以成为依赖

.PHONY : cleanall cleanobj cleaniff

cleanall : cleanobj cleaniff

多目标 涉及到函数后续

有时候多个目标可以共同使用一个依赖,这个叫做多目标

静态模式

更好的服务于多目标的使用

目标集合:目标集模式 依赖目标集模式

targets:%.o :%.c

表示对所有的.o文件进行了二次定义,也就是说去掉了.o的结尾,加上了.c的这个结尾

eg1:

objects = first.o second.o

$(object) : %.o : %.c

$(CC) -c $(CFLAGS) $< -o $@

也就是在objects这个变量中 将所有的.o文件修改成.c文件,所以修改之后我们的依赖目标集就是 first.c second.c

所以 $ <表示依赖目标集,$@表示目标集

eg2:

file = 1.o 2.o 3.elc

$ (filter %.o,$(file)) : %.o : %.c

gcc -c $< -o $@

$(filter %.elc,%(file)) : %.elc : %.el

emacs -f batch-byte-compile $<

filter是个函数,第一个参数是过滤条件,第二个参数过滤源

自动生成依赖性

对于每个.c文件,我们都得清楚包含了哪些头文件,这就大大增加了压力,但GNU编译器存在自动搜索头文件功能,使用 -MM参数

gcc -MM main.c = main.o : main.c defs.h

我们要为每个文件生成.d 文件之后才能自动化生成依赖关系

%.d : %.c

gcc -M $< > @ . @. @.$$$

书写命令

  • “@”

我们用“@”字符在命令行前,那么,这个命令将不被 make 显示出来

  • “;”

如果你要让上一条命令的结果应用在下一条命令时,你应该使用分号分隔这两条命令。比如你的第一条命令是 cd 命令,你希望第二条命令得在 cd 之后的基础上运行,那么你就不能把这两条命令写在两行上,而应该把这两条命令写在一行上,用分号分隔。

  • “-”

加一个减号“-”(在 Tab 键之后),标记为不管命令出不出错都认为是成功的

嵌套执行make

在不同的文件夹中编写不同的makefile,再由总控的makefile进行控制,可以有效的进行模块化的维护

例如我们在subdir下有个makefile,如果我们要在总控makefile中使用:

subsystem:

​ cd subdir && $(MAKE)

我们的make需要一点参数,所以定义成变量比较合适
如果你想要总控的makefile中的变量传递到下级的makefile

可以使用:export

export variable = value

其中如果定义了MAKEFLAGS和SHELL变量,将总会传递给下级的makefile
makefile教程(1)_第4张图片

make时的命令行的参数就会随着MAKEFLAGS传递到下级makefile

就会在终端打印信息

make:Entering directory ‘/home/hchen/gnu/make’

make:Leaving directory `/home/hchen/gnu/make’

使用make -w可以在总控makefile执行时查看 执行到哪一个下级makefile中

如果你不想要总控的makefile中的变量传递到下级的makefile

可以使用:unexport

定义命令包

我们可以单独定义一个新的命令包,使用命令序列define和endef来定义一个命令包,以供后面使用

eg:

define run-mvv 表示命令包的名字

mvv $(firstword $^) 表示第一个命令

mv first.c $@ 表示一个命令的作用

endef

将命令包使用时:

%.d : %.n

$(run-mvv)

使用命令包,就会依次执行各行命令,由于我只定义了一个命令,就执行一个。但是若定义了多个,则依次执行

你可能感兴趣的:(makefile编写,运维,linux,makefile)