How do I rewrite my makefile so that I can restructure the directories of my project?

Viewed 81

I've been working on a small game written entirely in C++ using SDL for graphics and audio. Until now all of my source files and headers have been located in the same directory as my executable, but I would like to move these to a ./src and an ./include folder.

However, I am really struggling with makefile syntax and how I would tell the compiler where to find the source and header files.

How can I tell the makefile to find source/headers/objects in subdirectories? Any improvements/mistakes for my makefile?

Here is my current makefile:

OBJS = main.o Window.o Entity.o Player.o Tile.o BoundingBox.o\
     MapReader.o Button.o Zombie.o Projectile.o Pathfinder.o
CPP = g++
CPPFLAGS = -c -g
LDFLAGS = -lmingw32 -lSDL2main -lSDL2 -lSDL2_image -lSDL2_mixer -mwindows

ZombieAttack: $(OBJS)
    $(CPP) -o ZombieAttack $(OBJS) $(LDFLAGS)

main.o: main.cpp
    $(CPP) $(CPPFLAGS) main.cpp

Window.o: Window.cpp
    $(CPP) $(CPPFLAGS) Window.cpp

Entity.o: Entity.cpp
    $(CPP) $(CPPFLAGS) Entity.cpp

Player.o: Player.cpp
    $(CPP) $(CPPFLAGS) Player.cpp

Tile.o: Tile.cpp
    $(CPP) $(CPPFLAGS) Tile.cpp

BoundingBox.o: BoundingBox.cpp
    $(CPP) $(CPPFLAGS) BoundingBox.cpp

MapReader.o: MapReader.cpp
    $(CPP) $(CPPFLAGS) MapReader.cpp

Button.o: Button.cpp
    $(CPP) $(CPPFLAGS) Button.cpp

Zombie.o: Zombie.cpp
    $(CPP) $(CPPFLAGS) Zombie.cpp

Projectile.o: Projectile.cpp
    $(CPP) $(CPPFLAGS) Projectile.cpp

Pathfinder.o: Pathfinder.cpp
    $(CPP) $(CPPFLAGS) Pathfinder.cpp

clean:
    del *.o

I am using mingw for compilation and mingw32-make

2 Answers

If you want the binaries to be in the highest level directory, and the .h / .cpp files to be in sub directories of that, you can simply prefix the files you wish to access with target directory/filename

so for example:

MapReader.o: MapReader.cpp
    $(CPP) $(CPPFLAGS) MapReader.cpp

becomes

MapReader.o: src/MapReader.cpp
    $(CPP) $(CPPFLAGS) src/MapReader.cpp

In general, it's best if you try to solve the problem yourself before posting to SO. Then when you have an issue you can't solve you should ask about that issue specifically, rather than broad questions like this.

However, it is the new year!!

First, make is not the same thing as the compiler. It's not enough to teach make where to find your files, you also have to teach the compiler where to find your files. If you're going to put header files in some other directory than the sources, then you have to give options to your compiler telling it where to find those headers. Just telling make where to find them won't do anything to help the compiler.

Second, unless you really have a pressing need you should use the standard variables for things like the C++ compiler (CXX not CPP) and the flags for it (CXXFLAGS not CPPFLAGS). You should also not put the option that chooses the type of output (-c) into the general flags variable: that should go into the rule. And finally, you should make sure you keep the distinction between LDFLAGS which are flags to the linker, and LDLIBS which are libraries to link. Here you're putting all the libraries to link into LDFLAGS.

Third, you can use automatic variables wherever possible to reduce duplication.

Fourth, you can greatly simplify your makefile by using a pattern rule. In fact, GNU make provides default pattern rules that can properly build C++ files, if you use the proper variables as above, so you don't even have to define your own.

Here's a version of your original makefile, with the above variables fixes and using builtin rules to reduce duplication:

OBJS = main.o Window.o Entity.o Player.o Tile.o BoundingBox.o \
     MapReader.o Button.o Zombie.o Projectile.o Pathfinder.o

CXX = g++
CXXFLAGS = -g
LDFLAGS = -mwindows
LDLIBS = -lmingw32 -lSDL2main -lSDL2 -lSDL2_image -lSDL2_mixer 

ZombieAttack: $(OBJS)
        $(CXX) -o $@ $(LDFLAGS) $^ $(LDLIBS)

clean:
        del *.o

That makefile should perform exactly like your original makefile if all files are in the same directory.

Now, if you want to move things around then you need to first explain to the compiler where to find your headers by adding a -I option to the compile line. If your headers are now in the include subdirectory, you can do that like this:

CXXFLAGS = -g -Iinclude

Now, if you're willing to have the object files in the same directory as the source files, then you can change the above makefile to tell make where to build them, like this:

BASE_OBJS = main.o Window.o Entity.o Player.o Tile.o BoundingBox.o \
     MapReader.o Button.o Zombie.o Projectile.o Pathfinder.o

SRC = src
OBJS = $(addprefix $(SRC)/,$(BASE_OBJS))

That, along with the changes above, should allow it to work.

If you want to put the object files in some other directory than the source files, then you will no longer be able to use make's built-in rules because make cannot know where you want to put things, so you'll have to write your own pattern rule.

Related