Makefile в исходном вопросе не работает для меня с gcc 4.8.2:
cc -MMD -MG -MT main.d -c main.c -o main.o
cc1: error: -MG may only be used with -M or -MM
Я думаю, что gcc изменил поведение -MG
в какой-то момент за последние 4 года.
Похоже, что если вы хотите поддерживать сгенерированные заголовочные файлы, больше нет
любой способ создать файл ".d" и файл ".o" одновременно, без
дважды вызывая препроцессор C.
Итак, я обновил рецепт:
%.o: %.c
$(CC) -MM -MG -MP -MT $*.o -MF $*.d $<
$(CC) -c $< -o $@
(Обратите внимание также, что gcc теперь имеет -MP
для генерации фальшивых целей для каждого заголовка,
так что вам больше не нужно запускать sed для вывода gcc.)
У нас все еще есть та же проблема, что и в первоначальном вопросе - запуск make
первый раз не удается сгенерировать foo.h
:
$ make
cc -MM -MG -MP -MT main.o -MF main.d main.c
cc -c main.c -o main.o
main.c:1:17: fatal error: foo.h: No such file or directory
#include "foo.h"
^
compilation terminated.
Makefile:7: recipe for target 'main.o' failed
make: *** [main.o] Error 1
Запуск снова работает:
$ make
./mk_header.sh foo
cc -MM -MG -MP -MT main.o -MF main.d main.c
cc -c main.c -o main.o
cc main.o -o main
Так как мы все равно должны дважды запустить препроцессор C, давайте сгенерируем .d
файл в отдельном правиле:
%.d: %.c
$(CC) -MM -MG -MP -MT $*.o -MF $@ $<
%.o: %.c
$(CC) -c $< -o $@
Теперь он правильно генерирует заголовочный файл:
$ make clean
rm -f *.o *.d main foo.h
$ make
cc -MM -MG -MP -MT main.o -MF main.d main.c
./mk_header.sh foo
cc -c main.c -o main.o
cc main.o -o main
Страдает ли это от проблемы производительности, из-за которой исходный вопрос был
пытаясь избежать? По сути, это решение «Базовые автозависимости»
описано в Расширенное автозависимость
Поколение бумага.
Эта статья требует 3 проблем с этим решением:
- Мы повторно выполняем
make
, если что-то меняется.
- Уродливое, но безобидное предупреждение: "main.d: такого файла или каталога нет"
- Неустранимая ошибка "нет правила для создания цели foo.h", если файл foo.h удален, даже
если упоминание об этом удалено из файла .c.
Проблема 2 решается с помощью -include
вместо include
. Насколько я могу
скажем, это ортогонально технике бумаги, чтобы избежать повторного выполнения
make
. По крайней мере, у меня не было проблем с использованием -include
вместо include
.
Проблема 3 решается с помощью -MP
GCC (или эквивалентного сценария sed) - это
также ортогонально технике, позволяющей избежать повторного исполнения make
.
Проблема 1 может быть несколько улучшена чем-то вроде этого:
%.d: %.c
$(CC) -MM -MG -MP -MT $*.o -MF $@.new $<
cmp $@.new $@ 2>/dev/null || mv $@.new $@; rm -f $@.new
До этого изменения:
$ make clean
rm -f *.o *.d main foo.h
$ make -d 2>&1 | grep Re-executing
Re-executing[1]: make -d
$ make -d 2>&1 | grep Re-executing
$ touch main.c; make -d 2>&1 | grep Re-executing
Re-executing[1]: make -d
После этого изменения:
$ make clean
rm -f *.o *.d main foo.h
$ make -d 2>&1 | grep Re-executing
Re-executing[1]: make -d
$ make -d 2>&1 | grep Re-executing
$ touch main.c; make -d 2>&1 | grep Re-executing
Чуть лучше. Конечно, если будет введена новая зависимость, make
все равно
нужно заново выполнить. Может быть, ничего нельзя сделать, чтобы улучшить это; похоже, что это компромисс между скоростью и правильностью.
Все вышеперечисленное было протестировано с маркой 3.81.