24

有没有办法让 GNU make 正确处理包含冒号的文件名?

我遇到的具体问题恰好涉及模式规则。这是一个不依赖于剪切和粘贴制表符的简化版本:

% 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-redhat-linux-gnu
% cat Makefile
COLON := \:
all: ; true
%.bar: ; cp $< $@
x.bar: x.foo
%.foo: ; touch $@
a$(COLON)b.bar: a$(COLON)b.foo
all: x.bar a$(COLON)b.bar
clean: ; rm -f *.foo *.bar
% make clean
rm -f *.foo *.bar
% make
touch x.foo
cp x.foo x.bar
cp  a\:b.bar
cp: missing destination file operand after `a:b.bar'
Try `cp --help' for more information.
make: *** [a\:b.bar] Error 1

将 $(COLON) 替换为文字 : 会产生完全相同的结果。如果没有反斜杠,它会这样做:

Makefile:6: *** target pattern contains no `%'.  Stop.
4

6 回答 6

14

我怀疑这是否可能:请参阅有关 Makefiles 中的冒号的讨论。总之,GNU make 从来没有很好地处理包含空格或冒号的文件名。维护者 Paul D. Smith 说,增加对转义的支持会破坏现有的 makefile。此外,添加此类支持需要对代码进行重大更改。

您也许可以解决某种令人讨厌的临时文件安排。

祝你好运!

于 2010-01-12T21:35:20.787 回答
7

这里的答案似乎都太复杂而无济于事。我终于在这里找到了解决方案:

colon := :
$(colon) := :

然后将文件名中的宏用作:

filename$(:)

在评估后成功转换为“文件名:”。

于 2020-04-03T16:37:08.793 回答
3

以下 hack 对我有用,但不幸的是它依赖于 $(shell)。

# modify file names immediately
PRE := $(shell rename : @COLON@ *)
# example variables that I need
XDLS = $(wildcard *.xdl)
YYYS = $(patsubst %.xdl,%.yyy,$(XDLS))
# restore file names later
POST = $(shell rename @COLON@ : *)

wrapper: $(YYYS)
    @# restore file names
    $(POST)

$(YYYS):
    @# show file names after $(PRE) renaming but before $(POST) renaming
    @ls

因为 PRE 是用 := 分配的,所以在计算 XDLS 变量之前运行其关联的 shell 命令。关键是在事后通过显式调用 $(POST) 将冒号放回原处。

于 2014-06-29T00:25:13.123 回答
2

我今天在处理定义文件名(包含冒号)的 Makefile 变量时发现了另一种方法。

# definition
SOME_FNAME = $(NAME)__colon__$(VERSION)

# usage in target
foo:
    $(do_something) $(subst __colon__,:,$(SOME_FNAME))
于 2015-09-18T16:11:26.957 回答
0

我不认为这应该有效,但它说“缺少目标文件”的原因很简单:

%.bar: ; cp $< $@

该行表示从第一个依赖项中复制目标。您的 a:b.bar 没有任何依赖关系,因此 cp 失败。你想要它复制什么?a:b.foo ? 在这种情况下,您需要:

%.bar: %.foo ; cp $< $@
于 2010-01-12T21:27:44.487 回答
0

我无法让@navjotk 发布的答案工作,所以我只会作弊并这样做;

FILENAME:=foo:bar
foo_bar:
    touch $(FILENAME)

run:
    if [ ! -e "$(FILENAME)" ]; then $(MAKE) foo_bar; fi

输出:

$ make run
if [ ! -e "foo:bar" ]; then /Library/Developer/CommandLineTools/usr/bin/make foo_bar; fi
touch foo:bar

$ ls
Makefile foo:bar

对我来说足够近了。

于 2020-06-18T16:10:24.710 回答