GNU Make简介
make是Unix和其他操作系统上最持久的工具之一。自1970年问世以来,make至今仍旧是大多数项目开发的核心工具,它甚至被用来编译Linux内核。 简单是make的设计理念,在你变更源代码文件之后,想重新编译你的程序或者其他输出文件之际,make会检查时间戳,找出被变更的文件并进行必要的重 编译动作。 GNU make这一版本已经成为make程序的行业标准。本文将介绍GNU make的用法和使用技巧。首先介绍一下GNU make的基本用法,接着介绍通配符的使用,最后介绍一下如何利用make组织你的项目文件目录。
基本语法
make描述文件的文件名为makefile、Makefile、GNUMakefile。为了简单起见,我们统一使用makefile。 makefile的文件一般采用“从上到下”的结构,下层工作目标用来让上层工作目标保持在最新的状态。下面是makefile的基本语法结构:
target1 target2 target3 : prerequisite1 prerequisite2
command1
command2
command3
冒号左边可以是一个或者多个工作目标,右边可以是零个或者多个必要条件。更新工作目标会执行一条或者多条命令,且每个命令必须以TAB开头。需要注意的是不能在非命令行的第一个字符前输入一个TAB,否则make会将其后的文字作为命令来解释。 另外如果一行文本太长,可以使用标准的反斜线分隔为多行。
规则
假想工作目标
假想工作目标就是贴上标签的工作目标,它把一个或者多个工作目标归类,几乎成为makefile里面的必备元素。表1是一些常用的假想工作目标。
工作目标 | 功能 |
---|---|
all | 执行编译应用程序的所有工作 |
install | 从已编译的二进制文件进行应用程序的安装 |
clean | 将编译产生的二进制文件删除 |
check | 执行与应用程序相关的任何测试 |
info | 从Texinfo源代码来创建GNU info文件 |
变量
make的变量来自以下几个来源:
- 文件,直接在makefile里面定义
- 命令行参数传入
- 环境变量
- make自动创建的变量
简单变量的具体语法:
$(variable-name)
表2是GNU make的核心自动变量列表:
自动变量 | 含义 |
---|---|
$@ | 工作目标的文件名 |
$% | 档案文件成员结构中的文件名 |
$< | 第一个必要条件的文件名 |
$^ | 所有必要条件的文件名,用空格隔开 |
$+ | 所有必要条件的文件名,用空格隔开,包括重复的文件名 |
$* | 工作目标的主文件名 |
模式规则
许多程序在读取文件以及输出文件时都会有惯例。比如,所有的C编译器都会假设:文件以.c为扩展名,目标文件以.o为扩展名。我们可以利用GNU make提供的模式规则来简化规则的建立。例如,从一个.c文件编译出一个.o文件,下面的模式规则实现这一功能:
%.o: %.c
$(CC) -c $<
指令
在makefile开始复杂起来的时候,一些实用的指令是必须的,下面介绍二个常用的指令:
- 条件指令
下面是基本语法的样例:
libtools.a: $(tools_objects)
$(AR) $(ARFLAGS) $@ $<
ifdef GUILIB
$(GUILIB)
endif
在样例中,ifdef可以替换为以下的几个指令:
- ifndef
- ifeq
- ifneq
- 引入指令
在多个makefile里面都要使用的变量、函数、语句,我们可以使用引入指令include来组织。 下面是引入指令的基本语法样例:
include makefile.common
函数
内置函数
函数原型 | 功能描述 | 样例 |
---|---|---|
$(filter pattern..., text) | filter将text视为一系列被空格隔开的单词,与pattern比较之后,接着会返回符合的单词列表 | $(gui_objects): $(filter gui/%.o, $(objects)) |
$(filter pattern...,text) | filter-out函数用来选出与模式不相符合的单词 | to_compile := $(filter-out %.h, $(all_source)) |
$(findstring string..., text) | 此函数将会在text搜索string。如果该字符串被找到了,此函数就会返回string;否则,返回空值。 | $(findstring /tom/book/zeuux, $(PWD)) |
$(subst search-string, replace-string, text) | 常用来在文件名列表中将一个名换成另外一个扩展名,不带通配符功能 | objects := $(subst .c, .o, $(sources)) |
$(patsubst search-pattern, replace-pattern, text) | replace-pattern中的%被扩展成与模式相符的文字,且search-pattern必须与text的整个值进行匹配 | strip-trailing-flash = $(patsub %/,%,%(directory-path)) |
$(words n, text) | 返回text中第n个单词 | $(words 2, $(version_list)) |
$(sort list) | 排序list参数并移出重复的项目 | d-prefix = $(sort dbase db2 mysql) |
$(shell command) | 执行command命令 | stdout := $(shell echo normal message) |
$(wildcard pattern...) | wildcard函数的参数是一份模式列表,它会对列表中的每个模式进行扩展的动作。如果找不到相符的文件,返回空字符串 | source := $(wildcard *.c *.h) |
$(suffix name...) | 返回参数中每个单词的后缀 | same-suffix = $(filter 1 $(words $(sort $(suffix $1)))) |
$(basename name...) | 返回文件名称中不含后缀的部分 | $(basename $1) |
自定义函数
如何应用make到项目中
前面介绍了如何使用make的基本用法,下面我们介绍一下使用make来进行项目开发的步骤:
- 需求
每个项目的需求是不一样的,如何在项目中使用make的功能也是仁者见仁、智者见智。 下面是一些常见的项目需求:
- 源代码和二进制码分开
- 需要管理不同版本的二进制发布文件
- 需要不同平台的版本
- 需要一个完整的安装文件
- 文件系统布局
当你进入二进制文件树的时候,你需要考虑二进制文件的文件系统布局问题。常见的文件系统布局有一下几种:
- 版本号
- 平台依赖
- 自动编译和测试
自动编译可以在晚上无人看守的情况下编译,从而节省开发人员白天的时间。以后的一些工具可以帮助开发人员完成这一任务,比如cron。 自动测试可以验证编译程序的正确性,同时会把结果反馈给开发人员。开发人员可以直接构建这样的自动测试工具,也有一些现成的工具可选,比如 GNU工具dejaGnu就可以用来测试需要交互的非图形实用程序。
总结
本文介绍了GNU Make的用法和使用技巧,我们可以看到make很强大,可以节省开发人员的时间,享受开发的乐趣。动起手来,使用make来管理你的项目吧!