Makefile学习笔记
发布日期:2021-05-14 09:31:54 浏览次数:22 分类:精选文章

本文共 5814 字,大约阅读时间需要 19 分钟。

目录

Linux下编译C

Makefile的用途

  • 描述整个工程的编译链接规则
  • 实现软件工程自动化编译

Makefile的工作过程

命令行执行make

从当前目录寻找Makefile
从Makefile第一行开始找目标,根据目标依赖关系执行命令

直接用GCC编译器进行编译

gcc -o main main.c

在这里插入图片描述

使用Makefile进行编译

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

在这里插入图片描述

main:main.c	gcc -o main main.cclean:	rm main

在这里插入图片描述

与Windows下的IDE比较

在这里插入图片描述

程序的编译与链接

程序的存储与运行

在这里插入图片描述

程序的编译和链接

在这里插入图片描述

程序文件的分类

在这里插入图片描述

ELF文件存在头部信息
头部信息描述文件要加载到内存的哪一个地址运行,各个数据段和代码段在内存的位置.

查看文件头部信息 readelf

readelf -h filename

在这里插入图片描述

  • Entry point address:入口地址
    程序运行时加载器会将程序加载到该地址执行.
  • 可重定位文件(目标文件.o)的入口地址为0,在链接的时候才会有地址.

查看文件类型 file

file filename

在这里插入图片描述

ELF 64-bit LSB relocatable 可重定位文件
在这里插入图片描述

动态库与静态库

  • 库的概念
    目标文件的归档
    也是可重定位文件
    静态库入口地址也为0
    静态库包含在可执行程序中,会使可执行程序体积变大
    动态库在程序运行时随着可执行程序加载到内存中,可以减少可执行程序体积大小

Makefile基本语法

Makefile 文件的主要内容

  • 规则
  • 变量
    ( ) 或 ()或 (){}来引用
  • 条件执行
  • 文本、文件名处理函数
    主要是字符串的替换,查找,过滤,排序,统计等操作
    如:文件名处理,加前缀,加后缀,连接等
  • 文件包含
  • 注释

规则

目标:目标依赖	命令
  • 命令必须以tab键开头
  • 一个规则中可以无目标依赖,仅仅实现某种操作
  • 一个规则中可以没有命令,仅仅描述依赖关系
  • 一个规则中必须有一个目标
  • $表示引用变量
  • @表示目标变量
  • ^表示所有依赖变量
  • <表示第一个依赖
  • $@表示引用目标变量字符串
  • $^表示引用依赖变量字符串
  • $<表示引用第一个依赖

在这里插入图片描述

目标

默认目标

  • 一个Makefile里可以有多个目标
  • 一般会选择第一个作为默认目标

多目标

  • 一个规则中可以有多个目标
  • 多个目标具有相同的生成命令和依赖文件
    在这里插入图片描述

多规则目标

  • 多个规则可以是同一个目标
  • Make在解析时,会将多个规则的依赖文件合并

在这里插入图片描述

伪目标

  • 并不是一个真正的文件名,可以看做是一个标签
  • 无依赖,相比一般文件不会去重新生成、执行
  • 伪目标,可以无条件执行

在这里插入图片描述

目标依赖

  • 根据时间戳来判断目标依赖文件是否更新
  • 所有文件编译过,则对所有文件编译,生成可执行程序
  • 在上次make之后修改过的C文件,会被重新编译
  • 在上次make之后修改过的头文件,依赖此头文件的会被重新编译

打印(产生)依赖 gcc -M / gcc -MM

gcc -M filename //打印filename文件的所有依赖,包括引用系统的库文件gcc -MM filename //打印filename文件的依赖,不包括引用系统的库文件

在这里插入图片描述

在这里插入图片描述

模式匹配(通配符)

match_test:test.o test.c %.o:%.c //任意的*.o依赖或目标都会来执行此规则	@echo "$@:$^"

在这里插入图片描述

隐式规则

  • 当不使用gcc -o …时Makefile会默认将.c文件编译成.o文件

命令的执行

  • 命令以tab开头
  • 每一条命令,make会开一个进程
  • 每一条命令执行完,make会检测每个命令的返回码,返回0则成功
  • 若命令返回成功,make继续执行下个命令
  • 若命令执行出错,make会终止执行当前规则,退出

并发执行命令

make -jn  //打开n个进程并发执行(编译)

使命令在同一进程执行

在这里插入图片描述

变量

变量基础

  • 变量在Makefile中一般是文本值

变量定义

  • CC = gcc
    在这里插入图片描述

变量赋值

  • 追加赋值: +=

    在这里插入图片描述

  • 条件赋值: ?= //若变量未被赋值则将右边赋值给左边,否则不赋值

    在这里插入图片描述

变量引用

  • $(CC) 或 ${CC}

变量分类

立即展开变量

  • 使用:=操作符赋值
  • 在解析阶段直接赋值常量字符串

延迟展开变量

  • 使用=操作符赋值
  • 在运行阶段,实际使用变量时再进行求值

注意事项

  • 一般在目标、目标依赖中使用立即展开变量
  • 在命令中一般使用延迟展开变量

在这里插入图片描述

目标变量

  • 一般变量: 默认为全局变量
  • 目标变量: 该目标所依赖的规则中都可以使用
  • 使用目标变量用途: 做到文件级的编译选项

在这里插入图片描述

模式变量

  • 目标变量可以定义在某个目标上
  • 模式变量可以定义在符合某种模式的目标上
obj:N=1 //目标变量%*.o:N=2 //模式变量

自动变量

  • 自动变量也是一个局部变量
  • 目标
    $@
  • 所有目标依赖
    $^
  • 第一个依赖
    $<
  • 使用举例
    gcc -o $@ $^

系统环境变量

  • 作用域:所有的Makefile
  • 变量在make 开始运行时被载入到Makefile中
  • 若Makefile中定义同名变量,系统环境变量将被覆盖
  • 命令行中传递同名变量,系统环境变量将被覆盖
常见的系统环境变量
  • CFLAGS
  • SHELL
  • MAKE

在这里插入图片描述

在这里插入图片描述

变量的传递

  • Makefile在多目录下递归执行
  • 先将当前目录的文件make,然后进入当前目录下的子目录执行子目录下的Makefile
  • $(MAKE) –C subdir
  • cd subdir && $(MAKE)

命令行传递变量

在这里插入图片描述

export传递变量

  • export声明变量为全局
    在这里插入图片描述

条件执行

  • 关键词
    ifeq
    else
    endif
    ifneq
  • 条件语句从ifeq开始,括号与关键字用空格隔开

在这里插入图片描述

函数

文本处理函数

$(subst FROM,TO,TEXT)

  • 函数名称:字符串替换函数—subst。
  • 函数功能:把字串“TEXT”中的“FROM”字符替换为“TO”。
  • 返回值:替换后的新字符串。

示例:

$(subst ee,EE,feet on the street)
替换“feet on the street”中的“ee”为“EE”,结果得到字符串“fEEt on the strEEt”

在这里插入图片描述

$(wildcard PATTERN)

  • 函数名称:获取匹配模式文件名函数—wildcard
  • 函数功能:列出当前目录下所有符合模式“PATTERN”格式的文件名。
  • 返回值:空格分割的、存在当前目录下的所有符合模式“PATTERN”的文件名。
  • 函数说明:“PATTERN”使用shell可识别的通配符,包括“?”(单字符)、“*”(多
    字符)等。

示例:

$(wildcard *.c)
返回值为当前目录下所有.c 源文件列表。

在这里插入图片描述

$(patsubst PATTERN,REPLACEMENT,TEXT)

  • 函数名称:模式替换函数—patsubst。
  • 函数功能:搜索“TEXT”中以空格分开的单词,将否符合模式“TATTERN”替换
    为“REPLACEMENT”。参数“PATTERN”中可以使用模式通配符“%”来代表一个单词中的若干字符。如果参数“REPLACEMENT”中也包含一个“%”,那么“REPLACEMENT”中的“%”将是“TATTERN”中的那个“%”所代表的字符串。在“TATTERN”和“REPLACEMENT”
    中,只有第一个“%”被作为模式字符来处理,之后出现的不再作模式
    字符(作为一个字符)。在参数中如果需要将第一个出现的“%”作为字
    符本身而不作为模式字符时,可使用反斜杠“\”进行转义处理
  • 返回值:替换后的新字符串。
  • 函数说明:参数“TEXT”单词之间的多个空格在处理时被合并为一个空格,并忽略
    前导和结尾空格。

示例:

$(patsubst %.c,%.o,x.c.c bar.c)
把字串“x.c.c bar.c”中以.c 结尾的单词替换成以.o 结尾的字符。函数的返回结果
是“x.c.o bar.o”

替换引用,它是

一个简化版的“patsubst”函数在变量引用过程的实现。变量替换引用中:
$(VAR:PATTERN=REPLACEMENT)
就相当于:
$(patsubst PATTERN,REPLACEMENT, $(VAR))
而另外一种更为简单的替换字符后缀的实现:
$(VAR:SUFFIX=REPLACEMENT)
它等于:
$(patsubst %SUFFIX,%REPLACEMENT, $(VAR))
例如我们存在一个代表所有.o 文件的变量。
定义为“objects = foo.o bar.o baz.o”。
为了得到这些.o 文件所对应的.c 源文件。我们可以使用以下两种方式的任意一个:
$(objects:.o=.c)
$(patsubst %.o,%.c, $(objects))

在这里插入图片描述

文件名处理函数

在这里插入图片描述

函数“foreach”不同于其它函数。它是一个循环函数。类似于 Linux 的 shell 中的
for 语句。
$(foreach VAR,LIST,TEXT)

  • 函数功能:这个函数的工作过程是这样的:如果需要(存在变量或者函数的引用),首先展开变量“VAR”和“LIST”的引用;而表达式“TEXT”中的变量引用不展开。执行时把“LIST”中使用空格分割的单词依次取出赋值给变量“VAR”,然后执行“TEXT”表达式。重复直到“LIST”的最后一个单词(为空时结束)。“TEXT”中的变量或者函数引用在执行时才被展开,因此如果在“TEXT”中存在对“VAR”的引用,那么“VAR”的值在每一次展开式将会到
    的不同的值。
  • 返回值:空格分割的多次表达式“TEXT”的计算的结果。
    在这里插入图片描述

shell函数

shell函数不同于除“wildcard”函数之外的其它函数。make可以使用它来和外部
通信。

  • 函数功能:函数“shell”所实现的功能和shell中的引用(``)相同。实现对命令的扩展。这就意味着需要一个shell 命令作为此函数的参数,函数的返回结果是此命令在shell中的执行结果。make仅仅对它的回返结果进行处理;make将函数返回结果中的所有换行符(“\n”)或者一对“\n\r”替换为单空格;并去掉末尾的回车符号(“\n”)或者“\n\r”。进行函数展开式时,它所调用的命令(它的参数)得到执行。除对它的引用出现在规则的命令行和递归变量的定义中以外,其它决大多数情况下,make是在读取解析Makefile时完成对函数shell的展开。
  • 返回值:函数“shell”的参数(一个 shell 命令)在 shell 环境中的执行结果。
  • 函数说明:函数本身的返回值是其参数的执行结果,没有进行任何处理。对结果的处理是由 make 进行的。当对函数的引用出现在规则的命令行中,命令行在执行时函数才被展开。展开时函数参数(shell 命令)的执行是在另外一个 shell进程中完成的,因此需要对出现在规则命令行的多级“shell”函数引用需要谨慎处理,否则会影响效率(每一级的“shell”函数的参数都会有各自的 shell进程)。

示例 1:

contents := $(shell cat foo)
将变量“contents”赋值为文件“foo”的内容,文件中的换行符在变量中使用空格代
替。
示例 2:
files := $ (shell echo *.c)
将变量“files”赋值为当前目录下所有.c文件的列表(文件名之间使用空格分割)。在shell中之行的命令是“echo *.c”,此命令返回当前目录下的所有.c文件列表。上例的执行结果和函数“$(wildcard *.c)”的结果相同,除非你使用的是一个奇怪的shell。
注意:通过上边的两个例子我们可以看到,当引用“shell”函数的变量定义使用直接展开式定义时可以保证函数的展开是在make读入Makefile时完成。后续对此变量的引用就不会有展开过程。这样就可以避免规则命令行中的变量引用在命令行执行时展开的情况发生(因为展开“shell”函数需要另外的shell进程完成,影响命令的执行效率)。
这也是我们建议的方式。
在这里插入图片描述

库的生成和使用:动态库,静态库

静态库

ar rcs libname.a libname.o //生成静态库gcc -o app main.c -L ./ -lname  //编译并引用库,库位于./下,库名字为name

在这里插入图片描述

在这里插入图片描述

动态库

gcc -o dll.o -c -fPIC dll.c //生成位置无关目标文件gcc -o libdll.so -shared dll.o //生成动态共享库gcc -o app main.c -L ./ -lname  //编译并引用库,库位于./下,库名字为name

在这里插入图片描述

Makefile的执行过程

执行过程

  1. 读取Makefile
  2. 执行make命令
  3. 解析Makefile
  4. 建立依赖关系树
  5. 按照依赖关系树和命令来执行并生成相应的文件

依赖关系解析阶段

  • 读取并解析Makefile
  • 建立依赖关系图
  • 引入其他Makefile进行解析、变量展开、条件执行
  • 生成依赖关系树

命令执行阶段

  • 将解析生成的依赖关系树加载到内存
  • 按照依赖关系,按顺序生成这些文件
  • 再次编译make会检查文件时间戳,判断是否过期,若无过期不再编译,若有更新,则依赖该文件的所有依赖关系上的目标重新更新、编译生成

make的执行结果

Make的退出码

  • 0:表示成功执行
  • 1:运行错误,make返回1

Makefile的隐含规则

  • make默认将.c文件编译成对应的.o目标文件
  • 对没有命令行的规则,寻址一个隐含的规则来执行
  • 取消隐含规则,使用-r或-R参数取消隐含规则

模式规则

使用模式规则来定义一个隐式规则

  • 模式规则,至少在规则的目标定义中包含%
  • %:任意长度的费控字符串,表示对文件名的匹配
  • 在目标文件名中这匹配的部分称为"茎"

目标和目标依赖同时含有%

  • 依赖目标的茎会传给目标

多目标模式规则

  • 同一个模式规则可以存在多个目标
  • 普通多目标规则:每一个目标作为一个独立规则处理:多个目标对应多个独立规则
  • 多目标模式规则:所有规则目标共同拥有依赖文件和规则的命令行,当文件符合多个目标模式中的任何一个时,规则定义的命令执行
上一篇:Mybatis-plus3.0 更新字段为 null
下一篇:MySQL group by 不对 null 进行分组统计

发表评论

最新留言

能坚持,总会有不一样的收获!
[***.219.124.196]2025年04月30日 01时41分13秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章