GNU Make - 在规则/目标中从shell命令输出设置MAKEFILE变量

时间:2014-06-19 23:09:54

标签: c++ c makefile gnu-make

我试图将一些复杂的makefile规则放在一起,以针对多个编译器自动构建项目。我有一个规则创建一些动态生成的变量并为它们分配变量,然后将这些变量传递给调用以构建单独的规则。

.PHONY: all
all:
    @echo "Detected CPULIST:${CPULIST_DETECTED}"
    @echo "CPULIST:${CPULIST}"
    @for cpu in $(CPULIST_DETECTED); do                   \
        echo "CPU:$${cpu}:";                                \
        eval VARIANTLIST_DETECTED=$(shell 2>&1 find ./.build/linux/$$cpu -mindepth 1 -maxdepth 1 | grep -v '\.svn' |  grep -v warning ); \
        eval echo "Detected Variant List:$${VARIANTLIST_DETECTED}"; \
        eval variant_$${cpu}=$${cpu};                      \
        eval echo "variant_\$${cpu}:\$${variant_$${cpu}}"; \
        $(MAKE) build CPULIST=$${cpu};                     \
    done

.PHONY: build
build: sanity_check $(TARGET)
    @true

我有两个问题。第一个是,尽管通过cpu双重转义$$cpu,但它在行中转换为null:

eval VARIANTLIST_DETECTED=$(shell 2>&1 find ./.build/linux/$$cpu -mindepth 1 -maxdepth 1 | grep -v '\.svn' |  grep -v warning ); \

因此,find命令会针对循环的每次迭代搜索./.build/linux/,而不是像我期望的那样循环遍历./.build/linux/arm./.build/linux/x86。我已经包含了一个帖子,在下面的参考文献中对此进行了描述。我怀疑这可能会导致问题,因为我在规则本身而不是在makefile的全局赋值部分(规则之前)中尝试此操作。

另一个问题发生在同一条线上。似乎shell命令被评估,分配给VARIANTLIST_DETECTED,但然后执行VARIANTLIST_DETECTED就好像它是一个命令,因为我在构建期间遇到以下错误:

Detected CPULIST:arm x86
CPULIST:
CPU:arm:
/bin/sh: 3: ./.build/linux/x86: Permission denied
Detected Variant List:
variant_arm:arm

它试图在我的查询中运行第一个结果,就像它是一个命令一样。该行也应该是./.build/linux/x86/so

如何解决这两个问题?它们是阻止我的makefile完成的最后两个荆棘。

谢谢!

参考

  1. 将一个makefile变量值分配给bash命令结果?,已访问2014-06-19,<https://stackoverflow.com/questions/2373081/assign-a-makefile-variable-value-to-a-bash-command-result>

2 个答案:

答案 0 :(得分:5)

你需要退一步思考这里发生了什么:你似乎只是把东西扔进你的makefile并希望结果是你想要的,而不是真正了解正在发生的事情并继续目的解决你的问题。

首先要了解的是make如何调用配方:首先,让扩展配方。扩展意味着查看配方文本并查找以$开头的任何内容,并将其视为make函数或变量,并对其进行求值。因此,$$转换为单个$$(CPU_LIST_DETECTED)扩展为该变量的值,$(shell ....)是一个运行的make函数,其结果包含在内在食谱文本中。也许你已经可以看到你的食谱出错了......

然后,第二个,AFTER 所有的配方已经扩展,make将获取配方的扩展文本并将其传递给shell。 Make然后等待shell完成,并查看shell的退出代码。如果它是0,则配方成功并继续。如果它不是0,则配方失败并退出并显示错误。 make和shell之间没有交互,除了make以脚本运行启动它,然后返回退出代码。如果你以前不能,也许你现在可以看到你的食谱出错了。

在配方中使用$(shell ...)函数几乎总是错误(或至少是不必要的)...配方已经在shell中运行了!你不需要make先运行另一个shell,然后将这些结果提供给一个新的shell。

您的shell调用正在尝试使用shell变量$cpu。但是当make运行$(shell ...)函数时,这个变量在这里没有任何价值,因为在之前运行的make运行它会启动你的shell脚本。你的make函数中没有东西可以引用在配方运行之前不会定义的东西。这将运行shell脚本2>&1 find ./.build/linux/$cpu -mindepth 1 -maxdepth 1 | grep -v '\.svn' | grep -v warning,但shell变量cpu未设置,因此查找将递归./.build/linux/下的所有内容。

其次,你在这里滥用eval。 shell函数扩展为单词列表,例如 foo bar baz 。然后eval将如下所示:

eval VARIANTLIST_DETECTED=foo bar baz

与写这个相同:

VARIANTLIST_DETECTED=foo bar baz

与将变量bar设置为值baz后,使用参数VARIANTLIST_DETECTED运行命令foo相同。这就是为什么它似乎是“运行你的目录”。

你可能想要这个:

VARIANTLIST_DETECTED="foo bar baz"

(注意引用)。但是,通过eval获取引号是很棘手的“你必须逃脱它们。幸运的是,无论如何你根本不需要eval,因为你并没有尝试分配一个动态命名的变量。你可以直接写下作业。

因此,丢失shell函数和eval,应写入该行:

VARIANTLIST_DETECTED=`2>&1 find ./.build/linux/$$cpu -mindepth 1 -maxdepth 1 | grep -v '\.svn' |  grep -v warning`; \

同样的回声:你不需要那里的eval

但是,我真的没有看到设置此变量或variant_$cpu变量的意义。你永远不会对它们做任何事情。

答案 1 :(得分:1)

通常,尽量避免将Make语法放在命令中。试试这个:

VARIANTLIST_DETECTED=$$(find ./.build/linux/$${cpu} -mindepth 1 -maxdepth 1 | grep -v '.svn' |  grep -v warning );