• Makefile(make)之(3)输出变量值


    Makefile(make)输出变量值

    Author:Onceday    date:2022年8月28日

    本文收集整理于陈皓大佬的文章,以便贴合自己的思维逻辑,以下是原文文档:

    基础知识请参考前两篇文章:

    GNU make官网链接:GNU make

    1.引言

    makefile文件里面,一般具有大量自定义的变量,如何只是一个文件的话,那还好,可以通过编辑器高亮提示和全文查找来捋清楚关系。

    但是,如果是Linux内核级别的源码,makefile文件嵌套多,难以找到所有makefile文件,这个时候,很难去确定某一个变量的值是多少,来自命令行输入,还是自动变量,或者是预定义的。

    ARCH		?= $(SUBARCH)
    # Architecture as present in compile.h
    UTS_MACHINE 	:= $(ARCH)
    SRCARCH 	:= $(ARCH)
    
    • 1
    • 2
    • 3
    • 4

    比如上面一段是linux内核makefile的脚本,这里将变量的值又赋给其他变量,如此嵌套下去,很难快速在某个小组件的makefile里确定变量的值。总不能一开始就把全部makefile读一遍吧,这不合适。

    因此,可以写一些额外的模式规则,即目标里面带有模式匹配符%,如%.0,表示所有带有后缀.0的目标,如x.oss.o等。

    例如:

    %.o :%.c
      gcc -c $< -o $@
    
    • 1
    • 2

    当我们使用make this.o时,便会匹配上该规则,相当于:

    this.o :this.o
      gcc -c this.c -o this.o
    
    • 1
    • 2

    $@表示规则中的目标文件集合(即%.o),$<表示依赖目标文件的集合(即%.c)。

    当然,实际上不会这么用,因为配合静态模式和自定义变量,可以更加有效率的完成此项工作。

    但在此文中,我们输出变量的值,就需要用到这个,一般这些脚本放在了.mk文件里面。

    2.查看变量值和目标命令

    首先感兴趣的可能是系统预定义的变脸和规则,可使用以下命令查看:

    make -p -f /dev/null
    
    • 1

    -p就是输出make所有数据信息,-f是指定makefile文件,/dev/null是一个特殊的设备文件,即空文件。

    所以上面命令能显示所有的预定义信息。那么如果想输出实际makefile的信息,可以用以下命令:

    make -qp [目标]
    
    • 1

    -q表示检查所指定的目标需不需要更新,因此不会真的执行命令,类似的命令还有其他的,如

    "-n"
    "--just-print"
    "--dry-run"
    "--recon"
    
    • 1
    • 2
    • 3
    • 4

    上面的四个命令是输出运行过程要执行的规则, 但不会输出变量信息和规则信息。

    因此,这些工具要灵活使用,而对于变量信息的输出,最大的问题在于文本太多了。一次性输出这么多信息,并非是我们所希望的。

    这里借助脚本的echo指令,可以写出以下规则:

    %:
      @echo '$*=$($*)'
    
    • 1
    • 2

    这个@,其作用是取消在终端输出@echo '$*=$($*)'这个命令的文本

    $:是自动化变量,取目标模式中"%"及其之前的部分,在这里,就是命令行输入的“目标”变量名。$($*)是取“目标”变量名的值。

    默认情况下,是可以看到make执行了哪些命令的。但是echo本身会输出替换了变量之后的字符串,因此就不需要重复输出了。如:

    test=1245
    %:
    	@echo "$*=$($*)"
    
    • 1
    • 2
    • 3

    那么在命令行里有:

    onceday@ubuntu:~$ make untest
    untest=
    onceday@ubuntu:~$ make test
    test=1245
    
    • 1
    • 2
    • 3
    • 4

    %会匹配所有的目标,如untest和test,但是这个不能输出目标的信息,例如:

    first:*.py
    	@echo "hello world"
    
    test=1245
    %:
    	@echo "$*=$($*)"
    
    second:*.py
    	@echo "hello"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这个里面,有:

    onceday@ubuntu:~$ make second
    hello
    onceday@ubuntu:~$ make se
    se=
    onceday@ubuntu:~$ make 
    hello world
    onceday@ubuntu:~$ make first
    hello world
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    可以看到,如果匹配上其他实际目标,则并不会执行这个模式匹配%:

    因此,可以使用以下这种命令来查看具体执行的目标所包含的命令:

    make -n 目标
    
    • 1

    但是工作不一定会正常,毕竟下一步的命令可能依赖上一步的具体执行结果。

    3.输出更加详细的变量信息

    编写模式规则如下:

    d-%:
    	@echo '$*=$($*)'
    	@echo '  变量类型= $(origin $*)'
    	@echo '  原始值 = $(value $*)'
    	@echo '  特点   = $(flavor $*)'
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这里,$(origin )函数表示获取变量类型,有如下几种:

    • “undefined”,如果从来没有定义过。

    • “default”,如果是一个默认的定义。

    • “environment”,如果是一个环境变量。

    • “file”,如果这个变量被定义在Makefile中。

    • “command line”,如果这个变量是被命令行定义的。

    • “override”,如果是被override指示符重新定义的。

    • “automatic”,如果是一个命令运行中的自动化变量。

    $(value )函数返回变量未被展开的样子。

    $(flavor )函数表示该变量量是如何展开的,有两个值,simple表示是一般展开的变量,recursive表示递归展开的变量。

    如下是一个测试的makefile文件:

    G := gcc
    foo = $(goo)
    goo = $(hoo)
    hoo = joo?
    CFLAGS = $(include_dirs) -O
    include_dirs = -Ifoo -Ibar
    CFLAGS := $(CFLAGS) -Wall
    test = $(CFLAGS) -xxx
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    注意这里:=赋值符号表示不使用“后定义的变量”。

    在命令行进行测试:

    onceday@ubuntu:~$ make d-G
    G=gcc
      变量类型= file
      原始值 = gcc
      特点   = simple
    onceday@ubuntu:~$ make d-foo
    foo=joo?
      变量类型= file
      原始值 = $(goo)
      特点   = recursive
    onceday@ubuntu:~$ make d-CFLAGS
    CFLAGS=-Ifoo -Ibar -O -Wall
      变量类型= file
      原始值 = -Ifoo -Ibar -O -Wall
      特点   = simple
    onceday@ubuntu:~$ make d-test
    test=-Ifoo -Ibar -O -Wall -xxx
      变量类型= file
      原始值 = $(CFLAGS) -xxx
      特点   = recursive
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    注意,这里echo ''必须使用单引号,如果是双引号,那么shell也会对$(xxx)进行取值,然后就是报错和显示异常。

    实际测试输出的结果非常明确,递归展开的变量其原始值都包含$(xx),直接展开的,其原始值都是固定的,无需根据后面的运行结果确定。

    这里就凸显的:=的作用,拒绝使用后定义的变量,避免在嵌套makefile中,前面的变量被后面使用的变量给干扰了。

    上面的模式规则和makefile写在了一个文件里,因此命令行可以直接填目标即可。实际情况下,往往是要同时指定makefile和mk(模式匹配)文件。

    即:

    make -f makefile -f var.mk d-xxxx
    
    • 1

    这样就将模式规则和具体的makefile文件分开了。

  • 相关阅读:
    深度解析Java JDK 1.8中Stream流的源码实现:带你探寻数据流的奥秘
    力扣1.两数之和(JavaScript版本)
    公共数据 | CnOpenData中国90座城市建筑物屋顶矢量数据集
    公众号爆文写作怎么做?或许这些领域才适合你!
    程序员公司保密协议
    《算法导论》15.5 最优二叉搜索树(含C++代码)
    Python:函数和代码复用
    pip配置多个国内的python镜像源
    vue-cli2 与vue-cli3,vue2与vue3 初始化项目,本地vue项目,详细解析区别(2024-04-19)
    CFCA企业版通配符SSL证书
  • 原文地址:https://blog.csdn.net/Once_day/article/details/126576182