GNU Make 项目管理

通过描述源文件、中间文件、目标文件之间的关系, 将源代码转换成可执行文件的例行工作自动化。

# comment
target1 target2 target3 : prereq1 prereq2
    command1
    command2
    command3

规则

通配符

依赖关系语境部分由 make 处理,命令部分由 shell 处理。

i.e *.[hc] –> *.h or *.c

假想工作目标

.PHONY: clean
clean:
    rm -f *.o hello.c

尽管存在 clean 是最新的,仍然可执行 clean。

all 执行编译程序的所有工作
install 进行二进制程序的安装
clean 清理产生的二制文件
distclean 编译过程产生的任何文件
TAGS 建立可供编辑器使用的标记表
info 创建 GNU info 文件
check 执行测试
uninstall 卸载程序

具体工作目标、假想工作目标、空文件工作目标

自动变量

$@ 工作目标的文件名
$* 工作目标的主文件名(stem)
$% 工作目标的归档文件结构中的文件名元素,i.e. foo.a(bar.o)$% = bar.o
$+ 所有必要条件的文件名(重复),在共享库循环引用时使用
$^ 所有必要条件的文件名 (去重)
$? 时间在工作目标后的所有必要条件
$< 第一个必要条件的文件名

后缀 D 表示目录部分,F 表示文件部分 i.e.

$(@D)
$(<F)

搜索路径

  VPATH = include src
  # vpath <patterns> <dir list>
  vpath %.h %.hpp include
  vpath %.c %.cpp src src2

分类

内置规则是模式规则的实例,模式规则是一般规则的范化。

其它

变量

命名

# 常量:命令行、环境变量
CC_NAME := gcc

# 变量:makefile
sources_files = *.c

# 函数:makefile
maybe-mk-dir = $(if $(wildcard $1),,$(MKDIR) $1)

类型

变量值会忽略赋值运算符右侧前导空格、保留后置空格。

SIMPLE_VAR := 'simple var'
RECURSIVE_VAR = 'recursive var'
SIMPLE_VAR ?= 'null'
SIMPLE_VAR += 'null'
libhello.so: LDFLAGS += -shared
libhello.so: hello.cpp
  $(LINK.cpp) $^ $(OUTPUT_OPTION)

定义

define create-jar
  @echo Creating $@
endef

调用

# 1 使用变量的形式展开
$(create-jar)

# 2 通过内置函数宏展开
$(call create-jar)

由于 call 调用的宏不存在时,不产生任何作用,利用该特性可实现函数挂钩。

# $(call build-libs, object-files)
define build-libs
	$(AR) $(ARFLAGS) $@ $1
	$(call build-hook,$1)
endef

libfoo.a: build-hook = $(RANLIB) $@
libfoo.a: foo.o
	$(call build-libs,$^)

扩展变量

变量来源

  1. 优先级:命令行(剔除 makefile 中的值) > 环境变量 -e(剔除 makefile 中的值) > makefile > 环境变量(替换内置变量) > 内置变量
  2. override 当使用命令行时,保留使用 makefile 的值。
  3. --environment-overrides 环境变量会覆盖 makefile 的值。

条件指令

ifdef/ifndef 中的变量不需要添加美元符号 $ifeq/ifneq 的条件变量的形式有两种:调用符号 ($(AA),$(BB)) 或者 空格符号 $(AA) $(BB)

ifdef COMPSEC
endif

ifndef COMPSEC
endif

ifeq $(AA) $(BB)
endif

ifneq $(AA) $(BB)
endif

函数

字符串函数

# 1. $(filter pattern...,text)
$(ui_library): $(filter ui/%.o,$(objects))

# 2. $(filter-out pattern...,text)
# 功能与 filter 相反

# 3. $(findstring string...,text)
# 返回 string

# 4. $(subst search-string,replace-string,text)
isouces := count_words.c counter.c lexer.c
objects := $(subst .c,.o,$(sources))

# 5. $(patsubst search-pattern,replace-pattern,text)
$(patsubst %/,%,$(directory-path))

# 6. $(words text)
# return word count

# 7. $(words n,text)
# return n's word

# 8. $(firstword text)
# equal to $(words 1,text)

# 9. $(wordlist start,end,text)
# return [start, end]'s text word

重要杂项

# 1. $(sort list)
# 排序并删除重复、前导空格

# 2. $(shell command)
# 扩展 command 到 shell 中运行,返回标准输出(换行被转换成空格)

文件名函数

# 1. $(wildcard pattern...)
# 通配符

# 2. $(dir list...)
# return directory path

# 3. $(notdir name...)
# return filename

# 4. $(suffix name...)
# return suffix, i.e. xxx.cpp --> .cpp

# 5. $(basename name...)
# return basename, i.e. xxx.cpp --> xxx

# 6. $(addsuffix suffix,name...)
# append suffix to name

# 7. $(addprefix prefix,name...)
# add prefix to name

# 8. $(join prefix-list,suffix-list)
# i.e. [a, b, c] join [.cpp, .cpp, .cpp] --> [a.cpp, b.cpp, c.cpp]

流程控制

# 1. $(if condition,then-part,else-part)

# 2. $(error text)

# 3. $(foreach var,list,body)

其它

# 1. $(strip text)
# 清除前后空格,多个空格由单空格替换

# 2. $(origin var)
# 返加变量来源 undefined default environment...

# 3. $(warning text)

# 4. $(eval text)
# 二次展开,未经求值

命令

命令修饰符

空命令

header.h: ;

通过 ; 避免使用隐含规则。

命令环境

递归式 make,命令行参数将通过 MAKEFLAGS 传递给子 make

对命令脚本求值

  1. 读取脚本并解释 ifdef/... 条件指令

  2. 扩展变量

  3. 对表达式求值

  4. 执行命令