Makefile编译所有.c文件,而无需指定它们

9vw9lbht  于 2023-11-17  发布在  其他
关注(0)|答案(2)|浏览(147)

我试图创建一个Makefile,它可以编译所有的.c文件,而不需要在Makefile中逐行添加文件名。我认为这与makefiles - compile all c files at once非常相似。下面是我想做的:
1 -验证/src中的所有.c文件是否都有/obj中的相应.o文件。如果.o文件不存在,则从.c文件构建它;
2 -从所有.o编译main.exe,并将其放在/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

wfsdck30

wfsdck301#

注意事项:这个答案假设你使用的是GNU make。2如果不是这样的话,可能有一些事情需要调整。3我不会回答你最后一个关于跨平台可移植性的问题。4首先,这是一个复杂的问题; 5其次,我没有Windows系统,无法使用这个操作系统进行任何测试。6但是,如果你知道一种可移植的方法来检测操作系统,请看最后一条注解。7同时,在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)

字符串
说明:

  1. wildcard make函数用于发现C源文件的列表。
  2. patsubst make函数用于将C的源文件名转换为目标文件名。
  3. .PHONY特殊目标告诉make,它的所有先决条件都是 * 虚假的 *:它们不是真实的的文件,make必须考虑它们,即使具有此名称的文件已经意外存在。
  4. $(OBJ)/%.o: $(SRC)/%.c | $(OBJ)是一个模式规则,一个通用规则,适用于所有类似的对象构建规则。这个规则告诉make如何通过编译相应的./src/xxx.c C源文件来生成每个./obj/xxx.o对象文件。
    1.模式规则的... | $(OBJ)部分告诉make $(OBJ)是仅限订单的先决条件。如果它还不存在,Make将构建它。否则,它将不考虑它的上次修改时间来决定是否必须重新生成目标。目录几乎总是作为仅限订单的先决条件列出,因为它们的上次修改时间是无关紧要的。在此,它用于告诉make在构建任何目标文件之前必须构建$(OBJ)目录。在$(EXE): $(OBJS) | $(BIN)规则中,同样适用于$(BIN)
  5. $@$<$^是make自动变量中的3个。在规则的配方中,它们分别作为目标、规则的第一个常规先决条件和所有常规(非仅限订单)先决条件展开。
  6. CCCFLAGSLDLIBS是分别用于定义C编译器、C编译器选项和-lxxx链接器选项的标准make隐式变量。
  7. :=变量赋值在这种情况下优于=赋值,这是出于性能考虑的。详细解释请参见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

pbgvytdp

pbgvytdp2#

% n模式规则可以帮助%.o:%c

相关问题