作为测试套件的 make install 规则的一部分,我想将一个目录(src 目录)中的所有二进制可执行文件移动到 bin 目录。我认为一个简单的方法是简单地遍历 src
中的每个文件。目录,然后使用 patsubst
替换 src
与 bin
在每个路径中。不幸的是,我无法让它工作,因为我无法获得 make
在每次循环迭代中评估当前 FILE 的名称。我只能访问 bash shell 变量 $$FILE
,但是当我将它与 make
一起使用时patsubst
函数,它实际上并不评估 shell 变量$$FILE
... 相反,patsubst
函数似乎只看到字符串 "$FILE"
.
所以,这就是我正在尝试的:
install :
-- irrelevant stuff snipped --
for FILE in $(BINARY_TARGETS); do \
if [ -f $$FILE ]; then mv -f $$FILE $(patsubst %/src/,%/bin/,$$FILE); fi \
done
这会导致每个文件出错:
mv: ‘./src/foo/bar’ and ‘./src/foo/bar’ are the same file
这个错误让我明白 patsubst
在 make
中发挥作用实际上并没有评估 shell 变量,而只是看到 $FILE
, 所以结果是它没有找到替换模式,最终命令传递给 mv
具有相同字符串的源路径和目标路径。
那么,有没有办法得到patsubst
评估shell变量的值?还是总体上有更好的方法来实现我在这里想要实现的目标?
最佳答案
make
处理优先于将命令传递给 shell。并且,一旦通过,它们将由 shell 执行。因此,首先 make
,处理命令并输入:
$(patsubst %/src/,%/bin/,$$FILE)
$$FILE
被替换为 $FILE
然后按字面意思处理。因此,没有模式匹配,实际上 patsubst
返回 $FILE
。请看下面的例子:
bar:
echo $(patsubst %/src/,%/bin/,$$whatever)
它给出:
arturcz@szczaw:/tmp/m$ make bar
echo $whatever
arturcz@szczaw:/tmp/m$
作为你的 makefile 规则的结果,bash 给出了以下命令来执行:
for FILE in src/a src/b src/c; do \
if [ -f $FILE ]; then mv -f $FILE $FILE; fi \
done
这就是您得到结果的原因。
解决方案
您可以依靠 bash
进行适当的替换,但您需要将其强制执行为 shell(默认情况下它是 sh
,它缺少一些必需的功能) :
SHELL=bash
install:
for FILE in $(BINARY_TARGETS); do \
if [ -f $$FILE ]; then echo $$FILE $${FILE/\/src\//\/bin\/}; fi \
done
您还可以要求make
进行循环和替换。您可以通过几种方法实现这一目标。这个正在执行所有替换并即时准备命令。
install:
$(foreach d,$(BINARY_TARGETS),if [ -f $(d) ]; then mv -f $(d) $(d:./src/%=./bin/%);fi;)
您也可以使用`$(wildcard) 函数停止检查要制作
的文件是否存在:
install:
$(foreach d,$(wildcard $(BINARY_TARGETS)),mv -f $(d) $(d:./src/%=./bin/%);)
最后,我个人更喜欢的解决方案 - 使用适当的依赖项和规则以 make
方式进行。
install: $(BINARY_TARGETS:./src/%=./bin/%)
bin/%: src/%
mv -f $< $@
如果 BINARY_TARGET
中存在任何文件是可选的,您可能需要再次使用 $(wildcard)
技巧:
install: $(patsubst ./src/%,./bin/%,$(wildcard $(BINARY_TARGETS)))
bin/%: src/%
mv -f $< $@
关于linux - Makefile patsubst 使用 shell 变量的值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44597823/