【问题标题】:Makefile to compile all .c files without needing to specify themMakefile 编译所有 .c 文件而无需指定它们
【发布时间】:2019-04-07 17:28:50
【问题描述】:

我正在尝试创建一个 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

【问题讨论】:

  • 我的 Makefile 是基于我的 Makefile,它执行您在 this 问题(或者更确切地说是答案)上解释的内容,我建议您查看 wildcardpatsubst 描述的 here 命令。跨度>
  • $(wildcard $(SRC)/*.c)$(wildcard $(addsuffix /*.c,$(SRCDIRS))) 类似 SRCDIRS = ./src/module1 ./src/module2

标签: c makefile


【解决方案1】:

注意:此答案假定您使用的是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)

解释:

  1. wildcard make function 用于发现 C 源文件列表。
  2. patsubst make function 用于将 C 源文件名转换为目标文件名。
  3. .PHONY special target 告诉 make 它的所有先决条件都是假的:它们不是真正的文件,即使偶然已经存在同名的文件,make 也必须考虑它们。
  4. $(OBJ)/%.o: $(SRC)/%.c | $(OBJ)pattern rule,一个适用于所有类似对象构建规则的通用规则。这个告诉make如何通过编译对应的./src/xxx.cC源文件来生成每个./obj/xxx.o目标文件。
  5. 模式规则的... | $(OBJ) 部分告诉make $(OBJ)order-only prerequisite。如果它不存在,Make 将构建它。否则它不会考虑它的最后修改时间来决定是否必须重建目标。目录几乎总是被列为仅订单的先决条件,因为它们的最后修改时间不相关。在这里,它用于告诉 make 在构建任何目标文件之前必须构建 $(OBJ) 目录。 $(EXE): $(OBJS) | $(BIN) 规则中的 $(BIN) 也是如此。
  6. $@$&lt;$^make automatic variables 中的 3 个。在规则的配方中,它们分别扩展为目标、规则的第一个常规先决条件和所有常规(非仅顺序)先决条件。
  7. CCCFLAGSLDLIBS 是标准的 make implicit variables,分别用于定义 C 编译器、C 编译器选项和 -lxxx 链接器选项。
  8. 出于性能原因,:= 变量赋值在这种特定情况下优于 = 赋值。有关详细说明,请参阅the GNU make documentation

注意:因为你有一个include 目录,我猜你也有自定义头文件。它们应该被列为相关目标文件的先决条件,以便 make 知道如果它依赖的头文件发生更改,则必须重新构建目标文件。您可以通过在模式规则之后的任何位置添加没有配方的规则来做到这一点:

$(OBJ)/Distances.o: $(INCLUDE)/foo.h $(INCLUDE)/bar.h

如果项目的所有 C 源文件都包含头文件,只需添加:

$(OBJS): $(INCLUDE)/common.h

如果头文件有某种模式(例如,如果每个$(SRC)/xxx.c 都包含$(INCLUDE)/xxx.h),您还可以添加模式规则来声明这种依赖关系:

$(OBJ)/%.o: $(INCLUDE)/%.h

注意:如果您知道一种将 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

【讨论】:

    【解决方案2】:

    % 通配符模式规则可以提供帮助 %.o:%c

    【讨论】:

    • 我明白你的意思,但是没有经验的人可能会对你的回答感到困惑。也许添加一个示例或获取他的 makefile 并为他转换它?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-02-02
    • 2013-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多