来自 Makefile 中 foreach 的额外链接

extra links from foreach in Makefile

我正在 mac 使用 GNU Make 来管理我的点文件。在 Makefile 中有一个包含我的 emacs 配置文件和相应目标的目录:

all: _emacs
.PHONY: all list $(MAKECMDGOALS)

....

EMACS_SOURCE_DIR   := $(abspath ./emacs)
EMACS_TARGET_DIR   := $(abspath $(HOME)/.emacs.d)
EMACS_CONFIG_FILES := $(wildcard $(EMACS_SOURCE_DIR)/*.el)

_emacs: | $(EMACS_TARGET_DIR)

    @echo $(call message,"Setting up config files for emacs")
    $(foreach file, \
     $(EMACS_CONFIG_FILES), \
     ln -sf $(file) $(addsuffix /, $(EMACS_TARGET_DIR)))

$(EMACS_TARGET_DIR):
    @echo "Creating directory $(EMACS_TARGET_DIR)"
    @mkdir -p $@

当我 运行 make _emacs 它创建了一些额外的链接:

     .emacs.d -> /Users/xxx/.emacs.d/
          -sf -> -sf
early-init.el -> /Users/xxx/Projects/bootstrap/emacs/early-init.el
  init-org.el -> /Users/xxx/Projects/bootstrap/emacs/init-org.el
 init-pkgs.el -> /Users/xxx/Projects/bootstrap/emacs/init-pkgs.el
      init.el -> /Users/xxx/Projects/bootstrap/emacs/init.el
           ln -> ln

我正在努力了解到底发生了什么以及如何避免创建前两个和最后一个软链接。

一般来说,尝试在配方中使用 make 函数构建复杂的 shell 命令是个坏主意。您应该简单地使用 shell 结构:例如使用 shell for 循环,而不是 make foreach 循环。

让我们看看你的食谱是做什么的:

_emacs: | $(EMACS_TARGET_DIR)
        $(foreach file, \
         $(EMACS_CONFIG_FILES), \
         ln -sf $(file) $(addsuffix /, $(EMACS_TARGET_DIR)))

使函数操纵 文本。他们对命令、shell 语法等一无所知。在 make 调用 shell 之前,它首先扩展脚本。这会导致什么结果?这个:

ln -sf .../emacs/early-init.el /Users/xxx/.emacs.d/ ln -sf .../emacs/init-org.el /Users/xxx/.emacs.d/ ln -sf ...

也许你现在可以看出问题所在了。

如果你想使用 make 的 foreach 来做到这一点,你必须添加一个 shell 分隔符,这样 shell 就知道一个命令在哪里结束,下一个命令从哪里开始;类似于:

        $(foreach file, \
         $(EMACS_CONFIG_FILES), \
         ln -sf $(file) $(addsuffix /, $(EMACS_TARGET_DIR)) ; )

(注意末尾的 ;)。现在,当 make 展开时,它将看起来像这样:

ln -sf .../emacs/early-init.el /Users/xxx/.emacs.d/ ; ln -sf .../emacs/init-org.el /Users/xxx/.emacs.d/ ; ln -sf ...

如果我是你,我会使用 shell 循环。只是更容易理解:

        for file in $(EMACS_CONFIG_FILES); do \
           ln -sf $$file $(addsuffix /, $(EMACS_TARGET_DIR)) ; \
        done

但是,对于您想做的事情,您根本不需要循环;您可以 link 使用一个命令将多个文件放入同一目录:

        ln -sf $(EMACS_CONFIG_FILES) $(EMACS_TARGET_DIR)

(我不确定 addsuffix 有什么用,或者为什么你不能把它写成 $(EMACS_TARGET_DIR)/ 而没有 addsuffix