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 的性能影响可以忽略不计 我建议我们不要为不存在的问题设计复杂的解决方案。