User3219
User3219

Reputation: 118

Makefile with two targets and separate build folders

I am trying to use one Makefile with two similar targets and two separate build folders. The only difference between the targets is the addition of a CFLAG define.

Here is a snippet of what I have, however I can't get the build folder to evaluate to something different depending on the target. I used foo and bar to represent the different targets.

...
foo_BUILD_DIR := build_foo/
bar_BUILD_DIR := build_bar/

C_SRCS := main.c

CFLAGS := -std=c99 -Os -Wall
foo_CFLAGS := $(CFLAGS) -DBLE=1

C_OBJS := $(addprefix $(BUILD_DIR),$(subst .c,.o,$(C_SRCS)))

$(BUILD_DIR)%.o: %.c
    @mkdir -p $(@D)
    $(CC) $(CFLAGS) $^ -o $@

foo:$(OBJS)
    $(CC) $(OBJS) $(LDFLAGS) -o $@ 

bar:$(OBJS)
    $(CC) $(OBJS) $(LDFLAGS) -o $@ 

Upvotes: 0

Views: 134

Answers (1)

Beta
Beta

Reputation: 99084

You have three problems to solve here.

The first is building object files in the build directories. First we write a pattern rule to build foo objects:

foo_BUILD_DIR := build_foo # Don't put the trailing slash in the dir name, it's a pain.

CFLAGS := -std=c99 -Os -Wall
foo_CFLAGS := $(CFLAGS) -DBLE=1

$(foo_BUILD_DIR)/%.o: %.c
    @mkdir -p $(@D)
    $(CC) $(foo_CFLAGS) $^ -o $@

Once that's working perfectly, we write another rule for the bar objects:

$(bar_BUILD_DIR)/%.o: %.c
    @mkdir -p $(@D)
    $(CC) $(CFLAGS) $^ -o $@

The second problem is tidying up what we've written so far. That foo_CFLAGS variable is now the only thing making these two recipes different, so let's get rid of it with a target-specific variable assignment:

$(foo_BUILD_DIR)/%.o: CFLAGS += -DBLE=1

$(foo_BUILD_DIR)/%.o: %.c
    @mkdir -p $(@D)
    $(CC) $(CFLAGS) $^ -o $@

Now we can combine the rules into one pattern rule with two targets:

$(foo_BUILD_DIR)/%.o: CFLAGS += -DBLE=1

$(foo_BUILD_DIR)/%.o $(bar_BUILD_DIR)/%.o: %.c
    @mkdir -p $(@D)
    $(CC) $(CFLAGS) $^ -o $@

The third problem is getting the foo and bar rules to require the right objects. Obviously this rule:

foo:$(OBJS)
    ...

won't work, we need something specific to foo:

foo: $(addprefix $(foo_BUILD_DIR)/, $(OBJS))
    ...

This works, but it requires us to write a foo rule that specifies $(foo_BUILD_DIR), a bar rule that specifies $(bar_BUILD_DIR), and so on. Is there a lazier way? All we need is to take the target (e.g. foo) and get it into the prerequisite list. As you know, the automatic variable $@ contains the target, but it isn't available in the prerequisite list, because the prerequisite list is expanded before a value is assigned to that variable-- unless we use Secondary Expansion. This is an advanced technique, but it lets us do a second expansion in the later phase (escaping our variables with an extra $ to protect them from the first expansion):

.SECONDEXPANSION:

foo: $(addprefix $$($$@_BUILD_DIR)/, $(OBJS))
    ...

And once that's working, we can add another target, or as many as we want:

foo bar: $(addprefix $$($$@_BUILD_DIR)/, $(OBJS))
    ...

There are one or two more refinements possible, but this is enough to start with.

Upvotes: 2

Related Questions