有没有办法让 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.
我怀疑这是可能的:请参阅关于 Makefiles 中冒号的讨论。总而言之,GNU make 从来没有很好地处理包含空格或冒号的文件名。维护者 Paul D. Smith 表示,添加对转义的支持往往会 break existing makefiles。此外,添加此类支持需要对代码进行重大更改。
您可以使用某种讨厌的临时文件安排来解决问题。
祝你好运!
这里的答案似乎都太复杂了,没有帮助。我终于找到了解决方案here:
colon := :
$(colon) := :
然后将文件名中的宏用作:
filename$(:)
经评估成功翻译为“文件名:”。
以下 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) 将冒号放回原处。
今天我在处理定义文件名(包含冒号)的 Makefile 变量时发现了另一种方法。
# definition
SOME_FNAME = $(NAME)__colon__$(VERSION)
# usage in target
foo:
$(do_something) $(subst __colon__,:,$(SOME_FNAME))
我不确定这应该有效,但它说“缺少目标文件”的原因很简单:
%.bar: ; cp $< $@
该行表示从 first 依赖项复制目标。你的 a:b.bar 没有 any 依赖,所以 cp 失败了。你想让它复制什么? a:b.foo ?在这种情况下,您需要:
%.bar: %.foo ; cp $< $@
像你一样用
\:
转义冒号应该有效。唯一的问题是你不能用这样的转义分号二次扩展(阅读.SECONDEXPANSION:
)变量,否则分号将保留在路径名中。
这里有一些使用简单 Makefile 的测试:
$ tree
.
├── Makefile
├── dst
│ ├── a
│ ├── b
│ └── c
└── src
└── :
6 directories, 2 files
$ cat Makefile
SRC := $(shell find src -type f | sed 's,:,\\:,')
DST_a := $(patsubst src/%,dst/a/%,$(SRC))
DST_b := $(patsubst src/%,dst/b/%,$(SRC))
DST_c := $(patsubst src/%,dst/c/%,$(SRC))
.PHONY: a
a: $(DST_a);
$(DST_a): $(SRC)
touch $@
.SECONDEXPANSION:
.PHONY: b
b: $(DST_b);
$(DST_b): $(SRC)
touch $@
.PHONY: c
c: $(DST_c);
$(DST_c): $$(SRC)
touch $@
$ make a
touch dst/a/:
$ make b
touch dst/b/:
$ make c
make: *** No rule to make target 'src/\:', needed by 'dst/c/:'. Stop.
$ tree
.
├── Makefile
├── dst
│ ├── a
│ │ └── :
│ ├── b
│ │ └── :
│ └── c
└── src
└── :
6 directories, 4 files
与这些交互的另一个功能是
$(wildcard ...)
。它取消了冒号,所以你可能需要在它之后再次转义。
这对我有用:
colon = :
C$(colon)/temp/foo: C$(colon)/temp/bar
cp $^ $@
我无法让@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
对我来说足够接近了。