我正在尝试创建一个 Makefile,它可以编译所有
.c
文件,而无需在 Makefile 中每行添加文件名行。我认为这与makefiles - 一次编译所有c 文件非常相似。这就是我想做的:
1 - 验证来自
.c
的所有 /src
文件是否都有来自 .o
的各自 /obj
文件。如果 .o
文件不存在,则从 .c
文件构建它;
2 - 从所有
main.exe
编译 .o
并将其放在 /bin
上。
我的Makefile:
BIN = ./bin
OBJ = ./obj
INCLUDE = ./include
SRC = ./src
all:
gcc -c "$(SRC)/Distances.c" -o "$(OBJ)/Distances.o"
gcc -c "$(SRC)/HandleArrays.c" -o "$(OBJ)/HandleArrays.o"
gcc -c "$(SRC)/HandleFiles.c" -o "$(OBJ)/HandleFiles.o"
gcc -c "$(SRC)/Classifier.c" -o "$(OBJ)/Classifier.o"
gcc -c main.c -o "$(OBJ)/main.o"
gcc -o $(BIN)/main.exe $(OBJ)/*.o -lm
run:
$(BIN)/main.exe
clean:
del /F /Q "$(OBJ)" ".o"
del /F /Q "$(BIN)" ".exe"
我想我需要使用像
$(SRC)/*.c
这样的东西,但我做不到。
现在,关于
make clean
,我不知道如何使其跨平台工作。我在 Windows 上使用 del
。
注意:此答案假设您正在使用 GNU make。如果情况并非如此,则可能需要进行一些调整。我不会回答你关于跨平台可移植性的最后一个问题。首先是因为这是一个复杂的问题,其次是因为我没有 Windows 机器,无法使用该操作系统进行任何测试。但如果您知道检测操作系统的便携式方法,请查看最后一条注释。同时,以下应该在 Windows 下工作。
在您的情况下使用 GNU make 最直接、最标准的方法可能是这样的:
MKDIR := md
RMDIR := rd /S /Q
CC := gcc
BIN := ./bin
OBJ := ./obj
INCLUDE := ./include
SRC := ./src
SRCS := $(wildcard $(SRC)/*.c)
OBJS := $(patsubst $(SRC)/%.c,$(OBJ)/%.o,$(SRCS))
EXE := $(BIN)/main.exe
CFLAGS := -I$(INCLUDE)
LDLIBS := -lm
.PHONY: all run clean
all: $(EXE)
$(EXE): $(OBJS) | $(BIN)
$(CC) $(LDFLAGS) $^ -o $@ $(LDLIBS)
$(OBJ)/%.o: $(SRC)/%.c | $(OBJ)
$(CC) $(CFLAGS) -c $< -o $@
$(BIN) $(OBJ):
$(MKDIR) $@
run: $(EXE)
$<
clean:
$(RMDIR) $(OBJ) $(BIN)
说明:
wildcard
make 函数 用于发现 C 源文件列表。patsubst
make 函数用于将 C 源文件名转换为目标文件名。.PHONY
特殊目标告诉make,它的所有先决条件都是phony:它们不是真正的文件,即使偶然已经存在具有该名称的文件,make也必须考虑它们。$(OBJ)/%.o: $(SRC)/%.c | $(OBJ)
是一个模式规则,一个适用于所有类似对象构建规则的通用规则。这个告诉 make 如何通过编译相应的 ./obj/xxx.o
C 源文件来生成每个 ./src/xxx.c
目标文件。... | $(OBJ)
部分告诉make,$(OBJ)
是一个仅订单先决条件。如果它尚不存在,Make 将构建它。否则它不会考虑其最后修改时间来决定是否必须重建目标。目录几乎总是被列为仅订单先决条件,因为它们的上次修改时间不相关。这里,它用于告诉 make 在构建任何目标文件之前必须构建 $(OBJ)
目录。与 $(BIN)
规则中的 $(EXE): $(OBJS) | $(BIN)
相同。$@
、$<
和 $^
是 3 个 make 自动变量。在规则的配方中,它们分别扩展为目标、第一个常规先决条件和规则的所有常规(非顺序)先决条件。CC
、CFLAGS
和LDLIBS
是标准的make隐式变量,分别用于定义C编译器、C编译器选项和-lxxx
链接器选项。:=
变量赋值在这种特定情况下优于
=
赋值。请参阅GNU make 文档了解详细说明。
include
目录,我猜您也有自定义头文件。它们应该被列为相关目标文件的先决条件,以便 make 知道如果目标文件依赖的头文件发生更改,则必须重建目标文件。您可以通过在模式规则之后的任何位置添加没有配方的规则来做到这一点:
$(OBJ)/Distances.o: $(INCLUDE)/foo.h $(INCLUDE)/bar.h
如果项目的所有 C 源文件都包含头文件,只需添加:
$(OBJS): $(INCLUDE)/common.h
如果头文件有一种模式(例如,如果每个
$(SRC)/xxx.c
都包含
$(INCLUDE)/xxx.h
),您还可以调整模式规则来声明这种依赖关系:$(OBJ)/%.o: $(SRC)/%.c $(INCLUDE)/%.h | $(OBJ)
(CC) $(CFLAGS) -c $< -o $@
注意:如果您知道如何将 make 变量(例如
OS
)设置为当前操作系统的名称,您可能可以使用 GNU make 条件修改前面的内容以使其可移植。例如,您可以将前两行替换为:
OS := $(shell <the-command-that-returns-the-current-OS-name>)
ifeq ($(OS),Windows)
MKDIR := md
RMDIR := rd /S /Q
else ifeq ($(OS),GNU/Linux)
MKDIR := mkdir -p
RMDIR := rm -rf
else ifeq ($(OS),pokemon)
MKDIR := bulbasaur
RMDIR := charmander
endif