首页 > 解决方案 > CMakeLists C++ 初学者

问题描述

我已经开始玩一点 C++,为了实现它,我决定编写一个简单的游戏引擎。

为此,我使用 CLion 作为我的 IDE,它运行良好,但添加库只是一场噩梦。首先,我使用 brew 安装了所有必需的库,如 glew、glfw 或 glm,一切正常。然后我花了将近 2 个小时让它在我的项目上工作。

我最大的谜团是它工作的原因,我曾使用 java、python 或 golang 构建系统,一切对我来说总是很清楚。但是,我不知道为什么它会以这种方式工作,我很想知道!

这是我的 CMakeLists 文件。

cmake_minimum_required(VERSION 3.10)

project(untitled2)

find_package(GLEW REQUIRED)
find_package(GLFW3 REQUIRED)

set(CMAKE_CXX_STANDARD 17)

add_executable(untitled2 main.cpp)
target_link_libraries(untitled2 ${GLEW_LIBRARIES})
target_link_libraries(untitled2 glfw)

现在我有几个问题: 1. 为什么我可以使用 GLM 库而不将其包含在 CMakeLists 中?2. 为什么我需要包括glfw和glew而不包括glm?3. 为什么我需要使用 ${GLEW_LIBRARIES} 而不是 glew 之类的名称?(我尝试了不同的名称,但没有任何效果。)

顺便提一句。我正在使用 macOS。

标签: c++macoscmakeglfwglew

解决方案


首先要记住的是,C++(还)没有像新语言那样的真正的模块系统。它只有一个用于搜索头文件的目录列表、一个用于搜索库的目录列表以及一个用于在链接时搜索符号的库列表。该target_link_libraries指令只是添加了添加到这三个列表的编译器标志。

现在,进入这个特定的场景。大多数魔法发生在find_package指令中。该指令实际上只是最终运行 cmake 脚本。有时它们与 cmake 一起打包,有时它们与您找到的包一起安装。最后,这些脚本基本上可以为所欲为。它们都有相同的目标,为您提供一种添加适当编译器标志以使用包的方法,但它们有几种常见的方法。

较旧的方法是设置变量,您可以使用这些变量告诉编译器搜索头文件和库的目录以及链接到的库。这就是 GLEW 似乎采取的方法。它设置变量GLEW_LIBRARIESGLEW_INCLUDE_DIRS然后您必须使用link_librariesandinclude_directories告诉编译器要做什么。这是旧版本 cmake(2.8 IIRC 之前)中唯一可用的方法,因此虽然使用起来不太好,但仍然有多少库的find_package脚本可以工作。

较新的方法是创建导入的目标。这些目标设置了适当的属性,以便链接到导入目标的任何目标都继承适当的包含目录和库标志。这是 GLFW 采用的方法。它创建一个名为的导入目标glfw,该目标具有INTERFACE_INCLUDE_DIRECTORIESINTERFACE_LINK_LIBRARIES属性集。当您将其传递给 时target_link_libraries,您的untitled2目标将继承那些包含目录和库。

最后,GLM 是一个只有头文件的库。没有要链接的库文件,因此只要将适当的目录添加到编译器头文件搜索路径,您就可以包含和使用 GLM。


由于您使用自制软件安装库,因此它们的所有标头都可能位于同一基本目录下;最有可能是“/usr/local/include”。他们所有的库文件都可能在同一个目录下;可能是“/usr/local/lib”。这意味着您的编译器将能够找到它们的任何头文件和库,如果您告诉它在“/usr/local/include”中搜索头文件并在“/usr/local/lib”中搜索库。

所以,最后回答这个问题:事情的工作是因为glfw目标告诉 cmake 它应该设置编译器标志以将“/usr/local/include”添加到它的包含目录列表中。由于这是搜索 GLM 和 GLEW 所需的同一目录,因此编译器能够找到所有库的标头。编译器还能够找到它需要链接到的库文件,因为 cmake 告诉它通过列表GLEW_LIBRARIES和从glfw目标继承的属性显式查找它们。GLM 没有任何要链接的库文件,所以没有什么可说的。


不过,您真的不应该依赖于所有东西都在同一个地方。你应该能够告诉编译器这样的事情(注意我还没有实际测试过):

cmake_minimum_required(VERSION 3.10)
project(untitled2)
set(CMAKE_CXX_STANDARD 17)

add_executable(untitled2 main.cpp)

# This will fill the variables GLEW_INCLUDE_DIRES and GLEW_LIBRARIES
# that you can use to add the appropriate compiler flags
find_package(GLEW REQUIRED)

# This will create an imported target named glfw that you can link to
# to inherit the appropriate include directories and libraries
find_package(GLFW3 REQUIRED)

# This also creates an imported target named glm that you can "link to"
# to inherit the appropriate include directories
find_package(glm REQUIRED)

# GLEW uses an old-style find_package script, so you have to
# explicitly tell cmake about GLEW's include directories
target_include_directories(untitled2 PUBLIC ${GLEW_INCLUDE_DIRS})
# And the library files to link to
target_link_libraries(untitled ${GLEW_LIBRARIES})

# cmake will automatically add the appropriate include directories
# and library files that the imported glfw target tells it about
target_link_libraries(untitled2 glfw)

# You use the target_link_libraries directive with the glm imported target
# even though you're not actually linking to any libraries.  It's just how
# you tell cmake you want your untitled2 target to inherit the appropriate
# include directories from the imported glm target
target_link_libraries(untitled2 glm)

推荐阅读