【问题标题】:Build libraries in different build folders in GNU make在 GNU make 的不同构建文件夹中构建库
【发布时间】:2019-02-12 00:24:11
【问题描述】:

我正在尝试设置一个构建系统,该系统支持在单独的文件夹中构建库和可执行文件,而不必求助于递归构建。 我当前的目录树如下:

Project
├── Foo
│   └── src
│       └── foo.c
├── Bar
|   └── Source
|       └── bar.c
├── App 
|   └── src
|       └── main.c
└── Makefile

注意 Bar 文件夹中的“Source”而不是“src”。

我希望能够生成以下构建目录:

Build
├── Foo
│   ├── foo.o
│   └── foo.a
├── Bar
│   ├── bar.o
│   └── bar.a
└── App 
    ├── main.o
    └── app.exe

我还没有找到一种方法来生成通配符配方以在正确的构建目录中生成对象/libs/bins而不重复自己:

BUILD_DIR := Path/To/Build
CC ?= gcc

.PHONY: all
all: Foo Bar

# Foo
FOO_DIR = Foo
FOO_SRCS = foo.c
FOO_OBJS = $(addprefix $(BUILD_DIR)/$(FOO_DIR)/,$(FOO_SRCS:.c=.o))

.PHONY: Foo
Foo: $(FOO_OBJS)

## This line has to be repeated for Bar as well
$(BUILD_DIR)/$(FOO_DIR)/%.o: $(FOO_DIR)/src/%.c | $(BUILD_DIR)/$(FOO_DIR)
    $(CC) -c $< -o $@

# Bar
BAR_DIR = Bar
BAR_SRCS = bar.c
BAR_OBJS = $(addprefix $(BUILD_DIR)/$(BAR_DIR)/,$(BAR_SRCS:.c=.o))

.PHONY: Bar
Bar: $(BAR_OBJS)

## Here, I am repeating the same line as in Foo
$(BUILD_DIR)/$(BAR_DIR)/%.o: $(BAR_DIR)/Source/%.c | $(BUILD_DIR)/$(BAR_DIR)
    $(CC) -c $< -o $@

# Utils
$(BUILD_DIR)/%:
    mkdir -p $@

【问题讨论】:

  • 有什么不使用递归make的具体原因吗?它将允许您在单独的 make 实例中隔离不同的构建配置,即减少污染的危险、不正确的构建工件和奇怪的构建问题。
  • 也就是说,您可能想学习this answer 如何从变量中动态生成源文件和目标文件之间的依赖关系,并使用静态模式规则来避免代码重复。
  • 请看this Q/A
  • @StefanBecker 我不能使用递归make,因为“Bar”库不在源代码树中(我在问题中省略了这个)。这也是它的源使用不同目录名称的原因。
  • @Rogozhin 一种解决方案是在您当前的树中为“Bar”添加一个 makefile,其中包含来自“Bar”源树的 Makefile(片段)。

标签: makefile gnu-make


【解决方案1】:

使用静态模式规则来避免代码重复和动态生成的依赖关系。应该这样做:

.DEFAULT_GOAL := all

FOO_DIR  := Foo
FOO_SRCS := a

BAR_DIR  := Bar
BAR_SRCS := b

# Macros to dynamically generate dependencies
OBJS :=
DIRS :=
# $(1): directory path
# $(2): source directory path relative to $(1)
#       (will also be the relative path in build directory)
# $(3): source/object file base name
# NOTE: the empty line at the end of the macro is on purpose!
define obj_dependencies
_dir := $(BUILD_DIR)/$(1)
_obj := $$(_dir)/$(3).o
$$(_obj): $(1)/$(2)/$(3).c | $$(_dir)
DIRS += $$(_dir)
OBJS += $$(_obj)
_dir :=
_obj :=

endef

# $(1): directory path
# $(2): source directory path relative to $(1)
# $(3): list of file base names
objs_for_dir = $(eval $(foreach _o,$(3),$(call obj_dependencies,$(strip $(1)),$(strip $(2)),$(_o))))

# Generate dependencies for given directories & sources
$(call objs_for_dir,$(FOO_DIR),src,$(FOO_SRCS))
$(call objs_for_dir,$(BAR_DIR),Source,$(BAR_SRCS))

# build all object files
all: $(OBJS)

# Sources -> objects
$(OBJS): %.o:
    $(CC) -c $< -o $@

# Utils
$(DIRS): | $(BUILD_DIR)
$(BUILD_DIR) $(DIRS):
    mkdir -p $@

请注意,我是直接从脑海中输入的,因此可能存在拼写错误。但至少它应该让你知道如何实现你想要的。


这可以优化为只调用一次$(eval),例如

objs_for_dir = $(foreach _o,$(3),$(call obj_dependencies,$(strip $(1)),$(strip $(2)),$(_o)))

# Generate dependencies for given directories & sources
$(eval \
    $(call objs_for_dir,$(FOO_DIR),src,$(FOO_SRCS))    \
    $(call objs_for_dir,$(BAR_DIR),Source,$(BAR_SRCS)) \
)

【讨论】:

  • 宏函数似乎没有产生依赖关系。评估宏时,Afaik "_dir" 和 "_obj" 为空。
  • 完成并验证。编写 $(eval)'d 的宏可能会导致美元炎 :-)
【解决方案2】:

有一个简单的方法可以做到这一点。让我们分阶段进行。我们从这些规则开始:

.PHONY: all
    all: Foo Bar

.PHONY: Foo
Foo: $(FOO_OBJS)

.PHONY: Bar
Bar: $(BAR_OBJS)

$(BUILD_DIR)/$(FOO_DIR)/%.o: $(FOO_DIR)/src/%.c | $(BUILD_DIR)/$(FOO_DIR)
    $(CC) -c $< -o $@

$(BUILD_DIR)/$(BAR_DIR)/%.o: $(BAR_DIR)/Source/%.c | $(BUILD_DIR)/$(BAR_DIR)
    $(CC) -c $< -o $@

(请注意,您的图表显示Foo/sourceBar/Src,但您的makefile 显示Foo/srcBar/Source。根据需要进行调整。) 我们去掉中间目标,把对象规则变成static pattern rules

.PHONY: all
    all:  $(FOO_OBJS) $(BAR_OBJS)

$(FOO_OBJS): $(BUILD_DIR)/$(FOO_DIR)/%.o: $(FOO_DIR)/src/%.c | $(BUILD_DIR)/$(FOO_DIR)
    $(CC) -c $< -o $@

$(BAR_OBJS): $(BUILD_DIR)/$(BAR_DIR)/%.o: $(BAR_DIR)/Source/%.c | $(BUILD_DIR)/$(BAR_DIR)
    $(CC) -c $< -o $@

然后我们将静态模式规则分成它们特定的通用部分:

$(FOO_OBJS): $(BUILD_DIR)/$(FOO_DIR)/%.o: $(FOO_DIR)/src/%.c | $(BUILD_DIR)/$(FOO_DIR)

$(FOO_OBJS):
    $(CC) -c $< -o $@

$(BAR_OBJS): $(BUILD_DIR)/$(BAR_DIR)/%.o: $(BAR_DIR)/Source/%.c | $(BUILD_DIR)/$(BAR_DIR)

$(BAR_OBJS):
    $(CC) -c $< -o $@

最后结合通用部分:

$(FOO_OBJS): $(BUILD_DIR)/$(FOO_DIR)/%.o: $(FOO_DIR)/src/%.c | $(BUILD_DIR)/$(FOO_DIR)

$(BAR_OBJS): $(BUILD_DIR)/$(BAR_DIR)/%.o: $(BAR_DIR)/Source/%.c | $(BUILD_DIR)/$(BAR_DIR)

$(FOO_OBJS) $(BAR_OBJS):
    $(CC) -c $< -o $@

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-04
    • 1970-01-01
    • 2012-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多