Holt
Holt

Reputation: 37626

Build (make) lib every time, recompile project only if lib is newer

I have the following project structure:

lib/
    Makefile
    src/...
    inc/...
    build/
        inc/...
        lib/libmylib.a
subproj1/
    src/main.cpp
    Makefile 

The Makefile in the lib folder is designed to create the file libmylib.a and copy the relevant header files to the build/inc folder.

I want the Makefile in subproj1 to always call make -C ../lib, but only re-compile file if headers have changed, and re-link only if necessary (one object file or libmylib.a is newer).

I have the following (non-defined variables such as CC are defined in another file):

LIBDIR = ../lib
SRCDIR = src
OBJDIR = obj
SRCS = $(SRCDIR)/main.cpp

MAIN=myexe

OBJS = $(SRCS:$(SRCDIR)/%.cpp=$(OBJDIR)/%.o)
DEPS = $(OBJS:.o=.d)

all: $(MAIN)

debug: CFLAGS += -g -DDEBUG
debug: LFLAGS += -g
debug: $(MAIN)

$(MAIN): $(OBJS) $(LIBDIR)/build/lib/libmylib.a
    $(CC) $^ -o $@ $(LIBS) $(LFLAGS)

$(OBJDIR)/%.o: $(SRCDIR)/%.cpp $(LIBDIR)/build/lib/libmylib.a
    mkdir -p $(OBJDIR)
    $(CC) -c -o $@ $(CFLAGS) $(INCS) -MD -MF $(patsubst %.o, %.d, $@) $<

$(LIBDIR)/build/lib/libmylib.a:
    make -C $(LIBDIR)

-include $(DEPS)

.PHONY: clean $(LIBDIR)/build/lib/libmylib.a

clean:
    $(RM) obj/* $(MAIN)

The above will re-compile main.cpp even if nothing has changed in the lib folder. If I remove $(LIBDIR)/build/lib/libmylib.a from the $(OBJDIR)/%.o rule, the .cpp file will not be re-compiled if a header changed (I would need to run make twice).

Is there a way to have the .cpp files in subproj1 being compiled only if the header files in lib have changed (or if the .cpp files themselves have changed), and to get myexe built only if one of the .cpp has been re-compiled (newer .o) or if libmylib.a is newer?

Upvotes: 2

Views: 294

Answers (2)

blackghost
blackghost

Reputation: 1825

This might be a bit cleaner.

LIBDIR = ../lib
SRCDIR = src
OBJDIR = obj
SRCS = $(SRCDIR)/main.cpp

MAIN=myexe

OBJS = $(SRCS:$(SRCDIR)/%.cpp=$(OBJDIR)/%.o)
DEPS = $(OBJS:.o=.d)

all: makelib
    $(MAKE) $(MAIN)

debug: CFLAGS += -g -DDEBUG
debug: LFLAGS += -g
debug: $(MAIN)

$(MAIN): $(OBJS)
    $(CC) $^ -o $@ $(LIBS) $(LFLAGS)

$(OBJDIR)/%.o: $(SRCDIR)/%.cpp
    mkdir -p $(OBJDIR)
    $(CC) -c -o $@ $(CFLAGS) $(INCS) -MD -MF $(patsubst %.o, %.d, $@) $<

makelib:
    make -C $(LIBDIR)

-include $(DEPS)

.PHONY: clean makelib

clean:
    $(RM) obj/* $(MAIN)

The main target all requires makelib to be built, and then it recursively calls itself for the target $(MAIN) (so all of $(MAIN)'s dependencies will be recalculated after makelib has finished building).

Upvotes: 0

Maxim Egorushkin
Maxim Egorushkin

Reputation: 136355

With recursive makefiles you need to execute the sub-project makefiles in correct order because the dependency tree is incomplete (e.g. this makefile does not know that updating $(LIBDIR)/build/lib/libmylib.a also updates those headers). It is easy to do that with a shell script or a top-level makefile.

Alternatively, your makefile must execute the sub-makefiles unconditionally in correct order, which can be done with shell function, e.g.:

LIBDIR := ../lib
pid := $(shell ps -o ppid= $$$$)
$(shell ${MAKE} -C ${LIBDIR} >/proc/$(pid)/fd/1 2>/proc/$(pid)/fd/2)

That $(LIBDIR)/build/lib/libmylib.a rule should be removed, the object files should not depend on the .a and it should not be marked as .PHONY.

This makes sure that building in ${LIBDIR} happens before this makefile analyzes file timestamps in ${LIBDIR}.

Now your auto-generated header dependencies should just work.

Upvotes: 1

Related Questions