material/program/tool/makefile.md
RongersLY 93cecd7544 BIG MERGE!!!
BIG ADD:
- docker
- archlinux

FIX:
- vim
- c_cpp
  - string hash
  - linux /dev/random
  - thread
  - STL
- linux
  - command
    - last

OTHERS:
- add antenna.md
- mirrors
- makefile.md
2025-04-04 17:35:35 +08:00

9.7 KiB
Raw Blame History

make

目录

参数

-n 参数可以打印出make会执行的命令但不真正执行
-f <filename> 指定makefile -i 忽略错误 -k 在某个目标失败时,尽量构建其他 -s 静默 -j4 4线程 -C /dir 进入指定目录再运行 -d debug -w 打印工作目录 -B 强制重新构建

makefile

Makefile 是一种用于自动化构建和管理项目的工具,尤其在 C/C++ 项目中广泛使用。除了基本的规则和依赖关系Makefile 还支持一些高级语法和功能,可以帮助你编写更灵活和高效的构建脚本

规则:依赖 命令 注意: 命令前面必须要用Tab

普通变量定义

OPTION = -O3 -Wall

变量使用

g++ $(OPTION) xxx

1. 变量和赋值

Makefile 支持多种变量赋值方式:

  • =:递归展开变量,变量值在使用时展开。
  • :=:简单展开变量,变量值在定义时展开。
  • ?=:条件赋值,只有当变量未定义时才赋值。
  • +=:追加赋值,将值追加到变量末尾。
CC = gcc
CFLAGS := -Wall -O2
CFLAGS += -g

2. 自动变量

Makefile 提供了一些自动变量,用于简化规则的编写:

  • $@:目标文件名。
  • $<:第一个依赖文件名。
  • $^:所有依赖文件列表。
  • $?:所有比目标新的依赖文件列表。
  • $*:目标文件的主干名(不包含扩展名)。
%.o: %.c
	$(CC) $(CFLAGS) -c $< -o $@

3. 模式规则

模式规则允许你使用通配符来定义规则,适用于多个文件。

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

4. 函数

Makefile 提供了一些内置函数,用于处理字符串、文件名等。

  • $(wildcard pattern):匹配文件名模式。
  • $(patsubst pattern,replacement,text):模式替换。
  • $(shell command):执行 shell 命令并返回结果。
  • $(foreach var,list,text):循环遍历列表。
SRCS = $(wildcard *.c)
OBJS = $(patsubst %.c,%.o,$(SRCS))

5. 条件判断

Makefile 支持条件判断,可以根据条件执行不同的操作。

ifeq ($(DEBUG),1)
	CFLAGS += -DDEBUG
else
	CFLAGS += -DNDEBUG
endif

6. 包含其他 Makefile

你可以使用 include 指令将其他 Makefile 包含到当前 Makefile 中。

include config.mk

7. 多目标规则

你可以为一个规则定义多个目标,这样它们可以共享相同的命令。

all: target1 target2

target1 target2:
	@echo Building $@

8. 伪目标

伪目标是指那些不对应实际文件的目标,通常用于执行一些操作,如清理、安装等。

.PHONY: clean

clean:
	rm -f *.o

9. 嵌套 Makefile

你可以在 Makefile 中调用另一个 Makefile通常用于构建子目录中的项目。

SUBDIRS = dir1 dir2

$(SUBDIRS):
	$(MAKE) -C $@

all: $(SUBDIRS)

10. 自定义函数

你可以通过 defineendef 定义自定义函数。

define greet
	@echo "Hello, $(1)!"
endef

all:
	$(call greet,World)

11. 错误处理

你可以使用 $(error text)$(warning text) 来生成错误或警告信息。

ifeq ($(CC),)
	$(error CC is not set)
endif

13. 隐式规则

Makefile 有一些内置的隐式规则,可以自动推导如何从源文件生成目标文件。你可以通过定义自己的隐式规则来覆盖默认行为。

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

14. 动态依赖

你可以使用 -include 来包含动态生成的依赖文件,通常用于自动生成头文件依赖。

-include $(OBJS:.o=.d)

%.d: %.c
	@$(CC) -MM $< > $@.$$$$; \
	sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
	rm -f $@.$$$$

16. 环境变量

Makefile 可以访问环境变量,并且可以通过 export 将变量传递给子 Makefile。

export PATH := $(PATH):/usr/local/bin

17. 多行命令

你可以使用反斜杠 \ 将长命令分成多行。

all:
	@echo "This is a long command that spans \
	multiple lines."

18. 命令前缀

命令前缀可以控制命令的执行方式:

  • @:不显示命令本身,只显示输出。
  • -:忽略命令的错误,继续执行。
all:
	@echo "This command will not be displayed"
	-rm -f non_existent_file

19. 文件搜索路径

你可以使用 VPATHvpath 来指定源文件的搜索路径。

VPATH = src:include

vpath %.c src
vpath %.h include

20. 递归扩展变量

递归扩展变量在使用时才会展开,可以用于动态生成内容。

FOO = $(BAR)
BAR = $(BAZ)
BAZ = Hello

all:
	@echo $(FOO)  # 输出 Hello

21. 静态模式规则

静态模式规则允许你为特定目标定义规则。

objects = foo.o bar.o

$(objects): %.o: %.c
	$(CC) $(CFLAGS) -c $< -o $@

22. 多架构支持

你可以通过条件判断来支持多架构编译。

ifeq ($(ARCH),x86)
	CFLAGS += -m32
else ifeq ($(ARCH),x64)
	CFLAGS += -m64
endif

23. 自定义后缀规则

你可以定义自定义的后缀规则来处理特定类型的文件。

.SUFFIXES: .cpp .o

.cpp.o:
	$(CXX) $(CXXFLAGS) -c $< -o $@

24. 使用 eval 动态生成规则

eval 函数可以动态生成 Makefile 规则。

define MAKE_RULE
$(1): $(2)
	$(CC) $(CFLAGS) -c $$< -o $$@
endef

$(eval $(call MAKE_RULE,foo.o,foo.c))

25. 使用 foreach 循环

foreach 函数可以用于循环处理列表中的每个元素。

DIRS = dir1 dir2 dir3

all:
	$(foreach dir,$(DIRS),$(MAKE) -C $(dir);)

26. 使用 call 调用自定义函数

call 函数可以调用自定义函数,并传递参数。

define greet
	@echo "Hello, $(1)!"
endef

all:
	$(call greet,World)

27. 使用 shell 函数执行外部命令

shell 函数可以执行外部命令并返回结果。

DATE := $(shell date)

28. 使用 infowarning 输出信息

infowarning 函数可以用于输出信息。

$(info Building target $(TARGET))
$(warning This is a warning message)

29. 使用 if 条件判断

if 函数可以用于条件判断。

DEBUG = 1

CFLAGS += $(if $(DEBUG),-g,-O2)

30. 使用 orand 逻辑操作

orand 函数可以用于逻辑操作。

ifeq ($(DEBUG),1)
	CFLAGS += -g
else ifeq ($(OPTIMIZE),1)
	CFLAGS += -O2
endif

31. 使用 not 取反

not 函数可以用于取反操作。

ifneq ($(DEBUG),1)
	CFLAGS += -O2
endif

32. 使用 filterfilter-out 过滤列表

filterfilter-out 函数可以用于过滤列表。

SRCS = foo.c bar.c baz.c

C_SRCS = $(filter %.c,$(SRCS))
NON_C_SRCS = $(filter-out %.c,$(SRCS))

33. 使用 sort 排序列表

sort 函数可以用于排序列表。

SRCS = foo.c bar.c baz.c

SORTED_SRCS = $(sort $(SRCS))

34. 使用 strip 去除空格

strip 函数可以用于去除字符串中的空格。

STR =   foo bar

STRIPED_STR = $(strip $(STR))

35. 使用 findstring 查找子字符串

findstring 函数可以用于查找子字符串。

ifeq ($(findstring foo,$(STR)),foo)
	@echo "Found foo"
endif

36. 使用 subst 替换字符串

subst 函数可以用于替换字符串。

STR = foo bar baz

NEW_STR = $(subst foo,FOO,$(STR))

37. 使用 patsubst 模式替换

patsubst 函数可以用于模式替换。

SRCS = foo.c bar.c baz.c

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

38. 使用 wildcard 匹配文件名

wildcard 函数可以用于匹配文件名。

SRCS = $(wildcard *.c)

39. 使用 dirnotdir 获取目录和文件名

dirnotdir 函数可以用于获取目录和文件名。

FILE = /path/to/file.c

DIR = $(dir $(FILE))
NAME = $(notdir $(FILE))

40. 使用 suffixbasename 获取后缀和主干名

suffixbasename 函数可以用于获取文件的后缀和主干名。

FILE = file.c

SUFFIX = $(suffix $(FILE))
BASENAME = $(basename $(FILE))

41. 使用 addsuffixaddprefix 添加后缀和前缀

addsuffixaddprefix 函数可以用于添加后缀和前缀。

FILES = foo bar baz

FILES_WITH_SUFFIX = $(addsuffix .c,$(FILES))
FILES_WITH_PREFIX = $(addprefix src/,$(FILES))

42. 使用 join 连接列表

join 函数可以用于连接两个列表。

LIST1 = foo bar
LIST2 = baz qux

JOINED_LIST = $(join $(LIST1),$(LIST2))

43. 使用 wordwordlist 获取列表中的元素

wordwordlist 函数可以用于获取列表中的元素。

LIST = foo bar baz

FIRST = $(word 1,$(LIST))
SUBLIST = $(wordlist 2,3,$(LIST))

44. 使用 words 获取列表长度

words 函数可以用于获取列表的长度。

LIST = foo bar baz

LENGTH = $(words $(LIST))

45. 使用 firstword 获取列表的第一个元素

firstword 函数可以用于获取列表的第一个元素。

LIST = foo bar baz

FIRST = $(firstword $(LIST))

46. 使用 lastword 获取列表的最后一个元素

lastword 函数可以用于获取列表的最后一个元素。

LIST = foo bar baz

LAST = $(lastword $(LIST))

47. 使用 abspath 获取绝对路径

abspath 函数可以用于获取绝对路径。

FILE = file.c

ABS_PATH = $(abspath $(FILE))

48. 使用 realpath 获取规范化的绝对路径

realpath 函数可以用于获取规范化的绝对路径。

FILE = ../file.c

REAL_PATH = $(realpath $(FILE))