About once a week I get an email from someone random asking a question about GNU Make. I do my best to answer and figured it might be helpful for others if I shared these questions and my answers. So, here goes. This one starts with a Princess Leia style appeal:
I stumbled across your name when googling for help on a GNUmake related problem, hope you have the time to look at it as you are my last hope. I have the following problem in my Makefile:
$(LIBDIR)/libfoo.a: $(filter $(OBJDIR)/foo/%,$(OBJECTS))
$(LIBDIR)/libbar.a: $(filter $(OBJDIR)/bar/%,$(OBJECTS))
$(LIBDIR)/libbaz.a: $(filter $(OBJDIR)/baz/%,$(OBJECTS))
$(LIBDIR)/%.a:
ar -r [email protected] $^
ranlib [email protected]
so far, so good - it works as expected. The only problem is, that in the real world it's not just foo, bar and baz for me and I hate having to maintain the list manually. What I would like to do is something like this:
$(LIBDIR)/lib%.a: $(filter $(OBJDIR)/%/%,$(OBJECTS))
ar -r [email protected] $^
ranlib [email protected]
but now the pattern for filter has two percentage-characters and this seem to confuse GNUmake. I tried to escape it, use $(call …), etc. but nothing really works. Do you have any trick/hint/idea how to solve this??
Thanks for your time, Best Regards,
And my reply:
Thanks for your mail. I can see what you are trying to do. I think the easiest way out of your predicament is as follows:
# Example of manually maintained list
#
# $(LIBDIR)/libfoo.a: $(filter $(OBJDIR)/foo/%,$(OBJECTS))
# $(LIBDIR)/libbar.a: $(filter $(OBJDIR)/bar/%,$(OBJECTS))
# $(LIBDIR)/libbaz.a: $(filter $(OBJDIR)/baz/%,$(OBJECTS))
#
# $(LIBDIR)/%.a:
# ar -r [email protected] $^
# ranlib [email protected]
# What you'd like to be able to do
#
# $(LIBDIR)/lib%.a: $(filter $(OBJDIR)/%/%,$(OBJECTS))
# ar -r [email protected] $^
# ranlib [email protected]
# The following will work
#
# Suppose, for example:
LIBDIR := lib
OBJDIR := obj
OBJECTS := $(OBJDIR)/foo/hello.o $(OBJDIR)/foo/bar.o $(OBJDIR)/bar/hello.o \
$(OBJDIR)/baz/bar.o
# Extract the names of the first level directories underneath
# $(OBJDIR) and make a unique list (sort removes duplicates) and store
# that in LIBNAMES. This assumes that there are no spaces in any of
# the filenames in $(OBJECTS) and that each element of $(OBJECTS)
# starts with $(OBJDIR)
LIBNAMES := $(sort $(foreach a,$(patsubst $(OBJDIR)/%,%,$(OBJECTS)),\
$(firstword $(subst /, ,$a))))
# The following function finds all the objects in $(OBJECTS) in a
# particular directory whose name can be found in LIBNAMES. So, in
# the example here doing $(call find-objects,foo) would return
# obj/foo/hello.o obj/foo/bar.o
find-objects = $(filter $(OBJDIR)/$1/%,$(OBJECTS))
# Now need to define the rule that handles each of the libraries
# mentioned in $(LIBNAMES). This function can be used with $(eval) to
# define the rule for a single directory
define make-library
$(LIBDIR)/lib$1.a: $(call find-objects,$1)
ar -r [email protected] $$^
ranlib [email protected]
endef
# Now define the rules for all the directories found in $(LIBNAMES)
$(foreach d,$(LIBNAMES),$(eval $(call make-library,$d)))
You will need a version of GNU Make that supports $(eval).
1 comment:
Thanks JGC!
Post a Comment