查看下面的更新
已完成研究:我发现很难学习如何将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
目录(尽管确实删除了目标文件)。无论我尝试构建哪种可执行文件,都会发生这种情况。
大量搜索都无法帮助我找出原因。
第二次更新
我发现问题也解决了。只需在干净目标中对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
行以反映我创建的新OBJ_DIR
。
但是,这不能将目标文件放在正确的位置。仍然会将它们放在src
目录中,而不是obj
目录中。
答案 0 :(得分:1)
这就是为什么不对Makefile做任何复杂的事情是有道理的。只需在命令中输入实际目录名称即可。永远不要依赖通配符。
使用C和C ++以及使用Makefiles的人们花太多时间试图使它们工作,而不仅仅是实际完成工作。这就是为什么您会看到如此多的问题以及答案如此之大的原因。
在您的特定情况下,目标不一定总是包含目录,这就是问题的一部分。由于要在所有内容之前添加目录,因此生成的规则在文件中没有实际的目标。您必须考虑每个目标生成的内容:含义,即输出。因此,如果c8_dasm获得输出,那就是您的目标。该目录与此无关。因此,您需要删除所有不需要的目录替换。
但是在执行此操作之前,请先自问:如果您的第一个解决方案有效,为什么要更改它?最好不要在使用Make时执行目录。只需将所有内容都放在与开始时相同的目录中即可。您甚至可以看到,这使您的Makefile更加整洁。
答案 1 :(得分:1)
我相信我可能已经知道了。以下是我的Makefile。它似乎可以满足我的要求。它执行以下操作:
obj
目录中。bin
目录中生成可执行文件。.c
文件被更改并相应地重新编译。.h
文件是否已更改,并重新编译引用该文件的所有C文件。这似乎满足所有条件,但是我无法确定我是否已经将自己画在一个看不见的角落。
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: $(OBJ_DIR)/c8_dasm.o
$(CC) $^ $(LDLIBS) -o $(BIN_DIR)/$@
@echo "C8 Disassembler Built"
c8_asm: $(OBJ_DIR)/c8_asm.o
$(CC) $^ $(LDLIBS) -o $(BIN_DIR)/$@
@echo "C8 Assembler Built"
c8_terp: $(OBJ_DIR)/c8_terp.o
$(CC) $^ $(LDLIBS) -o $(BIN_DIR)/$@
@echo "C8 Interpreter Built"
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c $(SRC_DIR)/chip8.h
$(CC) $(CFLAGS) -c $< -o $@
.PHONY: clean
clean:
rm -rf $(BIN_DIR)
rm -rf $(OBJ_DIR)
答案 2 :(得分:0)
Stackoverflow抱怨太多评论,因此我将其作为另一个“答案”。经过我们反复对我的原始评论,您的最后一条评论是正确的。那就是我希望你看到的。
了解您不能使用Make做您想做的事情。
因此,这实际上是答案:您不能创建多个可执行文件,并且在使用目录结构的同时仅将某些目标文件应用于每个文件并且。那个。
现在,您正以一种非预期的方式尝试使用Make,这就是为什么您遇到很多问题的原因。如果您不停地玩耍,将会遇到一系列“重复符号”错误,因为您将遵循大多数建议,为每个可执行文件多次编译每个文件。
查看此How can I create a Makefile for C projects with SRC, OBJ, and BIN subdirectories?以了解我的意思。之所以这样,是因为所有目标文件都用于创建单个可执行文件。但是正如您所说的,事实并非如此。这就是Make无法处理的。这就是为什么您找不到答案的原因。
虽然您的chip8.h文件现在不会在允许您进行编译方面引起问题,但是当chip8.h文件本身发生更改时,带有第三个更新的Makefile将无法识别。您将必须更改.c文件以强制重新编译,以便可以识别对.h的更改。因此,您要么必须坚持第二次更新,要么使用Make以外的其他功能。