Ваш подход к извлечению этой информации из Makefile
, вероятно, неправильный.make
не знает, какие заголовочные файлы используются на самом деле .make
знает только, какие заголовочные файлы вы явно указали make
в зависимостях, и это не очень надежно.Информация в Makefile
может быть неверной по двум причинам.Он может содержать неиспользуемые цели (как вы заметили) или неиспользуемые заголовочные файлы.Он может пропустить заголовочные файлы, которые на самом деле включены, но не упомянуты в Makefile
.Еще хуже то, что если фактическое включение заголовочных файлов зависит от макросов, например #ifdef XYZ_FEATURE #include "additionalHeaderFile.h" #endif
.
Существует как минимум три способа, которыми вы можете сгенерировать желаемый список, то есть список .cc
и .h
файлов, фактически используемых во время компиляции:
- (вслепую доверяет
Makefile
) make -n --print-data-base
- (следует за реальными файлами сборки, но также немного сложен для анализа)
strace -f make
- (полагается на функцию, присутствующую в GCC, clang, armcc и, возможно, других компиляторах для поколения
Makefile
, очень надежная) Добавить CPPFLAGS:=-MMD
к Makefile
, запустить make clean
,затем make
, затем используйте cat *.d
, чтобы получить список всех файлов .cc
и .h
, использованных для сборки run
.Вы даже можете сделать это, не изменяя Makefile
: make clean; make CPPFLAGS:=-MMD && cat *.d | sed -e 's/\\//g' -e 's/:/ /g' -e 's/ \+/\n/g' | sort -u
.
Кроме того, у Makefile
, которым вы поделились в гисте, есть большое количество проблем.
- По умолчанию цель должна быть названа
all
1 2 3 .Это не должно быть имя двоичного файла, это будет просто цель .PHONY
. - Переменная со списком объектных файлов должна иметь имя
OBJS
или OBJECTS
, а не OBJ
.Имя OBJ
вводит в заблуждение, потому что оно в единственном числе. - Вместо
rm
используйте $(RM)
, что подразумевает -f
и, следовательно, не будет проблем в случае, если файл не существует.(Как побочный эффект, Makefile
станет более переносимым, так как не все платформы используют rm
для удаления файлов.) clean
не является файлом и, следовательно, должно быть .PHONY
target. clean
должен использовать ::
вместо :
для своего рецепта, чтобы в будущем, когда Makefile
был больше и разделен на несколько файлов, каждый файл мог иметь свой собственный clean
target без проблем. - Переменные, не зависящие от правил, следует раскрывать в момент определения, а не в том случае, когда на них ссылаются, и, таким образом, они определяются с использованием
:=
вместо =
. - Вместо
C++
используйте CXX
, который уже определен. - Вместо помещения параметров в
C++
/ CXX
, используйте LDFLAGS
, потому что вы связываете. - У вас должен быть исходный файл с тем же базовым именем, что и двоичный файл.Затем вы можете использовать встроенное правило для связывания.
- Явные зависимости в
Makefile
являются трудной задачей для обслуживания.Каждый раз, когда оператор #include
для заголовочного файла проекта добавляется, удаляется или изменяется, Makefile
необходимо будет обновлять, и это легко забыть, и это очень неприятно, особенно когда оператор #include
находится взаголовочный файл.Даже с должной осмотрительностью, это также невидимый конфликт слияния.Вместо явных зависимостей в Makefile
, вы должны использовать CPPFLAGS+=-MMD
в начале вашего Makefile
и добавить -include $(wildcard *.d)
в конце вашего Makefile
, и добавить *.d
в список файлов дляудалить в clean
.Затем вы можете удалить все правила зависимостей из Makefile
(за исключением правила для связи). - Называть двоичный файл
run
не очень хорошая идея.Пользователи, которые видят, что у вашей Makefile
есть цель run
, ожидают, что она будет запускать реальной программы, а не связывать ее. - Лучше, чтобы каждый объект упоминался насобственная линия.Это значительно уменьшает конфликты слияния в проектах, когда несколько разработчиков одновременно изменяют список объектов.
Фактический Makefile
должен выглядеть следующим образом, с двоичным файлом, переименованным из run
в program
:
LDFLAGS:=-Wno-deprecated -lm
CPPFLAGS+=-MMD
BINARY:=program
OBJECTS:= \
$(BINARY).o \
binomh.o \
# More objects here
.PHONY: all
all: $(BINARY)
$(BINARY): $(OBJECTS)
.PHONY: clean
clean::
$(RM) \
$(BINARY) \
*.[adios] \
-include $(wildcard *.d)
То, что Makefile
будет делать то же самое, что и ваш Makefile
, но оно почти не требует обслуживания .Нет необходимости обновлять зависимости, так как они автоматически берутся из файлов зависимостей, сгенерированных препроцессором C.*.[adios]
также удалит файлы, созданные в случае, если вы добавили -save-temps
к любому из CFLAGS
, CXXFLAGS
или CPPFLAGS
.
Этот тип Makefile
, как известно, работает дляGCC, clang, AOCC (AMD Optimizing C Compiler) и armcc.Это, вероятно, работает и для ряда других компиляторов и препроцессоров, особенно когда они основаны или пытаются быть совместимыми с GCC или clang / LLVM.
Кстати, если вам интересно знать, чтоэто работает для вас с высокой степенью достоверности, помимо опыта: я взял ваш Makefile
и добавил к нему следующие строки, чтобы воспроизвести структуру вашего исходного кода.Заголовочные файлы будут просто пустыми файлами.Исходные файлы C ++ будут списками #include
операторов, взятых из зависимостей в вашем Makefile
.
%.cc:
grep '^$*\.o.*:' $(MAKEFILE_LIST) | sed -e 's/.*://' -e 's/.*$*\.cc//' -e 's/ \([^ ]\+\)/#include "\1"\n/g' >$@
%.h:
touch $@