跟我一起写 Makefile


1 概述

makefile可以指定那些文件需要先编译,那些文件需要后编译,那些文件需要重新编译。

makefile像shell脚本,可以执行操作系统的命令。

作用:自动化编译。

make是一个命令工具,用来解释makefile中的指令。


2 介绍

make的编译规则,以c文件和头文件为例:

  1. 若这个工程没有编译过,那么所有的c文件都要编译并被链接。
  2. 若这个工程的某几个c文件被修改,那么只编译被修改的c文件,并链接目标程序。
  3. 若这个工程中的头文件被改变了,那么需要编译引用了这几个头文件的c文件,并链接目标程序。

基本语法

target ...: prerequisites
    command
    

command前一定要以一个tab键开头

target是目标文件,prerequisites是源文件,也就是所谓的依赖文件,command就是shell命令,就是在命令行中编译的那一套命令。

基本规则就是,当prerequisites相较于target更新时,执行command。


判断更新的原理

通过比较target文件和prerequisites文件的修改日期,来判断是否执行command。


依赖关系的实质

说明目标文件是由那些文件生成的。


定义命令

在target后面没有prerequisites,也就是冒号之后什么都没有。

要执行命令,就需要显示的使用make label, 如

clean :
    rm edit main.o kid.o
    

就需要用make clean来执行。


如何工作

  1. 在当前目录下寻找makefileMakefile文件。
  2. 以文件中的第一个target为最终的目标文件。
  3. 如果edit文件不存在,或是后面的依赖修改时间更新,那么会执行命令
  4. 如果edit文件的依赖不存在,就会寻找依赖并生成

在makefile中使用变量

作用:便于维护,只需要修改一个地方,就可以把文件中所有使用到该变量的值修改。

如在makefile中一开始定义:

objects = main.o kid.o command.o

和其他语言中的变量有些许不一样。

使用

edit : $(objects)
    cc -o edit $(objects)
clean :
    rm edit $(objects)
    

利用make的自动推导功能

main.o : defy.h

会自动把main.c写进依赖,并自动推导出cc -c main.c.

发现头文件并不需要写到command中,应该是因为头文件会在c文件中展开。


用于清空目标文件的规则

每一个Makefile中都应该写一个清空目标文件的规则。

.PHONY : clean
clean :
    -rm edit $(objects)
    

.PHONY表示clean是一个“伪目标”。

有一个减号-表示,当某些文件出现问题时,继续完成后面的事。

clean一般放在文件的最后。


Makefile组成

  1. 显示规则,指出要生成的文件、文件的依赖和生成的命令。
  2. 隐晦规则,自动推导。
  3. 变量定义,多为字符串。
  4. 文件指示,引用另一个makefile,指定有效部分,定义多行命令。
  5. 注释,#

指定特定的Makefile

make -f Make.linux

或者

make —-file Make.ATX

引用其他Makefile

include filename

include foo.make a.my b.my c.mk

使用-I—-include-dir参数,指定目录寻找。

让make不理会无法读取的文件,从而继续执行, 添加-

-include filename

环境变量MAKEFILES

make会把这个变量的值做一个类似include的动作。


3 书写规则

可以把command和前面的写成一行

targets : prerequisites ; command

命令或其他的句子太长可以用\分隔换行

objects = main.o kid.o command.o display.o \
insert.o search.o file.o utility.o

一般,make以/bin/sh执行命令。


使用通配符

支持三个通配符:*,?,~


文件搜寻

VPATH变量

Makefile中的VPATH变量,指定目录查找,当make在当前目录中没有查找到时,到指定目录中查找

VPATH = src:../headers

使用:分隔。

vpath关键字

三种使用方法

  1. vpath
  2. vpath
  3. vpath

需包含%字符,表示
匹配0或多个字符。

伪目标

使用.PHONY显式表明其为伪目标,作用是避免和文件重名。

.PHONY : clean
clean :
    rm *.o temp
    

表明要运行clean就需要执行make clean.

目标和伪目标都可以成为依赖。


多目标

当多个目标依赖于同一个文件时。

使用自动化变量$@, 表示当前规则中所有的目标的集合。

bigoutput little output: text.g
    generate text.g -$(subset output,,$@) > $@
    

$表示执行一个函数,subset是函数名,output,,$@是参数。


静态模式

更容易定义多目标的规则。

语法:

<targets>: <target-pattern>: <prereq-patternn>
    <command>
    

objects = foo.o bar.o
    
all: $(objects)

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

依赖的名称来源于目标文件前的名称。

中间的%.o表示筛选尾缀为.o的目标。

$<$@ 是自动化变量。

$<表示所有的依赖目标集(这里是foo.c bar.c).

$@表示所有的目标集(这里是foo.o bar.o)

自动生成依赖

使用GNU C/C++编译器的-MM选项。

.d文件包含.c文件的依赖关系。

source = foo.c bar.c
include $(source:.c=.d)

做替换,把变量source中所有 .c 字符串替换为 .d

4 书写命令

注释的符号是#这个和shell脚本一样高。

make 一般使用环境变量SHELL中定义的Shell来执行。

make默认由/bin/sh执行。

显示命令

使用@在命令之前,命令将不会被打印在屏幕上。

调试Makefile,使用make的-n—-just-print参数,只会打印命令而不会执行。

make的-s—-silent—-quiet参数,表示禁止命令的显示。

命令执行

让后续命令在前面起作用之后执行,要用分号隔离开。

exec:
    cd /home/hchen; pwd
    

命令出错

忽略命令的出错, 使用-

clean:
    —rm -f *.o
    

也可以使用make的参数-i—-ignore-errors.

或者使用.IGNORE

终止出错的规则,继续执行其他规则的参数-k—-keep-going

嵌套执行

总控Makefile.

SHELL和MAKEFLAGS变量总是会传递给下层。

-w—-print-directory参数,打印目前工作目录信息。p

定义命令包

define开头,endif结束。

define run-yacc
yacc $(firstword $^)
mv y.tab.c $@
endif

使用命令包

foo.c: foo.y
    $(run-yacc)
    

5 使用变量

Makefile中定义的变量类似于C/C++中的宏。

变量命名,可以包含字符、数字、下划线(可以用数字开头),不应该含有 : # =或空字符。大小写敏感。

使用:=操作符,前面的变量不能使用后面的变量过,避免无限重复定义。

注意注释符#的特性

dir := /foo/bar     #directory

dir这个变量的值是/foo/bar后面跟了四个空格。

?=操作符

FOO ?= bar

如果FOO没有被定义,那么其值为bar。

变量的高级用法

替换。

把变量的值当作变量。

追加变量值

使用+=.


override指示符


make的运行

一般makefile中都包含了编译、安装、打包等功能。


跟我一起写 Makefile
http://example.com/2022/08/25/跟我一起写 Makefile/
作者
Jie
发布于
2022年8月25日
许可协议