Makefile всегда перекомпилирует файл

Я учусь настраивать 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'.

11

Решение

Ваш make -d вывод показывает, что make думает, что ваш каталог сборки был обновлен, и поэтому файл должен быть перестроен.

Я предполагаю, что это происходит потому, что какая-то операция, выполняемая частью вашей системы сборки или чем-то другим в вашей файловой системе, вызывает обновление некоторой метки времени в этом каталоге.

Вы можете решить проблему, сделав build предварительное условие только для заказа добавив | к этому правилу:

$(OBJECTS): $(OBJ)/%.o: %.m | build

Я удалил / тоже, так как он ничего не делал.

Так как вы спросили, некоторые другие редакционные заметки:

  1. Добавить clean цель. Что-то вроде:

    clean:
    rm -rf $(EXECUTABLE) $(OBJ)
    
  2. Вам не нужно ./ когда вы установите OBJ, Просто OBJ = build достаточно.

  3. Вам не нужно / на build как уже упоминалось выше. Но это на самом деле не имеет значения, так как вы все равно не должны ссылаться на это. Repalce build с $(OBJ) где бы вы ни видели это.

  4. mkdir потерпит неудачу, если каталог уже существует. Вы, вероятно, должны префикс этой команды с -:

    $(OBJ):
    -mkdir $(OBJ)
    

    Обратите внимание, что я сделал замену с $(OBJ) что я упоминал в № 3 выше.

  5. Автогенерация зависимостей очень полезна. Ваш проект, как показано, не достаточно большой, чтобы действительно нуждаться в нем, но его достаточно легко добавить, так почему бы и нет. Вам нужно будет сделать пару вещей. Сначала получите соответствующие имена файлов зависимостей:

    DEPFILES = $(addprefix $(OBJ)/,$(SOURCES:.m=.d))
    

    Затем получите компилятор, чтобы сгенерировать их, добавив -MMD флаг:

    gcc -MMD -c $< -o $@
    

    Наконец, включите их в свой make-файл, если они доступны, добавив строку в конце вашего make-файла:

    -include $(DEPFILES)
    
11

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]