Reputation: 3452
I have a pattern rule like this:
$(BIN_DIR)/%: $(SOURCES_DIR)/%/*.c
$(CC) $? $(CFLAGS) $(LDFLAGS) -o $@ $(LDLIBS) $(LIBS)
Is it possible to do something like:
$(BIN_DIR)/%: $(SOURCES_DIR)/%.c OR $(SOURCES_DIR)/%/*.c
$(CC) $? $(CFLAGS) $(LDFLAGS) -o $@ $(LDLIBS) $(LIBS)
Where OR
means if there the target $(BIN_DIR)/progname
requsted, first look for $(SOURCEDS_DIR)/progname.c
and it will be the only prerequisite. If there is no such file, prerequisites list will be $(SOURCES_DIR)/progname/*.c
as it is now in the original rule? Maybe some other way to achieve this behaviour?
Full Makefile (tests ommited as irrelevant):
SOURCES_DIR = src
INCLUDE_DIR = $(SOURCES_DIR)/include
OBJECTS_DIR = build
BIN_DIR = bin
LIB_DIR = lib
INCLUDE_FLAGS = -I$(INCLUDE_DIR)
CFLAGS=-g --std=c99 -Wall -Wextra -O2 $(INCLUDE_FLAGS)
LDLIBS=-ldl -lm $(OPTLIBS)
LIB_SOURCES=$(wildcard $(SOURCES_DIR)/lib*)
BIN_SOURCES=$(filter-out $(INCLUDE_DIR) $(LIB_SOURCES),\
$(patsubst %.c,%,$(wildcard $(SOURCES_DIR)/*)))
BINS=$(addprefix $(BIN_DIR)/, $(notdir $(BIN_SOURCES)))
OBJECTS=$(patsubst %.c,%.o,\
$(subst $(SOURCES_DIR),$(OBJECTS_DIR),\
$(wildcard $(SOURCES_DIR)/lib**/*.c)))
LIBS=$(addsuffix .a, $(addprefix $(LIB_DIR)/, $(notdir $(LIB_SOURCES))))
SO_LIBS=$(patsubst %.a,%.so,$(LIBS))
all: $(OBJECTS) $(LIBS) $(SO_LIBS) $(BINS)
$(OBJECTS_DIR)/%.o: CFLAGS+= -fPIC
$(OBJECTS_DIR)/%.o: $(SOURCES_DIR)/%*
@mkdir -p $(@D)
$(CC) $(CFLAGS) -c $(SOURCES_DIR)/$*.c -o $@ $(LDFLAGS) $(LDLIBS)
$(LIB_DIR)/%.a: $(OBJECTS)
$(AR) rcs $@ $(OBJECTS_DIR)/$*/*.o
ranlib $@
$(LIB_DIR)/%.so: $(LIBS)
$(CC) -shared $(OBJECTS_DIR)/$*/*.o -o $@ $(LDFLAGS) $(LDLIBS)
# the rule i'm talking about
$(BIN_DIR)/%: $(SOURCES_DIR)/%/*.c
$(CC) $? $(CFLAGS) $(LDFLAGS) -o $@ $(LDLIBS) $(LIBS)
Upvotes: 0
Views: 95
Reputation: 15493
Confession: tl;dr
Is it possible to do something like:
$(BIN_DIR)/%: $(SOURCES_DIR)/%.c OR $(SOURCES_DIR)/%/*.c $(CC) $? $(CFLAGS) $(LDFLAGS) -o $@ $(LDLIBS) $(LIBS)
You can do it by declaring the pattern rule twice. Put the recipe in a macro to avoid writing that twice.
define recipe
$(CC) $? $(CFLAGS) $(LDFLAGS) -o $@ $(LDLIBS) $(LIBS)
endef
$(BIN_DIR)/%: ${SOURCES_DIR}/%.c
$(recipe)
$(BIN_DIR)/%: $(SOURCES_DIR)/%/*.c
$(recipe)
Ok, but don't do this. Pattern rules are an ugly hack IMHO—I don't like the ambiguity. Different make versions do different things. (Wildcards aren't much better, but I won't comment further here.) It always feels cleaner to write explicitly what you want. You can still use patterns, but use static pattern rules by explicitly listing the targets that the patterns apply to.
recipe = $(CC) $? $(CFLAGS) $(LDFLAGS) -o $@ $(LDLIBS) $(LIBS)
single-source-targets := $(addprefix ${BIN_DIR}/,counter filter)# for example
multi-source-targets := $(addprefix ${BIN_DIR}/,engine database)
${single-source-targets}: ${BIN_DIR}/%: ${SOURCES_DIR}/%.c ; $(recipe)
${multi-source-targets}: ${BIN_DIR}/%: ${SOURCES_DIR}/%/*.c ; $(recipe)
Upvotes: 1
Reputation: 3452
Well, i got what i want, even though i don't really like how I did this, so any other variants are appritiated. It's difficult to build prerequisites list conditionally, so i decided to use different targets, and then just copy files where i wanted. This did the job, but still not very pretty. I just added following lines to the Makefile
:
ONEFILERS_SOURCES=$(wildcard $(SOURCES_DIR)/*.c)
ONEFILERS=$(patsubst %.c,%,$(subst $(SOURCES_DIR),$(ONEFILERS_TMP),\
$(ONEFILERS_SOURCES)))
$(ONEFILERS_TMP)/%: $(SOURCES_DIR)/%.c $(SHARED_INCLUDES)
@mkdir -p $(@D)
$(CC) $< $(CFLAGS) $(LDFLAGS) -o $@ $(LDLIBS) $(LIBS)
@install $@ $(BIN_DIR)
Any better solutions are welcome.
Upvotes: 0
Reputation: 1427
Its impossible to give a specific answer without the entire Makefile.
But since this is a question of two variable file paths: conditional search directories ($(SOURCES_DIR)
or $(SOURCES_DIR)/progname
), AND conditional files to search for ($(SOURCES_DIR)/progname.c
or $(SOURCEDS_DIR)/progname/*.c
), the solution is more complicated than a simple makefile rule.
The best way I can think of is to use conditionals to set the dependency directory and filename as shown below.
ifneq ("$(wildcard $(SOURCES_DIR)/progname.c)","")
DEPS = $(SOURCES_DIR)/progname.c
else
DEPS = $(SOURCES_DIR)/progname/*.c
endif
$(BIN_DIR)/%: $(DEPS)
$(CC) $? $(CFLAGS) $(LDFLAGS) -o $@ $(LDLIBS) $(LIBS)
Upvotes: 0