I like an uncluttered top-level directory in my C/C++ projects: a Makefile, README.md, maybe a LICENSE file, src directory for code and internal headers, includes directory for public-facing header files, deps for generated make dependency files, objs for object files, and test for unit test code and makefile. See my GitHub project, C-Plus-Plus-Template, for an example.
When my projects are initially cloned to a system, or following a clean, the deps and objs directories do not exist. Their contents are generated as needed but not part of the project.
The recipe for clean is:
clean: rm -rf deps objs $(PGM) make -C test clean
I thought I had things nice and tidy, but discovered an irritating bug in my Makefile: modules were being recompiled when no source had changed. Not a big deal for toy projects, but a disaster for a big one.
Below are the targets the Makefile used for building. WARNING – ERROR BELOW
SRC = $(shell ls src/*.cpp) OBJS = $(SRC:src/%.cpp=objs/%.o) all: $(PGM) -include deps/* $(OBJS): objs deps objs: mkdir objs deps: mkdir deps objs/%.o: src/%.cpp g++ $(MY_CFLAGS) -MMD -MF"$(@:objs/%.o=deps/%.d)" -o"$@" "$<" $(PGM): $(OBJS) g++ -o $@ $(OBJS) $(MY_LDFLAGS)
After a long period of staring, I finally figured out the culprit: directory objs should not be an intermediate target of $(OBJS) or $(PGM), because its “last modified” date changes when object files are added or removed. Ditto for directory deps.
The revised and better approach is below. (DO IT THIS WAY INSTEAD.)
SRC = $(shell ls src/*.cpp) OBJS = $(SRC:src/%.cpp=objs/%.o) all: $(PGM) -include deps/* objs/%.o: src/%.cpp @[ -d objs ] || mkdir objs @[ -d deps ] || mkdir deps g++ $(MY_CFLAGS) -MMD -MF"$(@:objs/%.o=deps/%.d)" -o"$@" "$<" $(PGM): $(OBJS) g++ -o $@ $(OBJS) $(MY_LDFLAGS)
That is, the Makefile uses shell commands to create directories objs and deps if necessary. This way, their modification times will not figure into the decision to compile.