Caveat: I haven't used make in a while so I may be a little rusty on POSIX vs. GNU-make specific stuff. There may also be new features released in the last few years that I'm not aware of. Please feel free to give corrections. Also most of this is from memory.
There are a few things missing from your knowledge set here that you can use to create a decent makefile that only re-compiles things when needed:
Generic Rules - These can be used to provide a generic rule for building a filename with one suffix from another. E.g. the following defines a rule for creating any *.o from its corresponding *.cpp:
%.o: %.cpp
stuff
In POSIX make these rules are actually specified as:
.cpp.o:
stuff
I'm using GNU syntax below but you can (and might want to) replace with POSIX syntax (moot if you leave them out and use the implicit rules, though, see below).
Automatic Variables
$<
will expand to the input of the rule.
$@
will expand to the target of the rule.
Variables - You can declare variables and give them values, e.g.:
SOURCES=2048.cpp gameutils.cpp saveandback.cpp
Etc.
Text Replacement - You can use text replacement functions to replace suffixes, e.g.:
OBJECTS=$(SOURCES:.cpp=.o)
Will set OBJECTS
equal to SOURCES
but with the .cpp
's changed to .o
.
Multiple Rules - If multiple rules are specified for the same target, their prerequisites are merged.
Phony Targets
Putting that all together you can get a start, leaving out header dependencies for now:
SOURCES=2048.cpp menu.cpp gameutils.cpp saveandback.cpp game.cpp
OBJECTS=$(SOURCES:.cpp=.o)
power: $(OBJECTS)
g++ $(OBJECTS) -o $@
%.o: %.cpp
g++ -c $< -o $@
And it's traditional to define an all
rule, which is a phony target since there isn't actually a file named "all":
.PHONY: all
SOURCES=2048.cpp menu.cpp gameutils.cpp saveandback.cpp game.cpp
OBJECTS=$(SOURCES:.cpp=.o)
all: power
power: $(OBJECTS)
g++ $(OBJECTS) -o $@
%.o: %.cpp
g++ -c $< -o $@
Now, make actually has some default rules already, including one for %.o: %.cpp
, and also it has some default variables. So you can reduce the above to this if you'd like (personally I prefer to explicitly specify rules, but that's just me):
.PHONY: all
SOURCES=2048.cpp menu.cpp gameutils.cpp saveandback.cpp game.cpp
OBJECTS=$(SOURCES:.cpp=.o)
all: power
power: $(OBJECTS)
$(CXX) $(CXXFLAGS) $(OBJECTS) -o $@
Now, as for your headers, keeping in mind the multiple rules thing, you can simply add those prerequisites by hand based on their includes, e.g.:
.PHONY: all
SOURCES=2048.cpp menu.cpp gameutils.cpp saveandback.cpp game.cpp
OBJECTS=$(SOURCES:.cpp=.o)
all: power
power: $(OBJECTS)
$(CXX) $(CXXFLAGS) $(OBJECTS) -o $@
2048.o: menu.hpp game.hpp
menu.o: menu.hpp
game.o: game.hpp
And so on. You probably also want a "clean" rule, another phony target, and it doesn't hurt to put your binary name in a variable since you use it in a few places, e.g.:
.PHONY: all clean
SOURCES=2048.cpp menu.cpp gameutils.cpp saveandback.cpp game.cpp
OBJECTS=$(SOURCES:.cpp=.o)
BINARY=power
all: $(BINARY)
clean:
$(RM) $(BINARY) $(OBJECTS)
$(BINARY): $(OBJECTS)
$(CXX) $(CXXFLAGS) $(OBJECTS) -o $@
And actually if you pass -MM
to gcc
it'll generate Makefile dependencies automatically for you, based on the source file's includes. See here for details and an example.