Start

代码变成可执行文件,叫做编译(compile);先编译这个,还是先编译那个(即编译的安排),叫做构建(build)

Make是最常用的构建工具,诞生于1977年,主要用于C语言的项目。但是实际上 ,任何只要某个文件有变化,就要重新构建的项目,都可以用Make构建。

Make要执行需要读取Makefile文件,也就是说make命令依赖Makefile文件进行构建

make 工作原理

    1. make会在当前的目录下找到名为“Makefile”或者“makefile”的文件。
    1. 如果找到,它会把文件中第一个target作为最终的目标文件。
    1. 如果目标文件不存在,或者目标所依赖的后面的.o文件的修改时间比目标本身更加新,那么,它会执行后面定义的命令来生成这个目标文件。如果目标所依赖的.o文件也不存在,那么make会继续按照前面的方式生成.o文件
    1. 找到相应的依赖,用来生成.o,然后再用.o完成make的最终任务
      工作原理

makefile 文件是什么

一句话:包含构建规则的文件。

makefile 格式

<target> : <prerequisites> 
[tab]  <commands>
  • target 目标, 是必须的,不可省略
  • prerequisites 前置条件
  • 第二行必须由一个tab键开头,后面跟命令

每条规则就明确两件事:构建目标的前置条件是什么,以及如何构建

target

一个target就构成一条规则,目标通常是文件名,指明Make命令所要构建的对象。 目标可以是一个文件名,也可以是多个文件名,之间用空格分隔。

除了文件名,目标还可以是某个操作的名字,这称为”伪目标”(phony target)

clean:
    rm *.o

注意: 如果当前目录中,正好有一个文件叫做clean,那么这个命令不会执行。因为Make发现clean文件已经存在,就认为没有必要重新构建了,就不会执行指定的rm命令,为了避免这种情况,可以明确声明clean是”伪目标”,写法如下:

.PHONY: clean
clean:
        rm *.o temp

如果Make命令运行时没有指定目标,默认会执行Makefile文件的第一个目标

前置条件

前置条件通常是一组文件名,之间用空格分隔。它指定了”目标”是否重新构建的判断标准:只要有一个前置文件不存在,或者有过更新(前置文件的last-modification时间戳比目标的时间戳新),”目标”就需要重新构建。

命令

命令(commands)表示如何更新目标文件,由一行或多行的Shell命令组成。它是构建”目标”的具体指令,它的运行结果通常就是生成目标文件。

** 每行命令在一个单独的shell中执行 **

all: 
        export A=10
        echo "A=$$A"

打印出A是空值,可以通过写成一条命令或者设置属性实现

all: a.php
        export A=10; echo "A=$$A"

或者

.ONESHELL:
all: a.php
        export A=10; 
        echo A=$$A

基本语法

  1. 注释 #

  2. 回声: 正常情况下,make会打印每条命令,然后再执行,这就叫做回声

    关闭回声 可以在命令前加上 @

  3. 通配符 * ? …

  4. 模式匹配 %

    %.o: %.c 表示将目录下所有的.o文件依赖同名的.c文件

  5. 自定义变量 $

    txt = hello world
    test:
    @echo $(txt)

  6. 调用shell变量 $$

    test:

     @echo $$HOME

    关于变量的赋值问题,makefile 提供了四个赋值运算符(=、:=、?=、+=)
    VARIABLE = value 在执行时扩展,允许递归扩展。
    VARIABLE := value 在定义时扩展。
    VARIABLE ?= value 只有在该变量为空时才设置值。
    VARIABLE += value 将值追加到变量的尾端。

  7. 内置变量见手册 https://www.gnu.org/software/make/manual/html_node/Implicit-Variables.html

    VPATH: include src 扩展搜索目录
    CC : cc (即gcc)
    APPFLAGS: 预处理使用的选项
    CFLAGS: -I include 编译时使用的选项, -I 为gcc的参数,表示添加.h文件搜索的目录
    LDFLAGS: 链接库使用的选项

  8. 自动变量

  • $@ 指代当前目标 如 make foo 的$@ 就是指 foo
  • $< 指第一个前置条件
  • $? 比目标更新的所有前置条件
  • $^ 所有前置条件,之间以空格分隔
  • $* 匹配符 % 匹配的部分
  • $(@D) 和 $(@F) 分别指向 $@ 的目录名和文件名, 比如,$@是 src/input.c,那么$(@D) 的值为 src ,$(@F) 的值为 input.c
  • $(<D) 和 $(<F) 分别指向 $< 的目录名和文件名
  1. 判断, 使用bash语法
    ifeq ($(CC),gcc)
    libs=$(libs_for_gcc)
    else
    libs=$(normal_libs)
    endif
  2. 循环, 使用bash语法
    LIST = one two three
    all:
    for i in $(LIST); do \
        echo $$i; \
    done
    等同于
    all:
    for i in one two three; do \
        echo $i; \
    done
  3. 函数

    $(function arguments)
    或者
    ${function arguments}

常用的内置函数:

  • shell 函数用来执行 shell 命令 : srcfiles := $(shell echo src/{00..99}.txt)

makefile 实例

VPATH = src include  # 添加文件搜索目录
CC = cc  # 编译器
CFLAGS = -I include  # 添加.h文件搜索目录
LDFLAGS = -lm  # 动态链接库 math

build: objs/ping

objs/ping: objs/ping.o objs/icmp.o objs/vars.o  objs/tool.o
    $(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@     

objs/ping.o: ping.c icmp.h
    $(CC) $(CFLAGS) -c $< -o $@

objs/icmp.o: icmp.c icmp.h tool.h vars.h
    $(CC) $(CFLAGS) -c $< -o $@

objs/vars.o: vars.c
    $(CC) $(CFLAGS) -c $<  -o $@

objs/tool.o: tool.c
    $(CC) $(CFLAGS) -c $< -o $@

.PHONY: clean install
clean:
    rm -rf objs/*

install: build
    test -d '/usr/local/cping/bin' \
        || mkdir -p '/usr/local/cping/bin'

    cp objs/ping  '/usr/local/cping/bin/cping'
    rm -rf /usr/bin/cping && ln -s /usr/local/cping/bin/cping  /usr/bin/cping
文档更新时间: 2021-03-08 11:49   作者:周国强