You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Makefile中用`%.o: %.c`让avr-gcc将*.o输出到独立文件夹?

解决AVR项目Makefile编译产物目录问题

核心问题分析

你的需求是将所有编译产物(.o、.elf、*.hex)统一放到build目录,但之前的尝试失败主要有两个原因:

  • OBJECTS变量直接把源文件路径替换成.o,导致Make期望的.o文件和源文件同目录,而非build目录
  • 模式规则中直接使用build/$@时,会尝试在build下创建和源文件相同的子目录(比如build/src/xxx.o),但该子目录不存在,导致编译失败

修正后的完整Makefile

# My whole Makefile

MCU   = atmega328p
F_CPU = 1000000UL  
BAUD  = 9600UL

LIBDIR = ~/Developer/bin/avr8-gnu-toolchain-darwin_x86_64/avr/include

PROGRAMMER_TYPE = usbtiny
PROGRAMMER_ARGS =   

CC = avr-gcc
OBJCOPY = avr-objcopy
OBJDUMP = avr-objdump
AVRSIZE = avr-size
AVRDUDE = avrdude

##########------------------------------------------------------##########

# 确保build目录存在
BUILD_DIR = build
$(BUILD_DIR):
	mkdir -p $@

TARGET = $(lastword $(subst /, ,$(CURDIR)))

# 收集所有源文件(根目录和src目录,排除库目录源文件避免不必要编译)
SOURCES = $(wildcard *.c src/*.c)
# 将所有源文件转换为build目录下的.o文件:根目录xxx.c→build/xxx.o;src/xxx.c→build/xxx.o
OBJECTS = $(patsubst %.c,$(BUILD_DIR)/%.o,$(notdir $(SOURCES)))

# 收集所有头文件
HEADERS = $(wildcard *.h src/*.h)

CPPFLAGS = -DF_CPU=$(F_CPU) -DBAUD=$(BAUD) -I. -Isrc -I$(LIBDIR)
CFLAGS = -Os -g -std=gnu99 -Wall
CFLAGS += -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums 
CFLAGS += -ffunction-sections -fdata-sections 
LDFLAGS = -Wl,-Map,$(BUILD_DIR)/$(TARGET).map 
LDFLAGS += -Wl,--gc-sections 
TARGET_ARCH = -mmcu=$(MCU)

# 模式规则:处理根目录的.c文件,生成到build目录
$(BUILD_DIR)/%.o: %.c $(HEADERS) | $(BUILD_DIR)
	$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@

# 模式规则:处理src目录的.c文件,生成到build目录
$(BUILD_DIR)/%.o: src/%.c $(HEADERS) | $(BUILD_DIR)
	$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@

$(BUILD_DIR)/$(TARGET).elf: $(OBJECTS)
	$(CC) $(LDFLAGS) $(TARGET_ARCH) $^ $(LDLIBS) -o $@

$(BUILD_DIR)/%.hex: $(BUILD_DIR)/%.elf
	$(OBJCOPY) -j .text -j .data -O ihex $< $@

all: $(BUILD_DIR)/$(TARGET).hex 

size:  $(BUILD_DIR)/$(TARGET).elf
	$(AVRSIZE) -C --mcu=$(MCU) $<

clean:
	rm -rf $(BUILD_DIR)

flash: $(BUILD_DIR)/$(TARGET).hex 
	$(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -U flash:w:$<

关键修改点说明

  1. 统一产物路径:

    • 使用$(notdir $(SOURCES))去掉源文件的目录前缀,再通过patsubst转换为build目录下的.o文件,确保所有.o都在build根目录下
    • 新增BUILD_DIR变量,所有编译产物路径都基于该变量,避免硬编码
  2. 修复模式规则:

    • 新增两条模式规则,分别处理根目录和src目录的源文件,直接生成到build目录
    • 使用| $(BUILD_DIR)作为顺序依赖,确保编译前先创建build目录
  3. 优化头文件依赖:

    • 明确收集根目录和src目录的头文件,同时在CPPFLAGS中添加-Isrc,让编译器能找到src目录下的头文件
  4. 简化clean命令:

    • 直接删除整个build目录,避免遗漏编译产物

之前方案的错误解析

  • 方案1:build/$@中的$@包含源文件的目录(比如src/RotaryEncoder.o),导致尝试创建build/src/RotaryEncoder.o,但build/src目录不存在,编译失败
  • 方案2:$(HEADERS)变量包含了库目录的头文件,可能导致Make认为目标已经是最新状态,跳过编译步骤,后续的mv和echo自然不会执行
  • 方案3:mv $@ build会把src/RotaryEncoder.o移动到build/src/RotaryEncoder.o,但build/src目录不存在,所以报错;同时$^会把所有依赖(包括头文件)传给编译器,可能引发编译错误

内容的提问来源于stack exchange,提问作者user19681631

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 01:40:24