网站维护建设,桂林模板网站建设,微企点做网站怎么样,工作总结开头和结束语文章目录 前言一、Makefile的引入——最简单的gcc编译过程二、Makefile的规则三、Makefile的语法3.1、通配符3.2、假想目标 .phony3.3、即时变量 延时变量 四、Makefile的函数4.1、foreach4.2、filter4.3、wildcard4.4、patsubst 五、Makefile升级5.1、包含头文件在内的依赖关系… 文章目录 前言一、Makefile的引入——最简单的gcc编译过程二、Makefile的规则三、Makefile的语法3.1、通配符3.2、假想目标 .phony3.3、即时变量 延时变量 四、Makefile的函数4.1、foreach4.2、filter4.3、wildcard4.4、patsubst 五、Makefile升级5.1、包含头文件在内的依赖关系自动生成依赖文件5.2、添加CFLAGS 六、通用Makefile模板 前言
开发板平台飞凌嵌入式ElfBoard ELF-1 参考视频和资料飞凌嵌入式ElfBoard ELF-1软件学习书册 韦东山https://www.bilibili.com/video/BV1kk4y117Tu?p6vd_source3018264d4331e8fc267f9d68c24ee20f
一、Makefile的引入——最简单的gcc编译过程
keil,mdk,avr这些工具全自动编译的内部机制依然是makefile
这里我们先随便写两个C文件a.c 和b.c用最传统的gcc编译一下
a.c:
#include stdio.h
void funB();
int main() {funB();return 0;
}b.c:
#include stdio.h
void funB() {printf(hello B!\n);
}然后我们上传到虚拟机上进行编译
gcc -o test a.c b.c然后执行
./test从a.c b.c到可执行文件test经历了什么 总的来说就是四步预处理编译汇编链接一般来说前三步统称为编译 a.c -a.s-a.o b.c -b.s-b.o 最后两个.o链接在一起生成可执行文件test
我们可以在gcc命令后加上 -v看到编译链接的完整过程
gcc -o test a.c b.c -v具体内容很多就不一一截图了 这样gcc有个很明显的缺点 不论a.c b.c有没有被更改过每次gcc都会重新编译链接所有的C文件。有的时候我们只修改了很小一部分的C文件但此时我们gcc会全部重新编译这很耽误时间。
makefile就能解决这个问题。 它可以把刚刚gcc这个过程解构成一系列小的编译过程
gcc -c -o a.o a.c
gcc -c -o b.o b.c
gcc -o test a.o b.o这三句命令的含义如下
gcc -c -o a.o a.c这个命令使用 GCC 编译器将源文件 a.c 编译成目标文件 a.o。具体解释如下 -c 选项表示只进行编译而不进行链接生成目标文件。 -o a.o 选项指定输出文件的名称为 a.o。 a.c 是源文件的名称。
gcc -c -o b.o b.c这个命令与第一条类似将源文件 b.c 编译成目标文件 b.o。
gcc -o test a.o b.o这个命令使用 GCC 编译器将目标文件 a.o 和 b.o 进行链接生成一个名为 test 的可执行文件。 -o test 选项指定输出文件的名称为 test。 a.o b.o 是链接的目标文件。
综合起来这三条命令用于分别编译两个源文件 a.c 和 b.c然后将生成的目标文件 a.o 和 b.o 链接在一起形成一个名为 test 的可执行文件。 makefile如何自己的这些文件被修改了 在第一行 判断到a.c比a.o新就说明a.c被更新过了就可以重新编译 在第二行 判断到b.c比b.o新就说明b.c被更新过了就可以重新编译 在第三行 判断到a.o b.o比test新就说明a.o b.o被更新过了就可以重新编译
二、Makefile的规则
makefile的基本语法格式为 目标文件依赖文件 TAB 命令 当依赖比目标新的时候 or 该目标文件直接不存在的时候就会执行命令
我们在刚刚的文件夹中新建一个makefile: 然后执行make两此看一下效果
make
make第一次Make正确得运行了我们makefile里面写的所有语句 第二次make因为没有检测到依赖项更新所以并没有重新编译
我们再做一个小实验即只修改a.c 只修改b.c 以及同时修改a.c和b.c再分别执行make的效果 可以看到我们修改了啥make时也只会重新执行依赖项变动的语句没有修改的部分不会重新执行命令。 touch 命令是用来更新文件的访问和修改时间戳的工具如果文件不存在则会创建一个空白文件。它不会修改文件的内容只是更新文件的元数据。所以touch 命令不会改变文件的内容只是改变文件的时间戳。相当于变相修改了文件
三、Makefile的语法
3.1、通配符
我们再增加一个c.c文件里面有一个函数func():
#include stdio.h
void funC() {printf(hello C!\n);
}再把a.c里的内容改一下把函数func加上去
#include stdio.h
void funB();
void funC();
int main() {funB();funC();return 0;
}
再使用通配符对makefile里面的语句进行一定的修改 1$^表示所有的依赖文件 2使用 $ 表示第一个依赖文件源文件使用 $ 表示目标文件。 这样就可以把gcc -o -c a.o a.c 和gcc -o -c b.o b.c合并成一句话了
我们在虚拟机上看一下效果 这里make没啥问题正常工作了。
3.2、假想目标 .phony
phony英语单词主要用作形容词、名词作形容词时译为假的欺骗的 我们首先补一下make clean的内容 我们修改一下makefile中的内容 增加了一句话
clean:rm *.o testMakefile 中还包含了一个 clean 目标用于清理生成的目标文件和可执行文件 当执行 make clean 时它将删除当前目录下的所有 .o 文件和 test 可执行文件。
我们上虚拟机实验一下 发现所有的.o文件和可执行文件test均被清除了这就是make clean的作用
在讲完clean之后正式引出我们的makefile的一个语法 make [目标] 比如说make clean就会寻找makefile里面名为clean的目标执行其tab后的命令 如果make后没有加东西直接就是make,那么系统会默认自动执行第一个目标的命令在上文中就会自动执行test目标的命令。 在这个规则下有一个bug就是遇到同名文件的情况这里以clean同名文件为例子**因为在makefile里并没有clean的依赖项文件。**之前是因为没有clean这个东西所以执行会很顺利。
我们在虚拟机上新建一个名为clean的文件我们先make再执行make clean。此时根据makefile的规则系统没有检测到clean发生改变因为存在同名文件clean那么执行make clean时就不会正确执行 解决办法我们把clean设置为假想目标就能解决这个问题。 我们稍微改一下makefile: 这里使用了 .PHONY 目标它告诉 Make 这个目标不对应真实的文件名。这样做的目的是防止与同名的实际文件冲突同时确保即使存在同名文件make clean 也能正常执行。 这回我们再实验一下就对了
3.3、即时变量 延时变量
我们写一个新的makefile: 这个 Makefile 定义了两个变量 A 和 B然后在 all 目标中使用了这两个变量。以下是中文解释
A : abc 表示定义了一个变量 A其值为 “abc”。: 是一种赋值方式表示覆盖先前的值。(即刻确定) B 123 表示定义了一个变量 B其值为 “123”。 也是一种赋值方式但是它是延迟赋值即在使用变量时才会展开。延迟确定
all 目标中使用了 echo $(A) 和 echo $ (B) 分别输出变量 A 和 B 的值。在 Makefile 中$() 用于引用变量的值。echo 是一个在命令行中常用的命令用于将文本输出到标准输出设备通常是终端。在类Unix系统如Linux和类似的命令行环境中echo 命令通常用于显示文本。
我们make一下看看效果 我们可以在echo前面加上 这样就不会打印命令本身了 到这里我们还看不出即时变量和延时变量的区别我们再makefile添一些代码 A是即可确定但此时C还并没被赋值所以这时会打印空 B是使用时才确定所以不会为空 我们实验一下 我们如果更改Cabc的位置呢 最后结果还是不影响即C位置不影响B系统会对makefile整体进行分析 最后我们再介绍两种符号 ? 在makefile里我们做如下更改 make一下看看效果 1 运算符用于追加值到变量。使用 时它会将右侧的值追加到已经存在的变量值的末尾。如果变量之前未定义则行为类似于简单的赋值。 2? 运算符用于给变量赋值但仅在该变量之前未定义时才赋值。使用 ? 时它会检查变量是否已经定义如果已定义则不进行赋值否则将变量赋予指定的默认值。
总结 1 即时变量 2 延时变量 3延时变量 是第一次定义才起效果 4 附加 它是即时变量还是延时变量 取决于前面
四、Makefile的函数
4.1、foreach
foreach 是GNU Make中的一个函数用于进行循环迭代。其基本语法如下
$(foreach var, list, text)var: 循环中的临时变量表示每次迭代中的当前元素。
list: 要迭代的列表可以是以空格分隔的多个元素。
text: 在每次迭代中对var进行操作的文本块。
我们写一个新的makefile: Aa b c: 定义了一个变量A包含三个元素a、b和c。 B$(foreach f, $(A), $(f).o): 使用foreach循环将A中的每个元素加上.o后缀并将结果存储到变量B中。 all:: 定义了一个目标规则名为all。echo B $(B): 在执行all目标时打印出变量B的值。 最后结果
4.2、filter
filter 是GNU Make中的一个函数用于从列表中筛选出符合指定条件的元素。其基本语法如下
$(filter pattern..., text)pattern…: 一个或多个模式用于指定筛选条件。可以包含通配符 %。 text: 要进行筛选的文本块通常是一个以空格分隔的元素列表。 filter函数会返回text中符合给定模式的元素列表。模式之间使用空格分隔。
filter函数还支持反向操作使用filter-out可以筛选出不符合指定条件的元素。例如
C $(filter-out a%, $(A))在这个例子中匹配A中不以字母’a’开头的元素。
我们写一个新的Makefile:
C a b c d/: 定义了一个变量C包含四个元素a、b、c和d/。 D $(filter %/, $©): 使用filter函数从C中筛选出以’/结尾的元素存储到变量D中。在这个例子中D的值为d/。 E $(filter-out %/, $©): 使用filter-out函数从C中筛选出不以’/结尾的元素存储到变量E中。在这个例子中E的值为a b c。
最后结果 4.3、wildcard
wildcard 是 GNU Make 中的一个函数用于匹配文件名模式返回匹配到的文件列表。其基本语法如下
$(wildcard pattern)pattern: 文件名模式可以包含通配符 * 和 ?。 wildcard 函数会返回符合指定文件名模式的文件列表。通常这个函数用于获取文件列表并将其赋值给一个变量以便在 Makefile 中进一步处理这些文件。 我们写一个新的makefile: files$(wildcard *.c)使用 wildcard 函数匹配当前目录下所有以 .c 结尾的文件并将结果存储到变量 files 中。 最后结果
扩展应用可以用检查目录下面哪些文件是真实存在的 定义了一个变量 files2 包含了一组源文件名然后使用 wildcard 函数来获取这些文件的实际存在的文件列表并存储在变量 files3 中。注意d.c e.c是不存在的 最后结果
4.4、patsubst
patsubst 是在 Makefile 中用来替换模式的函数之一。它用于将一个字符串中符合指定模式的部分替换成另一个模式。基本语法是
$(patsubst pattern,replacement,text)pattern 是要匹配的模式可以包含 % 通配符表示零个或多个字符。 replacement 是替换的模式。 text 是要进行替换操作的原始文本。
我们改一下makefile: Makefile 中定义了一个变量 files2 包含了一组源文件名其中包含一个不是以 .c 结尾的文件 abc。接着使用 patsubst 函数将每个源文件名的扩展名从 .c 替换为 .d并将结果存储在变量 dep_files 中。
最后结果
五、Makefile升级
5.1、包含头文件在内的依赖关系自动生成依赖文件
我们使用前面一样的程序来进行实验 a.c:
#include stdio.h
void funB();
void funC();
int main() {funB();funC();return 0;
}b.c:
#include stdio.h
void funB() {printf(hello B!\n);}c.c进行了一点点修改
#include stdio.h
#include c.h
void funC() {printf(This is C%d\n,C);
}新建一个c.h
#define C 1 然后我们make加执行一下 我们这里做一个小小的改动把头文件中define的数从1改为2
#define C 2 然后我们再make一下看看效果 我们可以看到make并没有顺利执行而且C依然为1 说明还是有点小问题
那这是因为什么原因导致的呢 因为我们的c.c是依赖于c.h的但是makefile中并没有把这种依赖关系写出来所以makefile也不知道c.h更新了。
我们再makefile中重新添加了
c.o:c.c c.h然后我们再make一下就对了 但是这样做是不可能的在大型项目里面我们每一个C文件几乎都有头文件我们不可能手动把这些头文件的依赖关系一行行在makefile全部都写出来我们需要自动去生成这些规则。 在讲解今天的正式内容前我们先介绍三个查看依赖关系的命令
gcc -M c.c上述命令会使用 gcc 编译器并使用 -M 选项来生成 c.c 源文件的依赖关系是立刻打印出来 gcc -M -MF c.d c.c这个命令也会生成 c.c 源文件的依赖关系但是通过 -MF c.d 选项指定了输出文件为 c.d。这意味着生成的依赖关系将被保存到 c.d 文件中而不是输出到标准输出流。
gcc -c -o c.o c.c -MD -MF c.d这个命令用于编译 c.c 源文件为目标文件 c.o。参数 -c 表示编译成目标文件-o c.o 指定输出文件为 c.o。-MD 选项用于生成 .d 文件-MF c.d 则指定生成的依赖关系文件为 c.d。这意味着除了生成目标文件 c.o 外还会生成一个描述依赖关系的文件 c.d。 按照我们刚刚介绍的gcc依赖关系规则我们修改一下makefile: ls 命令用于列出目录中的文件和子目录。而 ls -a 命令也会列出目录中所有的文件和子目录包括以 . 开头的隐藏文件或隐藏目录这些文件或目录在普通的 ls 命令中是不可见的。
从这里我们可以看到这些依赖关系被自动生成出来了 刚刚makefile里的这句话就可以不用写了
c.o:c.c c.h我们再改一下makefile: 这一行使用了 Makefile 中的函数 patsubst它用于替换模式。具体来说% 是一个通配符表示每个目标文件名。 ( p a t s u b s t (patsubst %,.%.d, (patsubst(objs)) 的作用是将目标文件列表中的每个目标文件名a.o, b.o, c.o替换成相应的依赖关系文件名.a.o.d, .b.o.d, .c.o.d。 可以看到我们的依赖文件已经被生成出来了。
接下来我们再改一改把这些检测到的依赖文件包含进去
objsa.o b.o c.o
dep_files:$(patsubst %,.%.d,$(objs))
dep_files:$(wildcard $(dep_files))这里定义了目标文件列表 objs 和依赖关系文件列表 dep_files。使用 patsubst 函数将目标文件列表转换为相应的依赖关系文件列表然后通过 wildcard 函数获取实际存在的依赖关系文件。
ifneq ($(dep_files),)
include $(dep_files)
endif这里使用条件语句检查是否存在依赖关系文件列表 dep_files如果存在则通过 include 关键字包含这些依赖关系文件。这样Make 就能够了解源文件之间的依赖关系从而在需要重新编译时执行相应的规则。
我们这时候试一下修改c.h里的宏定义如下
#define C 3 这时候我们再make就不会再出现之前的情况了即宏定义改了make之后打印出来依然不变的情况 最后结果 我们可以看到改为了3make后就自动识别了头文件的依赖关系打印出来了3实现了我们最初的目标自动识别依赖关系自动生成依赖文件。
5.2、添加CFLAGS
我们在makefile里添加几条命令 CFLAGS-Werror 设置了一个编译选项即启用了 -Werror。这个选项的含义是将所有警告视为错误即编译过程中如果产生了任何警告就会导致编译失败。这样做的目的是强制要求代码中不允许存在任何警告以确保代码的质量和稳定性。
CFLAGS 是一个用于存储传递给 C 编译器的额外参数和标志的 Makefile 变量。这些参数和标志可以影响编译的行为例如警告级别、优化选项、头文件路径等 在 Makefile 中使用 CFLAGS 变量有助于集中管理编译选项使得构建过程更加灵活和易于维护。
当你设置 CFLAGS 时可以包含各种编译器选项这些选项会影响代码的编译和生成。以下是一些常见的 CFLAGS 选项的例子
启用调试信息
CFLAGS -g这个选项启用了编译器生成的调试信息有助于在调试阶段中进行源代码级别的调试。
优化级别
CFLAGS -O2这个选项启用了优化级别 2以提高生成代码的运行性能。可选的优化级别包括 -O0无优化、-O1、-O2、-O3 等。
指定头文件搜索路径
CFLAGS -I/path/to/include这个选项指定了编译器在搜索头文件时要查找的路径。
定义宏
CFLAGS -DDEBUG这个选项定义了一个名为 DEBUG 的宏可以在源代码中使用条件编译。
关闭某些警告
CFLAGS -Wno-unused-variable这个选项关闭了关于未使用变量的警告。
启用全部警告
CFLAGS -Wall -Wextra这个选项启用了大多数可用的警告帮助开发者发现潜在的问题。
指定目标架构
CFLAGS -marchnative这个选项根据编译运行代码的计算机的架构进行优化。
来个实际点的makefile例子
CC gcc
CFLAGS -g# 目标文件
TARGET my_program# 源文件
SRC main.call: $(TARGET)$(TARGET): $(SRC)$(CC) $(CFLAGS) -o $(TARGET) $(SRC)clean:rm -f $(TARGET)在这个例子中
CC 定义了编译器的可执行文件gcc。 CFLAGS 包含了编译器选项 -g表示启用调试信息。 TARGET 定义了目标文件的名称为 my_program。 SRC 定义了源文件的名称为 main.c。
Makefile 中的规则 all 是默认目标它依赖于 $(TARGET)当你运行 make 时它将编译生成目标文件。 $(TARGET) 的规则指定了如何生成目标文件。在这里它使用 gcc 编译器传递了 CFLAGS 和源文件生成可执行文件 my_program。 clean 规则用于清理生成的文件执行 make clean 将删除可执行文件。
六、通用Makefile模板
这个意思就是找一个经典的makefile模板 以后要写自己的makefile就在这个基础上改就行了