如何重写我的 makefile 以便重构我的项目目录?

How do I rewrite my makefile so that I can restructure the directories of my project?

我一直在开发一个完全用 C++ 编写的小游戏,使用 SDL 处理图形和音频。到目前为止,我所有的源文件和 headers 都位于与我的可执行文件相同的目录中,但我想将它们移动到 ./src 和 ./include 文件夹中。

但是,我真的很难理解 makefile 语法以及如何告诉编译器在哪里可以找到源文件和 header 文件。

如何告诉 makefile 在子目录中查找 source/headers/objects?我的 makefile 有 improvements/mistakes 吗?

这是我当前的 makefile:

OBJS = main.o Window.o Entity.o Player.o Tile.o BoundingBox.o\
     MapReader.o Button.o Zombie.o Projectile.o Pathfinder.o
CPP = g++
CPPFLAGS = -c -g
LDFLAGS = -lmingw32 -lSDL2main -lSDL2 -lSDL2_image -lSDL2_mixer -mwindows

ZombieAttack: $(OBJS)
    $(CPP) -o ZombieAttack $(OBJS) $(LDFLAGS)

main.o: main.cpp
    $(CPP) $(CPPFLAGS) main.cpp

Window.o: Window.cpp
    $(CPP) $(CPPFLAGS) Window.cpp

Entity.o: Entity.cpp
    $(CPP) $(CPPFLAGS) Entity.cpp

Player.o: Player.cpp
    $(CPP) $(CPPFLAGS) Player.cpp

Tile.o: Tile.cpp
    $(CPP) $(CPPFLAGS) Tile.cpp

BoundingBox.o: BoundingBox.cpp
    $(CPP) $(CPPFLAGS) BoundingBox.cpp

MapReader.o: MapReader.cpp
    $(CPP) $(CPPFLAGS) MapReader.cpp

Button.o: Button.cpp
    $(CPP) $(CPPFLAGS) Button.cpp

Zombie.o: Zombie.cpp
    $(CPP) $(CPPFLAGS) Zombie.cpp

Projectile.o: Projectile.cpp
    $(CPP) $(CPPFLAGS) Projectile.cpp

Pathfinder.o: Pathfinder.cpp
    $(CPP) $(CPPFLAGS) Pathfinder.cpp

clean:
    del *.o

我正在使用 mingw 进行编译和 mingw32-make

如果您希望二进制文件位于最高级别的目录中,而 .h / .cpp 文件位于该目录的子目录中,您可以简单地在您希望访问的文件前加上 target directory/filename

例如:

MapReader.o: MapReader.cpp
    $(CPP) $(CPPFLAGS) MapReader.cpp

变成

MapReader.o: src/MapReader.cpp
    $(CPP) $(CPPFLAGS) src/MapReader.cpp

一般来说,最好在发布到 SO 之前尝试自己解决问题。然后,当您遇到无法解决的问题时,您应该具体询问该问题,而不是像这样宽泛的问题。

不过,过年了!!

首先,make 与编译器不是一回事。仅教 make 在哪里可以找到您的文件是不够的,您还必须教编译器在哪里可以找到您的文件。如果您打算将 header 文件放在源目录之外的其他目录中,那么您必须为编译器提供选项,告诉它在哪里可以找到这些 headers。仅仅告诉 make 在哪里可以找到它们对编译器没有任何帮助。

其次,除非你真的有紧迫的需要,否则你应该使用 standard variables 来处理 C++ 编译器(CXX 而不是 CPP)和它的标志(CXXFLAGS 而不是 CPPFLAGS)。您也不应该将选择输出类型的选项 (-c) 放入通用标志变量中:这应该进入规则。最后,您应该确保区分 LDFLAGS 和 link 之间的区别,LDFLAGS 是 linker 的标志,LDLIBS 是 link 的库。在这里,您将 link 的所有库放入 LDFLAGS.

第三,您可以尽可能使用automatic variables来减少重复。

第四,您可以使用可以正确构建 C++ 文件的 pattern rule. In fact, GNU make provides default pattern rules 来大大简化您的 makefile,如果您像上面那样使用正确的变量,那么您甚至不必定义自己的变量。

这是原始 makefile 的一个版本,修复了上述变量并使用内置规则减少重复:

OBJS = main.o Window.o Entity.o Player.o Tile.o BoundingBox.o \
     MapReader.o Button.o Zombie.o Projectile.o Pathfinder.o

CXX = g++
CXXFLAGS = -g
LDFLAGS = -mwindows
LDLIBS = -lmingw32 -lSDL2main -lSDL2 -lSDL2_image -lSDL2_mixer 

ZombieAttack: $(OBJS)
        $(CXX) -o $@ $(LDFLAGS) $^ $(LDLIBS)

clean:
        del *.o

如果所有文件都在同一个目录中,那么该 makefile 的性能应该与您的原始 makefile 完全一样。

现在,如果你想移动一些东西,那么你需要先向编译器解释在哪里可以找到你的 headers,方法是在编译行中添加一个 -I 选项。如果您的 header 现在位于 include 子目录中,您可以这样做:

CXXFLAGS = -g -Iinclude

现在,如果您愿意将 object 文件与源文件放在同一目录中,那么您可以更改上面的 makefile 以告诉 make 在何处构建它们,如下所示:

BASE_OBJS = main.o Window.o Entity.o Player.o Tile.o BoundingBox.o \
     MapReader.o Button.o Zombie.o Projectile.o Pathfinder.o

SRC = src
OBJS = $(addprefix $(SRC)/,$(BASE_OBJS))

加上上述更改,它应该可以正常工作。

如果你想把 object 文件放在源文件以外的其他目录中,那么你将无法再使用 make 的 built-in 规则,因为 make 不知道你想放在哪里放东西,所以你必须编写自己的模式规则。