Makefile 静态模式——$(objects): %.o: %.c

静态模式

最近看Makefile,正常依赖没什么问题,但是遇到一些特殊的符号就有点不明白了,例如:$(objects): %.o: %.c、$<之类的。最近看了篇文章写的不错,贴出来供大家分享。

静态模式可以更加容易地定义多目标的规则,可以让我们的规则变得更加的有弹性和灵活。我们还是先来看一下语法:

<targets ...>: <target-pattern>: <prereq-patterns ...>
  <commands>
...

如果我们的<target-parrtern>定义成“%.o”,意思是我们的<target>集合中都是以“.o”结尾的,而如果我们的<prereq-parrterns>定义成“%.c”,意思是对<target-parrtern>所形成的目标集进行二次定义,其计算方法是,取<target-parrtern>模式中的“%”(也就是去掉了[.o]这个结尾),并为其加上[.c]这个结尾,形成的新集合。所以,我们的“目标模式”或是“依赖模式”中都应该有“%”这个字符。

没看明白没有关系,看下面的例子:

objects = foo.o bar.o
all: $(objects)
$(objects): %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@

上面的例子中,指明了我们的目标从$objects中获取,“%.o”表明要所有以“.o”结尾的目标,也就是“foo.o bar.o”,也就是变量$object集合的模式,而依赖模式“%.c”则取模式“%.o”的“%”,也就是“foo bar”,并为其加下“.c”的后缀,于是,我们的依赖目标就是“foo.c bar.c”。而命令中的“$<”和“$@”则是自动化变量,“$<”表示所有的依赖目标集(也就是“foo.c bar.c”),“$@”表示目标集(也就是“foo.o bar.o”)。于是,上面的规则展开后等价于下面的规则:

foo.o : foo.c
$(CC) -c $(CFLAGS) foo.c -o foo.o
bar.o : bar.c
$(CC) -c $(CFLAGS) bar.c -o bar.o

试想,如果我们的“%.o”有几百个,那种我们只要用这种很简单的“静态模式规则”就可以写完一堆规则,实在是太有效率了。“静态模式规则”的用法很灵活,如果用得好,那会一个很强大的功能。 

.c.o:
gcc -c -o $*.o $<
.c.o:
这句话的意思就是 %.o : %.c
也就是说,所有的.o文件,依赖于对应的.c文件
比如有三个a.c b.c c.c
那么就会有 a.o b.o c.o
a.o : a.c
b.o : b.c
c.o : c.c
这是makefile依赖的一种简写方法。makefile的依赖关系有很多种写法。这是其中一种。

### 使用Makefile编译STM32 HAL库 为了使用Makefile编译基于STM32 HAL库的项目,需要理解Makefile的工作原理以及如何配置其规则以适应特定硬件平台的需求。以下是关于如何构建一个适用于STM32 HAL项目的Makefile的具体说明。 #### Makefile基本结构 Makefile是一种用于定义软件构建过程的脚本文件,在STM32开发中通常用来自动化编译、链接和烧录程序的过程。通过编写合适的规则,可以实现从源代码到最终二进制文件(如`.hex`或`.bin`)的一键生成[^1]。 #### 工具链准备 在Linux环境下,确保已安装必要的交叉编译工具链,例如GNU Arm Embedded Toolchain。可以通过以下命令验证是否已经安装: ```bash arm-none-eabi-gcc --version ``` 如果未安装,则需先完成安装操作后再继续后续步骤。 #### 定义变量 在一个典型的Makefile中,会定义多个全局变量来简化维护工作并提高灵活性。这些可能包括但不限于目标名称(`TARGET`)、使用的CPU架构(`MCU`)、优化选项(OPTIMIZE)等参数设置: ```makefile # Project name (without extension) TARGET := LED_project # MCU type, e.g., STM32F103RCT6 -> mcu=stm32f1xx MCU := cortex-m3 # Optimization level (-O0,-Os,...) OPTIMIZE := Os # Compiler flags CFLAGS += -std=c99 \ -Wall \ -Wextra \ -pedantic-errors \ -mcpu=$(MCU) \ -mlittle-endian \ -mthumb \ -ffunction-sections \ -fdata-sections \ -nostartfiles \ -MD \ -MP \ -MF"$(@:%.o=%.d)" \ -MT"$@" \ -MMD \ -g3 \ -O$(OPTIMIZE) LDFLAGS += -T linker_script.ld \ -Map="$(OUTPUT_DIRECTORY)/$(TARGET).map" \ -specs=nano.specs \ -specs=nosys.specs \ -Wl,--gc-sections \ -lm ``` 上述片段展示了如何指定编译器标志(CFLAGS)与连接器标志(LDFLAGS)。 #### 添加头文件路径和支持库 为了让编译器能够找到所有的依赖项,还需要声明额外的包含目录(-I)及静态/动态库位置(-L): ```makefile INCLUDE_PATHS = ./Inc ../Drivers/STM32F1xx_HAL_Driver/Inc ../Middlewares/Third_Party/FatFs/src LIBRARY_PATHS = ../Drivers/STM32F1xx_HAL_Driver/Src vpath %.h $(INCLUDE_PATHS) vpath %.c $(SOURCE_FILES_DIR):../Drivers/STM32F1xx_HAL_Driver/Src:../Middlewares/Third_Party/FatFs/src ``` 这里假设HAL驱动位于相对路径下的`Drivers/STM32F1xx_HAL_Driver`子文件夹内。 #### 构建规则 最后部分描述实际的操作流程——即当运行`make all`时应该做什么;同样重要的是清理残留物的功能(`clean target`) : ```makefile all : $(BUILD_OUTPUT)/$(TARGET).elf $(BUILD_OUTPUT)/$(TARGET).hex $(BUILD_OUTPUT)/$(TARGET).bin %.elf : $(OBJECTS) @echo 'Building target: $@' @echo 'Invoking: Cross ARM C Linker' arm-none-eabi-gcc $(LDFLAGS) -o "$@" $^ @echo 'Finished building target: $@' %.hex : %.elf @echo 'Creating hex file from ELF output...' arm-none-eabi-objcopy -O ihex "$<" "$@" %.bin : %.elf @echo 'Generating binary image...' arm-none-eabi-objcopy -O binary "$<" "$@" clean : rm -rf $(BUILD_OUTPUT)/*.o *.d *~ core .depend .*.cmd *.list *.bak *.mod o* *.a *.lnk *.map V* ``` 以上代码块解释了整个制作过程中涉及的主要环节及其相互关系。 #### 处理C++兼容性问题 对于某些特殊场景比如混合编程环境里既有纯C也有现代C++组件存在的情况下,则需要注意解决两者之间互操作性的挑战。一种常见做法是在适当的地方加入条件判断语句以便正确处理跨语言调用情况: ```cpp #ifdef __cplusplus extern "C" { #endif void SysTick_Handler(void); #ifdef __cplusplus } #endif ``` 这样做的目的是告诉C++编译器按照传统方式对待这部分函数签名而不是遵循名字修饰惯例[^3]。 ---
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值