A makefile faux-pas

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.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s