Why does my target always get recompiled, even if nothing has changed in code?
Asked Answered
S

2

6

I'm almost new in writing Makefiles, so sorry for possible trivial mistakes.

My Makefile keeps recompiling the executable file (.out) even if nothing has changed in source code. The target has some dependencies on other objects, but they don't recompile their source files (as expected). So, why does the target recompile the .out file instead?

Any other advice would be really appreciated.

# -*- 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

For example when I type

make future

the first time I get everything compiled:

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

If I don't change the code and immediately re-type make future, I expect something like "nothing to be done for...". What I get instead is:

nvcc -std=c++14 -g -DMEASURES -Iinclude/  bin/main_cos.o bin/farmkernels.o bin/cudaUtils.o -o bin/future.out
Suggest answered 4/5, 2019 at 14:41 Comment(5)
Quite, but not entirely unrelated: https://mcmap.net/q/1770258/-makefile-is-always-not-up-to-date-even-without-any-changesDowntrend
@melpomene: linux seems more relevant to the question than c++. Since dependency determination depends on timestamps, the filesystem could be relevant (it isn't but we don't expect the asker to know that). Also, the OS definitely influences which version of make is in use.Raddled
@BenVoigt The tag description says "Use this tag only if your question relates to programming using Linux APIs or Linux-specific behavior". I don't think it applies here. You'd have the same issue if you used this Makefile on e.g. BSD or Mac OS.Downtrend
@melpomene: When asking the question you wouldn't know if the behavior is linux-specific or not.Raddled
@BenVoigt Yeah, but I knew it wasn't, so that's why I removed the tag. :-)Downtrend
S
5

Why does the target always recompile?

You have indicated that future is a "phony target". This means that:

  • future doesn't correspond to an actual file, i.e.
  • There is nothing with a date which Make can check to determine whether future is up-to-date, therefore
  • future can never be up-to-date, therefore
  • Whenever you build future, you have to execute the commands for it

And you have your linking command listed under the future target; so it gets re-run every time.

For a more in-depth explanation about.PHONY, see: What is the purpose of .PHONY in a Makefile?

What you can do about it

Two options:

  1. Use file targets. Your future target's commands generate $(BIN)future.out, right? So replace it with a $(BIN)future.out target, and build that.

  2. Add a $(BIN)future.out target, but for convenience, don't build that directly - have the future target depend on it, like @BenVoigt suggested:

     .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
    
Somewhere answered 4/5, 2019 at 19:13 Comment(2)
As a beginner I didn't know about "future can never be up-to-date" in .PHONY targets! About solutions you proposed, for me the second ( @BenVoigt ) is the right one. Because target names (for example future) are then used in objects as c++ macros: -D$(shell echo $(MAKECMDGOALS) | tr a-z A-Z)Suggest
@MariaChiaraCecconi: There's no such rule as "future can never be up-to-date". Rather than a rule, it is a result of your design. You never create a file named "future", so there is no file timestamp. Therefore when make compares file timestamps, it finds that "future" is missing and needs to be rebuilt. .PHONY additionally causes the timestamp to be ignored if there is one (that is, there is also no rule that "phony targets do not correspond to actual files"), although targets that don't correspond to actual files should be phony, the reverse is not required.Raddled
R
8

You specifically told make to always rebuild without considering dependency timestamps:

.PHONY: future managed stream clean

make is doing what you asked.


If you want nice named targets without causing a rebuild, don't write rules for the named targets. Instead, give them dependencies. As you've already noticed, .PHONY doesn't force rebuild all the dependencies, it only runs the direct rule.

.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 $@

(Also used automatic variables according to "Don't Repeat Yourself" principle)

Raddled answered 4/5, 2019 at 14:43 Comment(2)
Very useful solution! And I didn't know about automatic variables, so I read something about that. I found that $+ "is like $^, but prerequisites listed more than once are duplicated in the order they were listed in the makefile. This is primarily useful for use in linking commands where it is meaningful to repeat library file names in a particular order." So I was asking to myself if it's the same to use $^ in my case. Why did you specifically use $+?Suggest
@MariaChiaraCecconi: Because it's a linking command, and I didn't want you to run into trouble if you eventually started adding libraries to the dependency lines instead of plain object files.Raddled
S
5

Why does the target always recompile?

You have indicated that future is a "phony target". This means that:

  • future doesn't correspond to an actual file, i.e.
  • There is nothing with a date which Make can check to determine whether future is up-to-date, therefore
  • future can never be up-to-date, therefore
  • Whenever you build future, you have to execute the commands for it

And you have your linking command listed under the future target; so it gets re-run every time.

For a more in-depth explanation about.PHONY, see: What is the purpose of .PHONY in a Makefile?

What you can do about it

Two options:

  1. Use file targets. Your future target's commands generate $(BIN)future.out, right? So replace it with a $(BIN)future.out target, and build that.

  2. Add a $(BIN)future.out target, but for convenience, don't build that directly - have the future target depend on it, like @BenVoigt suggested:

     .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
    
Somewhere answered 4/5, 2019 at 19:13 Comment(2)
As a beginner I didn't know about "future can never be up-to-date" in .PHONY targets! About solutions you proposed, for me the second ( @BenVoigt ) is the right one. Because target names (for example future) are then used in objects as c++ macros: -D$(shell echo $(MAKECMDGOALS) | tr a-z A-Z)Suggest
@MariaChiaraCecconi: There's no such rule as "future can never be up-to-date". Rather than a rule, it is a result of your design. You never create a file named "future", so there is no file timestamp. Therefore when make compares file timestamps, it finds that "future" is missing and needs to be rebuilt. .PHONY additionally causes the timestamp to be ignored if there is one (that is, there is also no rule that "phony targets do not correspond to actual files"), although targets that don't correspond to actual files should be phony, the reverse is not required.Raddled

© 2022 - 2024 — McMap. All rights reserved.