Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Makefile -- compile only modified C++ files

This is my current makefile

CFLAGS = -Iheaders/
CC = g++

PROGRAM_NAME = sportsmanager
rwildcard    = $(wildcard $1$2) $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2))
SOURCES      = $(call rwildcard,sources/,*.cpp)
OFILES       = $(call rwildcard,obj-tmp/,*.o)
OBJDIR       = obj-tmp/

compileAndRun: 
    make -s compile && make -s $(PROGRAM_NAME)
    ./$(PROGRAM_NAME)

compile: $(SOURCES)
    mkdir -p $(OBJDIR)
    $(CC) $(CFLAGS) -c $(SOURCES) && mv *.o $(OBJDIR)

$(PROGRAM_NAME): $(OFILES)
    $(CC) $(CFLAGS) $(OFILES) -o $(PROGRAM_NAME)

Whenever I run $ make, target compile is triggered which compiles all .cpp files in directory sources/ to .o files which are then moved to obj-tmp/. Then the target $(PROGRAM_NAME) is triggered, which links all the .o files and outputs the executable file.

The problem is that all files are compiled each time I run make. What ideally should happen if I run 'make' twice in succession is that make should know that the program is up to date the second time. If I modify only one file, only that file should be compiled.

Heads up: I know that there exists similar questions regarding this, but I've yet to see a solution which works in conjunction with the above makefile.

Any input is greatly appreciated.

like image 1000
Audun Olsen Avatar asked Apr 01 '26 23:04

Audun Olsen


2 Answers

The whole point of make is to compile only those files which have been modified since the last build. The problem in your makefile is that your compile recipe has the $(SOURCES) variable as a dependency. As in, all the source files.

I would use vpath to organize the project folder like so:

vpath %.cpp src
vpath %.h include 

This will tell make to look for c++ files in ./src and header files in ./include. Then, you can simplify your recipe for individual files like this:

%.o: %.cpp
    $(CC) $(CFLAGS) -c -o $@ $<

Having done this, you can now define an $(OBJECTS) variable with a wildcard that matches .o files and continue from there. As an aside, moving your object files into a separate folder is considered bad practice and I agree; it really adds nothing substantial of value but complicates recipes.

Remember that object files represent a dependency for the $(PROGRAM) recipe. So naturally, make looks for the necessary object files to see if they need to be rebuilt. If they've been moved, one of two things happens. Either make will determine that they don't exist and will rebuild all the object files again from scratch, thereby invalidating the very reason we use make in the first place, or you'll have to define a folder where the object files will live, and every time you handle wildcards, searches, etc., literally anything that has to do with the object files, you'll have to take this added complexity into account.

I agree that having a ton of object files in the project folder can be a little annoying, but it definitely beats waiting forever for the project to compile. Just remember to add *.o to your .gitignore or whatever source control platform you use and they'll be nothing more than an eyesore, while make will be that much easier to use.


To answer your question on handling subdirectories in the source folder, the answer is a little more complicated.

Rather than using the specific vpath <pattern> <folder> directive as above, you could just outright use the VPATH variable like this:

VPATH = include src src/sub

This would handle the job, but the first method is usually preferred because when using VPATH, make searches every directory every time when looking for a file, rather than being location-constrained by file extension.

It is possible to use make to conveniently manage large projects though, and it involves calling make itself recursively, writing makefiles for each module in the build process. This process is obviously much more complicated, and I would strongly recommend considering whether the project genuinely necessitates this, as any potential gains in build-process modularization may not be recuperated due to the complexity involved in implementation.

I'd like to point you to this and this, both of which are phenomenal resources on makefiles.

like image 113
Jose Fernando Lopez Fernandez Avatar answered Apr 03 '26 14:04

Jose Fernando Lopez Fernandez


  1. Change the dependency of compile to be the object files.
  2. Add a pattern rule for the object files.

compile: $(OFILES)

$(OBJDIR)/%.o: sources/%.cpp
    mkdir -p $(OBJDIR)
    $(CC) $(CFLAGS) -c $< -o $@
like image 44
R Sahu Avatar answered Apr 03 '26 16:04

R Sahu



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!