使用函数
在更复杂的工程中,头文件、源文件可能会放在二级目录,为了实现这种操作通常需要使用Makefile的函数。
函数格式及示例
在Makefile中调用函数的方法跟变量的使用类似,以“$()”或“${}”符号包含函数名和参数,具体语法如下:
$(函数名 参数)
${函数名 参数}
notdir函数
通常用于去除文件路径中的目录部分。
$(notdir 文件名)
例如输入参数“./sources/hello_func.c”,函数执行后的输出为“hell_func.c”,也就是说它会把输入中的“./sources/”路径部分去掉,保留文件名。
$(notdir ./sources/hello_func.c)
wildcard函数
wildcard 函数用于获取文件列表,并使用空格分隔开。它的格式如下:
$(wildcard 匹配规则)
例如使用函数将sources文件夹中的所有c文件列出:
$(wildcard sources/*.c)
# 函数输出为:
sources/hello_func.c sources/hello_main.c sources/test.c
patsubst函数
函数功能为模式字符串替换,格式如下:
$(patsbust 匹配规则,替换规则,输入的字符串)
用以下的例子来讲解如何作用的:
$(patsubst %.c,build_dir/%.o,hello_main.c)
# 函数的输出为:
build_dir/hello_main.o
# 若执行以下函数,
$(patsubst %.c,build_dir/%.o,hello_main.xxx)
# 由于hello_main.xxx不符合匹配规则“%.c”,所以函数没有输出
第一个函数调用中,由于“hello_main.c”符合“%.c”的匹配规则(%在Makefile中的类似于*通配符),而且“%”从“hello_main.c”中提取出了“hello_main”字符,把这部分内容放到替换规则“build_dir/%.o”的“%”号中,所以最终的输出为”build_dir/hello_main.o”
第二个函数调用中,由于由于“hello_main.xxx”不符合“%.c”的匹配规则,“.xxx”与“.c”对不上,所以不会进行替换,函数直接返回空的内容。
多级结构工程的Makefile
ARCH?=x86
TARGET=hello_main
# 存放中间文件的路径
BUILD_DIR=build_$(ARCH)
# 存放源文件的文件夹
SRC_DIR=sources
# 存放头文件的文件夹
INC_DIR=includes .
# 源文件
SRCS=$(wildcard $(SRC_DIR)/*.c)
# 目标文件(*.o)
OBJS=$(patsubst %.c, $(BUILD_DIR)/%.o, $(notdir $(SRCS)))
# 头文件
DEPS=$(wildcard $(INC_DIR)/*.h)
# 指定头文件的路径
CFLAGS=$(patsubst %,-I%, $(INC_DIR))
# 根据输入的 ARCH 变量来选择编译器
ifeq ($(ARCH),x86)
CC = gcc
else
CC = arm-linux-gnueabihf-gcc
endif
# 目标文件
$(BUILD_DIR)/$(TARGET):$(OBJS)
$(CC) -o $@ $^ $(CFLAGS)
#*.o 文件的生成规则
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c $(DEPS)
# 创建一个编译目录,用于存放过程文件
# 命令前带“@”, 表示不在终端上输出
@mkdir -p $(BUILD_DIR)
$(CC) -c -o $@ $< $(CFLAGS)
.PHONY:clean cleanall
# 按架构删除
clean:
rm -rf $(BUILD_DIR)
cleanall:
rm -rf build_x86 build_arm
注意工程结构如上图。
使用tree命令可以查看目录结构,如果提示找不到命令可以使用以下命令安装:
sudo apt install tree
编译结果如下: