Я учусь настраивать make-файлы и столкнулся с проблемой. Чтобы продемонстрировать это, я создал простой «проект», состоящий из исходных файлов. main.m
а также test.m
,
Я пытаюсь настроить make, чтобы скомпилировать эти файлы (только если что-то изменилось) и сохранить объектные файлы в другом месте (здесь build/
)
Мой Makefile:
OBJ = ./build
SOURCES=main.m test.m
OBJECTS=$(addprefix $(OBJ)/,$(SOURCES:.m=.o))
EXECUTABLE=test
all: $(EXECUTABLE)
$(EXECUTABLE): $(OBJECTS)
gcc $(OBJECTS) -o $(EXECUTABLE)
$(OBJECTS): $(OBJ)/%.o: %.m build/
gcc -c $< -o $@
build/:
mkdir build
Когда я запускаю его впервые (только с Makefile и исходными текстами в текущем каталоге), он делает то, что я ожидаю:
gcc -c main.m -o build/main.o
gcc -c test.m -o build/test.o
gcc ./build/main.o ./build/test.o -o test
Однако, если я бегу make
снова:
gcc -c main.m -o build/main.o
gcc ./build/main.o ./build/test.o -o test
Что я сделал не так? Также приветствуются любые другие ошибки в Makefile, так как я пытаюсь научиться создавать «хорошие» Makefile.
РЕДАКТИРОВАТЬ:
От чего я заметил make -d
:
Finished prerequisites of target file `build/main.o'.
Prerequisite `main.m' is older than target `build/main.o'.
Prerequisite `build/' is older than target `build/main.o'.
No need to remake target `build/main.o'.
а также
Finished prerequisites of target file `build/test.o'.
Prerequisite `test.m' is older than target `build/test.o'.
Prerequisite `build/' is newer than target `build/test.o'.
Must remake target `build/test.o'.
Ваш make -d
вывод показывает, что make
думает, что ваш каталог сборки был обновлен, и поэтому файл должен быть перестроен.
Я предполагаю, что это происходит потому, что какая-то операция, выполняемая частью вашей системы сборки или чем-то другим в вашей файловой системе, вызывает обновление некоторой метки времени в этом каталоге.
Вы можете решить проблему, сделав build
предварительное условие только для заказа добавив |
к этому правилу:
$(OBJECTS): $(OBJ)/%.o: %.m | build
Я удалил /
тоже, так как он ничего не делал.
Так как вы спросили, некоторые другие редакционные заметки:
Добавить clean
цель. Что-то вроде:
clean:
rm -rf $(EXECUTABLE) $(OBJ)
Вам не нужно ./
когда вы установите OBJ
, Просто OBJ = build
достаточно.
Вам не нужно /
на build
как уже упоминалось выше. Но это на самом деле не имеет значения, так как вы все равно не должны ссылаться на это. Repalce build
с $(OBJ)
где бы вы ни видели это.
mkdir
потерпит неудачу, если каталог уже существует. Вы, вероятно, должны префикс этой команды с -
:
$(OBJ):
-mkdir $(OBJ)
Обратите внимание, что я сделал замену с $(OBJ)
что я упоминал в № 3 выше.
Автогенерация зависимостей очень полезна. Ваш проект, как показано, не достаточно большой, чтобы действительно нуждаться в нем, но его достаточно легко добавить, так почему бы и нет. Вам нужно будет сделать пару вещей. Сначала получите соответствующие имена файлов зависимостей:
DEPFILES = $(addprefix $(OBJ)/,$(SOURCES:.m=.d))
Затем получите компилятор, чтобы сгенерировать их, добавив -MMD
флаг:
gcc -MMD -c $< -o $@
Наконец, включите их в свой make-файл, если они доступны, добавив строку в конце вашего make-файла:
-include $(DEPFILES)
Других решений пока нет …