Pergunta

I am trying to setup a Makefile to build either static (.a) and dynamic (.so) libraries depending on the target file extension.

I previously used the following Makefile for static libraries only :

NAME   := config
LIB    := lib$(NAME).a
SRC    := $(wildcard *.c)
OBJ    := $(SRC:.c=.o)
CFLAGS += -W -Wall

.PHONY: all clean fclean re

all: $(LIB)

clean:
    @$(RM) $(OBJ)

fclean: clean
    @$(RM) $(LIB)

re: fclean all

$(LIB): $(LIB)($(OBJ))
    ranlib $@

My main goal is to be able to compile multiple libraries by only changing the LIB and NAMEvariables.

Everything worked fine, so I added the following for the dynamic libraries :

LDFLAGS += -shared

%.so: CFLAGS += -fPIC
%.so: $(OBJ)
    $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^

And changed the $(LIB): rule by the following generic rule :

%.a: %.a($(OBJ))
    ranlib $@

If I change LIB to lib$(NAME).so everything works as expected, but with the .a extension, make prints me this error :

make: *** No rule to make target 'libconfig.a', needed by 'all'. Stop.

The only solution I found was to add another explicit rule like that :

%.a($(OBJ)): $(OBJ)
    $(AR) rv $@ $^

And now everything works.

But adding this explicit rule prevents me from relying only upon GNU make's implicit rules, and makes me call explicitly ar, which I wanted to avoid.

Is this some kind of bug or am I missing something ?

GNU Make 3.82
Built for x86_64-unknown-linux-gnu
Foi útil?

Solução 3

Archives and shared libraries don't share the same prerequisites syntax, so it is not possible to have a single rule that handle both.

The simplest solution is to use a conditional on the target extension:

ifeq "$(suffix $(LIB))" ".a"
$(LIB): $(LIB)($(OBJ))
else ifeq "$(suffix $(LIB))" ".so"
$(LIB): override CFLAGS += -fPIC
$(LIB): $(OBJ)
    $(CC) $(LDFLAGS) $^ $(LDLIBS) -o $@
endif

A working Makefile that meets the requirements looks now like this:

NAME := config

LIB := lib$(NAME).so

SRC := $(wildcard *.c)
OBJ := $(SRC:.c=.o)
DEP := $(OBJ:.o=.d)

CPPFLAGS := -MMD -MP
CFLAGS   := -W -Wall
LDFLAGS  := -shared
ARFLAGS  := rs

.PRECIOUS: $(OBJ)
.PHONY: all clean fclean re

all: $(LIB)

clean:
    $(RM) $(OBJ) $(DEP)

fclean: clean
    $(RM) $(LIB)

re: fclean all

ifeq "$(suffix $(LIB))" ".a"
$(LIB): $(LIB)($(OBJ))
else ifeq "$(suffix $(LIB))" ".so"
$(LIB): override CFLAGS += -fPIC
$(LIB): $(OBJ)
    $(CC) $(LDFLAGS) $^ $(LDLIBS) -o $@
endif

ifeq "$(MAKECMDGOALS)" ""
-include $(DEP)
endif

Note that the ARFLAGS variable controls which flags are passed to the invocation of ar. Here I use the r flag to replace existing object if it exists and the s flag to build or update the index (ranlib is not necessary anymore with this).

Outras dicas

Some quick testing seems to indicate that this can't be done. It appears that the specialized make archive support is a makefile parse time feature. That is the literal archive name must exist in the actual makefile for it to take effect.

I tried a couple of workarounds but wasn't able to get any of them to work correctly. The closest I could manage was:

$(foreach a,$(filter %.a,$(MAKECMDGOALS) $(.DEFAULT_GOAL)),$(eval $a: $a($$(OBJ)); ranlib $$@))

which doesn't work for a default goal of all but would if the default was the library name and/or if the library name is an explicit make target. You could stick any other known library names in there too and then they should work even as implicit requirements of other targets but that's a manual process.

You need to tell make which .o files a given .a file depends on. You can do that with a simple dpendency with no action:

libconfig.a: libconfig.a($(OBJ))

make will then invoke the default rule for putting .o files into a .a file to actually build libconfig.a

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top