【问题标题】:Makefile file matching sources from different subfolders into single build folderMakefile 文件将来自不同子文件夹的源匹配到单个构建文件夹中
【发布时间】:2018-07-03 11:31:38
【问题描述】:

我想创建一个 make 文件,它获取多个 src 子目录中的所有文件,并将它们每个都直接编译到一个单独的构建目录中。

即我有例如

  • src/main.c
  • src/i2c/i2c.c
  • src/i2c/i2c.h

作为输出,我想要目标文件以及最终的二进制文件 - 构建/main.o - 构建/i2c.o - 构建/发布.elf

我设法将所有源文件作为一个列表及其各自的子目录路径放入一个变量中,并且我还设法获取所有输出文件的列表,但是当我尝试创建一个目标来构建该构建中的所有 .o 文件时目录它与对应的 .c 文件与 .o 文件不匹配。在这里我只是不确定如何将这两者联系起来。

尝试将 main.o 与 i2c.c 匹配时失败。

这是 Makefile 的“相关”部分:

TARGET = $(lastword $(subst /, ,$(CURDIR)))

BUILD_DIR  := buildDir

SOURCES = $(wildcard src/*.c src/*/*.c)
BROKENOBJECTS = $(SOURCES:.c=.o)
LESSBROKEN = $(notdir $(BROKENOBJECTS))
OBJECT_FILES = $(addprefix $(BUILD_DIR)/, $(LESSBROKEN))

$(BUILD_DIR)/%.o: $(SOURCES) $(BUILD_DIR)
    $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -o $@ $<

$(BUILD_DIR)/$(TARGET).elf: $(OBJECT_FILES)
    $(CC) $(LDFLAGS) $(TARGET_ARCH) $^ $(LDLIBS) -o $@

$(BUILD_DIR) :
    mkdir -p $@

compile : $(BUILD_DIR)/$(TARGET).elf

我将如何解决这个问题,从 $(SOURCES) 运行每个 .c 文件的配方,然后在 buildDir/ 中创建相应的 .o 文件?

【问题讨论】:

  • 您的所有*.c 文件都有唯一的名称吗?即如果在目录src/i2c 中有一个文件i2c.c,那么没有其他源目录将有一个名为i2c.c 的文件。
  • 暂时不会。在遥远的将来,我可能会遇到一个案例,我想将不同的实现链接到该案例,但在那种情况下,我认为我会“只是”将具有该特定实现的不同源文件夹包含在其中。或专门添加该源文件。现在应该包括所有现有的东西。

标签: makefile gnu-make avr-gcc


【解决方案1】:

假设您使用 GNU make 并且您的 C 源文件都是 *.c 可以在当前目录及其所有子目录(不限深度)中找到,这应该接近您想要的:

BUILDDIR := build
SRC      := $(shell find . -type f -name '*.c')

# Convert C source file name(s) to object file name(s)
# $(1): C source file name(s)
define c2o
$(patsubst %.c,$(BUILDDIR)/%.o,$(notdir $(1)))
endef

OBJ := $(call c2o,$(SRC))

.PHONY: all

all: $(OBJ)

# Compilation rule for a C source file (use echo for testing)
# $(1): C source file name
define MY_rule
$$(call c2o,$(1)): $(1)
    @echo $$(CC) $$(CFLAGS) $$(CPPFLAGS) $$(TARGET_ARCH) -c -o $$@ $$<
endef

# Instantiate compilation rules for all C source files
$(foreach s,$(SRC),$(eval $(call MY_rule,$(s))))

演示:

host> tree .
.
├── Makefile
├── a.c
├── b
│   └── b.c
└── c
    └── c
        └── c.c

host> make
cc -c -o build/a.o a.c
cc -c -o build/c.o c/c/c.c
cc -c -o build/b.o b/b.c

注意$$MY_rule 的定义中的使用。之所以需要它,是因为它被扩展了两次:一次是扩展 eval 函数的参数,第二次是 make 将结果解析为常规 make 语法。

正如其他 cmets 和答案中所解释的,这仅在您没有多个具有相同基本名称的 C 源文件时才有效。有一种方法可以检测这种情况并在遇到这种情况时发出错误。 make sort 函数对其单词列表参数进行排序,但它也会删除重复项。所以,如果排序前后的字数不同,你就有重复。在OBJ的定义之后添加以下内容:

SOBJ     := $(sort $(OBJ))

ifneq ($(words $(OBJ)),$(words $(SOBJ)))
$(error Found multiple C source files with same base name)
endif

演示:

host> touch c/c/a.c
host> make
Makefile:13: *** Found multiple C source files with same base name.  Stop.

【讨论】:

    【解决方案2】:

    这是一个修改后的 sn-p,它应该可以满足您的需求,但如果不手动指定 src/ 中的每个子目录,我就找不到解决方案。

    SOURCES = $(wildcard src/*.c)
    SUBSOURCES = $(wildcard src/*/*.c)
    
    OBJECTS = $(addprefix $(BUILD_DIR)/, $(notdir $(SOURCES:.c=.o)))
    SUBOBJECTS = $(addprefix $(BUILD_DIR)/, $(notdir $(SUBSOURCES:.c=.o)))
    
    compile : $(BUILD_DIR)/$(TARGET).elf
    
    $(BUILD_DIR)/$(TARGET).elf: $(OBJECTS) $(SUBOBJECTS)
        $(CC) $(LDFLAGS) $(TARGET_ARCH) $^ $(LDLIBS) -o $@
    
    # save some typing for the rules below
    COMPILE = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -o $@ $<
    
    $(OBJECTS): $(BUILD_DIR)/%.o: src/%.c | $(BUILD_DIR)
        $(COMPILE)
    
    $(BUILD_DIR)/%.o: src/i2c/%.c | $(BUILD_DIR)
        $(COMPILE)
    
    $(BUILD_DIR)/%.o: src/someOtherSubdir/%.c | $(BUILD_DIR)
        $(COMPILE)
    

    作为@G.M.在 cmets 中建议,您必须确保源文件名在子目录中是唯一的。另请注意,我将$(BUILD_DIR) 变成了order only prerequisite,这应该更准确地反映了您的意图。

    【讨论】:

    • 我担心现在是这种情况。我特别想避免在扩展程序时添加每个新的子文件夹。感觉有点……不方便。
    • 也许其他人可能会想出一个解决方案?!尽管您也可以将不便视为暗示不同的方法可能会更好? :) 没有错,例如$(BUILD_DIR) 中的子文件夹与 src/ 中的子文件夹相同。
    • 我确实看到了,但是这些例子看起来更加复杂,我觉得不值得。如果在构建文件夹中创建相同的文件夹结构,那实际上看起来会如何?我努力相应地创建子目录。
    • 现在我无法告诉你。并且可能是它增加了makefile的复杂性。你有这么多子目录,像上面的 sn-p 那样列出它们是不是很痛苦?
    【解决方案3】:

    您可以使用makevpath mechanism。因此,与其指定可能的源路径,不如使用...

    SOURCES = $(wildcard src/*.c src/*/*.c)
    

    你会...

    # Build a list of directories under src
    #
    SOURCE_DIRS := $(shell find src -type d)
    
    # Use the list in $(SOURCE_DIRS) as a search path for .c files.
    #
    vpath %.c $(SOURCE_DIRS)
    

    现在,当尝试更新 i2c.o(例如)时,规则...

    $(BUILD_DIR)/%.o: %.c $(BUILD_DIR)
        $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -o $@ $<
    

    将导致make 自动在源目录列表中搜索依赖项i2c.c


    注意:出于显而易见的原因,不同源目录下的多个同名文件会在此处引起问题。因此,我最初的问题(在 cmets 中)关于不同目录下源文件名的唯一性

    【讨论】:

    • 到目前为止,这似乎完全按照我的预期解决了我的问题。很聪明的事情。我想对此表示赞同,但还没有足够的声誉来这样做......无论如何,谢谢你的回答!
    • 不完全。 vpath 指令告诉make$(SOURCE_DIRS) 指定的目录列表下搜索与%.c 模式匹配的任何名称。
    • 当然,但是问题出在哪里呢?我可能错过了什么
    • 假设您有两个文件src/a/i2c.csrc/b/i2c.c。应该使用哪一个来构建$(BUILD_DIR)/i2c.o
    • 我认为我有特殊情况,例如与该示例中的平台相关代码一样,我会将它们放入与该示例相同级别的单独 src 文件夹中,并添加单独的具体规则来编译它们并分别链接它们,具体取决于我要编译的目标平台。我可以想象,这很可能需要一些额外的调整。
    猜你喜欢
    • 1970-01-01
    • 2022-08-21
    • 1970-01-01
    • 1970-01-01
    • 2020-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多