Makefile 替换文件的符号链接列表
Makefile replace symlink list of files
使用 GNU Make,我想获取一个文件列表并在另一个目录(具有相同的文件名)中创建它们的符号 link,但也覆盖任何现有的 link相同的文件名。
existing_files = $(wildcard dir1/dir2/*.txt)
# The next line shows where I would want to put the symbolic links
symlinks = $(wildcard new_dir1/new_dir2/*.txt)
make_some_links:
# Remove previous symlinks if they share name as existing_files
ifeq ($(notdir $(existing_files)), $(notdir $(symlinks)))
$(info Removing previous symlinks)
@rm $(symlinks)
endif
# Loop to link here
顺便说一句,上面的条件是尝试匹配每个列表的每个元素,还是如果只有一个元素匹配就成功了?
我不太明白如何在 Make 中循环,包括使用 $(foreach)
,所以我编写了以下循环来展示我在类似 Python 中使用相同变量的意思名字。
for i in len($(existing_files)):
@ln -s $(existing_files)[i] $(symlinks)[i]
在这里,$(list_files)
中的第一个元素被 linked 到 $(symlinks)
中的第一个元素。任何关于如何在 Make 中编写这个循环的见解,或者如果有更好的直接方法来处理这个问题,都会非常有帮助。谢谢。
让我们分阶段进行。
首先,创建一个符号 link,并删除该名称先前存在的 link,如果有:
ln -fs filename linkname
现在列出现有文件:
existing_files = $(wildcard dir1/dir2/*.txt)
到目前为止,还不错。假设这给了我们 dir1/dir2/red.txt dir1/dir2/green.txt
.
# The next line shows where I would want to put the symbolic links
symlinks = $(wildcard new_dir1/new_dir2/*.txt)
这将为您提供该目录中已经存在的内容列表,这可能不是您想要的。我们必须从我们拥有的文件列表中构建我们想要的 link 列表:
filenames := $(notdir $(existing_files))
symlinks := $(addprefix new_dir1/new_dir2/, $(filenames))
现在为构建 symlink 的一个或多个规则。我们可以编写两个明确的规则:
new_dir1/new_dir2/red.txt: dir1/dir2/red.txt
ln -fs dir1/dir2/red.txt new_dir1/new_dir2
new_dir1/new_dir2/green.txt: dir1/dir2/green.txt
ln -fs dir1/dir2/green.txt new_dir1/new_dir2
但这太多余了,而且我们事先不知道文件名。首先,我们可以通过定义一个变量并使用 the automatic variale $<
:
来删除一些冗余
DEST_DIR := new_dir1/new_dir2
$(DEST_DIR)/red.txt: dir1/dir2/red.txt
ln -fs $< $(DEST_DIR)
$(DEST_DIR)/green.txt: dir1/dir2/green.txt
ln -fs $< $(DEST_DIR)
现在我们可以看到如何用 pattern rule:
替换这些规则
$(DEST_DIR)/%.txt: dir1/dir2/%.txt
ln -fs $< $(DEST_DIR)
现在我们只需要一个需要 links:
的主规则
.PHONY: make_some_links
make_some_links: $(symlinks)
在大多数情况下,当您开始在 make 配方中设计 shell 循环时,您会错过 make 的重要功能。 Make 会自然地“loop”遍历您声明的目标以达到您要求它达到的目标。 make 提供了许多分解相似规则的方法。
但是在你的目标是象征性的 links 的情况下,有几个问题需要考虑:
- 如果您将 links 声明为常规目标,并且如果其中一些已经存在但指向您想要的文件以外的其他文件,make 可以将它们视为最新的并跳过它们,而不是替换它们。因此,您必须找到一种方法来强制 make 重新创建所有 link,或者至少,那些没有指向正确文件的。最后的修改时间还不够。
- 即使使用
-f
选项 ln
也不会替换现有目录(或 link 到目录),而是会在目录。因此,您必须首先删除与目标 link. 冲突的任何目录(或 link 到目录)
ln
命令应与 -sr
选项一起使用,以创建相对于 link 位置的符号 link。
- 在您尝试创建第一个 link.
之前,目标目录 (new_dir1/new_dir2
) 应该存在
如果您不关心重新创建已经正确的 link,事情很简单:
- 声明一个虚假的虚拟目标 (
force
) 并将其作为所有 link 的先决条件,以强制 make 重新创建它们,
- 在创建您的 link 之前删除现有的 link 、与它们冲突的文件或目录,
- 使用正确的
ln
选项,
- 将目标目录声明为 link 的 order-only prerequisite 并添加规则以在缺少时创建它。
以下内容应该可以完成这项工作:
source_dir := dir1/dir2
link_dir := new_dir1/new_dir2
existing_files := $(wildcard $(source_dir)/*.txt)
symlinks := $(patsubst $(source_dir)/%,$(link_dir)/%,$(existing_files))
.PHONY: all force
all: $(symlinks)
$(link_dir)/%: $(source_dir)/% force | $(link_dir)
rm -rf $@
ln -sr $< $@
force:;
$(link_dir):
mkdir -p $@
如果你想避免重新创建现有的正确links,事情就更难了,因为make需要知道哪些是正确的,哪些不是,而这不能基于上次修改次...但我怀疑重新创建现有正确 links 的性能影响可以忽略不计 我建议我们不要为不存在的问题设计复杂的解决方案。
使用 GNU Make,我想获取一个文件列表并在另一个目录(具有相同的文件名)中创建它们的符号 link,但也覆盖任何现有的 link相同的文件名。
existing_files = $(wildcard dir1/dir2/*.txt)
# The next line shows where I would want to put the symbolic links
symlinks = $(wildcard new_dir1/new_dir2/*.txt)
make_some_links:
# Remove previous symlinks if they share name as existing_files
ifeq ($(notdir $(existing_files)), $(notdir $(symlinks)))
$(info Removing previous symlinks)
@rm $(symlinks)
endif
# Loop to link here
顺便说一句,上面的条件是尝试匹配每个列表的每个元素,还是如果只有一个元素匹配就成功了?
我不太明白如何在 Make 中循环,包括使用 $(foreach)
,所以我编写了以下循环来展示我在类似 Python 中使用相同变量的意思名字。
for i in len($(existing_files)):
@ln -s $(existing_files)[i] $(symlinks)[i]
在这里,$(list_files)
中的第一个元素被 linked 到 $(symlinks)
中的第一个元素。任何关于如何在 Make 中编写这个循环的见解,或者如果有更好的直接方法来处理这个问题,都会非常有帮助。谢谢。
让我们分阶段进行。
首先,创建一个符号 link,并删除该名称先前存在的 link,如果有:
ln -fs filename linkname
现在列出现有文件:
existing_files = $(wildcard dir1/dir2/*.txt)
到目前为止,还不错。假设这给了我们 dir1/dir2/red.txt dir1/dir2/green.txt
.
# The next line shows where I would want to put the symbolic links
symlinks = $(wildcard new_dir1/new_dir2/*.txt)
这将为您提供该目录中已经存在的内容列表,这可能不是您想要的。我们必须从我们拥有的文件列表中构建我们想要的 link 列表:
filenames := $(notdir $(existing_files))
symlinks := $(addprefix new_dir1/new_dir2/, $(filenames))
现在为构建 symlink 的一个或多个规则。我们可以编写两个明确的规则:
new_dir1/new_dir2/red.txt: dir1/dir2/red.txt
ln -fs dir1/dir2/red.txt new_dir1/new_dir2
new_dir1/new_dir2/green.txt: dir1/dir2/green.txt
ln -fs dir1/dir2/green.txt new_dir1/new_dir2
但这太多余了,而且我们事先不知道文件名。首先,我们可以通过定义一个变量并使用 the automatic variale $<
:
DEST_DIR := new_dir1/new_dir2
$(DEST_DIR)/red.txt: dir1/dir2/red.txt
ln -fs $< $(DEST_DIR)
$(DEST_DIR)/green.txt: dir1/dir2/green.txt
ln -fs $< $(DEST_DIR)
现在我们可以看到如何用 pattern rule:
替换这些规则$(DEST_DIR)/%.txt: dir1/dir2/%.txt
ln -fs $< $(DEST_DIR)
现在我们只需要一个需要 links:
的主规则.PHONY: make_some_links
make_some_links: $(symlinks)
在大多数情况下,当您开始在 make 配方中设计 shell 循环时,您会错过 make 的重要功能。 Make 会自然地“loop”遍历您声明的目标以达到您要求它达到的目标。 make 提供了许多分解相似规则的方法。
但是在你的目标是象征性的 links 的情况下,有几个问题需要考虑:
- 如果您将 links 声明为常规目标,并且如果其中一些已经存在但指向您想要的文件以外的其他文件,make 可以将它们视为最新的并跳过它们,而不是替换它们。因此,您必须找到一种方法来强制 make 重新创建所有 link,或者至少,那些没有指向正确文件的。最后的修改时间还不够。
- 即使使用
-f
选项ln
也不会替换现有目录(或 link 到目录),而是会在目录。因此,您必须首先删除与目标 link. 冲突的任何目录(或 link 到目录)
ln
命令应与-sr
选项一起使用,以创建相对于 link 位置的符号 link。- 在您尝试创建第一个 link. 之前,目标目录 (
new_dir1/new_dir2
) 应该存在
如果您不关心重新创建已经正确的 link,事情很简单:
- 声明一个虚假的虚拟目标 (
force
) 并将其作为所有 link 的先决条件,以强制 make 重新创建它们, - 在创建您的 link 之前删除现有的 link 、与它们冲突的文件或目录,
- 使用正确的
ln
选项, - 将目标目录声明为 link 的 order-only prerequisite 并添加规则以在缺少时创建它。
以下内容应该可以完成这项工作:
source_dir := dir1/dir2
link_dir := new_dir1/new_dir2
existing_files := $(wildcard $(source_dir)/*.txt)
symlinks := $(patsubst $(source_dir)/%,$(link_dir)/%,$(existing_files))
.PHONY: all force
all: $(symlinks)
$(link_dir)/%: $(source_dir)/% force | $(link_dir)
rm -rf $@
ln -sr $< $@
force:;
$(link_dir):
mkdir -p $@
如果你想避免重新创建现有的正确links,事情就更难了,因为make需要知道哪些是正确的,哪些不是,而这不能基于上次修改次...但我怀疑重新创建现有正确 links 的性能影响可以忽略不计 我建议我们不要为不存在的问题设计复杂的解决方案。