分享一个万能的 Makefile。
在 Linux 里开发代码Makefile是不可绕过的一个环节,之前为了方便代码编译和移植,自己也写过一些傻瓜式的 Makefile ,但是感觉还是不够傻瓜智能。
最近正好在 Makefile 上遇到了点问题,于是顺手将之前写的 Makefile 优化了一下,嗯,这次觉得比较够傻瓜了,所以分享上来,希望对新手能有一些帮助。
上图为我自己创建的代码工程结构。src文件夹用于放置工程源文件(支持子文件夹嵌套)、inc文件夹用于放置头文件(可以有子文件夹,编译时会指定为头文件搜索路径)、obj文件夹用于存放编译时生成的中间文件。
话不多说先贴一下 Makefile
。
TARGET := main
#CROSS_COMPILE := arm-linux-gnueabihf-
CC := $(CROSS_COMPILE)gcc
SRC := src
INC := inc
OBJ := obj
BUILD := $(shell find $(SRC)/* | grep "\.c$$")
HEAD_PATH := $(foreach n, $(shell ls -l $(INC) | awk '/^d/ {print $$NF}'), /$(n)/) /
C_FLAGS += $(foreach n, $(HEAD_PATH), -I $(INC)$(n))
#C_FLAGS += -I /usr/local/mysql/include
O_FLAGS += -g
#O_FLAGS += -L /usr/local/mysql/lib
#O_FLAGS += -lmysqlclient
O_FLAGS += -lpthread
O_FLAGS += -lusb-1.0
all : $(TARGET)
$(TARGET) : $(foreach n, $(BUILD), $(patsubst $(SRC)%.c,$(OBJ)%.o, $(n)))
$(CC) -o $@ $^ $(O_FLAGS)
# $(CC) -o $@ $(foreach n, $^, $(OBJ)/$(notdir $(n))) $(O_FLAGS)
$(OBJ)/%.o : $(SRC)/%.c
$(shell mkdir -p $(dir $@))
$(CC) -c $< -o $@ $(C_FLAGS)
# $(CC) -c $< -o $(OBJ)/$(notdir $@) $(C_FLAGS)
clean :
# @echo $(foreach n, $(BUILD), $(patsubst $(SRC)%.c,$(OBJ)%.o, $(n)))
rm -f $(foreach n, $(BUILD), $(patsubst $(SRC)%.c,$(OBJ)%.o, $(n)))
rm -f $(TARGET)
简单介绍一下功能,Makefile 将 src 路径下扫描到的所有源文件并保存到 BUILD 变量中,并将inc路径下扫描到的文件夹作为头文件搜索路径保存 HEAD_PATH 中。
在编译时,BUILD 变量中的源文件名会进行一系列的替换操作,其中后缀会被替换成 .o 且根路径会被替换为 obj ,如: src/function/complete.c 会被替换为 obj/function/complete.o 。
替换后的内容作为链接时生成目标文件的依赖,用于后续编译中间文件( .o 文件)时 Makefile 的规则匹配。中间文件的编译过程比较简单,不太懂Makefile编译规则的小伙伴可以去搜一下相关的帖子学习学习。
简单介绍一下 Makefile 里用到的几个函数:
-
patsubst
原型:$(patsubst <pattern>,<to>,<text>)
功能:比较 text 中的字符串 是否符合 pattern 中的格式, 符合的话, 用 to 替代,否则返回空。
举例:
$(patsubst abc_%.c, def_%.o, abc_123.c)
首先比较字符串 “abc_123.c” 是否符合 “abc_%.c” 的格式,符合的话,匹配成功,这时%的内容变为123(相当于对%赋值为123),再将%代入 “def_%.o” ,最终输出 “def_123.o” 。 -
subst
原型: $(subst <from>,<to>,<text>)
功能:把字符串 text 中的 from 替换为 to。
举例:
$(subst .c,.o,main.c)
“main.c” 中的 “.c” 被 “.o” 替换,最终输出 “main.o” 。
subst 与 patsubst 函数类似,但是 patsubst 强调一个匹配的过程,而 subst 只是一个简单的替换。 -
notdir
原型:$(notdir <names>)
功能:从文件名序列 names 中取出非目录部分。
举例:
$(notdir src/function/complete.c)
去掉路径,最终输出“complete.c”。 -
dir
原型:$(dir <names>)
功能:从文件名序列 names 中取出目录部分。
举例:
$(dir src/function/complete.c)
取出路径,最终输出“src/function/”。 -
foreach
原型:$(foreach <value>,<list>,<text>)
功能:类似于for循环,把 list 中的内容逐一赋值给 value ,然后执行 text,list 中的内容以空格或 tab 键分割。
举例:
names := a b c
$(foreach n,$(names),$(n).o)
依次取出 names 中的内容赋值给 n ,然后执行 $(n).o,最后输出 “a.o b.o c.o”。 -
wildcard
原型:$(wildcard <pattern>)
功能:扫描文件列表,输出符合 pattern 条件的所有文件名称,文件名称之间使用空格分开。
举例:
$(wildcard *.c)
扫描当前路径下的文件列表,输出所有文件名后缀为 “.c” 的文件名称,文件名之间用空格分开。