可以自动搜索的Makefile

刚开始接触linux的时候,编译应用层经常使用Makefile,每次新增和删除.c文件都要在Makefile中重新指定,有时候还要修改头文件和库文件路径,当时会写很多可执行程序,难忍其烦,所以利用find指令构建了一个可以自动搜索.c,.cpp或者自定义后缀的文件,搜索.h,.so, .a。(尽管cmake也有这个功能,但是cmake自己也很麻烦)

首先提供一个linux下同时编译.c和.cpp的,链接成opencv可执行程序,所使用的Makefile

# linux下使用/bin/dash,echo -e 会将 -e 输出,windows下可删除此行
SHELL = /bin/bash

# 定义目标文件名称
BUILD_DIR := ./build/
TARGET := ./execute

# 定义工具链路径和编译器路径
TOOLCHAIN_DIR := /usr/
COMPILER_PATH := $(TOOLCHAIN_DIR)bin/

# 定义编译器和链接器命令和标志
CC := $(COMPILER_PATH)gcc
CX := $(COMPILER_PATH)g++
LD := $(COMPILER_PATH)g++
SZ := $(COMPILER_PATH)size
OBJCOPY := $(COMPILER_PATH)objcopy

CC_MARK := .xc
CX_MARK := .xc++
CC_FLAG := -xc -g
CX_FLAG := -xc++ -g
LD_FLAG :=

HEAD_PATH := -I$(TOOLCHAIN_DIR)/include/ -I/usr/local/include/opencv4/
LIB_PATH := -L$(TOOLCHAIN_DIR)/lib/ -L/usr/local/lib/
LIB_FLAG := -lopencv_core -lopencv_highgui -lopencv_imgproc -lopencv_imgcodecs
SRC_LIB :=
SRC_CC :=
SRC_CX :=
EXCLUDE_FILES :=

# 设置源文件列表
HEAD_TYPE := .h .hpp .hh
LIB_TYPE := .a .so
CC_TYPE := .c
CX_TYPE := .cpp .cc .cu

HEAD_TYPE_SIFT := $(patsubst .%,%,$(subst $(empty) .,\|,$(HEAD_TYPE)))
LIB_TYPE_SIFT := $(patsubst .%,%,$(subst $(empty) .,\|,$(LIB_TYPE)))
CC_TYPE_SIFT := $(patsubst .%,%,$(subst $(empty) .,\|,$(CC_TYPE)))
CX_TYPE_SIFT := $(patsubst .%,%,$(subst $(empty) .,\|,$(CX_TYPE)))

LOCAL_HEAD := $(shell find . -type f -regex ".*\.\($(HEAD_TYPE_SIFT)\)" -printf "%P ")
LOCAL_LIB := $(shell find . -type f -regex ".*\.\($(LIB_TYPE_SIFT)\)" -printf "%P ")
LOCAL_CC := $(shell find . -type f -regex ".*\.\($(CC_TYPE_SIFT)\)" -printf "%P ")
LOCAL_CX := $(shell find . -type f -regex ".*\.\($(CX_TYPE_SIFT)\)" -printf "%P ")

HEAD_PATH += $(addprefix -I,$(sort $(dir $(filter-out $(EXCLUDE_FILES),$(LOCAL_HEAD)))))
SRC_LIB += $(filter-out $(EXCLUDE_FILES),$(LOCAL_LIB))
SRC_CC += $(filter-out $(EXCLUDE_FILES),$(LOCAL_CC))
SRC_CX += $(filter-out $(EXCLUDE_FILES),$(LOCAL_CX))

# 生成对象文件和依赖文件的列表
OBJ_CC := $(addprefix $(BUILD_DIR),$(notdir $(SRC_CC:%=%$(CC_MARK).o)))
OBJ_CX := $(addprefix $(BUILD_DIR),$(notdir $(SRC_CX:%=%$(CX_MARK).o)))

# 生成源文件查找路径
vpath % $(sort $(dir $(SRC_CC))) $(sort $(dir $(SRC_CX)))


# 定义all依赖
all : $(TARGET)
	@echo "  CHECK     $<"
	@$(SZ) $<

# 定义编译和链接命令和规则
$(TARGET): $(OBJ_CC) $(OBJ_CX) $(SRC_LIB)
	@echo "  LN   $^ -> $@"
	@$(LD) $(LD_FLAG) -o $@ $^ $(LIB_PATH) $(LIB_FLAG)

$(BUILD_DIR)%$(CC_MARK).o : % Makefile | $(BUILD_DIR) 
	@echo "  CC   $<"
	@$(CC) $(CC_FLAG) $(HEAD_PATH) -MMD -MP -MF"$(@:%.o=%.d)" -c $< -o $@

$(BUILD_DIR)%$(CX_MARK).o : % Makefile | $(BUILD_DIR) 
	@echo "  CX   $<"
	@$(CX) $(CX_FLAG) $(HEAD_PATH) -MMD -MP -MF"$(@:%.o=%.d)" -c $< -o $@

$(BUILD_DIR) :
	@echo "  MK   $@"
	@mkdir $@

# 包含依赖文件
-include $(wildcard $(BUILD_DIR)*.d)

# 生成compile_commands.json文件的规则
json: $(SRC_CC) $(SRC_CX) Makefile
	@echo "Generating compile_commands.json"
	@rm -f compile_commands.json
	@echo "[" > compile_commands.json
	@for file in $(SRC_CC); do \
		compile_cmd="$(CC) $(CC_FLAG) $(HEAD_PATH) -c $${file} -o $${file}.$(CC_MARK).o" \
		command_json=" { \"arguments\": [ \"$$compile_cmd\" ], \"directory\": \"${PWD}\", \"file\": \"$$file\" },"; \
		echo -e "$$command_json" >> compile_commands.json ; \
	done
	@for file in $(SRC_CX); do \
		compile_cmd="$(CX) $(CX_FLAG) $(HEAD_PATH) -c $${file} -o $${file}.$(CX_MARK).o" \
		command_json=" { \"arguments\": [ \"$$compile_cmd\" ], \"directory\": \"${PWD}\", \"file\": \"$$file\" },"; \
		echo -e "$$command_json" >> compile_commands.json ; \
	done
	@sed -i '$$ s/,$$/\n]/' compile_commands.json

# 定义清理规则
clean:
	rm -fR $(TARGET) $(BUILD_DIR)

.PHONY: all clean json

然后是一个windows下,使用minGW编译简单可执行程序的Makefile

# linux下使用/bin/dash,echo -e 会将 -e 输出,windows下可删除此行
# SHELL = /bin/bash

# 定义目标文件名称
BUILD_DIR := ./build/
TARGET := ./execute.exe

# 定义工具链路径和编译器路径
TOOLCHAIN_DIR := C:/PATH/w64devkit/
COMPILER_PATH := $(TOOLCHAIN_DIR)bin/

# 定义编译器和链接器命令和标志
CC := $(COMPILER_PATH)gcc.exe
CX := $(COMPILER_PATH)g++.exe
LD := $(COMPILER_PATH)g++.exe
SZ := $(COMPILER_PATH)size.exe
OBJCOPY := $(COMPILER_PATH)objcopy.exe

CC_MARK := .xc
CX_MARK := .xc++
CC_FLAG := -xc
CX_FLAG := -xc++
LD_FLAG :=

# 定义头文件、库文件和源文件路径和类型
HEAD_PATH := -I$(TOOLCHAIN_DIR)x86_64-w64-mingw32/include/
LIB_PATH := -L$(TOOLCHAIN_DIR)x86_64-w64-mingw32/lib/
LIB_FLAG :=
SRC_LIB :=
SRC_CC :=
SRC_CX :=
EXCLUDE_FILES :=

HEAD_TYPE := .h .hpp .hh
LIB_TYPE := .a .so
CC_TYPE := .c
CX_TYPE := .cpp

# 设置源文件列表
HEAD_TYPE_SIFT := $(patsubst .%,%,$(subst $(empty) .,\|,$(HEAD_TYPE)))
LIB_TYPE_SIFT := $(patsubst .%,%,$(subst $(empty) .,\|,$(LIB_TYPE)))
CC_TYPE_SIFT := $(patsubst .%,%,$(subst $(empty) .,\|,$(CC_TYPE)))
CX_TYPE_SIFT := $(patsubst .%,%,$(subst $(empty) .,\|,$(CX_TYPE)))

LOCAL_HEAD := $(shell find . -type f -regex ".*\.\($(HEAD_TYPE_SIFT)\)" -printf "%P ")
LOCAL_LIB := $(shell find . -type f -regex ".*\.\($(LIB_TYPE_SIFT)\)" -printf "%P ")
LOCAL_CC := $(shell find . -type f -regex ".*\.\($(CC_TYPE_SIFT)\)" -printf "%P ")
LOCAL_CX := $(shell find . -type f -regex ".*\.\($(CX_TYPE_SIFT)\)" -printf "%P ")

HEAD_PATH += $(addprefix -I,$(sort $(dir $(filter-out $(EXCLUDE_FILES),$(LOCAL_HEAD)))))
SRC_LIB += $(filter-out $(EXCLUDE_FILES),$(LOCAL_LIB))
SRC_CC += $(filter-out $(EXCLUDE_FILES),$(LOCAL_CC))
SRC_CX += $(filter-out $(EXCLUDE_FILES),$(LOCAL_CX))

# 生成对象文件和依赖文件的列表
OBJ_CC := $(addprefix $(BUILD_DIR),$(notdir $(SRC_CC:%=%$(CC_MARK).o)))
OBJ_CX := $(addprefix $(BUILD_DIR),$(notdir $(SRC_CX:%=%$(CX_MARK).o)))

# 生成源文件查找路径
vpath % $(sort $(dir $(SRC_CC))) $(sort $(dir $(SRC_CX)))

# 定义all依赖
all : $(TARGET)
	@echo "  CHECK     $<"
	@$(SZ) $<

# 定义编译和链接命令和规则
$(TARGET): $(OBJ_CC) $(OBJ_CX) $(SRC_LIB)
	@echo "  LN   $^ -> $@"
	@$(LD) $(LD_FLAG) -o $@ $^ $(LIB_PATH) $(LIB_FLAG) $(SRC_LIB)

$(BUILD_DIR)%$(CC_MARK).o : % Makefile | $(BUILD_DIR) 
	@echo "  CC   $<"
	@$(CC) $(CC_FLAG) $(HEAD_PATH) -MMD -MP -MF"$(@:%.o=%.d)" -c $< -o $@

$(BUILD_DIR)%$(CX_MARK).o : % Makefile | $(BUILD_DIR) 
	@echo "  CX   $<"
	@$(CX) $(CX_FLAG) $(HEAD_PATH) -MMD -MP -MF"$(@:%.o=%.d)" -c $< -o $@

$(BUILD_DIR) :
	@echo "  MK   $@"
	@mkdir $@

# 包含依赖文件
-include $(wildcard $(BUILD_DIR)*.d)

# 生成compile_commands.json文件的规则
json: $(SRC_CC) $(SRC_CX) Makefile
	@echo "Generating compile_commands.json"
	@rm -f compile_commands.json
	@echo "[" > compile_commands.json
	@for file in $(SRC_CC); do \
		compile_cmd="$(CC) $(CC_FLAG) $(HEAD_PATH) -c $${file} -o $${file}.$(CC_MARK).o" \
		command_json=" { \"arguments\": [ \"$$compile_cmd\" ], \"directory\": \"${PWD}\", \"file\": \"$$file\" },"; \
		echo -e "$$command_json" >> compile_commands.json ; \
	done
	@for file in $(SRC_CX); do \
		compile_cmd="$(CX) $(CX_FLAG) $(HEAD_PATH) -c $${file} -o $${file}.$(CX_MARK).o" \
		command_json=" { \"arguments\": [ \"$$compile_cmd\" ], \"directory\": \"${PWD}\", \"file\": \"$$file\" },"; \
		echo -e "$$command_json" >> compile_commands.json ; \
	done
	@sed -i '$$ s/,$$/\n]/' compile_commands.json

# 定义清理规则
clean:
	rm -fR $(TARGET) $(BUILD_DIR)

.PHONY: all clean json

最后是一个windows下,使用arm-none-eabi-gcc为 STM32F103C8T6   编译程序的Makefile,可以同事编译.c和.s

# linux下使用/bin/dash,echo -e 会将 -e 输出,windows下可删除此行
# SHELL = /bin/bash

# 定义目标文件名称
BUILD_DIR := ./build/
TARGET := app

# 定义工具链路径和编译器路径
TOOLCHAIN_DIR := C:/PATH/Arm_Development_Toolchains/gcc-arm-none-eabi-10.3-2021.10/
COMPILER_PATH := $(TOOLCHAIN_DIR)bin/

# 定义编译器和链接器命令和标志
CC := $(COMPILER_PATH)arm-none-eabi-gcc.exe
CX := $(COMPILER_PATH)arm-none-eabi-gcc.exe
LD := $(COMPILER_PATH)arm-none-eabi-gcc.exe
SZ := $(COMPILER_PATH)arm-none-eabi-size.exe
OBJCOPY := $(COMPILER_PATH)arm-none-eabi-objcopy.exe

CC_MARK := .xc
CX_MARK := .xs
CC_FLAG := -xc -mcpu=cortex-m3 -mthumb -DUSE_HAL_DRIVER -DSTM32F103xB -Og -Wall -fdata-sections -ffunction-sections -g -gdwarf-2
CX_FLAG := -x assembler-with-cpp -mcpu=cortex-m3 -mthumb -DUSE_HAL_DRIVER -DSTM32F103xB -Og -Wall -fdata-sections -ffunction-sections -g -gdwarf-2
LD_FLAG := -mcpu=cortex-m3 -mthumb -specs=nano.specs -TConfig/STM32F103C8Tx_FLASH.ld -Wl,--gc-sections 

# 定义头文件、库文件和源文件路径和类型
HEAD_PATH :=
LIB_PATH :=
LIB_FLAG := -lc -lm -lnosys
SRC_LIB :=
SRC_CC :=
SRC_CX :=
EXCLUDE_FILES := 
HEAD_TYPE := .h
LIB_TYPE := .a .so
CC_TYPE := .c
CX_TYPE := .s

# 设置源文件列表
HEAD_TYPE_SIFT := $(patsubst .%,%,$(subst $(empty) .,\|,$(HEAD_TYPE)))
LIB_TYPE_SIFT := $(patsubst .%,%,$(subst $(empty) .,\|,$(LIB_TYPE)))
CC_TYPE_SIFT := $(patsubst .%,%,$(subst $(empty) .,\|,$(CC_TYPE)))
CX_TYPE_SIFT := $(patsubst .%,%,$(subst $(empty) .,\|,$(CX_TYPE)))

LOCAL_HEAD := $(shell find . -type f -regex ".*\.\($(HEAD_TYPE_SIFT)\)" -printf "%P ")
LOCAL_LIB := $(shell find . -type f -regex ".*\.\($(LIB_TYPE_SIFT)\)" -printf "%P ")
LOCAL_CC := $(shell find . -type f -regex ".*\.\($(CC_TYPE_SIFT)\)" -printf "%P ")
LOCAL_CX := $(shell find . -type f -regex ".*\.\($(CX_TYPE_SIFT)\)" -printf "%P ")

HEAD_PATH += $(addprefix -I,$(sort $(dir $(filter-out $(EXCLUDE_FILES),$(LOCAL_HEAD)))))
SRC_LIB += $(filter-out $(EXCLUDE_FILES),$(LOCAL_LIB))
SRC_CC += $(filter-out $(EXCLUDE_FILES),$(LOCAL_CC))
SRC_CX += $(filter-out $(EXCLUDE_FILES),$(LOCAL_CX))

# 生成对象文件和依赖文件的列表
OBJ_CC := $(addprefix $(BUILD_DIR),$(notdir $(SRC_CC:%=%$(CC_MARK).o)))
OBJ_CX := $(addprefix $(BUILD_DIR),$(notdir $(SRC_CX:%=%$(CX_MARK).o)))

# 生成源文件查找路径
vpath % $(sort $(dir $(SRC_CC))) $(sort $(dir $(SRC_CX)))

# 定义all依赖
all : $(TARGET).elf $(TARGET).hex $(TARGET).bin
	@echo "  CHECK     $<"
	@$(SZ) $<

$(TARGET).hex : $(TARGET).elf
	@echo "  OBJCOPY   $^ -> $@"
	@$(OBJCOPY) -O ihex $< $@

$(TARGET).bin : $(TARGET).elf
	@echo "  OBJCOPY   $^ -> $@"
	@$(OBJCOPY) -O binary -S $< $@

# 定义编译和链接命令和规则
$(TARGET).elf : $(OBJ_CC) $(OBJ_CX) $(SRC_LIB)
	@echo "  LN   $^ -> $@"
	@$(LD) $(LD_FLAG) -Wl,-Map=$(BUILD_DIR)/$(TARGET).map,--cref -Wl,--gc-sections -o $@ $^ $(LIB_PATH) $(LIB_FLAG) $(SRC_LIB)

$(BUILD_DIR)%$(CC_MARK).o : % Makefile | $(BUILD_DIR) 
	@echo "  CC   $<"
	@$(CC) $(CC_FLAG) $(HEAD_PATH) -Wa,-a,-ad,-alms="$(@:%.o=%.lst)" -MMD -MP -MF"$(@:%.o=%.d)" -c $< -o $@

$(BUILD_DIR)%$(CX_MARK).o : % Makefile | $(BUILD_DIR) 
	@echo "  CX   $<"
	@$(CX) $(CX_FLAG) $(HEAD_PATH) -Wa,-a,-ad,-alms="$(@:%.o=%.lst)" -MMD -MP -MF"$(@:%.o=%.d)" -c $< -o $@

$(BUILD_DIR) :
	@echo "  MK   $@"
	@mkdir $@

# 包含依赖文件
-include $(wildcard $(BUILD_DIR)*.d)

# 生成compile_commands.json文件的规则
json: $(SRC_CC) $(SRC_CX) Makefile
	@echo "Generating compile_commands.json"
	@rm -f compile_commands.json
	@echo "[" > compile_commands.json
	@for file in $(SRC_CC); do \
		compile_cmd="$(CC) $(CC_FLAG) $(HEAD_PATH) -c $${file} -o $${file}.$(CC_MARK).o" \
		command_json=" { \"arguments\": [ \"$$compile_cmd\" ], \"directory\": \"${PWD}\", \"file\": \"$$file\" },"; \
		echo -e "$$command_json" >> compile_commands.json ; \
	done
	@for file in $(SRC_CX); do \
		compile_cmd="$(CX) $(CX_FLAG) $(HEAD_PATH) -c $${file} -o $${file}.$(CX_MARK).o" \
		command_json=" { \"arguments\": [ \"$$compile_cmd\" ], \"directory\": \"${PWD}\", \"file\": \"$$file\" },"; \
		echo -e "$$command_json" >> compile_commands.json ; \
	done
	@sed -i '$$ s/,$$/\n]/' compile_commands.json

# 定义清理规则
clean:
	rm -fR $(BUILD_DIR) $(TARGET).elf $(TARGET).hex $(TARGET).bin

.PHONY: all clean json

通过对比,大家可以发现,对于这个Makefile,无论是编译linux还是windows,opencv还是单片机,主体都是不需要修改的,只需要改下编译器和标志

最后说三个注意地方:

一、是这个Makefile会自动搜索所在目录及子目录下的所有符合HEAD_TYPE、LIB_TYPE、CC_TYPE 、CX_TYPE 指定后缀的文件,所以使用这个Makefile时,要保证所在目录及子目录下不要包含无用代码,实在要包含,就在EXCLUDE_FILES 中指定,会被排除

二、是可以使用make json命令,生成compile_commands.json,使用clangd调试代码很方便

三、如果要编译动态库,比如.so,需要先把TARGET的后缀换成.so, 然后在LD_FLAG 后添加 -shared

    如果要编译静态库,比如.a, 先把TARGET的后缀换成.a,例如lib.a
    新增
    AR := $(COMPILER_PATH)ar
    最后把
    @$(LD) $(LD_FLAG) -o $@ $^ $(LIB_PATH) $(LIB_FLAG) $(SRC_LIB)
    替换为
    @$(AR) $(AR_FLAG) rcs $@ $^

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值