Почему моя цель всегда перекомпилирует исполняемый файл, даже если в коде ничего не изменилось?

Я почти новичок в написании Makefile, так что извиняюсь за возможные тривиальные ошибки. Мой Makefile продолжает перекомпилировать исполняемый файл (.out), даже если в исходном коде ничего не изменилось. У цели есть некоторые зависимости от других объектов. В любом случае эти объекты не перекомпилируют относительные исходные файлы (как и должно быть). Так зачем же вместо этого цель перекомпилировать файл .out?

Любой другой совет будет очень признателен.

# -*- Makefile -*-

CC:= nvcc
CFLAGS:= -std=c++14 -g -DMEASURES
ALLFLAGS:= $(CFLAGS) -Iinclude/ 
LOWPAR:= $(ALLFLAGS) -DLOWPAR


BIN:=bin/
SRC:=src/
INCLUDE:=include/


.PHONY: future managed stream clean

####DEVICE####
#cos future, stream, managed
future: $(BIN)main_cos.o $(BIN)farmkernels.o $(BIN)cudaUtils.o
    $(CC) $(ALLFLAGS) $(BIN)main_cos.o $(BIN)farmkernels.o $(BIN)cudaUtils.o -o $(BIN)future.out

managed: $(BIN)main_cos.o $(BIN)farmkernels.o $(BIN)cudaUtils.o
    $(CC) $(ALLFLAGS) $(BIN)main_cos.o $(BIN)farmkernels.o $(BIN)cudaUtils.o -o $(BIN)managed.out

stream: $(BIN)main_cos.o $(BIN)farmkernels.o $(BIN)cudaUtils.o
    $(CC) $(ALLFLAGS) $(BIN)main_cos.o $(BIN)farmkernels.o $(BIN)cudaUtils.o -o $(BIN)stream.out


$(BIN)main_cos.o: $(SRC)main_cos.cpp $(INCLUDE)cosFutStr.h $(INCLUDE)cudaUtils.h
    $(CC) $(ALLFLAGS) -c $(SRC)main_cos.cpp -D$(shell echo $(MAKECMDGOALS) | tr a-z A-Z) -o $(BIN)main_cos.o

$(BIN)farmkernels.o:  $(SRC)farmkernels.cu $(INCLUDE)cosFutStr.h $(INCLUDE)cudaUtils.h
    $(CC) $(ALLFLAGS) -c $(SRC)farmkernels.cu -o $(BIN)farmkernels.o

$(BIN)cudaUtils.o: $(SRC)cudaUtils.cpp  $(INCLUDE)cudaUtils.h
    $(CC) $(ALLFLAGS) -c $(SRC)cudaUtils.cpp -o $(BIN)cudaUtils.o

####clean####
clean:
    rm -f $(BIN)*.o 
    rm -f $(BIN)*.out

Например, когда я печатаю

make future

в первый раз все скомпилировано:

nvcc -std=c++14 -g -DMEASURES -Iinclude/  -c src/main_cos.cpp -DFUTURE -o bin/main_cos.o
nvcc -std=c++14 -g -DMEASURES -Iinclude/  -c src/farmkernels.cu -o bin/farmkernels.o
nvcc -std=c++14 -g -DMEASURES -Iinclude/  -c src/cudaUtils.cpp -o bin/cudaUtils.o
nvcc -std=c++14 -g -DMEASURES -Iinclude/  bin/main_cos.o bin/farmkernels.o bin/cudaUtils.o -o bin/future.out

Если я ничего не изменяю в коде и сразу же набираю команду make future, я ожидаю чего-то вроде «ничего не нужно делать для ...». Но вместо этого я получаю:

nvcc -std=c++14 -g -DMEASURES -Iinclude/  bin/main_cos.o bin/farmkernels.o bin/cudaUtils.o -o bin/future.out

person Maria Chiara    schedule 04.05.2019    source источник
comment
Совершенно, но не совсем не связанно: stackoverflow.com/a/32782220/1848654   -  person melpomene    schedule 04.05.2019
comment
@melpomene: linux кажется более актуальным для вопроса, чем c ++. Поскольку определение зависимости зависит от временных меток, файловая система может иметь значение (это не так, но мы не ожидаем, что спрашивающий об этом узнает). Кроме того, ОС определенно влияет на то, какая версия make используется.   -  person Ben Voigt    schedule 04.05.2019
comment
@BenVoigt В описании тега говорится: Используйте этот тег только в том случае, если ваш вопрос касается программирования с использованием Linux API или поведения, специфичного для Linux. Я не думаю, что здесь это применимо. У вас была бы такая же проблема, если бы вы использовали этот Makefile, например, BSD или Mac OS.   -  person melpomene    schedule 04.05.2019
comment
@melpomene: Задавая вопрос, вы не узнаете, является ли поведение специфичным для Linux или нет.   -  person Ben Voigt    schedule 04.05.2019
comment
@BenVoigt Да, но я знал, что это не так, поэтому я удалил тег. :-)   -  person melpomene    schedule 05.05.2019


Ответы (2)


Почему цель всегда перекомпилируется?

Вы указали, что future является «фальшивой целью». Это означает, что:

  • future не соответствует реальному файлу, т.е.
  • Нет ничего с датой, которую Make может проверить, чтобы определить, является ли future актуальным, поэтому
  • future никогда не может быть актуальным, поэтому
  • Всякий раз, когда вы строите future, вы должны выполнять для него команды

И ваша команда связывания указана под целью future; так что он запускается каждый раз заново.

Для более подробного объяснения about.PHONY см .: What какова цель .PHONY в make-файле?

Почему вы можете с этим поделать

Два варианта:

  1. Используйте файловые цели. Команды вашей future цели генерируют $(BIN)future.out, верно? Так что замените его целью $(BIN)future.out и создайте ее.
  2. Добавьте цель $(BIN)future.out, но для удобства не создавайте ее напрямую - пусть цель future зависит от нее, как предложил @BenVoigt:

    .PHONY: future other_pho ny_target s_here
    
    future: $(BIN)future.out
    
    $(BIN)future.out: $(BIN)main_cos.o $(BIN)farmkernels.o $(BIN)cudaUtils.o
        $(CC) $(ALLFLAGS) $(BIN)main_cos.o $(BIN)farmkernels.o $(BIN)cudaUtils.o -o $(BIN)future.out
    
person einpoklum    schedule 04.05.2019
comment
Как новичок, я не знал, что будущее никогда не может быть актуальным в целях .PHONY! Что касается предложенных вами решений, то для меня второе (@BenVoigt) является правильным. Поскольку целевые имена (например, future) затем используются в объектах как макросы C ++: -D$(shell echo $(MAKECMDGOALS) | tr a-z A-Z) - person Maria Chiara; 05.05.2019
comment
@MariaChiaraCecconi: Нет такого правила, что будущее никогда не может быть актуальным. Это не правило, а результат вашего дизайна. Вы никогда не создаете файл с именем future, поэтому отметка времени файла отсутствует. Поэтому, когда make сравнивает временные метки файлов, он обнаруживает, что future отсутствует и требует восстановления. .PHONY дополнительно приводит к тому, что метка времени игнорируется, если она есть (т. Е. Также не существует правила, согласно которому фальшивые цели не соответствуют фактическим файлам), хотя цели, которые не соответствуют фактическим файлам, должны быть фальшивыми, обратное - нет. обязательный. - person Ben Voigt; 05.05.2019

Вы специально сказали make всегда перестраивать без учета временных меток зависимостей:

.PHONY: future managed stream clean

make делает то, что вы просили.


Если вам нужны красиво именованные цели, не вызывающие перестройки, не пишите правила для именованных целей. Вместо этого дайте им зависимости. Как вы уже заметили, .PHONY не выполняет принудительную перестройку всех зависимостей, а выполняет только прямое правило.

.PHONY: future managed stream clean

future: $(BIN)future.out

$(BIN)future.out: $(BIN)main_cos.o $(BIN)farmkernels.o $(BIN)cudaUtils.o
    $(CC) $(ALLFLAGS) $+ -o $@

(Также использовались автоматические переменные по принципу «Не повторяйся»)

person Ben Voigt    schedule 04.05.2019
comment
Очень полезное решение! И я не знал об автоматических переменных, поэтому я кое-что читал об этом. Я обнаружил, что $+ похож на $^, но предварительные требования, перечисленные более одного раза, дублируются в том порядке, в котором они были перечислены в make-файле. Это в первую очередь полезно для использования при связывании команд, когда имеет смысл повторять имена файлов библиотеки в определенном порядке. Поэтому я спрашивал себя, можно ли использовать $^ в моем случае. Почему вы специально использовали $+? - person Maria Chiara; 05.05.2019
comment
@MariaChiaraCecconi: Потому что это команда связывания, и я не хотел, чтобы у вас возникли проблемы, если вы в конечном итоге начали добавлять библиотеки в строки зависимостей вместо простых объектных файлов. - person Ben Voigt; 05.05.2019