多目录时Makefile 的编写方法

时间:2020-12-09 12:50:18

多目录时Makefile 的编写方法

1 make分中预定义变量表

  • $* 不包含扩展名的目标文件名称。
  • $+ 所有的依赖文件,以空格分开,并以出现的先后为序,可能包含重复的依赖文件。
  • $< 第一个依赖文件的名称。
  • $? 所有的依赖文件,以空格分开,这些依赖文件的修改日期比目标的创建日期晚。
  • $@ 目标的完整名称。
  • $^ 所有的依赖文件,以空格分开,不包含重复的依赖文件。
  • $% 如果目标是归档成员,则该变量表示目标的归档成员名称。例如,如果目标名称
  • 为 mytarget.so(image.o),则 @mytarget.so % 为 image.o。
  • AR 归档维护程序的名称,默认值为 ar。
  • ARFLAGS 归档维护程序的选项。
  • AS 汇编程序的名称,默认值为 as。
  • ASFLAGS 汇编程序的选项。
  • CC C 编译器的名称,默认值为 cc。
  • CCFLAGS C 编译器的选项。
  • CPP C 预编译器的名称,默认值为 $(CC) -E。
  • CPPFLAGS C 预编译的选项。
  • CXX C++ 编译器的名称,默认值为 g++。
  • CXXFLAGS C++ 编译器的选项。
  • FC FORTRAN 编译器的名称,默认值为 f77。
  • FFLAGS FORTRAN 编译器的选项。

2 典型例子

CC := g++  
CFLAGS := -g
TARGET := test
SRCS := $(wildcard *.cpp)
OBJS := $(patsubst %cpp,%o,$(SRCS))

all:$(TARGET)
%.o:%.cpp
$(CC) $(CFLAGS) -c $< -o $@
$(TARGET):$(OBJS)
$(CC) $(CFLAGS) -o $@
clean:
rm -rf $(TARGET) *.o

2.1 常见赋值操作的含义

  • = 是最基本的赋值
  • := 是覆盖之前的值
  • ?= 是如果没有被赋值过就赋予等号后面的值
  • += 是添加等号后面的值

2.2 := 和 = 的区别

“=”

make会将整个makefile展开后,再决定变量的值。也就是说,变量的值将会是整个makefile中最后被指定的值。看例子:

x = foo
y = $(x) bar
x = xyz

在上例中,y的值将会是 xyz bar ,而不是 foo bar 。

“:=”

“:=”表示变量的值决定于它在makefile中的位置,而不是整个makefile展开后的最终值。

x := foo
y := $(x) bar
x := xyz

在上例中,y的值将会是 foo bar ,而不是 xyz bar 了。

3 多个文件时,如何编写Makefile

例子1:将 bin src obj include 单独存放时,Makefile文件的通用编写方法

文件存放说明:

  • bin: 存放编译生成的二进制文件
  • src: 存放源文件 (add.c multis.c sub.c main.c)
  • obj: 存放编译生成的目标文件
  • include: 存放头文件 (add.h multis.h sub.h)
  • Makefile 文件和 bin、src、include处于同一级目录。

Makefile文件的写法:

INC_DIR=./include
BIN_DIR=./bin
SRC_DIR=./src
OBJ_DIR=./obj

SRC=${wildcard ${SRC_DIR}/*.c}
OBJ=${patsubst %.c, $(OBJ_DIR)/%.o, ${notdir ${SRC}}}

#用于查看变量的值
#test:
# echo $(SRC)
# echo $(OBJ)

TARGET=main
BIN_TARGET=${BIN_DIR}/${TARGET}

CC=gcc
CFLAGS= -g -Wall -I${INC_DIR}

${BIN_TARGET}:${OBJ}
${CC} ${OBJ} -o $@

${OBJ_DIR}/%.o:${SRC_DIR}/%.c
${CC} ${CFLAGS} -c $< -o $@

clean:
find ${OBJ_DIR} -name *.o -exec rm -rf {} \;

说明:

  • notdir ${SRC}: 去除.c 文件中的目录,如:./src/mytest.c, 通过notdir之后得到 mytest.c。
  • $(wildcard .cpp /xxx/xxx/.cpp) 为获取当前目录下和/xxx/xxx/目录下所有.cpp文件名
  • (patsubst (SRC))为替换所有的.cpp为.o
  • find ${OBJ_DIR} -name *.o -exec rm -rf {} \;输入man find,查看find命令中 -exec的具体用法。

Execute command; true if 0 status is returned. All following arguments to find are taken to be argu‐
ments to the command until an argument consisting of ;' is encountered. The string{}’ is replaced
by the current file name being processed everywhere it occurs in the arguments to the command, not just
in arguments where it is alone, as in some versions of find. Both of these constructions might need to
be escaped (with a `\’) or quoted to protect them from expansion by the shell. See the EXAMPLES sec‐
tion for examples of the use of the -exec option. The specified command is run once for each matched
file. The command is executed in the starting directory. There are unavoidable security problems
surrounding use of the -exec action; you should use the -execdir option instead.

例子2:多个文件存放在不同的目录时,Makefile的写法。

文件存放说明:

  • add 目录 (add.c add.h)
  • sub 目录 (sub.c sub.h)
  • mutis 目录 (mutis.c mutis.h)
  • main 目录(main.c Makefile)

Makefile文件的写法:

CUR_DIR=add/sub/mutis/main文件所处的目录

ADD_DIR=${CUR_DIR}/add
SUB_DIR=${CUR_DIR}/sub
MUL_DIR=${CUR_DIR}/multis
MAIN_DIR=${CUR_DIR}/main

INC_DIR= -I${ADD_DIR} \
-I${SUB_DIR} \
-I${MUL_DIR} \
-I${MAIN_DIR}

SRC = ${wildcard ${ADD_DIR}/*.c} \
${wildcard ${SUB_DIR}/*.c} \
${wildcard ${MUL_DIR}/*.c} \
${wildcard ${MAIN_DIR}/*.c}

OBJ = ${patsubst %.c, %.o, ${SRC}}

TARGET=main
CC=gcc
CCFLAGS=-g -Wall ${INC_DIR}

${TARGET}: ${OBJ}
${CC} ${notdir ${OBJ} } -o $@
echo "Compile done."
${OBJ}:${SRC}
$(CC) ${CCFLAGS} -c $?

clean:
rm -f ${OBJ}
rm -f *.o
rm -f *~
rm -f ${TARGET}
echo "Clean done."

改进版本:

CUR_DIR=/home/xiaojie/Desktop/demo_multi_makefile/test3

ADD_DIR=${CUR_DIR}/add
SUB_DIR=${CUR_DIR}/sub
MUL_DIR=${CUR_DIR}/multis
MAIN_DIR=${CUR_DIR}/main

INC_DIR= -I${ADD_DIR} \
-I${SUB_DIR} \
-I${MUL_DIR} \
-I${MAIN_DIR}

SRC = ${wildcard ${ADD_DIR}/*.c} \
${wildcard ${SUB_DIR}/*.c} \
${wildcard ${MUL_DIR}/*.c} \
${wildcard ${MAIN_DIR}/*.c}
OBJ = ${patsubst %.c, %.o, ${SRC}}

TARGET=main
CC=gcc
CCFLAGS=-g -Wall ${INC_DIR}


${TARGET}: ${OBJ}
${CC} ${OBJ} -o $@
@echo "Compile done."

#${OBJ}:${SRC}
# $(CC) ${CCFLAGS} -c $?

$(OBJ):%.o:%.c
@echo "Compiling $< ==> $@"
${CC} ${CCFLAGS} -c $< -o $@

clean:
@rm -f ${OBJ}
@echo "Clean object files done."

@rm -f *~
@echo "Clean tempreator files done."

@rm -f ${TARGET}
@echo "Clean target files done."

@echo "Clean done."

说明:

1.@ echo "" 表示执行该条命令,但不输出该命令的内容。

2.改进版本中将

${OBJ}:${SRC}
$(CC) ${CCFLAGS} -c $?

替换成了,

$(OBJ):%.o:%.c
@echo "Compiling $< ==$@"
${CC} ${CCFLAGS} -c $< -o $@

参考链接