Make-File:将多个 SRC 文件夹编译为单个 OBJ 文件夹

Posted

技术标签:

【中文标题】Make-File:将多个 SRC 文件夹编译为单个 OBJ 文件夹【英文标题】:Make-File: compile multiple SRC-Folders to single OBJ-Folder 【发布时间】:2021-04-03 19:07:09 【问题描述】:

我的项目层次结构如下所示:

+Makefile
+---src
|   main.c
|   ...
|   +---block
|   |       air.c
|   |       ...
|   |       
|   +---entity
|   |       esc.c
|   |       esc.h
|   |       ...
|   |       
|   \---world
|           \---gen
|                noise.c
|                ...
|           xyz.c
|           ...
\---obj
    main.o
    air.o
    esc.o
    noise.o
    xyz.o
    ...

我想使用 make 将层次结构中的所有 .c 文件编译到一个 obj 文件夹中。 到目前为止,我得到了:

UNAME_S = $(shell uname -s)

CC = clang
CFLAGS = -std=c11 -O3 -g -Wall -Wextra -Wpedantic -Wstrict-aliasing
CFLAGS += -Wno-pointer-arith -Wno-newline-eof -Wno-unused-parameter -Wno-gnu-statement-expression
CFLAGS += -Wno-gnu-compound-literal-initializer -Wno-gnu-zero-variadic-macro-arguments
CFLAGS += -Ilib/cglm/include -Ilib/glad/include -Ilib/glfw/include -Ilib/stb -Ilib/noise -fbracket-depth=1024
LDFLAGS = lib/glad/src/glad.o lib/cglm/libcglm.a lib/glfw/src/libglfw3.a lib/noise/libnoise.a -lm

# GLFW required frameworks on OSX
ifeq ($(UNAME_S), Darwin)
    LDFLAGS += -framework OpenGL -framework IOKit -framework CoreVideo -framework Cocoa
endif

ifeq ($(UNAME_S), Linux)
    LDFLAGS += -ldl -lpthread
endif

OBJ_DIR = obj

SRC = $(wildcard src/**/*.c) $(wildcard src/*.c) $(wildcard src/**/**/*.c) $(wildcard src/**/**/**/*.c)
OBJ = $(addprefix $(OBJ_DIR)/,$(addsuffix .o,$(notdir $(basename $(SRC)))))

SRC_DIRS = $(sort $(dir $(SRC)))
BIN = bin

.PHONY: all clean

all: dirs libs game

libs:
    cd lib/cglm && cmake . -DCGLM_STATIC=ON && make
    cd lib/glad && $(CC) -o src/glad.o -Iinclude -c src/glad.c
    cd lib/glfw && cmake . && make
    cd lib/noise && make

dirs:
    mkdir -p ./$(BIN) ./$(OBJ_DIR)

run: all
    $(BIN)/game

game: $(OBJ)
    $(CC) -o $(BIN)/game $^ $(LDFLAGS)

$(OBJ_DIR)/%.o: src/%.c
    $(CC) -o $@ -c $< $(CFLAGS)

$(OBJ_DIR)/%.o: src/block/%.c
    $(CC) -o $@ -c $< $(CFLAGS)

$(OBJ_DIR)/%.o: src/entity/%.c
    $(CC) -o $@ -c $< $(CFLAGS)

$(OBJ_DIR)/%.o: src/gfx/%.c
    $(CC) -o $@ -c $< $(CFLAGS)

$(OBJ_DIR)/%.o: src/ui/%.c
    $(CC) -o $@ -c $< $(CFLAGS)

$(OBJ_DIR)/%.o: src/util/%.c
    $(CC) -o $@ -c $< $(CFLAGS)

$(OBJ_DIR)/%.o: src/world/%.c
    $(CC) -o $@ -c $< $(CFLAGS)

$(OBJ_DIR)/%.o: src/world/gen/%.c
    $(CC) -o $@ -c $< $(CFLAGS)

clean:
    rm -rf $(BIN) $(OBJ_DIR)

有什么方法可以更有效地完成这项工作吗?尤其是 $(OBJ_DIR)/%.o 的情况?

变量 $(SRC_DIRS) 存储所有 src 文件夹 变量 $(SRC) 存储所有 .c 文件及其路径 变量 $(OBJ) 存储所有 .o 文件路径和名称

【问题讨论】:

** 对于 make 的 wildcard 函数并不特殊。 src/**/*.csrc/*/*.c 相同。 一般来说,将所有目标文件放在一个目录中并不是一个好主意:如果将名为 foo.c 的源文件放在两个不同的源目录中,则会发生错误。 我不确定你所说的“同样的事情”发生是什么意思。那是什么东西?显然,如果您将目标文件放在每个源目录的不同目标目录中,则不会发生冲突。 我用 ** 来提醒自己,我指的是文件夹,而不是文件 同一个目录下不可能有两个同名的源文件吧?因此,如果每个源文件都转换为具有相同目录结构的目标文件,则不能有两个冲突的目标文件。也就是说,如果你有src/foo/bar.c并且你写出obj/foo/bar.o,那么它不能与src/blah/bar.c冲突,因为那将变成obj/blah/bar.o而不是obj/foo/bar.o 【参考方案1】:

最好的方法是using VPATH。

例如:

SRC := $(wildcard src/*/*.c) $(wildcard src/*.c) $(wildcard src/*/*/*.c) $(wildcard src/*/*/*/*.c)
OBJ := $(addprefix $(OBJ_DIR)/,$(addsuffix .o,$(notdir $(basename $(SRC)))))

SRC_DIRS := $(sort $(dir $(SRC)))

VPATH := $(SRC_DIRS)

  ...

$(OBJ_DIR)/%.o: %.c
        $(CC) -o $@ -c $< $(CFLAGS)

你只需要一个模式规则。

【讨论】:

以上是关于Make-File:将多个 SRC 文件夹编译为单个 OBJ 文件夹的主要内容,如果未能解决你的问题,请参考以下文章

是否可以将 C# 项目编译为单文件 UMD JavaScript 库?

我可以将 src/ 中的所有 .cpp 文件编译为 obj/ 中的 .o,然后链接到 ./ 中的二进制文件吗?

将单声道编译为静态库

将Gulp编译为父文件夹

protocol buffer如何将输出文件编译为DLL

create-react-app 如何在构建过程中将特定的 .ts 文件编译为 .js 文件?