如何编写Makefile实现按目标切换编译器与编译标志?
解决Makefile多平台编译变量生效问题
你的问题根源在于:bm/linux目标里的eval是目标执行阶段才会运行的,但Make在解析规则时,会先处理依赖的OBJECTS编译逻辑,这时候变量还是默认值,所以编译.o文件时根本没用到你设置的编译器和标志。
推荐两种可行的Makefile结构,优先选第一种:
方案一:独立目录+目标专属变量(推荐)
核心思路是为不同平台单独存放目标文件,避免编译冲突,同时利用Make的目标专属变量特性,让对应平台的编译规则自动继承正确的编译器和标志:
SRC := . BINARY := hello_world # 为裸金属、Linux平台分别创建独立的目标文件目录 OBJ_BM := build/bm OBJ_LINUX := build/linux # 生成各平台对应的源文件、目标文件列表 SOURCES := $(wildcard $(SRC)/*.c) OBJECTS_BM := $(patsubst $(SRC)/%.c, $(OBJ_BM)/%.o, $(SOURCES)) OBJECTS_LINUX := $(patsubst $(SRC)/%.c, $(OBJ_LINUX)/%.o, $(SOURCES)) # 默认编译Linux版本 all: linux # 裸金属平台目标:生成专属二进制文件 bm: $(BINARY)_bm $(BINARY)_bm: $(OBJECTS_BM) $(CC) $(LDFLAGS) $^ -o $@ # Linux平台目标:生成专属二进制文件 linux: $(BINARY)_linux $(BINARY)_linux: $(OBJECTS_LINUX) $(CC) $(LDFLAGS) $^ -o $@ # 裸金属平台的.o文件编译规则 $(OBJ_BM)/%.o: $(SRC)/%.c | $(OBJ_BM) $(CC) $(CFLAGS) -I$(SRC) -c $< -o $@ # 为bm目标绑定专属编译器和编译标志 bm CC := aarch64-none-elf-gcc bm CFLAGS += -DBAREMETAL -ffreestanding # Linux平台的.o文件编译规则 $(OBJ_LINUX)/%.o: $(SRC)/%.c | $(OBJ_LINUX) $(CC) $(CFLAGS) -I$(SRC) -c $< -o $@ # 为linux目标绑定专属编译器 linux CC := aarch64-none-linux-gnu-gcc # 自动创建目标文件目录 $(OBJ_BM) $(OBJ_LINUX): mkdir -p $@ # 清理所有编译产物 clean: rm -rf build $(BINARY)_bm $(BINARY)_linux
方案优势:
- 不同平台的目标文件完全隔离,切换平台无需清理旧文件,避免编译冲突
- 目标专属变量会自动传递给所有依赖该目标的规则,确保编译阶段使用正确的编译器和标志
- 生成不同命名的二进制文件(如
hello_world_bm、hello_world_linux),方便区分使用
方案二:递归Make调用(简易版)
如果不想维护多目录,可以通过递归调用Make,直接在命令行传递变量:
SRC := . OBJ := . SOURCES := $(wildcard $(SRC)/*.c) OBJECTS = $(patsubst $(SRC)/%.c, $(OBJ)/%.o, $(SOURCES)) BINARY := hello_world all: linux # 调用make时传递裸金属平台的编译参数 bm: $(MAKE) CC=aarch64-none-elf-gcc CFLAGS="-DBAREMETAL -ffreestanding" $(BINARY) # 调用make时传递Linux平台的编译参数 linux: $(MAKE) CC=aarch64-none-linux-gnu-gcc $(BINARY) $(BINARY): $(OBJECTS) $(CC) $(LDFLAGS) $(OBJECTS) -o $@ $(OBJ)/%.o: $(SRC)/%.c $(CC) $(CFLAGS) -I$(SRC) -c $< -o $@ clean: rm -f $(OBJECTS) $(BINARY)
注意事项:
- 切换平台前必须执行
make clean,否则旧的.o文件会被复用,导致编译结果不符合预期 - 所有平台共用一套目标文件,适合简单场景,复杂项目容易出现问题
内容的提问来源于stack exchange,提问作者jkang
相关产品推荐
相关产品推荐

