【问题标题】:Makefile specific to directory structure特定于目录结构的 Makefile
【发布时间】:2023-09-18 11:03:01
【问题描述】:

我正在尝试使用 makefile 编译我的项目。我是makefile的新手,所以请原谅我的无知。我遇到的问题是我希望输出命令构建所有 .o 文件,但是如果我这样运行它,它将不会构建 .o 文件,而是会尝试编译所有 .o 文件/obj 目录。

如何编译输出规则中的所有 .o 文件规则?

The directory structure for this project is:
recipe_book
|-GFX         (source directory)
|-PageManager (source directory)
|-Pages       (source directory)
|-main        (source directory)
|-obj         (.o files directory)
|-out         (application output)

这是我的 Makefile:

#COMPILER_FLAGS specifies the additional compilation options we're using
# -w suppresses all warnings
COMPILER_FLAGS = -w -g

#LINKER_FLAGS specifies the libraries we're linking against
LINKER_FLAGS = -lSDL2 -lSDL2_image

G++ = g++ -std=c++17 $(LINKER_FLAGS) $(COMPILER_FLAGS)

#Source Directories:
GFX := GFX
PAGE_MANAGER := PageManager
PAGES := Pages

#Object Directory:
OBJ := obj

# Complile the objects into the executable
output: main/main.cpp main/main.hpp $(OBJ)/*.o
    $(G++) main/main.cpp $(OBJ)/*.o -o out/recipe_book.out

#make all object files
#'.' turns them into macros?
#GFX
$(OBJ)/%.o: $(GFX)/%.cpp $(GFX)/%.hpp
    $(G++) -I$(GFX) -c $< -o $@

#PageManager
$(OBJ)/%.o: $(PAGE_MANAGER)/%.cpp $(PAGE_MANAGER)/%.hpp
    $(G++) -I$(PAGE_MANAGER) -c $< -o $@

#Pages
$(OBJ)/%.o: $(PAGES)/%.cpp $(PAGES)/%.hpp
    $(G++) -I$(PAGES) -c $< -o $@

clean:
    rm out/*.out obj/*.o

【问题讨论】:

  • 你必须使用make文件吗?其他构建系统更容易学习/使用
  • 我使用 make 只是为了让事情变得简单......如果我列出所有生成的 .o 文件,我觉得如果我能弄清楚最后一个,使用 make 会很好部分。我也在为多个平台(Linux、Rasperry Pi、Windows)开发这个
  • 您想要工作目录中的目标文件 (.o)?如果是这样,那么我认为将OBJ := obj 更改为OBJ := . 就足够了。
  • 哦,对不起,我可能没有把自己说得那么清楚。我希望 .o 文件位于 obj 目录中。问题是,当我构建它时,它不会构建任何 .o 文件。我想让输出将 obj 目录中的所有内容编译到 out 目录中,以保持整洁。

标签: c++ makefile


【解决方案1】:

这一行:

output: main/main.cpp main/main.hpp $(OBJ)/*.o

无法按所写的那样正常工作。这是做什么的?它告诉make,目标output(顺便说一句,你从来没有真正创建过:这条规则创建了一个完全不同的文件out/recipe_book.out,但现在把它放在一边)取决于文件main/main.cppmain/main.hpp ,以及 glob 表达式 obj/*.o 的扩展。因此,make 查找与 glob 表达式 obj/*.o 匹配的文件,即所有 已经存在的目标文件

如果所有目标文件都已经存在,那么它们都将被链接。如果你还没有创建任何目标文件,那么它们都不会被找到(就像你运行ls obj/*.o没有找到文件一样),所以它们都不会被链接。

您必须在此处列出要链接的明确目标文件集。您不能使用 glob 表达式来查找它们。

您可以使用 glob 表达式查找始终(可能)存在的 文件,然后将它们转换为目标文件。也许是这样的:

SOURCES := $(wildcard $(GFX)/*.cpp $(PAGE_MANAGER)/*.cpp $(PAGES)/*.cpp)
OBJECTS := $(addprefix $(OBJ)/,$(patsubst %.cpp,%.o,$(notdir $(SOURCES))))

output: main/main.cpp main/main.hpp $(OBJECTS)

(顺便说一句,你真的应该为这个目标使用真实的文件名而不是output:在 make 中,所有非虚假目标都应该始终使用它们创建的实际文件的名称)。

【讨论】:

    【解决方案2】:

    您的output 目标具有$(OBJ)/*.o 作为先决条件。

    但是,当尚未构建任何内容时,该通配符将扩展为空!

    您将需要列出项目最终链接所需的对象,例如

    output: main/main.cpp main/main.hpp $(OBJ)/GFX.o $(OBJ)/PageManager.o $(OBJ)/Pages.o
    

    这将在需要时触发这些文件的创建(通过编译)。

    您可以通过将GFX PageManager Pages 放入变量中,然后执行替换以将$(OBJ)/ 附加到每个变量并将.o 附加到每个变量来简化它。

    顺便说一句,我可能会将main 加入其中,而不是特别对待它。

    【讨论】:

    • 哎呀,没有意识到这是一个旧标签,在此期间有一个答案? 嗯