利用make命令编译链接STM32工程

一、前言

入门STM32开发时,用的是keil 这个IDE。后面因为要提高开发效率和keil 版权问题,选择开源的arm-none-eabi-gcc ,通过命令行调用make工具进行编译、链接、烧录,打包。

二、要达到的效果

2.1 编译STM32 工程

make all

2.2 烧录单片机

make install

三、几个重要的问题点

3.1 如何高效地编译每一个源文件

3.1.1 源文件编译的原理

我们知道,编译器是对每一个源文件进行编译,生成对象文件,然后再把所有的对象文件链接起来,生成最终的elf文件。一个工程那么多源文件,怎么高效地组织源文件进行编译?

3.1.2 常见的一些组织源文件的方式

我之前在网上看到的做法,都是在makefile 文件中定义一个C_SOURCES 变量 , 作为源文件容器, 将工程中的源文件,都一一写到这个变量中去,然后在后面的处理中,利用这个C_SOURCES 变量 进行处理。

3.1.3 存在的问题

一个工程那么大,每次添加或减少一个源文件,或者代码重构时,移动源文件的路径,都要修改一次makefile ,太麻烦。按照一般的处理,makefile 文件更新时,都会重新编译整个工程,效率太低。

3.1.4 自动生成源文件列表

C_SOURCES =$(shell find . -name "*.c")

这里利用了make的shell 函数,调用外部bash 的find 命令,找到所有的当前目录下的.c 文件,保存到列表中。我们也可以利用这个思路,解决源文件并不完全在当前目录的情况。

3.2 如何找到所有的头文件

与前面描述源文件的情况类似,我们需要自动生成一个所有头文件的路径的列表。
C_INCLUDES=$(addprefix -I, sort $(dir $(shell find . -name "*.h")))

  • 利用make 的shell函数,调用外部bash 的find 命令,找到当前工程的所有.h 文件(包含路径)。
  • 我们需要的其实是路径。所以调用make 的dir 命令,截取路径。
  • 如果同一个路径下,有多个.h 文件,我们上述处理后,会有重复的路径。所以调用make 的sort 命令,主要是为了利用它过滤掉重复字符串的特性。
  • 上述处理,已经得到工程中所有头文件的路径。由于gcc 寻找头文件时,需要在每个路径前面加 ‘-I’ 前缀,所以,调用make的addprefix 函数,给每个路径加上-I 前缀。

3.3 怎么解决头文件依赖关系

每个源文件都可能包含一个或者多个头文件,它包含的头文件还可能包含其他头文件。我们要解决的问题有两个:

3.3.1 怎样高效找到源文件依赖的所有头文件

gcc 的-M 选项自动生成目标文件和源文件和依赖关系。-M 选项会把系统头文件都包含进来,如果不需要输出系统头文件的依赖关系,可以-MM 选项。一般情况下,依赖文件是后缀为.d 的文件。例如main.c 的依赖文件是main.d 。

CFLAGS += -MMD -MP -MF"$(@:%.o=%.d)"

$(BUILD_DIR)/%.o: %.c Makefile | $(BUILD_DIR)
    $(CC) -c $(CFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $@

3.3.2 依赖的头文件修改了,怎么自动编译源文件

上面针对所有的.c 文件生成依赖文件(.d)文件存放在BUILD_DIR 目录下。依赖文件,以规则的语法,列出了对象文件的依赖头文件
利用make命令编译链接STM32工程
利用make命令编译链接STM32工程

既然依赖文件是一条条规则,我们将所有的规则包含进makefile,make就可以自动推导了
-include $(wildcard $(BUILD_DIR)/*.d)

3.4 编译链接优化问题

3.4.1 设置编译优化等级

$(CC) -O2 main.c -o main.o

3.4.2 没使用的函数不链接

  • 在编译源文件时,在gcc 编译选项中增加 -ffunction-sections、-fdata-sections , 在编译生成的目标文件中,会将每个函数或者数据段,放在单独的section中。
  • 在前面的基础上,链接对象文件时,加上-Wl,--gc-sections 参数,链接器不会链接未使用的函数,从而达到压缩hex文件的目的。

四、 用到的make 相关知识整理

4.1 shell 函数的用法

  • shell 函数的参数是操作系统的shell 命令,shell函数把执行操作系统命令后的输出作为函数返回。
    函数使用格式如下:

  • $(shell <shell cmd> <shell cmd argmuments>)

  • 示例:
    contents := $(shell cat foo)

4.2 排序函数sort

  • $(sort <list>)
  • 函数给列表中的子字符串排序(升序),并且会去掉相同的子串
  • 示例: $(sort foo bar lose) 返回 bar foo lose

4.3 取目录函数dir

  • $(dir <names...> )
  • 从文件名系列中取出目录部分,目录部分是指最后一个反斜杠(/)之前的部分。如果没有反斜杠,那么返回./
  • 示例:$(dir src/foo.c hacks) 返回 src/ ./

4.4 加前缀函数

  • $(addprefix <prefix>, <names...> )
  • 把前缀 加到 中的每个字符串前面。返回加过前缀的字符串系列。
  • 示例:$(addprefix src/, foo bar) 返回 src/foo src/bar

五、makefile 源码

gitee

六、参考链接:

上一篇:xv6实验0-环境搭建


下一篇:Netty网络框架学习笔记V4.x-1(NIO知识_2022-01-20)