Question

Say I have a list of source files and each are to be compiled to separate binaries:

SRCS = abcd.c efgh.c ijkl.c

And I want output files in separate subdirectories based on the file names like this:

  • build/abcd/abcd
  • build/efgh/efgh
  • build/ijkl/ijkl

I'm thinking a static pattern rule is the way to go. The pseudo-make-rule can be something like:

$(TARGETS): build/%/%: %.c
    # stuff ...

I started by making a list of the subdirectories based on the filenames:

DIRS = $(SRCS:%.c=build/%)

So now we have DIRS = build/abcd build/efgh build/ijkl. I thought I can make the list of targets now with something like:

BLDS = $(DIRS:%=%/$(basename %))

But of course this doesn't work since the wildcard can not be used multiple times within a pattern. Therefore I'm now stuck at BLDS = build/abcd/% build/efgh/% build/ijkl/%.

Obviously I'm totally going about this the wrong way. How would you go about this?

For now I'm writing each rule explicitly, which is starting to get a bit tedious:

compile = # command to do stuff
BD = build

all: $(BD)/abcd/abcd $(BD)/efgh/efgh $(BD)/ijkl/ijkl

$(BD)/abcd/abcd: abcd.c
    $(call compile)

$(BD)/efgh/efgh: efgh.c
    $(call compile)

$(BD)/ijkl/ijkl: ijkl.c
    $(call compile)

clean:
    rm -rf build/*

.PHONY: all
Was it helpful?

Solution

I believe this does what you want:

SRCS:=abcd.c efgh.c ijkl.c

# We could fold NAMES into BLDS's definition if NAMES is not used elsewhere.
NAMES:=$(SRCS:%.c=%)

BLDS:=$(foreach name,$(NAMES),$(subst foo,$(name),build/foo/foo))

# We don't use DIRS below but the question had this variable.
DIRS:=$(dir $(BLDS))

TARGETS:=$(BLDS)

.PHONY: all
all: $(TARGETS)

.SECONDEXPANSION:
$(TARGETS): $$(notdir $$@).c
    @echo Build $@ from $^
    mkdir -p $(dir $@)
    touch $@

There are two important changes. The first is to reorder how the variables are created, and use subst, which allows replacing a matched string multiple times. The second is to use secondary expansion so that make builds rules for each of your targets. You initially a pattern with two %, but the docs say:

A pattern rule looks like an ordinary rule, except that its target contains the character `%' (exactly one of them).

(Emphasis added.)

I've tested the above with fake files abcd.c efgh.c and ijkl.c and get the following output:

$ make
Build build/abcd/abcd from abcd.c
mkdir -p build/abcd/
touch build/abcd/abcd
Build build/efgh/efgh from efgh.c
mkdir -p build/efgh/
touch build/efgh/efgh
Build build/ijkl/ijkl from ijkl.c
mkdir -p build/ijkl/
touch build/ijkl/ijkl
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top