具有两个目标和单独生成文件夹的Makefile

具有两个目标和单独生成文件夹的Makefile,makefile,gnu,Makefile,Gnu,我正在尝试使用一个Makefile,其中包含两个相似的目标和两个独立的构建文件夹。目标之间的唯一区别是添加了一个CFLAGdefine 这里是我所拥有的一个片段,但是我无法让build文件夹根据目标计算出不同的值。我使用foo和bar来表示不同的目标 ... foo_BUILD_DIR := build_foo/ bar_BUILD_DIR := build_bar/ C_SRCS := main.c CFLAGS := -std=c99 -Os -Wall foo_CFLAGS := $(

我正在尝试使用一个Makefile,其中包含两个相似的目标和两个独立的构建文件夹。目标之间的唯一区别是添加了一个
CFLAG
define

这里是我所拥有的一个片段,但是我无法让build文件夹根据目标计算出不同的值。我使用
foo
bar
来表示不同的目标

...
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 $@ 

你有三个问题要解决

第一个是在构建目录中构建对象文件。首先,我们编写一个模式规则来构建foo对象:

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 $@
一旦工作正常,我们将为bar对象编写另一条规则:

$(bar_BUILD_DIR)/%.o: %.c
    @mkdir -p $(@D)
    $(CC) $(CFLAGS) $^ -o $@
第二个问题是整理我们到目前为止写的东西。这个
foo\u CFLAGS
变量现在是唯一使这两个食谱不同的东西,所以让我们用一个:

现在,我们可以将这些规则组合为一个模式规则和两个目标:

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

$(foo_BUILD_DIR)/%.o $(bar_BUILD_DIR)/%.o: %.c
    @mkdir -p $(@D)
    $(CC) $(CFLAGS) $^ -o $@
第三个问题是让
foo
bar
规则需要正确的对象。显然,这条规则:

foo:$(OBJS)
    ...
不行,我们需要一些特定于
foo

foo: $(addprefix $(foo_BUILD_DIR)/, $(OBJS))
    ...
这是可行的,但它需要我们编写一个
foo
规则来指定
$(foo\u BUILD\u DIR)
,一个
bar
规则来指定
$(bar\u BUILD\u DIR)
,等等。有更懒的方法吗?我们所需要的只是将目标(例如,
foo
)放入先决条件列表中。正如您所知,
$@
包含目标,但它在前提条件列表中不可用,因为前提条件列表是在为该变量赋值之前展开的,除非我们使用。这是一项高级技术,但它允许我们在后期进行第二次扩展(使用额外的
$
来转义变量,以保护它们不受第一次扩展的影响):

一旦这起作用,我们可以添加另一个目标,或者添加任意数量的目标:

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

还有一个或两个改进是可能的,但这已经足够开始了。

这里有三个问题需要解决

第一个是在构建目录中构建对象文件。首先,我们编写一个模式规则来构建foo对象:

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 $@
一旦工作正常,我们将为bar对象编写另一条规则:

$(bar_BUILD_DIR)/%.o: %.c
    @mkdir -p $(@D)
    $(CC) $(CFLAGS) $^ -o $@
第二个问题是整理我们到目前为止写的东西。这个
foo\u CFLAGS
变量现在是唯一使这两个食谱不同的东西,所以让我们用一个:

现在,我们可以将这些规则组合为一个模式规则和两个目标:

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

$(foo_BUILD_DIR)/%.o $(bar_BUILD_DIR)/%.o: %.c
    @mkdir -p $(@D)
    $(CC) $(CFLAGS) $^ -o $@
第三个问题是让
foo
bar
规则需要正确的对象。显然,这条规则:

foo:$(OBJS)
    ...
不行,我们需要一些特定于
foo

foo: $(addprefix $(foo_BUILD_DIR)/, $(OBJS))
    ...
这是可行的,但它需要我们编写一个
foo
规则来指定
$(foo\u BUILD\u DIR)
,一个
bar
规则来指定
$(bar\u BUILD\u DIR)
,等等。有更懒的方法吗?我们所需要的只是将目标(例如,
foo
)放入先决条件列表中。正如您所知,
$@
包含目标,但它在前提条件列表中不可用,因为前提条件列表是在为该变量赋值之前展开的,除非我们使用。这是一项高级技术,但它允许我们在后期进行第二次扩展(使用额外的
$
来转义变量,以保护它们不受第一次扩展的影响):

一旦这起作用,我们可以添加另一个目标,或者添加任意数量的目标:

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

可能还有一到两个改进,但这已经足够开始了。

谢谢!虽然没有这些编辑,
foo:$(addprefix$$($$@\u BUILD\u DIR)/,$(OBJS))
但我无法让它工作:
foo:$(addprefix$(foo\u BUILD\u DIR)/,$(OBJS))
。调用
make foo
后未进行编辑的错误:
make:**。停止。
我还注意到,对
LDFLAGS
执行相同的策略由于某种原因不会产生任何效果:
$(foo\u BUILD\u DIR)/%.o:LDFLAGS+=-Tmylinkerfile.ld
@User3219:我忘记显示
.SECONDEXPANSION:
行。很抱歉,我将编辑。@User3219:…您的行似乎按照您的意愿修改了
LDFLAGS
。但是,当您构建对象文件时,为什么要关心
LDFLAGS
?谢谢!虽然没有这些编辑,
foo:$(addprefix$$($$@\u BUILD\u DIR)/,$(OBJS))
但我无法让它工作:
foo:$(addprefix$(foo\u BUILD\u DIR)/,$(OBJS))
。调用
make foo
后未进行编辑的错误:
make:**。停止。
我还注意到,对
LDFLAGS
执行相同的策略由于某种原因不会产生任何效果:
$(foo\u BUILD\u DIR)/%.o:LDFLAGS+=-Tmylinkerfile.ld
@User3219:我忘记显示
.SECONDEXPANSION:
行。很抱歉,我将编辑。@User3219:…您的行似乎按照您的意愿修改了
LDFLAGS
。但是,当您构建一个对象文件时,为什么要关心
LDFLAGS