【问题标题】:Problem with a loop inside makefilemakefile中的循环问题
【发布时间】:2011-02-12 17:20:58
【问题描述】:

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

【问题讨论】:

  • for MODULE... 命令是否可以在您的操作系统的命令行中工作?
  • 我怀疑这是通过改进make 更好地实现的,但让我看看我是否理解。您使用-n 运行make 来计算进度标记的数量,然后重新定义BUILD 以输出分数进度?好的,这很聪明。但看起来您必须手动干预每条规则才能调用BUILD。如果是这样,那不就是让知道如何在不被告知的情况下执行大多数构建的许多步骤的目的吗?
  • @dmckee 是的,计算用于特定构建目标的命令总数是正确的。然而,它只是知道目标必须调用多少命令。对于目标“simple”,它是 5。但是,对于目标“for”,它是 2,这就是为什么我必须创建另一个虚拟目标“progress”,当在 makefile 中使用循环时,它实际上给出了总命令。我的问题是当循环运行时,计数器没有增加,我什至没有丝毫线索知道出了什么问题! :(
  • 当你make for时会发生什么?
  • 这些是输出: :~/Desktop$ make simple # T=5 并且每次访问都会增加 C [20%] 清理模块 "module1" [40%] 清理模块 "module2" [60 %] 清理模块 "module3" [80%] 清理模块 "module4" [100%] 清理模块 "module5" ~/Desktop$ make for # T=1 并且 C 每次访问都会增加,但不在 for 循环内 [20% ] 清洁模块“module1” [20%] 清洁模块“module2” [20%] 清洁模块“module3” [20%] 清洁模块“module4” [20%] 清洁模块“module5”

标签: linux makefile gnu-make


【解决方案1】:

正如您在评论中指出的那样,问题在于 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 更新为环境变量。我找不到一种方法来完成这项工作,因为它需要某种方式将 updated N 值 back 放入 Make after命令已编写,因此单独的命令仍然有效。


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

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

【讨论】:

  • 这听起来像是完成这项工作的完美想法。但是我收到错误消息:makefile:23:***缺少分隔符。停止。 makefile:24: *** 缺少分隔符。停止。
  • SO 代码框不保留制表符和空格之间的区别,这(不幸的是)是 Makefile 语法的重要部分。检查配方缩进是否正确(即它们使用制表符而不是空格)
  • 完美!然而,这些数字并不是渐进的。在我的例子中,它总是显示两个目标的 20%。
  • 糟糕!这是我的错误。我不知何故弄乱了剧本。我很抱歉。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-28
  • 2018-04-13
相关资源
最近更新 更多