First of all, good for you for learning how to write a proper makefile
. Few people are any good at it, and it's a really fundamental tool. Even when nice tools such as CMake
are used, it's very useful to understand how to write one from scratch.
With that said, here are some things that may help you improve your makefile
:
Prefer Makefile
to makefile
The names are equivalent, but since you're using GNU Make, you may as well follow its conventions, and one convention is to name it Makefile
instead of makefile
. Either will work, but quoting from the GNU Make man page:
We recommend 'Makefile' because it appears prominently near the beginning of a directory listing, right near other important files such as 'README'.
Use standard variable names
There are standard names for some of the variables you're using. For example, use CFLAGS
instead of COMPILE_FLAGS
and CXXFLAGS
which is the same thing, but for invocations of g++
(or more accurately invocations of $(CXX)
). Similarly, use LDFLAGS
instead of LINK_FLAGS
.
Add INCLUDES
to CFLAGS
It's good practice to use variables like INCLUDES
to keep things tidy, but it would be better if you added that to CFLAGS
:
CFLAGS += $(INCLUDES)
The reason for doing that is in the next point.
Use the implicit rules
GNU Make already has an implicit rule for creating .o
files from .c
files. That rule looks like this:
%.o : %.c
$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@
The result of this is that if you have defined the flags correctly, you can omit an explicit rule and rely instead on the implicit rule. In this particular Makefile
, it means that you can entirely omit the rule for glad.o
. As long as glad.c
is in the current directory, and main.cpp
is in the current directory, the built-in rules will understand how to create both glad.o
using gcc and main.o
using g++.
Add LIBRARIES
to LDFLAGS
Your main rule (pardon the pun) does this:
$(CXX) $(LIBRARIES) -o main.exe $(objects) glad.o $(LINK_FLAGS)
There are a number of changes I'd make to that line, but one is to consolidate $(LIBRARIES)
and $(LINK_FLAGS)
into a single variable with a standard name, which is LDFLAGS
.
Use predefined variables
Try to avoid repeating hard-coded names in the production rules. For example, the rule for main.exe
could be much simplified to this:
$(CXX) -o $@ $^ $(LDFLAGS)
Add a clean
psuedotarget
It's often useful (not least when testing makefiles!) to provide a clean
psuedotarget to remove all of the files that were generated, providing a "clean slate" from which to start. It might be written like this:
.PHONY : clean
clean :
-rm main.exe $(objects) glad.o
This also makes it less likely that you'll want to have the makefile itself as a dependency, which makes the set of rules cleaner. This would work even if the first line were omitted, unless there were actually a file named clean
. To tell the makefile that it's not a real file, we use the line .PHONY : clean
.
Add glad.o
to objects
If you add glad.o
to the objects
list, you can simplify the rules.
Consider isolating TARGET
For Windows, executables typically end in .exe
but not so for most other platforms. For that reason you could either have a specific variable for the executable extension or keep the target name in a variable:
TARGET = main.exe
Putting it all together
If we take all of these suggestions and apply them, the resulting files looks like this:
CC = gcc
CXX = g++
INCLUDES = -I$(glfw_inc) -I$(glad_inc)
LIBRARIES = -L$(glfw_lib)
glfw = d:/external/glfw-3.1
glfw_inc = $(glfw)/include
glfw_lib = $(glfw)/lib64
glad = d:/external/glad-c
glad_inc = $(glad)/include
CFLAGS = -Wall -ggdb -O3 $(INCLUDES)
CXXFLAGS = -Wall -ggdb -O3 $(INCLUDES)
LDFLAGS = $(LIBRARIES) -lglfw3 -lopengl32 -lglu32 -lgdi32
TARGET = main.exe
cpp_files = main.cpp
objects = $(cpp_files:.cpp=.o) glad.o
headers =
all: $(TARGET)
$(TARGET): $(objects)
$(CXX) -o $@ $^ $(LDFLAGS)
.PHONY : clean
clean :
-rm $(TARGET) $(objects)