Makefile学习笔记

    科技2022-08-05  109

    目录

    Linux下编译CMakefile的用途Makefile的工作过程直接用GCC编译器进行编译使用Makefile进行编译与Windows下的IDE比较 程序的编译与链接程序的存储与运行程序的编译和链接程序文件的分类查看文件头部信息 readelf查看文件类型 file 动态库与静态库 Makefile基本语法Makefile 文件的主要内容规则目标默认目标多目标多规则目标伪目标目标依赖打印(产生)依赖 gcc -M / gcc -MM模式匹配(通配符)隐式规则命令的执行并发执行命令使命令在同一进程执行 变量变量基础变量定义变量赋值变量引用 变量分类立即展开变量延迟展开变量注意事项 目标变量模式变量自动变量系统环境变量常见的系统环境变量 变量的传递命令行传递变量export传递变量 条件执行函数文本处理函数文件名处理函数 库的生成和使用:动态库,静态库静态库动态库 Makefile的执行过程执行过程依赖关系解析阶段命令执行阶段make的执行结果 Makefile的隐含规则模式规则使用模式规则来定义一个隐式规则目标和目标依赖同时含有%多目标模式规则

    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.c clean: 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中定义同名变量,系统环境变量将被覆盖命令行中传递同名变量,系统环境变量将被覆盖
    常见的系统环境变量
    CFLAGSSHELLMAKE

    变量的传递

    Makefile在多目录下递归执行先将当前目录的文件make,然后进入当前目录下的子目录执行子目录下的Makefile$(MAKE) –C subdircd 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的执行过程

    执行过程

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

    依赖关系解析阶段

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

    命令执行阶段

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

    make的执行结果

    Make的退出码

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

    Makefile的隐含规则

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

    模式规则

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

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

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

    依赖目标的茎会传给目标

    多目标模式规则

    同一个模式规则可以存在多个目标普通多目标规则:每一个目标作为一个独立规则处理:多个目标对应多个独立规则多目标模式规则:所有规则目标共同拥有依赖文件和规则的命令行,当文件符合多个目标模式中的任何一个时,规则定义的命令执行
    Processed: 0.012, SQL: 8