创建一个 Makefile 以编译 folder/subfolder 中的所有 .c 文件并将二进制文件发送到其他地方

Create a Makefile to compile all .c files in a folder/subfolder and ship the binaries elsewhere

我目前正在研究 K&R,我想创建一个 Makefile 来自动化编译过程。

我的目录结构如下:

k-r/
    chapter-1/
        exe/
        1-1/
            program1.c
            program2.c
            ...
        1-2/
            program3.c
            program4.c
            ...
    ch1.c
    ch1.h

大多数程序都是独立的,但有些程序需要在 k-r/chapter-1/ch1.c 和 k-r/chapter-1/ch1.h.

中定义的函数

我想编译k-r/chapter-1/1-1、k-r/chapter-1/1-2等中的所有程序,并将可执行二进制文件发送到k-r/chapter-1/exe。

以下是我到目前为止所拥有的并且它有效,但我想知道是否有更有效的方法来做到这一点,例如不必列出文件名?

CC := clang
CFLAGS := -Wall
LIB := ../ch1.c
BIN := ../exe/

.PHONY: all clean 1-1 1-2

all: 1-1 1-2

1-1:
    cd 1-1 && \
    $(CC) $(CFLAGS) program1.c -o $(BIN)program1.exe && \
    $(CC) $(CFLAGS) program2.c -o $(BIN)program2.exe && \

1-2:
    cd 1-2 && \
    $(CC) $(CFLAGS) program3.c $(LIB) -o $(BIN)program3.exe && \
    $(CC) $(CFLAGS) program4.c $(LIB) -o $(BIN)program4.exe && \

clean:
    rm exe/*

我假设您所说的“高效”是指“更多 generic/automated”。这是我能想到的最“有效”的解决方案:

CC := gcc
CFLAGS := -Wall
BIN_DIR := exe
SRC_DIRS := $(wildcard *-*)

.PHONY: all clean $(SRC_DIRS)
all: $(SRC_DIRS)

clean:
    del /s /q $(BIN_DIR)\*

define SETUP_TARGETS
$(1): $$(patsubst $(1)/%.c,$(BIN_DIR)/%.exe,$$(wildcard $(1)/*.c))
$(BIN_DIR)/%.exe: $(1)/%.c $$(wildcard *.c)
    $(CC) $(CFLAGS) $$(addprefix -I ,$$(patsubst %/,%,$$(dir $$^))) $$^ -o $$@
endef

$(foreach target,$(SRC_DIRS),$(eval $(call SETUP_TARGETS,$(target))))

关于 makefile 的观察:

  • 由于您使用的是文件扩展名 .exe,我假设您是 运行 MS-Windows 上的 GNU Make。因此,我使用了命令提示符语法和 .exe 扩展。
  • 您必须将 makefile 放在本章的根目录中(在本例中为 chapter-1/)。
  • 当您将代码复制到您的makefile 时,请注意缩进部分(每个规则的配方)。每个命令行必须以 TAB 字符开头,而不是空格。
  • 我假设目录 exe/ 已经存在并且永远存在。
  • makefile 将检测每个与模式 *-*(例如 1-1、1-2...)匹配的子目录,并为每个子目录创建一个目标。
  • 命令行 make target(目标是一个子目录,例如 1-1、1-2、...)将为目标子目录中的每个源文件在 exe/ 中创建一个可执行文件.我假设每个源文件都有一个函数 main().
  • 每个目标构建还将包含根目录中的每个源文件 *.c(在本例中为 ch1.c)。我假设这些来源不包含函数 main().
  • 构建中涉及的文件不必以任何特定方式命名。将检测所有文件。
  • 编译中使用的包含路径与包含所涉及源的目录相同,即目标子目录和根目录。因此,您可以 #include 从它们到您的程序的头文件。
  • 命令行 makemake all 将构建所有目标。
  • 命令行make clean将删除exe/中的所有文件。
  • 创建此 makefile 是为了最大限度地减少构建操作,以便仅构建相对于其依赖源之一而言已过时的可执行文件。如果您尝试在不修改任何源的情况下重建目标,您将收到类似 make: Nothing to be done for '1-1'.
  • 的警告
  • 您也可以将此生成文件用于其他章节,只要它们具有相同的目录结构即可。

尽情享受吧:)