• GNU make笔记


    是看跟我一起写makefile做的一点笔记。

    编译和链接

    在Makefile中定义整个编译流程以及各个目标文件与源文件之间的依赖关系,并且只重新编译新的修改会影响到的部分,从而降低编译的时间。

    编译只检查函数、变量是否被声明,并生成.o(Linux)或.obj(Windows)文件。由于编译生成的中间目标文件太多,因此通常会将其打包成为.a(Linux)或.lib(Windows)文件,以进行链接。

    依赖关系

    target ... : prerequisites ...
    	command
    	...
    
    • 1
    • 2
    • 3
    • target:可以是一个目标文件,可执行文件,或一个标签。
    • prerequisites:target所依赖的文件和target。
    • command:该target要执行的shell命令。

    注意makefile中的command必须以Tab开头。
    另外,在执行有顺序性要求的命令时,不应该分写两行,而应该用分号隔开,例如:

    	# 错误写法!pwd将在本目录执行
    	cd ../
    	pwd
    
    	# 正确写法
    	cd ../; pwd
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    makefile会检查文件的更新时间,当prerequisites中有文件比target新时,command所定义的命令就会被执行。

    以1个.h文件 + 2个.c文件为例:

    # myProcess是可执行文件
    myProcess : main.o somec.o
    	gcc -o myProcess main.o somec.o
    
    main.o : main.c somec.h
    	gcc -c main.c
    somec.o : somec.c somec.h
    	gcc -c somec.c
    
    clean :
    	rm myProcess main.o somec.o
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    注意,这里的clean是一个标签而非文件,使用类似make clean这样的形式来执行标签后的命令。

    makefile会以完成第一个target为目标,逐次展开依赖,去更新涉及到的依赖的文件。

    变量

    将上例改为用变量的形式:

    OBJ = main.o somec.o
    TARGET = myProcess
    CC = gcc
    
    $(TARGET) : $(OBJ)
    	$(CC) -o $(TARGET) $(OBJ)
    
    main.o : main.c somec.h
    	$(CC) -c main.c
    somec.o : somec.c somec.h
    	$(CC) -c somec.c
    
    clean :
    	rm $(TARGET) $(OBJ)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    也可以将clean改为下面这种写法:

    .PHONY : clean
    clean :
    	-rm $(TARGET) $(OBJ)
    
    • 1
    • 2
    • 3

    其中,.PHONY显式地表示clean是一个伪目标,即表明clean不是需要检查依赖的目标。
    另外,rm前的-表示执行时即使出错,也继续执行命令。

    一般更推荐使用:=来赋值变量,与=的区别在于,前者立即展开,而后者在使用时才会展开。

    定义空格变量

    使用如下的方法定义空格变量:

    nullstring :=
    space := $(nullstring) #end of line
    
    • 1
    • 2

    nullstring是空变量,其后加一个空格,在用注释表示结束,即可将space展开为一个空格变量。

    变量的替换

    例如:

    var1 := a.o b.o
    var2 := $(var1:.o=.c)
    
    • 1
    • 2

    表示var2是将var1中的.o替换为.c,即var2为a.c b.c。

    override

    override强制修改变量,例如:

    var := var1
    override var1 := var2
    
    • 1
    • 2

    多行变量

    define multi_line_var
    xxx
    xxx
    endef
    
    • 1
    • 2
    • 3
    • 4

    目标变量

    在目标后的依赖里写变量,表示不管全局上该变量如何,在此目标引发的行为中变量都是这样的,例如:

    CFLAGS := -o
    # 由prog引发的行为中CFLAGS都是-g
    prog : CFLAGS = -g
    
    • 1
    • 2
    • 3

    自动变量

    使用模式规则来定义一个隐含规则,需要在目标中使用%。目标中的%取决于依赖中的值。
    而自动化变量,就会把模式中所定义的一系列的文件自动地挨个取出。

    下面是一些常用的自动变量:

    自动变量含义
    $@规则中的目标文件名
    $<第一个依赖文件
    $^所有依赖文件
    $*匹配通配符%的部分

    make自动推导

    make看到xx.o,可以自动地推导出它依赖同名的xx.c,并且gcc -c xx.c也会被自动加上。
    由此,上例又可以省略为:

    OBJ = main.o somec.o
    TARGET = myProcess
    CC = gcc
    
    $(TARGET) : $(OBJ)
    	$(CC) -o $(TARGET) $(OBJ)
    
    main.o : somec.h
    somec.o : somec.h
    
    clean :
    	rm $(TARGET) $(OBJ)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    包含其他makefile文件

    默认情况下,make会寻找命名为makefile和Makefile的文件,当然也可以是其他任意命名的makefile文件,不过需要使用make -f 来显式地指定。

    在一个makefile中使用include 来包含其他makefile文件,

    文件搜索

    默认情况下,makefile只会在当前目录搜索文件。

    • 使用VPATH变量指明其他的目录用于搜索。
      例如VPATH = src:../headers,其中src和headers都是搜索目录,它们之间用:隔开。
    • 使用vpath关键字。
      例如vpath %.h ../headers表示在headers下搜索所有以.h结尾的文件。

    一次生成多个程序

    按基本的写法,makefile只会从第一行,即第一个生成程序开始查找依赖,如果后面还有程序要生成则可能被忽略。
    可以使用伪目标来达成这一目的:

    all : prog1 prog2
    .PHONY : all
    
    prog1 : prog1.o
    	gcc -o prog1 prog1.o
    
    prog2 : prog2.o
    	gcc -o prog2 prog2.o
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    声明一个伪目标all,它依赖于两个程序,但由于伪目标不会被生成文件,因此all总是需要更新其依赖,也就总是生成这多个程序。

    条件判断

    使用ifeq, ifneq, ifdef, ifndef来进行条件判断,并且都以endif结尾。
    make读取Makefile时就计算条件表达式的值,并根据其值来选择语句,所以,不要把自动化变量放入条件表达式中,因为自动化变量是在运行时才有的。

    函数

    格式为$(function argument1,argument2)。函数和参数以空格隔开,参数见以逗号隔开。
    下面介绍一些常用的函数。

    字符串函数

    • subst
      $(subst from,to,text),把字符串text中的from替换为to。
    • patsubst
      $(patsubst pattern,replacement,text),模式替换,把text中的单词pattern替换为replacement。
    • strip
      $(strip string),去空格
    • filter
      $(filter pattern,text),只保留text中符合pattern模式的单词。
      例如```
    sources := a.c b.o c.s
    $(filter %.c %.o,$(sourecs))
    
    • 1
    • 2
    返回a.c b.o
    
    • 1
    • sort
      $(sort string),给string中的单词排序,注意会去重。
    • word
      $(word n,string),取string中的第n个单词。
    • words
      $(words string),统计单词个数。

    文件函数

    • dir
      $(dir names),从文件名列表names中取出目录,例如$(dir /src/foo.c temp)返回/src/ ./
    • suffix
      $(suffix names),从names中取出各个文件的后缀。
    • basename
      同上,只不过取的是前缀名。

    控制流函数

    • foreach
      $(foreach var,list,expr),把list中的单词拿出来赋给var,然后执行expr。expr每次的返回会以空格隔开,最后组成整个函数的返回值。
      例如:$(foreach n,a b,$(n).o)返回a.o b.o
      注意var是一个局部变量,其作用域只在foreach中。
    • if
      $(if condition,then-part,else-part),else-part可以省略。
    • shell
      $(shell expr),执行一个shell命令作为返回值。
    • error
      $(error text),停止make并报错text。
    • warning
      同上,不过不退出make而是继续执行。

    总结

    GNU make的执行步骤如下:

    1. 读入所有的Makefile。
    2. 读入被include的其它Makefile。
    3. 初始化文件中的变量。
    4. 推导隐式规则,并分析所有规则。
    5. 为所有的目标文件创建依赖关系链。
    6. 根据依赖关系,决定哪些目标要重新生成。
    7. 执行生成命令。

    第三步并非一开始就将所有变量展开,而是其出现在依赖关系中且要被使用了才展开。

  • 相关阅读:
    k8s集群Job负载 支持多个 Pod 可靠的并发执行,如何权衡利弊选择适合的并行计算模式?
    DolphinScheduler3.0.0 集群部署过程
    使用主成分分析进行模态分解(Matlab代码实现)
    QT静态链接库
    8、Bean的循环依赖问题
    算法笔记——路径问题
    单bit脉冲信号跨时钟域处理——展宽信号 + 握手协议
    C++ Reference: Standard C++ Library reference: Containers: array: array: back
    分布式本地缓存 ehcache 缓存同步复制
    8个维度,详细分析JDK11的新特性!
  • 原文地址:https://blog.csdn.net/F_Arisa/article/details/134200007