0

I need help to create a proper makefile (supporting incremental compilation) for multiple .hpp and .cpp files.

I've been looking for information about how to create a proper makefile, but I'm not really sure on how to do it.

I have the following files: 2048.cpp, game.cpp, game.hpp, gameutils.cpp, gameutils.hpp, menu.cpp, menu.hpp, saveandback.cpp, saveandback.hpp and tile.hpp

Right now I'm using the following makefile:

all: 2048.cpp tile.hpp menu.hpp menu.cpp gameutils.hpp gameutils.cpp saveandback.hpp saveandback.cpp game.hpp game.cpp
    g++ -g -W -Wall --pedantic -DNDEBUG 2048.cpp -o power

clean: 
    $(RM) power

Thank you for your help.

elena.bdc
  • 89
  • 1
  • 2
  • 6
  • 2
    Providing headers looks excessive. What is your question? – alexeykuzmin0 Aug 30 '16 at 15:18
  • How to write it properly. – elena.bdc Aug 30 '16 at 15:19
  • @CAB my homework are not based on create a makefile but I have to provide one, as I'm not really kind on this, that's why I'm asking you if you can help me. I red lots of documentation on who to write makefiles, but it doesn't provide me much help. But thank you, I'm going to take a look to the GNU Build System. – elena.bdc Aug 30 '16 at 15:30
  • 1
    Take a look at [POSIX make](http://pubs.opengroup.org/onlinepubs/009695399/utilities/make.html), then. Read the section on "Target Rules". Try making your own rules. How do you tell make that game.cpp requires game.hpp? Come back when you have something that doesn't work the way you think it should, and ask 'Why doesn't this work?' – CAB Aug 30 '16 at 15:42
  • @CAB, good comment... :) your comment only means you cannot write good makefiles. All BSD stuff is based on good makefile writing. Why are you giving that help if you don't like makefiles? Have you actually understood the real question here? – Luis Colorado Sep 01 '16 at 07:53

1 Answers1

5

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.

Jason C
  • 38,729
  • 14
  • 126
  • 182
  • Thank you very much for your help :) – elena.bdc Aug 30 '16 at 15:56
  • 2
    That's a nice, thorough answer! At the risk of lengthening even further, it would be better if you ended up at Make's builtin `$(LINK.cpp) $^ $(LOADLIBES) $(LDLIBS) -o $@` - so close from where you are, but so ready for the asker's next steps... – Toby Speight Aug 30 '16 at 17:41