дополнительные ссылки из каждого файла в файле Makefile

#makefile #gnu-make

Вопрос:

Я на Mac использую GNU Make для управления своими dot-файлами. В файле 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 -gt; /Users/xxx/.emacs.d/  -sf -gt; -sf early-init.el -gt; /Users/xxx/Projects/bootstrap/emacs/early-init.el  init-org.el -gt; /Users/xxx/Projects/bootstrap/emacs/init-org.el  init-pkgs.el -gt; /Users/xxx/Projects/bootstrap/emacs/init-pkgs.el  init.el -gt; /Users/xxx/Projects/bootstrap/emacs/init.el  ln -gt; ln  

Я изо всех сил пытаюсь понять, что именно происходит и как избежать создания первых двух и последних мягких ссылок.

Ответ №1:

В общем, это плохая идея-пытаться создать сложную команду оболочки, используя функции make внутри рецепта. Вы должны просто использовать конструкции оболочки: например, используйте цикл оболочки for , а не foreach цикл создания.

Давайте посмотрим, что делает ваш рецепт:

 _emacs: | $(EMACS_TARGET_DIR)  $(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 ...  

Может быть, теперь вы видите проблему.

Если вы хотите сделать это с помощью make foreach , вам нужно добавить разделитель оболочки, чтобы оболочка знала, где заканчивается одна команда и начинается следующая; что-то вроде:

 $(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 ...  

На вашем месте я бы вместо этого использовал контур оболочки. Просто это гораздо проще понять:

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

Но для того, что вы хотите сделать, вам вообще не нужен цикл; вы можете связать несколько файлов в один каталог с помощью одной команды:

 ln -sf $(EMACS_CONFIG_FILES) $(EMACS_TARGET_DIR)  

(Я не уверен, для чего addsuffix это было нужно, или почему вы не могли просто написать это как $(EMACS_TARGET_DIR)/ без addsuffix )

Комментарии:

1. Я использовал эту addsuffix штуковину, например, для переименования целевых ссылок zshrc -gt; .zshrc . Вы правы, в данном случае это ничего не значит. Спасибо за вашу помощь!