创建一个 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
从它们到您的程序的头文件。
- 命令行
make
和 make all
将构建所有目标。
- 命令行
make clean
将删除exe/
中的所有文件。
- 创建此 makefile 是为了最大限度地减少构建操作,以便仅构建相对于其依赖源之一而言已过时的可执行文件。如果您尝试在不修改任何源的情况下重建目标,您将收到类似
make: Nothing to be done for '1-1'
. 的警告
- 您也可以将此生成文件用于其他章节,只要它们具有相同的目录结构即可。
尽情享受吧:)
我目前正在研究 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
从它们到您的程序的头文件。 - 命令行
make
和make all
将构建所有目标。 - 命令行
make clean
将删除exe/
中的所有文件。 - 创建此 makefile 是为了最大限度地减少构建操作,以便仅构建相对于其依赖源之一而言已过时的可执行文件。如果您尝试在不修改任何源的情况下重建目标,您将收到类似
make: Nothing to be done for '1-1'
. 的警告
- 您也可以将此生成文件用于其他章节,只要它们具有相同的目录结构即可。
尽情享受吧:)