查看以下更新
完成的研究:我发现学习如何将 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/