首页
社区
课程
招聘
shell+Makefile,坑我晚下班
发表于: 2024-12-6 12:49 2111

shell+Makefile,坑我晚下班

2024-12-6 12:49
2111

昨天遇到一个这样的问题:

test
 |- test.sh
 |- 1.txt
 |- hello
     |- test.c
     |- Makefile
     |- 2.txt

test.sh读取1.txt,每读一行,执行一下"make clean; make pat=22":

#!/bin/bash

while read line || [[ -n "${line}" ]]; do
    echo "${line}"
    cd hello
    make clean; make pat=22
    cd ..
done < ./1.txt

1.txt内容如下,所以按道理,test.sh应该循环3次:

1111111
aaaaaaa
AAAAAAA

Makefile用于编译test.c,每次执行,都会执行一条shell命令,并将结果赋值给var变量:

var := $(shell grep $(pat) 2.txt)

test: test.o
    @echo "$(pat), $(var) ......."
    gcc test.c -o test -g -Wall

.PHONY: clean
clean:
    rm test test.o -f

test.c(不重要):

#include <stdio.h>

int main()
{
    printf("hello\n");
    return 0;
}

2.txt:

2222222
bbbbbbb
BBBBBBB


执行结果:

test.sh只循环了1次,但是期望循环3次。


分析:

  1. 实际项目的代码,要复杂的多,先通过不断的注释执行,将原因锁定到Makefile的这一行:"var := $(shell grep $(pat) 2.txt)";

  2. 但是这一行只读不写,并且读取对象还是2.txt,并不会影响1.txt内容;

  3. 从test.sh的打印结果看,第一遍执行"make clean; make pat=22",看着也没什么问题,脚本并没有退出或卡住,因为test可执行文件编译出来了,循环结束位置打印的分割线,也显示了;

  4. 将var赋值语句换成"var := $(shell cat 2.txt | grep $(pat))",语义一样,只是方式不同,竟然就正常了;

  5. 为了搞清楚到底怎么回事,就构造了以上demo,然后想到,执行"make clean"时,没有传pat参数,会导致"grep $(pat) 2.txt",等效于"grep 2.txt",手动执行这条命令是会卡住的,先忽略是否现象吻合,这确实就是问题所在,在$(pat)外面加个双引号,即可解决问题。


疑惑:

但是,从test.sh的打印结果看,并不是"make clean"卡住了(通过ps命令,也能确认make clean和grep命令并没有继续在执行),而是test.sh脚本的循环中断了。


猜测:

跟Makefile中的shell命令卡住有关,因为以上4中的修改方式也没在$(pat)外面加双引号,不同的是,手动执行这条命令,会失败,但不会卡住,就没有导致test.sh的循环中断。


进一步想:

test.sh会创建一个新的进程,执行make命令,make也会创建一个新的进程,执行grep命令,孙子进程理论上会卡住,但从结果来看,test.sh却能继续往后执行,所以大概可以推测,make对于会卡住的shell命令,会有一套自己的处理,总之也是瞬间就执行完了。至于为什么能影响到test.sh进程,而且影响结果还这么奇怪,暂时还没能搞清楚。

在test.sh循环结尾,显示一下1.txt,也还是完整的:


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 1
支持
分享
最新回复 (2)
雪    币: 3472
活跃值: (11138)
能力值: ( LV9,RANK:240 )
在线值:
发帖
回帖
粉丝
2

今天又遇到一个情况,和demo的区别是,Makefile不再用于编译用户程序,而是用于编译驱动。


错误Makefile:

var := $(shell grep -c "$(shell uname -r)" $(realpath 2.txt))

ifneq "$(var)" ""
EXTRA_CFLAGS += -DTEST
endif

ifneq ($(KERNELRELEASE),)
        obj-m := test.o
else
        KERNELDIR ?= /lib/modules/$(shell uname -r)/build
        PWD := $(shell pwd)

default:
        @echo "$(EXTRA_CFLAGS) ......."
        $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
        $(MAKE) -C $(KERNELDIR) M=$(PWD) clean
endif
endif

愿望很简单:如果2.txt,包含当前系统的内核版本,则在编译test.c时,加上TEST宏定义。

现象:

  1. 使用2.txt的绝对路径,没问题,相对路径,却不行;

  2. 先不去想现象1的原因,既然绝对路径没问题,直觉上就可以换成$(realpath 2.txt),这样可以保证Makefile的通用性,然而,不行!

原因:

驱动程序的Makefile,每次会执行2次,首次执行走到"$(MAKE) -C ..."的时候,会进入内核头文件目录,再次执行这个Makefile,那个时候,"$(realpath 2.txt)"的结果,就不再是驱动目录中的2.txt路径了。


正确Makefile:

txt_path ?= $(shell pwd)
var ?= $(shell grep -c "$(uname -r)" $(txt_path)/2.txt)

ifneq "$(var)" ""
EXTRA_CFLAGS += -DTEST
endif

ifneq ($(KERNELRELEASE),)
        obj-m := test.o
else
        KERNELDIR ?= /lib/modules/$(shell uname -r)/build
        PWD := $(shell pwd)

default:
        @echo "$(EXTRA_CFLAGS) ......."
        $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
        $(MAKE) -C $(KERNELDIR) M=$(PWD) clean
endif

使用"?="赋值符号,第二次make时,发现变量已经有定义了,就继续使用之前的值。


test.c:

#include <linux/module.h>

#ifdef TEST
#pragma message("TEST defined")
#endif

static int __init test_init(void)
{
    printk("hello\n");
    return 0;
}

static void __exit test_exit(void)
{
    printk("byebye\n");
}


module_init(test_init);
module_exit(test_exit);

MODULE_LICENSE("GPL");

问题到这就已经解决了。


但是同样有个疑惑:仅仅改个赋值符号,也是编译不了的

var := $(shell grep -c "$(uname -r)" $(realpath 2.txt))
var ?= $(shell grep -c "$(uname -r)" $(realpath 2.txt))

通过现象看,虽然第二次make时,var不需要赋值了,但是后面的shell语句仍然会执行,不知道为什么?

2024-12-6 17:50
0
雪    币: 464
活跃值: (2782)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3

个人认为是var := 的原因  make clean; make pat=22时候触发了


make   在使用:=定义变量时  如果:=右边的表达式如果包含对变量的引用,则这些对变量的引用会直接展开

           在使用“=”定义变量时  如果=右边存在对其他变量或函数的引用,这些引用并不会立即展开。在实际使用到递归展开变量时,递归展开变量才会展开


var = $(shell grep $(pat) 2.txt)
print:
	@echo $(var)
test:
	@echo "test"

make test        输出   test

make pat=2    输出   2222222

var := $(shell grep $(pat) 2.txt)
print:
	@echo $(var)
test:
	@echo "test"

make test     卡住

make pat=2  输出   2222222


#!/bin/bash

while read line || [[ -n "${line}" ]]; do
	echo "${line}"
	make test; echo "over"
done < ./1.txt

分号";"连接多条命令,这些命令会一次执行,各命令之间没有任何逻辑关系,不论哪条命令报错了,后面的命令仍会依次执行 所以结果只有一行1111111

(make test; echo "over" 好像只在这里的文件输入流下循环一次后结束 其他循环还是会卡住)

最后于 2024-12-7 00:13 被contain_of编辑 ,原因:
2024-12-6 19:01
1
游客
登录 | 注册 方可回帖
返回
//