Home > Back-end >  Subdirectories and Makefile?
Subdirectories and Makefile?

Time:03-09

I am struggling with a C project having multiple subdirectories where the .o files should reside in a subdirectory "obj/". Like this:

/hzg/src/
/hzg/src/adaemon
/hzg/src/adaemon/obj
/hzg/src/bdaemon
/hzg/src/bdaemon/obj
/hzg/src/notrelateddir

C files are in *daemon/, matching .o files in *daemon/obj. A main Makefile should reside in /hzg/src and there should be a Makefile in every /hzg/src/*daemon

I have defined my $(SOURCES) and derived the $(OBJECTS) with $(subst...).

What I am struggling with is to get the prerequisites in a rule properly defined...

Here's what I have so far:

(Sub-)Makefile in adaemon:

[...]
SOURCES         =       $(wildcard *.c)
OBJECTS         =       $(patsubst %.c, obj/%.o, $(SOURCES))

all: $(OBJECTS)

$(OBJECTS):  obj/%.o : %.c $(HEADERS)
        $(CC) $(CLFAGS) -c $< -o $@

This works like a charm. What I am struggling with is the "master" Makefile. How can I tell make to take the .o from the obj/ directory and search for the matching .c files?

Makefile

[...]
SUBDIRS         :=      common:hzgd:masterd:mqttd:

SOURCES         =       $(wildcard $(subst :,/*.c , $(SUBDIRS)))
OBJECTS         =       $(subst /,/obj/,$(patsubst %.c,%.o,$(SOURCES)))

BIN             =       hzgmasterd 

all             :       $(BIN)

$(BIN)          :       $(OBJECTS)
        $(CC) $(CFLAGS) -rdynamic -o $(BIN) $(OBJECTS) $(LIBS)
        @echo "Server done."

$(OBJECTS)      :       obj/%.o : %.c $(HEADERS)
        $(CC) $(CFLAGS) -c $< -o $@

Oh, I would not mind using the "make -C" command but in this case it should only descend into the directory when an .o file needs to be recompiled.

CodePudding user response:

Here is one way to structure your top-level makefile:

SUBDIRS := common hzgd masterd mqttd

SOURCES := $(wildcard $(addsuffix /*.c ,$(SUBDIRS)))
OBJECTS := $(foreach D,$(SUBDIRS),$(patsubst $D/%.c,$D/obj/%.c,$(SOURCES)))

BIN      =  hzgmasterd 

all : $(BIN)

$(BIN) : $(OBJECTS)
        $(CC) $(CFLAGS) -rdynamic -o $@ $^ $(LIBS)
        @echo "Server done."

$(OBJECTS): $(SUBDIRS) ;

$(SUBDIRS):
        $(MAKE) -C $@

.PHONY: $(SUBDIRS)

I didn't actually try this but I think it will work. By adding the semicolon these are actual rules and make will check the timestamps on the targets.

If you wanted to create a library in each subdirectory you'd define a rule that did that, probably naming the library after the directory it was in, that listed all the objects as prerequisites, then in this makefile you'd list one library per subdirectory as the prerequisites.

CodePudding user response:

thanks @MadScientist. Some minor glitches and types (ie foreach did not work) and I have a working solutions now.

"Master"-Makefile:

SUBDIRS     :=  common hzgd masterd mqttd
CSUBDIRS    :=  client

CFLAGS      =   -Wall
LIBS        :=  -lconfig -lcurl -ljson-c -lm -lmodbus -lmosquitto -lownet -lpthread -lsystemd -lical

SOURCES     :=  $(wildcard $(addsuffix /*.c ,$(SUBDIRS)))
OBJECTS     :=  $(subst /,/obj/,$(subst .c,.o,$(SOURCES)))
HEADERS     :=  $(wildcard *h)

CSOURCES    :=  $(wildcard $(addsuffix /*.c ,$(CSUBDIRS)))
COBJECTS    :=  $(subst /,/obj/,$(subst .c,.o,$(CSOURCES)))
CHEADERS    :=  $(wildcard client/*h)

BIN         :=  hzgmasterd
CBIN        :=  client/hzgclnt

all     :   $(BIN) $(CBIN)
server      :   $(BIN)
client      :   $(CBIN)

#====================== Master ============================
$(BIN)      :   $(OBJECTS)
    $(CC) $(CFLAGS) -rdynamic -o $@ $^ $(LIBS)
    @echo "Server done."

$(OBJECTS)  :   $(SUBDIRS) ;

.PHONY: $(SUBDIRS)
$(SUBDIRS)  :
    $(MAKE) -C $@


#====================== Client ============================
.PHONY: $(CBIN)
$(CBIN)     :   $(CSUBDIRS)/
    $(MAKE) -C $<


#====================== Aufräumen ============================
.PHONY      :   clean
clean       :
    rm -f $(BIN) $(OBJECTS) $(COBJECTS)

"Sub"-Makefiles:

[...]
SOURCES     =   $(wildcard *.c)
HEADERS     =   $(wildcard ../*h)
OBJECTS     =   $(patsubst %.c, obj/%.o, $(SOURCES))

all: $(OBJECTS)
#==================================================
$(OBJECTS):  obj/%.o : %.c $(HEADERS)
    $(CC) $(CLFAGS) -c $< -o $@

#====================== Aufräumen ============================
.PHONY: clean
clean:
    rm -f *.o obj/*.o

A minor glitch is now make descends into every subdirectory and executes another make process to decide if the target is up to date. I understand this is by design even though it will cause additional steps.... however, it is working now even though I do not understand everything in the Makefile you posted.

Thanks a lot! /Christian

  • Related