makefile 内循环的问题

发布于 2024-10-17 09:18:05 字数 1525 浏览 5 评论 0原文

我正在尝试实现在 makefile 中显示构建进度的逻辑。

我可以成功地在级联的 makefile 中为目标“simple”打印它。然而,当涉及到 makefile 中的另一个目标“for”时,出现了问题,我无法弄清楚它是什么。

任何帮助将不胜感激。

## BUILD is initially undefined
ifndef BUILD
# T estimates how many targets we are building by replacing BUILD with a special string
T := $(shell $(MAKE) progress --no-print-directory \
      -nrRf $(firstword $(MAKEFILE_LIST)) \
      BUILD="COUNTTHIS" | grep -c "COUNTTHIS")
## N is the target number
N := x
## incrementing counter
C = $(words $N)$(eval N := x $N)$(shell export $N)
## BUILD is now defined to show the progress, this also avoids redefining T in loop
BUILD = echo "`expr "   [\`expr $C '*' 100 / $T\`" : '.*\(....\)$$'`%]"
endif

MODULE_LIST  = module1
MODULE_LIST := $(MODULE_LIST) module2
MODULE_LIST := $(MODULE_LIST) module3
MODULE_LIST := $(MODULE_LIST) module4
MODULE_LIST := $(MODULE_LIST) module5

progress:
    @$(BUILD)
    @$(BUILD)
    @$(BUILD)
    @$(BUILD)
    @$(BUILD)

simple:
    # T=5 and C increases for every access
    @$(BUILD) "Cleaning Module \"module1\""
    @sleep 0.1
    @$(BUILD) "Cleaning Module \"module2\""
    @sleep 0.1
    @$(BUILD) "Cleaning Module \"module3\""
    @sleep 0.1
    @$(BUILD) "Cleaning Module \"module4\""
    @sleep 0.1
    @$(BUILD) "Cleaning Module \"module5\""
    @sleep 0.1

for:
    # T=1 and C increases for every access but not inside the for loop
    @for MODULE in $(MODULE_LIST); do \
        $(BUILD) "Cleaning Module \"$$MODULE\"" ; \
        sleep 0.1 ; \
    done

I am trying to implement the logic to display the progress of the build in a makefile.

I can successfully print it for the target "simple" in the makefile cascaded herewith. However when it comes to another target "for" in the makefile, something goes wrong and I am not able to figure out what it is.

Any help would be really appreciated.

## BUILD is initially undefined
ifndef BUILD
# T estimates how many targets we are building by replacing BUILD with a special string
T := $(shell $(MAKE) progress --no-print-directory \
      -nrRf $(firstword $(MAKEFILE_LIST)) \
      BUILD="COUNTTHIS" | grep -c "COUNTTHIS")
## N is the target number
N := x
## incrementing counter
C = $(words $N)$(eval N := x $N)$(shell export $N)
## BUILD is now defined to show the progress, this also avoids redefining T in loop
BUILD = echo "`expr "   [\`expr $C '*' 100 / $T\`" : '.*\(....\)$'`%]"
endif

MODULE_LIST  = module1
MODULE_LIST := $(MODULE_LIST) module2
MODULE_LIST := $(MODULE_LIST) module3
MODULE_LIST := $(MODULE_LIST) module4
MODULE_LIST := $(MODULE_LIST) module5

progress:
    @$(BUILD)
    @$(BUILD)
    @$(BUILD)
    @$(BUILD)
    @$(BUILD)

simple:
    # T=5 and C increases for every access
    @$(BUILD) "Cleaning Module \"module1\""
    @sleep 0.1
    @$(BUILD) "Cleaning Module \"module2\""
    @sleep 0.1
    @$(BUILD) "Cleaning Module \"module3\""
    @sleep 0.1
    @$(BUILD) "Cleaning Module \"module4\""
    @sleep 0.1
    @$(BUILD) "Cleaning Module \"module5\""
    @sleep 0.1

for:
    # T=1 and C increases for every access but not inside the for loop
    @for MODULE in $(MODULE_LIST); do \
        $(BUILD) "Cleaning Module \"$MODULE\"" ; \
        sleep 0.1 ; \
    done

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

不气馁 2024-10-24 09:18:06

正如您在评论中指出的那样,问题在于 for 循环在 shell 内部执行,因此不会更新 Makefile 的变量(或者至少在 Make 通过评估其中的变量引用来构建命令字符串时不会更新一次) 。

我能看到的唯一可行的解​​决方案是将循环结构移至 Makefile 中。尝试一下:

## PRINT_PROGRESS is initially undefined
ifndef PRINT_PROGRESS
# T estimates how many targets we are building by replacing PRINT_PROGRESS with a special string
T := $(shell $(MAKE) $(MAKECMDGOALS) --no-print-directory \
      -rRf $(firstword $(MAKEFILE_LIST)) \
      PRINT_PROGRESS="echo COUNTTHIS" BUILD="test x ||" | grep -c "COUNTTHIS")
N := 1
## PRINT_PROGRESS is now defined to show the progress and update N
PRINT_PROGRESS = echo "`expr "   [\`expr $N '*' 100 / $T\`" : '.*\(....\)$'`%]"$(eval N := $(shell expr $N + 1))
endif
ifndef BUILD
BUILD := #blank
endif

MODULE_LIST  = module1
MODULE_LIST += module2
MODULE_LIST += module3
MODULE_LIST += module4
MODULE_LIST += module5

simple:
    # T=5 and C increases for every access
    @$(PRINT_PROGRESS) "Cleaning Module \"module1\""
    @$(BUILD) { sleep 0.1 ; echo "doing some work" ; }
    @$(PRINT_PROGRESS) "Cleaning Module \"module2\""
    @$(BUILD) { sleep 0.1 ; echo "doing some work" ; }
    @$(PRINT_PROGRESS) "Cleaning Module \"module3\""
    @$(BUILD) { sleep 0.1 ; echo "doing some work" ; }
    @$(PRINT_PROGRESS) "Cleaning Module \"module4\""
    @$(BUILD) { sleep 0.1 ; echo "doing some work" ; }
    @$(PRINT_PROGRESS) "Cleaning Module \"module5\""
    @$(BUILD) { sleep 0.1 ; echo "doing some work" ; }

for:
    @$(foreach MODULE,$(MODULE_LIST),\
        $(PRINT_PROGRESS) "Cleaning Module \"$(MODULE)\"" ; \
        $(BUILD) { \
            sleep 0.1 ; \
            echo "doing some work" ; \
        } ; \
    )

这还包括一些其他更改。

乍一看似乎合理的解决方案是从 makefile 中导出 N 并设计 PRINT_PROGRESS 命令,以便 N 始终作为环境进行更新多变的。不过,我找不到一种方法来完成这项工作,因为它需要某种方式将更新 N值返回放入之后命令已被写入,因此单独的命令仍然有效。


编辑:运行上面的确切脚本时的输出(带有更正的制表符缩进)以及使用的版本:

jpab@oberon : /memtmp
$ make --version
GNU Make 3.81
Copyright (C) 2006  Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.

This program built for x86_64-pc-linux-gnu

jpab@oberon : /memtmp
$ bash --version
GNU bash, version 4.1.5(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

jpab@oberon : /memtmp
$ make
# T=5 and C increases for every access
 [20%] Cleaning Module "module1"
doing some work
 [40%] Cleaning Module "module2"
doing some work
 [60%] Cleaning Module "module3"
doing some work
 [80%] Cleaning Module "module4"
doing some work
[100%] Cleaning Module "module5"
doing some work

jpab@oberon : /memtmp
$ make for
 [20%] Cleaning Module "module1"
doing some work
 [40%] Cleaning Module "module2"
doing some work
 [60%] Cleaning Module "module3"
doing some work
 [80%] Cleaning Module "module4"
doing some work
[100%] Cleaning Module "module5"
doing some work

As you noted in your comment, the problem is that the for loop executes inside the shell, and so does not update the Makefile's variables (or at least not more than once when Make builds the command string up by evaluating the variable references inside it).

The only feasible solution I can see is to move your loop construct into the Makefile. Try this:

## PRINT_PROGRESS is initially undefined
ifndef PRINT_PROGRESS
# T estimates how many targets we are building by replacing PRINT_PROGRESS with a special string
T := $(shell $(MAKE) $(MAKECMDGOALS) --no-print-directory \
      -rRf $(firstword $(MAKEFILE_LIST)) \
      PRINT_PROGRESS="echo COUNTTHIS" BUILD="test x ||" | grep -c "COUNTTHIS")
N := 1
## PRINT_PROGRESS is now defined to show the progress and update N
PRINT_PROGRESS = echo "`expr "   [\`expr $N '*' 100 / $T\`" : '.*\(....\)$'`%]"$(eval N := $(shell expr $N + 1))
endif
ifndef BUILD
BUILD := #blank
endif

MODULE_LIST  = module1
MODULE_LIST += module2
MODULE_LIST += module3
MODULE_LIST += module4
MODULE_LIST += module5

simple:
    # T=5 and C increases for every access
    @$(PRINT_PROGRESS) "Cleaning Module \"module1\""
    @$(BUILD) { sleep 0.1 ; echo "doing some work" ; }
    @$(PRINT_PROGRESS) "Cleaning Module \"module2\""
    @$(BUILD) { sleep 0.1 ; echo "doing some work" ; }
    @$(PRINT_PROGRESS) "Cleaning Module \"module3\""
    @$(BUILD) { sleep 0.1 ; echo "doing some work" ; }
    @$(PRINT_PROGRESS) "Cleaning Module \"module4\""
    @$(BUILD) { sleep 0.1 ; echo "doing some work" ; }
    @$(PRINT_PROGRESS) "Cleaning Module \"module5\""
    @$(BUILD) { sleep 0.1 ; echo "doing some work" ; }

for:
    @$(foreach MODULE,$(MODULE_LIST),\
        $(PRINT_PROGRESS) "Cleaning Module \"$(MODULE)\"" ; \
        $(BUILD) { \
            sleep 0.1 ; \
            echo "doing some work" ; \
        } ; \
    )

This includes a couple of other changes too.

A solution that seems plausible at first glance is to export N from the makefile and design the PRINT_PROGRESS command so that N is always updated as an environment variable. I couldn't find a way to make this work though, since it requires some way of getting the updated N value back into Make after the command has been written, so that separate commands still work.


Edit: Output when running the exact script above (with corrected tab indents), plus versions used:

jpab@oberon : /memtmp
$ make --version
GNU Make 3.81
Copyright (C) 2006  Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.

This program built for x86_64-pc-linux-gnu

jpab@oberon : /memtmp
$ bash --version
GNU bash, version 4.1.5(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

jpab@oberon : /memtmp
$ make
# T=5 and C increases for every access
 [20%] Cleaning Module "module1"
doing some work
 [40%] Cleaning Module "module2"
doing some work
 [60%] Cleaning Module "module3"
doing some work
 [80%] Cleaning Module "module4"
doing some work
[100%] Cleaning Module "module5"
doing some work

jpab@oberon : /memtmp
$ make for
 [20%] Cleaning Module "module1"
doing some work
 [40%] Cleaning Module "module2"
doing some work
 [60%] Cleaning Module "module3"
doing some work
 [80%] Cleaning Module "module4"
doing some work
[100%] Cleaning Module "module5"
doing some work
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文