#makefile #preprocessor #gnu
#makefile #препроцессор #gnu
Вопрос:
с gnu make у меня могут быть команды и переменные, которые, afaik, расширяются на первом шаге (своего рода препроцессор) и фактически выполняются на втором шаге.
Итак, когда я пишу:
$(OBJECTSFULL) :
@echo ' '
@echo '*** Compiling: $(basename $(notdir $@)).cpp'
$(CXX) $(CPPFLAGS) $(LIBCCFLAGS) -I$(MAIN_INCLUDES) $(CXXFLAGS)
-MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@:%.o=%.d)"
-o"$@" -c $(filter %/$(basename $(notdir $@)).cpp, $(SOURCES)) 2>amp;1
@echo '*** Compiler finished'
и моя переменная $(OBJECTSFULL)
содержит это: src/foo.o src/bar.o src/baz.o
,
Я ожидаю, что «препроцессор» создаст что-то подобное до фактического построения целевых объектов:
src/foo.o :
@echo ' '
@echo '*** Compiling: foo.cpp'
g -c ......
src/bar.o :
@echo ' '
@echo '*** Compiling: bar.cpp'
g -c ......
src/baz.o :
@echo ' '
@echo '*** Compiling: baz.cpp'
g -c ......
Вопрос:
Есть ли какой-либо способ распечатать эти результаты после первого шага? Это помогло бы мне отладить довольно сложную функциональность makefile с несколькими $()-вызовами…
——редактировать——
Чтобы сделать его более точным: я генерирую код makefile динамически. Представьте, что у меня есть несколько «устройств», я определяю их в списке:
devices := Car Bike Plane Ship
Затем я определяю шаблон:
define make-device-lib
LIBBASENAME$(1) := device$(1)
LIBNAME$(1) := lib$$(LIBBASENAME$(1))
LIBSONAME$(1) := $$(LIBNAME$(1)).so
LIBSOX$(1) := $$(LIBSONAME$(1)).1
LIBSOXX$(1) := $$(LIBSONAME$(1)).1.1
LIBSOXXFULL$(1) := $$(SHAREDLIBDIR)/$$(LIBSOXX$(1))
DEVICELIBS = $$(LIBSOXXFULL$(1))
OBJECTS$(1) := $$(call objects_from_dirs, $$(SOURCEDIRS_$(1)))
OBJECTSCOMMON := $$(filter-out $$(OBJECTS$(1)), $$(OBJECTSCOMMON))
$$(LIBSOXXFULL$(1)) : $$(OBJECTS$(1))
@echo ' '
@echo '*** Linking: $$(LIBSONAME$(1))'
$(CXX) $(CXXFLAGS) $(LIBLNFLAGS) -Wl,-soname,$$(LIBSONAME$(1)) -L$(SHAREDLIBDIR) $$^ -o $$@
-@ln -f -s $$(LIBSOXX$(1)) $$(SHAREDLIBDIR)/$$(LIBSOX$(1))
-@ln -f -s $$(LIBSOX$(1)) $$(SHAREDLIBDIR)/$$(LIBSONAME$(1))
endef
Теперь код фактически генерируется для каждого «устройства», вызывая «make-device-lib»:
$(foreach device,$(devices),$(eval $(call make-device-lib,$(device))))
Каждый $ (1) в шаблоне заменяется именем каждого устройства.
Например: $$(LIBSOXXFULL$(1))
в конечном итоге будет расширен до /usr/lib/libdeviceCar.so.1.1
в первом блоке, а /usr/lib/libdeviceBike.so.1.1
во втором и т.д. $OBJECTSCar
содержит автоматически собранные объекты, от которых зависит цель («objects_from_dirs» — это другой макрос, который делает это).
Итак, когда строка
$$(LIBSOXXFULL$(1)) : $$(OBJECTS$(1))
расширяется, make ведет себя так, как если бы я писал от руки:
/usr/lib/libdeviceCar.so.1.1 : Logging.o Printer.o Engine.o Seats.o
Что я хотел бы видеть, так это этот «виртуальный» makefile, так как если бы я написал все вручную.
Комментарии:
1. Вы пробовали
make -p
?2. Спасибо за хорошую подсказку! не совсем то, что я ожидал, но, по крайней мере, он разрешает все переменные и показывает их содержимое. Я постараюсь привыкнуть к этому.
3. Не все переменные расширяются при первом проходе. Рекурсивно расширенные переменные не расширяются до тех пор, пока не будут использованы. Тем не менее, я также часто хотел увидеть makefile, который видит первый проход синтаксического анализа.
4. @minastaros Я столкнулся с той же проблемой при работе со сложными make-файлами. Вы нашли решение? Это было бы очень полезно?
5. @jmlemetayer Вы смотрели,
make -p
когда вам нужно.SUFFIXES:
отключить неявные правила? Это запутывает все это кучей неявного мусора, который должен был быть отключен. Это полная вводящая в заблуждение бессмыслица.
Ответ №1:
Make всегда печатает команду после ее развертывания и перед отправкой в оболочку, если вы не ставите перед строкой префикс @
. Вывод, который вы видите, — это именно то, что make отправляет в оболочку, и, похоже, это то, что вы ищете.
Можете ли вы объяснить, какую информацию вам не хватает?
Если проблема в том, что вы хотите увидеть содержимое, скрытое @
, вам нужно будет либо использовать --trace
флаг (обратите внимание, что это было введено в GNU make 4.0), либо удалить @
(часто люди используют переменную, содержащую @
которую они могут сбросить), либо запустить с -n
, но это также может изменить поведение make.
ETA:
Нет ничего, кроме make -p
, что напечатает весь makefile целиком, и это не показывает makefile в том порядке, в каком он был написан изначально; он просто выгружает внутреннюю базу данных make в том порядке, в котором она сохранена.
В частности, для eval
функций вы можете точно увидеть, что разбирает make, если вы замените eval
вызов на info
call; так, например:
$(foreach device,$(devices),$(eval $(call make-device-lib,$(device))))
был бы заменен на:
$(foreach device,$(devices),$(info $(call make-device-lib,$(device))))
Конечно, вы можете сделать и то, и другое, и вы можете поместить info
версию в какое-то условное выражение, чтобы оно выполнялось, только если вы укажете переменную или что-то в этом роде:
$(foreach device,$(devices),$(eval $(call make-device-lib,$(device))))
ifneq ($(VERBOSE),)
$(foreach device,$(devices),$(info $(call make-device-lib,$(device))))
endif
Однако, чтобы использовать это, вам, очевидно, придется изменить свой makefile для его поддержки; он не встроен в make.
Комментарии:
1. Привет, MacScientist, я не имею в виду вывод каждой @-строки. Это намного сложнее: генерация кода с использованием шаблонов, а также циклов, управляемых списками. Смотрите мою правку выше.
2.
make -p
печатает базу данных всех переменных, но в случае сложных make-файлов все еще сложно развернуть вызов макроса / функции, который генерирует правила на лету.3. Особенно в случае ядра Linux Makefile
make -p
не так уж полезен для расширения такого макроса, как этот: github.com/torvalds/linux/blob/v5.0/scripts/Makefile.lib#L181 . Этот макрос генерирует предварительные условия для целевого объекта. Потребовалось некоторое время, чтобы понять, что он делает. Может быть, вы знаете какой-нибудь метод упрощения такого расширения макроса при отладкеMakefile
?4. Для подобных вещей обычно вы снова пишете то же самое, но заменяете
eval
наinfo
:$(foreach m, $(notdir $1), $(info $(obj)/$m: ...
, чтобы он выводил то, что он будет оценивать. Однако я должен отметить, что-p
также выводит все правила, поэтому вы можете найти цель, которую, по вашему мнению, должно создать это расширение, и посмотреть, как она определена.