开发者

Problem with a loop inside makefile

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 开发者_JS百科$(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


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
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜