Как просмотреть результат расширения после первого шага

#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 также выводит все правила, поэтому вы можете найти цель, которую, по вашему мнению, должно создать это расширение, и посмотреть, как она определена.