aBarocio80
aBarocio80

Reputation: 966

How can I pass a rule pattern in a makefile to filter dependencies?

I have a rule in my project to generate libraries from sourcecode. I already have the function to compile %.c to %.o, but I split my library code in multiple source files that begin with the same prefix. I have two separate library code in the same directory, but their source files have different prefixes, that's why I am trying to build a single rule for both (or maybe more) libraries. But I can't pass the prefix of the codebase to the dependencies to filter the object files needed.

The rule I have is this in my Makefile:

# ...

BINDIR = bin
LIBDIR = lib

# ...

# These are all the libraries source files.
LIB_SOURCES = $(wildcard $(LIBDIR)/*.c)
# These are all the libraries "main" source files.
LIB_SRCS = $(filter-out $(wildcard $(LIBDIR)/*_*.c), $(LIB_SOURCES))
# These are all the source files to which I have exported some code from the "main" library source file.
LIB_CODE = $(filter-out $(LIB_SRCS), $(LIB_SOURCES))
# These are all the objects produced by compiling the c source files.
LIB_OBJS = $(patsubst %.c, %.o, $(LIB_CODE))

# ...

# These are all the libraries produced.
LIBS = $(patsubst $(LIBDIR)/%.c,$(BINDIR)/lib%.so,$(LIB_SRCS))

# ...

.PHONY: libs

# ...

%/:
    mkdir -p $@

libs: $(BINDIR)/ $(LIBS)
.SECONDEXPANSION:
$(BINDIR)/lib%.so: $(LIBDIR)/%.o $(filter $(LIBDIR)/$*_%.o, $(LIB_OBJS))
    @echo $(CC) $(CFLAGS) $(CLNKERFLAGS) -o $@ $^

$(LIBDIR)/%.o: $(LIBDIR)/%.c $(LIBDIR)/%.h
    @echo $(CC) $(CFLAGS) -o $@ -c $<

For the moment I just print the (incomplete) command, but it still doesn't get the correct output with all the objects needed.

In the directory lib I have the following files:

$ tree lib
lib
├── app.c
├── app_db.c
├── app_db.h
├── app.h
├── app_logic.c
├── app_logic.h
├── app_net.c
├── app_net.h
├── server.c
├── server.h
├── server_queue.c
└── server_queue.h

But it never builds the dependencies correctly.

$ make libs
gcc -Wpedantic -O3 -o lib/app.o -c lib/app.c
gcc -Wpedantic -O3 -o bin/libapp.so lib/app.o
gcc -Wpedantic -O3 -o lib/server.o -c lib/server.c
gcc -Wpedantic -O3 -o bin/libserver.so lib/server.o

I have read the following question How to use pattern-dependent variables in dependencies in make pattern rules, which had a tip I thought it would help me, but it didn't.

Any idea how can I achive this?

EDIT 1:

I want the output of the last command to be:

$ make libs
gcc -Wpedantic -O3 -o lib/app.o -c lib/app.c
gcc -Wpedantic -O3 -o lib/app_db.o -c lib/app_db.c
gcc -Wpedantic -O3 -o lib/app_logic.o -c lib/app_logic.c
gcc -Wpedantic -O3 -o lib/app_net.o -c lib/app_net.c
gcc -Wpedantic -O3 -o bin/libapp.so lib/app.o lib/app_db.o lib/app_logic.o lib/app_net.o
gcc -Wpedantic -O3 -o lib/server.o -c lib/server.c
gcc -Wpedantic -O3 -o lib/server_queue.o -c lib/server_queue.c
gcc -Wpedantic -O3 -o bin/libserver.so lib/server.o lib/server_queue.o

Thanks for your attention.

Upvotes: 0

Views: 353

Answers (2)

MadScientist
MadScientist

Reputation: 101051

You added .SECONDEXPANSION, but you didn't escape anything in the prerequisites list so it doesn't actually do anything:

$(BINDIR)/lib%.so: $(LIBDIR)/%.o $(filter $(LIBDIR)/$*_%.o, $(LIB_OBJS))

None of these variables/functions are escaped, so everything here is expanded during the initial read-in, so there's nothing for secondary expansion to do.

The secondary expansion feature consists of two parts: first, you enable it with the special target. Second, you escape the variables and/or functions you want to delay expansion of. So this could be:

getobjs = $(filter $(LIBDIR)/$*_%.o, $(LIB_OBJS))

$(BINDIR)/lib%.so: $(LIBDIR)/%.o $$(getobjs)
        @echo $(CC) $(CFLAGS) $(CLNKERFLAGS) -o $@ $^

Note how we escape the $(getobjs) as $$(getobjs) so that this variable is not expanded until the second pass.

Upvotes: 1

Beta
Beta

Reputation: 99164

Make isn't very deft with wildcards, and your approach requires it to handle two different wildcards in one line. If that's possible at all, it will be a terrible thing to see. I suggest a different approach.

First, your variables are wrong. It's not clear what your intention was, but here are the ones that fit your desired results:

LIB_SOURCES := $(wildcard $(LIBDIR)/*.c)
LIB_OBJS := $(patsubst %.c,%.o,$(LIB_SOURCES))

Now, you want your makefile to act as if it had these two rules:

$(BINDIR)/libapp.so: $(filter $(LIBDIR)/app%,$(NEW_LIB_OBJS))
    @echo build $@ somehow from $^

$(BINDIR)/libserver.so: $(filter $(LIBDIR)/server%,$(NEW_LIB_OBJS))
    @echo build $@ somehow from $^

But rather than spelling them out in the makefile, you want Make to build them at run time. So we'll use a "canned recipe":

define librule
$(BINDIR)/lib$(1).so: $(filter $(LIBDIR)/$(1)%,$(LIB_OBJS))
    @echo building $$@ somehow from $$^
endef

$(eval $(call librule,app))
$(eval $(call librule,server))

Then rather than writing those last two lines in the makefile, specifying app and server, we can have Make extract them from LIBS:

LIB_NAMES := $(patsubst $(BINDIR)/lib%.so,%,$(LIBS))
$(foreach libname,$(LIB_NAMES),$(eval $(call librule,$(libname))))

Upvotes: 0

Related Questions