Получение имени make-файла из make-файла
Как получить имя make-файла в make-файле?
Благодарю.
Замечания:
Мне это нужно, потому что я хотел бы, чтобы мой make-файл вызывался сам, но make-файл не называется Makefile, поэтому я хотел бы написать что-то вроде этого:
target:
($MAKE) -f ($MAKEFILENAME) other_target
7 ответов
location = $(CURDIR)/$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))
WHERE_ART_THOU := $(location)
$(warning $(WHERE_ART_THOU))
Я также верю, что это GNU-специфика, но я не слишком уверен.
(Если у вас есть какие-либо вопросы, обратитесь к удивительно написанному руководству по GNU make. Но помните, что, как и Makefile, это руководство следует полностью прочитать, прежде чем применять концепции на практике).
Я не мог понять, как это сделать легко. Насколько я понимаю, вам придется делать какую-то ручную работу.
Позже я опишу, как это можно сделать, и покажу сценарии, которые вводят current_makefile
переменная. Но я хотел бы подчеркнуть важную концепцию в первую очередь.
Вы должны понимать, что если бы у нас была какая-то переменная current_makefile
, который расширяется до текущего имени файла makefile, тогда его придется изменить в процессе чтения файлов makefile. Это означает, что он должен использоваться в "непосредственном" контексте расширения - то есть в командах, которые выполняются во время чтения make-файла. Однако большинство команд выполняются после чтения make-файлов. Поэтому некоторые команды будут печатать правильное значение плавно, в то время как в определенных местах, где используется "отложенное" расширение, оно всегда будет расширяться до имени корневого make-файла.
Например, если вы захотите использовать эту переменную в тексте правила, вам придется делать трюки, потому что текст правила всегда имеет отложенное расширение. Итак, если у вас есть правило
rule:
echo In makefile $(current_makefile):
echo Making target $@
он всегда будет печатать имя корневого make-файла. Вместо этого, чтобы вызвать немедленное расширение, вам нужно будет создать другую переменную с именем, специфичным для make-файла (т.е. имена таких переменных должны быть разными в каждом make-файле):
this_makefile_unique_name := $(current_makefile)
rule:
echo In makefile $(current_makefile):
echo Making target $@
или используйте eval:.
define make_rule
rule:
echo In makefile $(1):
echo Making target $$@
$(eval $(call make_rule,$(current_makefile)))
Если вы хотите использовать имя текущего make-файла только для целей отладки, рассмотрите специальные функции отладки, такие как предупреждение или информация:.
$(warning We're in makefile $(current_makefile))
Эти функции используют "немедленное" расширение и выводят правильное значение.
Как определить такой $(current_makefile)?
Вы должны вручную поддерживать стек включений make-файла. Когда вы включаете make-файл, его имя помещается на вершину стека; когда вы возвращаетесь из включенного make-файла во внешний, самое верхнее имя выталкивается из стека. Это достигается путем вставки специальных вызовов в начало и конец make-файла:
# Beginning of makefile
$(eval $(makefile_names_push))
#... makefile text
$(warning $(current_makefile))
#...
$(eval $(makefile_names_pop))
#End of file
Теперь определите функции в начале вашего корневого make-файла.
lastword=$(word $(words $(1)),$(1))
define makefile_names_push
current_makefile := $$(CURDIR)/$$(call lastword,$$(MAKEFILE_LIST))
makefile_stack :=$$(makefile_stack) $$(current_makefile)
endef
define makefile_names_pop
makefile_stack := $$(filter-out $$(current_makefile),$$(makefile_stack))
current_makefile := $$(call lastword,$$(makefile_stack))
endef
Если вы уверены, что ваш бренд достаточно новый (версия 3.81+), замените lastword
вызов со встроенной функцией:
#inctead of $$(call lastword,$$(MAKEFILE_LIST))
$$(lastword $$(MAKEFILE_LIST))
Это полезно?
Совершенно бесполезно. Единственное использование, которое может быть здесь полезным, - это сделать 100 make-файлов, которые являются символическими ссылками на один make-файл, правила в этих make-файлах зависят от их имен. Но это может быть достигнуто с помощью одного make-файла и метода foreach-eval, описанного в руководстве. Так что мой пост был пустой тратой времени, хотя я повеселился:-)
Это возвращает имя первого вызванного Makefile, то есть того, что находится внизу стека вызовов:
MAKEFILE_JUSTNAME := $(firstword $(MAKEFILE_LIST))
MAKEFILE_COMPLETE := $(CURDIR)/$(MAKEFILE_JUSTNAME)
Когда используется в нерекурсивных рекурсивных ситуациях (например, для makedepend), это просто имя текущего make-файла.
Я хотел сделать нечто подобное (для отображения содержимого Makefile), когда я использую Make для управления простыми повторяющимися задачами. Я наткнулся на эту страницу и обнаружил, что это именно то, что мне нужно, и это действительно полезно для моего ограниченного понимания марки.
Мой результат после прочтения этой страницы:
# Makefile - 'make' and 'make help' now echo the makefile.
help:
cat $(lastword $(MAKEFILE_LIST))
start:
sudo -u www /path/to/webapp/myhttpd restart
stop:
sudo kill `cat /path/to/webapp/data/httpd.pid`
Решения здесь касаются 1) POSIX make с 2) вызываемым, не включенным makefile в 3) Unix-подобной платформой.
Что попросил ОП:
target:
@pid=$$$$; \
while test `ps -ocomm= $$pid` != make; do \
pid=`ps -oppid= $$pid`; \
done; \
MAKEFILENAME=`ps -oargs= $$pid|sed 's/^.* -f *\([^ ]*\).*$$/\1/'`; \
test -z "$$MAKEFILENAME" -a -f Makefile && MAKEFILENAME=Makefile; \
test -z "$$MAKEFILENAME" -a -f makefile && MAKEFILENAME=makefile; \
export MAKEFILENAME; \
$(MAKE) -e -f $$MAKEFILENAME other_target
Цели зависят от make-файла, немного раздутого:
TARGET1_MAKEFILENAME = target1_preamble
all: target1 target2...
target1: $(TARGET1_MAKEFILENAME) other_dependencies...
@test $(TARGET1_MAKEFILENAME) == target1_preamble && exit 0; \
built_instructions_for_target1;
target1_preamble:
@pid=$$$$; \
while test `ps -ocomm= $$pid` != make; do \
pid=`ps -oppid= $$pid`; \
done; \
MAKEFILENAME=`ps -oargs= $$pid|sed 's/^.* -f *\([^ ]*\).*$$/\1/'`; \
test -z "$$MAKEFILENAME" -a -f Makefile && MAKEFILENAME=Makefile; \
test -z "$$MAKEFILENAME" -a -f makefile && MAKEFILENAME=makefile; \
export MAKEFILENAME; \
$(MAKE) -e -f $$MAKEFILENAME target1;
Может быть немного упрощено, если make вызывается только для всех целей.
MAKEFILENAME = invoked_makefile_placeholder
all: target1 target2...
target1: $(MAKEFILENAME) other_dependencies...
@test $(MAKEFILENAME) == invoked_makefile_placeholder && exit 0; \
built_instructions_for_target1;
invoked_makefile_placeholder:
@pid=$$$$; \
while test `ps -ocomm= $$pid` != make; do \
pid=`ps -oppid= $$pid`; \
done; \
MAKEFILENAME=`ps -oargs= $$pid|sed 's/^.* -f *\([^ ]*\).*$$/\1/'`; \
test -z "$$MAKEFILENAME" -a -f Makefile && MAKEFILENAME=Makefile; \
test -z "$$MAKEFILENAME" -a -f makefile && MAKEFILENAME=makefile; \
export MAKEFILENAME; \
$(MAKE) -e -f $$MAKEFILENAME
С предыдущим подходом тривиально реализовать решение для включенных make -файлов на основе
grep
и уникальный шаблон, содержащийся в make-файле.
Я никогда не отвечаю, когда чувствую, что вопрос получил правильное решение.
G'day,
Если вы делаете копию своего исходного make-файла, произнесите makefile_test, а затем введите команду:
make -np -f makefile_test 2>&1 | tee output
Это оценит make-файл и вашу среду make, но не выполнит ни одну из команд. Просмотр выходного файла на наличие ссылок на makefile_test покажет вам, что установлено в среде make и где устанавливается это значение.
NB Это может генерировать много информации! И не добавляйте ключ -d (debug), который будет генерировать тонны дополнительной информации о процессе принятия решения make, но минимальную дополнительную информацию о среде en make.
НТН