0voice-1.4.1-Makefile

简单 Makefile

darren:
	@echo "hello darren"
all:
	@echo "hello all"
test:
	@echo "hello test"
clean:
	@echo "hello clean"
  • 下载 \(make\) 工具后,\(make \ darren\) 就会输出 hello darren, \(make \ all\) 就会输出 hello all
  • 如果直接 \(make\) 呢?经过查询,\(make\) 会默认地执行 \(Makefile\) 中第一个非特殊目标

Makefile 三要素

简单来讲就是:依赖、目标和命令

例子 \(1\)

all (目标) : test (依赖)
	@echo "hello all" (命令)
test (子目标) : 
	@echo "hello test"
  • 有依赖的话,相当于一个递归执行的关系。
  • \(Makefile\) 命令的前面必须有一个制表符 ( \(Tab\) )

例子 \(2\)

.PHONY: main clean
simple: main.o foo.o
	@echo "simple: main.o foo.o"
	gcc -o simple main.o foo.o
main.o: main.c
	@echo "gcc -o main.o -c main.c"
	gcc -o main.o -c main.c
foo.o: foo.c
	@echo "gcc -o foo.o -c foo.c"
	gcc -o foo.o -c foo.c
clean:
	rm simple main.o foo.o
  • make simple 时,结果是
gcc -o main.o -c main.c
gcc -o main.o -c main.c
gcc -o foo.o -c foo.c
gcc -o foo.o -c foo.c
simple: main.o foo.o
gcc -o simple main.o foo.o
  • gcc -o main.o -c main.c 会多打印出一遍的原因是,作为 \(shell\) 指令输出。
  • \(Makefile\) 每次编译链接只会去管修改的部分,这样就大大节省编译时间。

伪对象 .PHONY

上一个例子,当把 .PHONY: main clean 注释掉的话,则没有 rm simple main.o foo.o 的效果的(源文件夹下有 \(clean\)) 文件。

原因:当一个目标没有被声明为 \(.PHONY\) 时,\(make\) 会将其视为一个实际的文件目标。

  • \(make\) 会检查当前目录下是否存在一个名为 \(clean\) 的文件。
  • 如果 \(clean\) 文件存在,并且它没有依赖(或者它的依赖都比它旧),\(make\) 就会认为 \(clean\) 目标已经是最新的("\(up \ to \ date\)"),因此不会执行 \(clean\) 规则下的命令。

变量

几个约定俗称的变量名 :

  • \(CC\) 保存编译器名
  • \(RM\) 用于指示删除文件的命令
  • \(EXE\) 存放可执行文件
  • \(OBJS\) 放置所有的目标文件名

总的来说就是,类比 \(C++\) 的宏定义,很多地方要用到,一旦要修改,就可以只修改最开始的定义。

其次来讲,像文件路径之类的,比较长,就可以用简短的变量名取代。

.PHONY: clean
CC = gcc
RM = rm
EXE = simple

OBJS = main.o foo.o foo2.o
$(EXE): $(OBJS)
	$(CC)   -o $(EXE) $(OBJS)
main.o: main.c
	$(CC) -o main.o -c main.c
foo.o: foo.c
	$(CC)   -o foo.o -c foo.c
foo2.o: foo2.c
	$(CC)   -o foo2.o -c foo2.c	
clean:
	$(RM) $(EXE) $(OBJS)

自动变量

.PHONY: all
all: first second third
	@echo "\$$@ = $@"
	@echo "$$^ = $^"
	@echo "$$< = $<"
first:
	@echo "1 first"
second:
	@echo "2 second"	
third:
	@echo "3 third"
  • \(\$@\) 用于表示一个规则中的目标。当我们的一个规则中有多个目标时,$@ 所指的是其中任何造成命令被运行的目标。(理解一个规则有多个目标)

  • $^ 则表示的是规则中的所有依赖

  • $< 表示的是规则中的第一个依赖

1 first
2 second
3 third
$@=all
$^ = first second third
$< = first

自动变量-编译

.PHONY: clean
CC = gcc
RM = rm
EXE = simple
#SRCS = main.c foo.c
SRCS = $(wildcard *.c)
# 把.c换成对应的.o
#OBJS = foo.o foo2.o main.o
OBJS = $(patsubst %.c,%.o,$(SRCS))

$(EXE): $(OBJS)
	$(CC) -o $@ $^
%.o: %.c
	$(CC) -o $@ -c $^ 
clean:
	$(RM) $(EXE) $(OBJS)
src: # 测试make src显示相应的xx.c
	@echo $(SRCS)
objs:# 测试make objs显示相应的xx.o
	@echo $(OBJS)
  • $(wildcard pattern)\(make\) 的一个函数。它会查找当前目录下所有匹配 \(pattern\) 的文件,并返回一个文件列表,比如这里的 \(pattern\) 就是 *.c
  • 意义:不用手动维护文件列表(当要删除和添加时),make 自动更新。
  • $(patsubst pattern,replacement,text) 用于模式替换。比如上述的例子,将捕获的 \(SRCS\) 文件列表里面的 .c 结尾的文件换成 .o 结尾的文件(注意这里不是 *.c , *shell 的通配符,%是字符串通配符,也就是说前者应用的场景是文件系统)。
  • -c 指的是只编译,不链接。
  • %.o: %.c: 当需要生成 main.o 时:gcc -o main.o -c main.c ; 当需要生成 foo.o 时:gcc -o foo.o -c foo.c

依赖第三方库

.PHONY: clean
CC = $(CROSS)gcc
CXX = $(CROSS)g++
DEBUG = -g -O2
CFLAGS = $(DEBUG) -Wall -c
RM = rm -rf

SRCS = $(wildcard ./*.c)
OBJS = $(patsubst %.c,%.o,$(SRCS))

VERSION = 1.0.0
TARGET = simple.$(VERSION)

HEADER_PATH = -I ./include
LIB_PATH = -L ./lib
$(warning LIB_PATH) # 这一行是用于调试,会打印LIB_PATH的值

LIBS = -lpthread

$(TARGET): $(OBJS)
	$(CXX) $^ -o $@ $(LIB_PATH) $(LIBS)

%.o: %.c
	$(CXX) $(CFLAGS) $< -o $@ $(HEADER_PATH)

clean:
	$(RM) $(TARGET) *.o
  • %.o: %.c 是一一对应关系,$<$^ 的效果是一样的。
  • simple.$(VERSION),强调版本,好习惯。
  • 编译阶段 -I找头文件,链接阶段 -L 找依赖的库,不写出来去系统默认路径上找。
posted @ 2025-09-13 17:24  xqy2003  阅读(3)  评论(0)    收藏  举报