1
votes

I'm writing a makefile for handling the dependency of build units ONE and TWO -> build unit LIB.

By "build unit" I mean a directory containing directories src, lib, include and bin, as well as a makefile for compiling the source code in src. Libraries are placed in "lib" and library header-files in "include". The compiled binary is placed in "bin". A build unit accepts "make", "make all", "make lint" and "make clean".

When a change is made to a header-file in LIB, this makefile is meant to detect it and recompile + install (copy .a+.h-files to ONE and TWO) the new version of LIB before compiling.

.PHONY: ONE TWO CLEAN LINT

ALL: ONE TWO

%.a %.h:
    @echo ---------- Compiling LIB ----------
    @cd LIB && gmake.exe LIB

LIB_HEADERS := $(wildcard LIB/src/*.h)

ONE: ONE/lib/libLIB.a $(subst LIB/src/,ONE/include/,$(LIB_HEADERS))
    @echo ---------- Compiling $@ ----------
    @cd $@ && gmake.exe

TWO: TWO/lib/libLIB.a $(subst LIB/src/,TWO/include/,$(LIB_HEADERS))
    @echo ---------- Compiling $@ ----------
    @cd $@ && gmake.exe

CLEAN LINT:
    @cd ONE && gmake.exe $@
    @cd TWO && gmake.exe $@
    @cd LIB && gmake.exe $@

(Assume the makefile for LIB handles copying into ONE and TWO)

  1. How come make does not run the rule "%.a %.h:" when I make a change to one of the header-files in LIB/src?
  2. How do I generalize ONE and TWO into a single rule? I want to do this (but a target cannot be used in dependencies, at least this way):

    ONE TWO: $@/lib/libLIB.a $(subst LIB/src/,$@/include/,$(LIB_HEADERS))
        @echo ---------- Compiling $@ ----------
        @cd $@ && gmake.exe
    

UPDATE:

I found the solution by taking a step back, drawing Directed Acyclic Graphs, and thinking in terms of single files, instead of all files of a certain type.

Here is the (not very elegant) solution, for completeness:

.PHONY: ONE TWO CLEAN LINT

ALL: ONE TWO

LIB_HEADERS := $(sort $(subst name.h,,$(wildcard LIB/src/*.h)))

# ------------------------------------------------------------
ONE/include/%.h: LIB/src/%.h
    @echo Copying $< to $@
    @mkdir ONE\\include 2> NUL || :)
    @copy $(subst /,\,$<) ONE\\include\\ 1> NUL

TWO/include/%.h: LIB/src/%.h
    @echo Copying $< to $@
    @mkdir TWO\\include 2> NUL || :)
    @copy $(subst /,\,$<) TWO\\include\\ 1> NUL
# ------------------------------------------------------------
ONE/lib/liblib.a: LIB/bin/liblib.a
    @echo Copying $< to $@
    @mkdir ONE\\lib 2> NUL || :)
    @copy $(subst /,\,$<) ONE\\lib\\ 1> NUL

TWO/lib/liblib.a: LIB/bin/liblib.a
    @echo Copying $< to $@
    @mkdir TWO\\lib 2> NUL || :)
# Windows-equivalent of touch (discarding any output to stdout):
    @copy $(subst /,\,$<) TWO\\lib\\ 1> NUL
# ------------------------------------------------------------
LIB/bin/liblib.a: $(LIB_HEADERS) $(wildcard LIB/src/*.cpp)
    @echo ---------- Looking for changes to liblib ----------
    @cd LIB && gmake.exe LIB
    @copy /b $(subst /,\,$@) +,, 1> NUL || :)
# ------------------------------------------------------------
ONE: ONE/lib/liblib.a $(subst LIB/src/,ONE/include/,$(LIB_HEADERS))
    @echo ---------- Compiling ONE ----------
    @cd ONE && gmake.exe

TWO: TWO/lib/liblib.a $(subst LIB/src/,TWO/include/,$(LIB_HEADERS))
    @echo ---------- Compiling TWO ----------
    @cd TWO && gmake.exe
# ------------------------------------------------------------
CLEAN LINT:
    @cd ONE && gmake.exe $@
    @cd TWO && gmake.exe $@
    @cd LIB && gmake.exe $@

I very much welcome tips on how to generalize ONE and TWO further.

2

2 Answers

0
votes

For question 1 : Maybe you need to move the LIB_HEADERS definition up and change the rule to
%.a %.h: $(LIB_HEADERS) ?

0
votes

I'm sorry to have to tell you this, but your build system is a monstrosity. You are attempting to do sophisticated dependency handling by means of extravagant use of recursive Make; recursive Make has its uses, but one of its drawbacks is that it defeats Make's native ability to handle dependencies. There are other problems that indicate that the author of this system didn't really understand how Make works, or what a good makefile should look like.

The reason Make doesn't run this %.a %.h: rule when you change a header file in LIB/src/ is that this rule has no dependencies, and nothing in this makefile depends on a header file that doesn't exist. If that makes no sense to you, then you don't understand how Make rules work.

These rules:

ONE: ONE/lib/libLIB.a $(subst LIB/src/,ONE/include/,$(LIB_HEADERS))
    @echo ---------- Compiling $@ ----------
    @cd $@ && gmake.exe

TWO: TWO/lib/libLIB.a $(subst LIB/src/,TWO/include/,$(LIB_HEADERS))
    @echo ---------- Compiling $@ ----------
    @cd $@ && gmake.exe

can be combined into one rule:

ONE TWO: % : %/lib/libLIB.a $(addprefix %/include/,$(notdir $(LIB_HEADERS)))
    @echo ---------- Compiling $@ ----------
    @cd $@ && gmake.exe

But don't do this. Instead, eliminate ONE/include/ and TWO/include/, since they bring nothing but headaches. Then the rule here can be

ONE TWO: % : %/lib/libLIB.a $(LIB_HEADERS)
    @echo ---------- Compiling $@ ----------
    @cd $@ && gmake.exe

and the makefiles in ONE/ and TWO/ can refer to LIB/src/.