makefile中的循环问题

时间:2011-02-12 17:20:58

标签: linux makefile gnu-make

我正在尝试实现逻辑以在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

1 个答案:

答案 0 :(得分:4)

正如您在评论中指出的那样,问题是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中export 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