学习GNU Make

1 GNU Make简介

make是Unix和其他操作系统上最持久的工具之一。自1970年问世以来,make至今仍旧是大多数项目开发的核心工具,它甚至被用来编译Linux内核。 简单是make的设计理念,在你变更源代码文件之后,想重新编译你的程序后者其他输出文件之际,make会检查时间戳,找出被变更的文件并进行必要的重 编译动作。 GNU make这一版本已经成为make程序的行业标准。本文将介绍GNU make的用法和使用技巧。首先介绍一下GNU make的基本用法,接着介绍通配符的使用,最后介绍一下如何利用make组织你的项目文件目录。

2 基本语法

make描述文件的文件名为makefile、Makefile、GNUMakefile。为了简单起见,我们统一使用makefile。 makefile的文件一般采用“从上到下”的结构,下层工作目标用来让上层工作目标保持在最新的状态。下面是makefile的基本语法结构:

target1 target2 target3 : prerequisite1 prerequisite2
command1
command2
command3

冒号左边可以是一个或者多个工作目标,右边可以是零个或者多个必要条件。更新工作目标会执行一条或者多条命令,且每个命令必须以TAB开头。需要注意的是不能在非命令行的第一个字符前出入一个TAB,否则make会将其后的文字作为命令来解释。 另外如何一行文本太长,可以使用标准的反斜线分隔为多行。

3 规则

3.1 假想工作目标

假想工作目标就是贴上标签的工作目标,它把一个或者多个工作目标归类,几乎成为makefile里面的必备元素。表1是一些常用的假想工作目标。

表1 常用的假想工作目标

工作目标 功能
all 执行编译应用程序的所有工作
install 从已编译的二进制文件进行应用程序的安装
clean 将编译产生的二进制文件删除
check 执行与应用程序相关的任何测试
info 从Texinfo源代码来创建GNU info文件

3.2 变量

make的变量来自以下几个来源:

  • 文件,直接在makefile里面定义
  • 命令行参数传入
  • 环境变量
  • make自动创建的变量

简单变量的具体语法:

$(variable-name)

表2是GNU make的核心自动变量列表:

表2 核心自动变量

自动变量 含义
$@ 工作目标的文件名
$% 档案文件成员结构中的文件名
$< 第一个必要条件的文件名
$^ 所有必要条件的文件名,用空格隔开
$+ 所有必要条件的文件名,用空格隔开,包括重复的文件名
$* 工作目标的主文件名

3.3 模式规则

许多程序在读取文件以及输出文件时都会有惯例。比如,所有的C编译器都会假设:文件以.c为扩展名,目标文件以.o为扩展名。我们可以利用GNU make提供的模式规则来简化规则的建立。例如,从一个.c文件编译出一个.o文件,下面的模式规则实现这一功能:

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

3.4 指令

在makefile开始复杂起来的时候,一些实用的指令是必须的,下面介绍二个常用的指令:

  • 条件指令
    • ifndef
    • ifeq
    • ifneq
  • 下面是基本语法的样例:

    libtools.a: $(tools_objects)
    $(AR) $(ARFLAGS) $@ $<
    ifdef GUILIB
    $(GUILIB)
    endif

    在样例中,ifdef可以替换为以下的几个指令:

  • 引入指令
  • 在多个makefile里面都要使用的变量、函数、语句,我们可以使用引入指令include来组织。 下面是引入指令的基本语法样例:

    include makefile.common

4 函数

4.1 内置函数

表3 GNU make内置函数

函数原型 功能描述 样例
$(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)

4.2 自定义函数

5 如何应用make到项目中

前面介绍了如何使用make的基本用法,下面我们介绍一下使用make来进行项目开发的步骤:

  1. 需求
    • 源代码和二进制码分开
    • 需要管理不同版本的二进制发布文件
    • 需要不同平台的版本
    • 需要一个完整的安装文件
  2. 每个项目的需求是不一样的,如何在项目中使用make的功能也是仁者见仁、智者见智。 下面是一些常见的项目需求:

    在开始项目之前,最后考虑一下你使用make的需求是什么。
  3. 文件系统布局
    • 版本号
    • 平台依赖
  4. 当你进入二进制文件树的时候,你需要考虑二进制文件的文件系统布局问题。常见的文件系统布局有一下几种:

  5. 自动编译和测试
  6. 自动编译可以在晚上无人看守的情况下编译,从而节省开发人员白天的时间。以后的一些工具可以帮助开发人员完成这一任务,比如cron。 自动测试可以验证编译程序的正确性,同时会把结果反馈给开发人员。开发人员可以直接构建这样的自动测试工具,也有一些现成的工具可选,比如 GNU工具dejaGnu就可以用来测试需要交互的非图形实用程序。

6 总结

本文介绍了GNU Make的用法和使用技巧,我们可以看到make很强大,可以节省开发人员的时间,享受开发的乐趣。动起手来,使用make来管理你的项目吧!

7 参考资料

  • GNU Make项目管理,O'Reilly

你可能感兴趣的:(学习GNU Make)