makefile - 将 Makefile 从平面目录结构发展为子目录结构

标签 makefile

查看以下更新


完成的研究:我发现学习如何将 Makefile 从一种情况发展到另一种情况很困难。那里有大量的问题和答案,但其中很少有人真正展示了 Makefile 如何随着项目的变化而发展。它们似乎都使用了 Makefile 的各种不同技术和习语,因此当您像我一样是第一次学习 Makefile 时,在一个问题和另一个问题之间进行转换可能会很棘手。

问题:我的问题是我有一个项目,该项目以平面目录结构开始,但后来迁移到具有子目录的结构。我不能做的是让我的 Makefile 顺其自然。

首先,我将展示我创建的有效的东西,然后展示我希望它如何发展以及它如何不起作用。

平面目录结构,工作 Makefile

我的项目目录包含我的所有 C 文件和一个头文件以及我的 Makefile:

project
  Makefile
  c8_asm.c
  c8_dasm.c
  c8_terp.c
  chip8.h

这是我的 Makefile(工作正常):

CC = gcc

CFLAGS += -c -Wall -std=c99
CFLAGS += -D_POSIX_C_SOURCE=200809L

LDLIBS += -lm

# Targets

all: c8_dasm c8_asm c8_terp

c8_dasm: c8_dasm.o
    $(CC) $(LDLIBS) c8_dasm.o -o $@

c8_asm: c8_asm.o
    $(CC) $(LDLIBS) c8_asm.o -o $@

c8_terp: c8_terp.o
    $(CC) $(LDLIBS) c8_terp.o -o $@

# Using implicit rules for updating an '.o' file from a correspondingly
# named '.c' file.

c8_dasm.o: chip8.h
c8_asm.o: chip8.h
c8_terp.o: chip8.h

.PHONY: clean
clean:
    rm c8_dasm c8_asm c8_terp c8_dasm.o c8_asm.o c8_terp.o

我得到了我所有的 .o 文件,我的可执行文件是在项目目录中创建的。

发展项目

但我想做的是将我的源文件(所有 .c 和 .h)放在 src 目录中。我想构建一个 obj 目录并将可执行文件放入 bin 目录中。所以我的项目看起来像这样:

project
  src
    c8_asm.c
    c8_dasm.c
    c8_terp.c
    chip8.h
  Makefile

子目录结构,Makefile 不工作

为了适应上述情况,我相应地更改了 Makefile:

CC = gcc

CFLAGS += -c -Wall -std=c99
CFLAGS += -D_POSIX_C_SOURCE=200809L

LDLIBS += -lm

SRC_DIR = src
OBJ_DIR = obj
BIN_DIR = bin

SOURCES := $(wildcard $(SRC_DIR)/*.c)
OBJECTS := $(SOURCES:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o)

MKDIR_P ?= mkdir -p

# Targets

all: $(BIN_DIR)/c8_dasm $(BIN_DIR)/c8_asm $(BIN_DIR)/c8_terp

$(BIN_DIR)/c8_dasm: $(OBJ_DIR)/c8_dasm.o
    $(CC) $(LDLIBS) $(OBJ_DIR)/c8_dasm.o -o $@

$(BIN_DIR)/c8_asm: $(OBJ_DIR)/c8_asm.o
    $(CC) $(LDLIBS) $(OBJ_DIR)/c8_asm.o -o $@

$(BIN_DIR)/c8_terp: $(OBJ_DIR)/c8_terp.o
    $(MKDIR_P) $(dir $@)
    $(CC) $(LDLIBS) $(OBJ_DIR)/c8_terp.o -o $@

$(OBJECTS): $(OBJ_DIR)/%.o : $(SRC_DIR)/%.c
    $(MKDIR_P) $(dir $@)
    $(CC) $< -o $(OBJ_DIR)/$@

# Using implicit rules for updating an '.o' file from a correspondingly
# named '.c' file.

$(OBJ_DIR)/c8_dasm.o: $(SRC_DIR)/chip8.h
$(OBJ_DIR)/c8_asm.o: $(SRC_DIR)/chip8.h
$(OBJ_DIR)/c8_terp.o: $(SRC_DIR)/chip8.h

.PHONY: clean
clean:
    rm -r $(BUILD_DIR)
    rm $(OBJECTS)

运行后我得到以下信息:

mkdir -p obj/obj/
gcc src/c8_dasm.c -o obj/c8_dasm.o
gcc -lm obj/c8_dasm.o -o bin/c8_dasm
ld: can't link with a main executable file 'obj/c8_dasm.o' for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [bin/c8_dasm] Error 1

我想在这里停下来寻求一些帮助,因为我担心我制作的这个 Makefile 太复杂了,我正试图避免养成坏习惯。

我希望听到关于我在这里没有正确概念化的内容的意见。

第一次更新

我设法一点一点地使用它并使其基本正常工作。这是我最终得到的结果:

CC = gcc

CFLAGS += -c -Wall -std=c99
CFLAGS += -D_POSIX_C_SOURCE=200809L

LDLIBS += -lm

# Directories.

SRC_DIR = src
BIN_DIR = bin

$(shell mkdir -p $(BIN_DIR))

# Patterns for files.

SOURCES := $(wildcard $(SRC_DIR)/*.c)
OBJECTS := $(SOURCES:$(SRC_DIR)/%.c=$(SRC_DIR)/%.o)

EXECUTABLES := c8_dasm c8_asm c8_terp

# Targets

all: $(EXECUTABLES)

c8_dasm: $(SRC_DIR)/c8_dasm.o
    $(CC) $^ $(LDLIBS) -o $(BIN_DIR)/$@
    @echo "C8 Disassembler Built"

c8_asm: $(SRC_DIR)/c8_asm.o
    $(CC) $^ $(LDLIBS) -o $(BIN_DIR)/$@
    @echo "C8 Assembler Built"

c8_terp: $(SRC_DIR)/c8_terp.o
    $(CC) $^ $(LDLIBS) -o $(BIN_DIR)/$@
    @echo "C8 Interpreter Built"

# Using implicit rules for updating an '.o' file from a correspondingly
# named '.c' file.

c8_dasm.o: $(SRC_DIR)/chip8.h
c8_asm.o: $(SRC_DIR)/chip8.h
c8_terp.o: $(SRC_DIR)/chip8.h

.PHONY: clean
clean:
    rm $(OBJECTS)
    rm -r $(BIN_DIR)

当然,正如我在 Make 中发现的那样,这会导致其他难以理解的问题。例如这样做:

make
make clean

工作正常。意味着生成所有文件并清理文件,包括 bin 目录。

但是,如果我这样做:

make c8_dasm
make clean

构建良好。但是清理无法删除 bin 目录(尽管它确实删除了目标文件)。无论我尝试构建哪个可执行文件,都会发生这种情况。

再多的搜索也无法帮助我找出原因。

第二次更新

我发现问题也解决了。它只需要对 clean 目标中的 rm 语句使用“-f”。

第三次更新

为了使目标文件目录部分正常工作,我尝试(来自:path include and src directory makefile)按如下方式构建我的 Makefile:

CC = gcc

CFLAGS += -c -Wall -std=c99
CFLAGS += -D_POSIX_C_SOURCE=200809L

LDLIBS += -lm

SRC_DIR = src
OBJ_DIR = obj
BIN_DIR = bin

$(shell mkdir -p $(BIN_DIR))
$(shell mkdir -p $(OBJ_DIR))

SOURCES := $(wildcard $(SRC_DIR)/*.c)
OBJECTS := $(SOURCES:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o)

EXECUTABLES := c8_dasm c8_asm c8_terp

all: $(EXECUTABLES)

c8_dasm: $(SRC_DIR)/c8_dasm.o
    $(CC) $^ $(LDLIBS) -o $(BIN_DIR)/$@
    @echo "C8 Disassembler Built"

c8_asm: $(SRC_DIR)/c8_asm.o
    $(CC) $^ $(LDLIBS) -o $(BIN_DIR)/$@
    @echo "C8 Assembler Built"

c8_terp: $(SRC_DIR)/c8_terp.o
    $(CC) $^ $(LDLIBS) -o $(BIN_DIR)/$@
    @echo "C8 Interpreter Built"

$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c
    $(CC) $(CFLAGS) -c $< -o $(BIN_DIR)/$@

.PHONY: clean
clean:
    rm -rf $(BIN_DIR)
    rm -f $(OBJECTS)

我能够使用 chip8.h 将原来的三行压缩到一个目标中,但我无法知道这是否正确。它至少编译。我还更改了 OBJECTS 行以反射(reflect)我创建的新 OBJ_DIR

但是,这并没有将目标文件放在正确的位置。它仍然将它们放在 src 目录中,而不是 obj 目录中。

最佳答案

这就是为什么不对 Makefile 做任何复杂的事情是有意义的。只需将实际的目录名称放入您的命令中即可。永远不要依赖通配符。

使用 C 和 C++ 以及使用 Makefile 的人花费太多时间试图让它们工作,而不是仅仅实际完成工作。这就是为什么您会看到如此多的问题以及答案差异如此之大的原因。

在您的特定情况下,您的目标并不总是必须包含目录,这是问题的一部分。生成的规则在您的文件中没有实际目标,因为您将目录添加到所有内容之前。您必须考虑每个目标生成的内容:意思是输出。因此,如果 c8_dasm 正在获取输出,那就是您的目标。该目录与此无关。因此,您需要删除所有不需要的目录替换。

但在此之前,问问自己:如果您的第一个解决方案有效,为什么要改变它?在使用 Make 时最好不要创建目录。只需将所有内容都放在与开​​始时相同的目录中。您甚至可以看到,这让您的 Makefile 变得更加简洁。

关于makefile - 将 Makefile 从平面目录结构发展为子目录结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54693275/

相关文章:

c++ - 如何在makefile中添加.c、.cc和.so文件来创建另一个.so?

c - 在Solaris9机器上生成文件错误: Dont know how to make target

ubuntu - 理解 make.config 文件

gcc - 什么调用了这个模式匹配规则?

c - 等待运行目标的 makefile 列表

c++ - 在 Visual Studio (2005) 下使用 Makefile 代替解决方案/项目文件

Makefile 缺少分隔符。停止

c++ - “Make”因Clang错误而失败-如何从Clang中获取错误?

makefile - Make 不喜欢 $(addprefix a,b)

c++ - 查找二进制到 C/C++ 的链接