通用 Makefile 的编写方法以及多目录 makefile 写法

Makefile实战指南
本文详细介绍Makefile中的预定义变量及应用场景,包括多个文件管理和编译动态库、静态库的方法。并提供具体实例帮助理解不同赋值操作的区别。

1 make分中预定义变量表

  • $* 不包含扩展名的目标文件名称。
  • $+ 所有的依赖文件,以空格分开,并以出现的先后为序,可能包含重复的依赖文件。
  • $< 第一个依赖文件的名称。
  • $? 所有的依赖文件,以空格分开,这些依赖文件的修改日期比目标的创建日期晚。
  • $@ 目标的完整名称。
  • $^ 所有的依赖文件,以空格分开,不包含重复的依赖文件。
  • $% 如果目标是归档成员,则该变量表示目标的归档成员名称。例如,如果目标名称
  • 为 mytarget.so(image.o),则 @为mytarget.so,而@为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 -Wall -c -std=c++17 -I/usr/local/libusb-1.0.24/include/
LDFLAG := -L/usr/local/libusb-1.0.24/lib/ -lusb-1.0
SRCS := $(wildcard *.cpp)
OBJS := $(patsubst %cpp,%o, $(SRCS))
TARGET := test

all:$(TARGET)

%.o:%.cpp
        $(CC) $(CFLAGS) $^ -o $@

$(TARGET):$(OBJS)
        $(CC) $(LDFLAG) $^ -o $@

clean:
        rm -rf $(TARGET) *.o 

典型例子2:

#多文件多目录c源文件与cpp源文件混和编译的情况:
INC_DIR=./include
BIN_DIR=./
SRC_DIR=./src
OBJ_DIR=./obj
 
SRC=${wildcard ${SRC_DIR}/*.c}
OBJ=${patsubst %.c, $(OBJ_DIR)/%.o, ${notdir ${SRC}}}

SRCPP=${wildcard ${SRC_DIR}/*.cpp}
OBJPP=${patsubst %.cpp, $(OBJ_DIR)/%.o, ${notdir ${SRCPP}}}
 
TARGET=DataServer
BIN_TARGET=${BIN_DIR}/${TARGET}
 
CC := gcc
CCPP:= g++
CFLAGS := -g -Wall -c -std=c++17 -I${INC_DIR}
LDFLAG := -L/usrusr -ldl -lpthread

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

${OBJ_DIR}/%.o:${SRC_DIR}/%.cpp
	${CCPP} ${CFLAGS} $< -o $@

${BIN_TARGET}:${OBJ} ${OBJPP}
	${CCPP} ${OBJ} ${OBJPP} ${LDFLAG} -o $@
 
 
clean:
    rm ${OBJ} ${OBJPP} ${BIN_TARGET}
#以下两行与上面意思相同
#   rm ${OBJ_DIR}/*.o ${BIN_TARGET}
#	find ${OBJ_DIR} -name *.o -exec rm -rf {} \;

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(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/plv/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 $@

4、编译动态库

CC = g++
CC_FLAG = -D_NOMNG -D_FILELINE

#set your inc and lib
INC = 
LIB = -lpthread -L./ -lsvrtool

#make target lib and relevant obj 
PRG = libsvrtool.so
OBJ = Log.o

#all target
all:$(PRG)

$(PRG):$(OBJ)
	$(CC) -shared -o $@ $(OBJ) $(LIB)

.SUFFIXES: .c .o .cpp
.cpp.o:
	$(CC) $(CC_FLAG) $(INC) -c $*.cpp -o $*.o

.PRONY:clean
clean:
	@echo "Removing linked and compiled files......;
	rm -f $(OBJ) $(PRG)

5、编译静态库

CC = g++
CC_FLAG = -D_NOMNG -D_FILELINE

#static library use 'ar' command 
AR = ar

#set your inc and lib
INC = 
LIB = -lpthread -L./ -lsvrtool

#make target lib and relevant obj 
PRG = libsvrtool.a
OBJ = Log.o

#all target
all:$(PRG)
$(PRG):$(OBJ)
	${AR} rv ${PRG} $?

.SUFFIXES: .c .o .cpp
.cpp.o:
	$(CC) $(CC_FLAG) $(INC) -c $*.cpp -o $*.o

.PRONY:clean
clean:
	@echo "Removing linked and compiled files......"
	rm -f $(OBJ) $(PRG)

随机组合、举一反三会写出适合项目的makefile.

<think>我们正在讨论Makefile的基本使用方法和模式规则写法。结合用户提供的引用和之前的回答,我们可以这样组织内容: 1. 基本结构:Makefile由规则组成,每条规则包括目标(target)、依赖(prerequisites)和命令(recipe)。 2. 变量:使用变量提高可维护性(引用[1]提到变量是高级用法的一部分,但这里我们介绍基本变量)。 3. 模式规则:使用通配符%来定义通用规则,避免为每个文件重复写规则(引用[3]中的例子展示了重写单个文件foo.c的Makefile,我们可以扩展为模式规则)。 4. 条件语句:引用[2]展示了if语句的使用,我们可以在基本用法中简单提及。 根据用户要求,我们将重点放在基本使用和模式规则上。 以下为回答结构: ### 一、Makefile基本结构 1. 规则语法: ```makefile 目标: 依赖 命令 # 注意:命令前必须是Tab,不能是空格 ``` 2. 示例:编译单个C文件 ```makefile # 基本编译示例 hello: hello.c gcc hello.c -o hello ``` 3. 常用变量: ```makefile CC = gcc # 定义编译器 CFLAGS = -Wall -O2 # 编译选项 TARGET = hello # 目标文件 $(TARGET): hello.c $(CC) $(CFLAGS) hello.c -o $(TARGET) ``` ### 二、模式规则(Pattern Rules) 模式规则使用`%`通配符匹配文件名,实现通用规则: ```makefile # 将任意.c文件编译为同名.o文件 %.o: %.c $(CC) -c $< -o $@ # 说明: # % : 匹配任意非空字符串(如 hello 匹配 hello.c 和 hello.o) # $< : 表示第一个依赖(这里为%.c对应的具体文件名) # $@ : 表示目标文件名(这里为%.o对应的具体文件名) ``` ### 三、文件项目的模式规则应用 假设项目有个.c文件,最终链接成一个可执行文件: ```makefile # 定义变量 CC = gcc CFLAGS = -Wall TARGET = program SRCS = main.c utils.c # 源文件列表 OBJS = $(SRCS:.c=.o) # 将SRCS中的.c替换为.o(得到main.o utils.o) # 最终目标:链接所有.o文件 $(TARGET): $(OBJS) $(CC) $^ -o $@ # $^ 表示所有依赖文件 # 模式规则:编译每个.c到.o(无需为每个.c重复写规则) %.o: %.c $(CC) $(CFLAGS) -c $< -o $@ # 清理目标 clean: rm -f $(OBJS) $(TARGET) ``` ### 四、条件语句(简单示例) 引用[2]中展示了if语句,我们可以用于条件判断: ```makefile # 检查是否定义DEBUG变量,若定义则添加调试选项 ifdef DEBUG CFLAGS += -g endif ``` 使用:`make DEBUG=1` 会启用调试选项。 ### 五、自动变量总结 | 变量 | 含义 | |------|-----------------------| | `$@` | 当前目标文件名 | | `$<` | 第一个依赖文件名 | | `$^` | 所有依赖文件列表 | | `$?` | 比目标新的依赖文件列表 | | `$*` | 匹配通配符%的部分 | ### 六、运行Makefile - 默认执行第一个目标:`make` - 指定目标:`make clean` - 定义变量:`make CFLAGS="-O0"` ### 七、最佳实践建议 1. 使用模式规则减少重复代码 2. 分离编译和链接阶段(先编译成.o再链接) 3. 声明伪目标避免与文件名冲突(引用[3]中提到伪目标): ```makefile .PHONY: clean ``` ### 八、示例:完整Makefile ```makefile CC = gcc CFLAGS = -Wall TARGET = app SRCS = main.c util.c OBJS = $(SRCS:.c=.o) .PHONY: all clean all: $(TARGET) $(TARGET): $(OBJS) $(CC) $^ -o $@ %.o: %.c $(CC) $(CFLAGS) -c $< -o $@ clean: rm -f $(OBJS) $(TARGET) ``` > 注意:模式规则`%.o: %.c`告诉make如何从.c文件生成.o文件,当make发现需要构建一个.o文件时,就会应用这条规则[^3]。 §§相关问题§§ 1. Makefile中如何管理头文件依赖? 2. 如何编写跨平台的Makefile? 3. Makefile中如何实现并行编译?</think>### Makefile 基本用法与模式规则详解 #### 一、Makefile 基础结构 Makefile 的核心是**规则**,由目标、依赖和命令组成: ```makefile 目标: 依赖 命令 # 必须用 Tab 开头 ``` 示例:编译单个 C 文件 ```makefile # 基础变量定义 CC = gcc CFLAGS = -Wall -O2 # 简单规则 hello: hello.c $(CC) $(CFLAGS) hello.c -o hello ``` > 执行 `make hello` 将编译程序[^3] #### 二、模式规则核心语法 模式规则使用 `%` 通配符实现通用匹配: ```makefile %.o: %.c $(CC) -c $< -o $@ ``` - `%`:匹配任意非空字符串(如 `main` 匹配 `main.c` 和 `main.o`) - `$<`:表示第一个依赖文件 - `$@`:表示目标文件名 #### 三、文件项目示例 ```makefile # 定义变量 TARGET = app SRCS = main.c utils.c OBJS = $(SRCS:.c=.o) # 将.c替换为.o # 最终目标规则 $(TARGET): $(OBJS) $(CC) $^ -o $@ # $^ 表示所有依赖文件 # 模式规则:编译所有.c文件 %.o: %.c $(CC) $(CFLAGS) -c $< -o $@ # 清理规则 clean: rm -f $(OBJS) $(TARGET) .PHONY: clean # 声明伪目标[^3] ``` > 执行流程: > 1. `make` → 查找第一个目标 `$(TARGET)` > 2. 发现依赖 `main.o utils.o` > 3. 通过模式规则编译 `.c` → `.o` > 4. 链接所有 `.o` 生成可执行文件 #### 四、模式规则进阶技巧 1. **目录处理** ```makefile OBJDIR = obj SRCDIR = src # 带目录的模式规则 $(OBJDIR)/%.o: $(SRCDIR)/%.c @mkdir -p $(@D) # 创建目标目录 $(CC) -c $< -o $@ ``` 2. **条件语句应用**(引用[2]) ```makefile # 根据环境变量切换目录 BUILD_DIR := $(if $(BUILD),$(BUILD),build) release: $(MAKE) BUILD=release ``` 3. **静态模式规则** ```makefile # 只对特定文件应用规则 $(OBJS): %.o: %.c $(CC) -c $< -o $@ ``` #### 五、自动变量速查表 | 变量 | 含义 | 示例 | |------|--------------------|-----------------------| | `$@` | 当前目标文件名 | `main.o` | | `$<` | 第一个依赖文件名 | `main.c` | | `$^` | 所有依赖文件列表 | `main.c utils.c` | | `$?` | 更新的依赖文件列表 | 修改过的 `.c` 文件 | | `$*` | 匹配 `%` 的部分 | `main` (针对 `%.c`) | #### 六、执行控制 ```bash # 基本编译 make # 指定目标 make clean # 并行编译(加速构建) make -j4 # 覆盖变量 make CFLAGS="-O0 -g" ``` > **最佳实践**: > 1. 始终声明 `.PHONY` 目标(如 `clean`) > 2. 使用 `@` 前缀隐藏命令回显(如 `@echo "Compiling..."`) > 3. 通过 `-include` 自动引入依赖关系(进阶用法)[^1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值