【问题标题】:How to link and compile a C++ project for Linux using CMake? (Adapting CMakeLists.txt)如何使用 CMake 为 Linux 链接和编译 C++ 项目? (改编 CMakeLists.txt)
【发布时间】:2020-12-14 06:08:15
【问题描述】:

上下文:我想为 Linux、Windows 和 MacOS 创建一个 GameClient、GameServer 和自定义库(客户端和服务器都使用)跨平台。服务器和客户端都依赖于我更喜欢​​静态链接的各种库和标题。我听说我可以通过将我目前 VisualStudio 开发的 3 个项目包装到 CMake 环境中来做到这一点,所以我首先为 Server 和 Shared lib 制作 CMakeLists.txt,它们是最简单的并且具有较少的库依赖项。

问题:如何调整这些 CMakeLists.txt(目前在 Windows 上构建),以便我可以选择是为 Windows 还是 Linux(最好是从 Windows)编译? p>

共享库 CMakeLists.txt:

cmake_minimum_required(VERSION 3.10)

project(MORPH_Server VERSION 1.0)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

set(SFML_STATIC_LIBRARIES TRUE) #Link to statically
set(SFML_DIR "C:/libraries/SFML/SFMLx64SourceAndCompile")
find_package(SFML 2.5.1 COMPONENTS system network REQUIRED)

add_library(MORPH_Shared_Functions)
target_sources(MORPH_Shared_Functions
               PRIVATE Transform.cpp
               PRIVATE World.cpp
               PRIVATE Player.cpp
               PRIVATE PropsData.cpp
               PRIVATE Prop.cpp
               PRIVATE udp_network_manager.cpp
               PRIVATE utility_functions.cpp
               )

target_link_libraries(MORPH_Shared_Functions 
                      sfml-system #Basically already found sfml modules and their dependencies (find-package)
                      sfml-network
                      )

target_include_directories(MORPH_Shared_Functions
                           PRIVATE C:/libraries/SFML/SFML-2.5.1/include
                           PRIVATE C:/libraries/GLM/glm
                           PRIVATE C:/libraries/CEREAL
                           )

服务器 CMakeLists.txt:

cmake_minimum_required(VERSION 3.10)

project(MORPH_Server VERSION 1.0)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

configure_file(CmakeConfig.h.in CmakeConfig.h)

set(SFML_STATIC_LIBRARIES TRUE) #Link to statically
set(SFML_DIR "C:/libraries/SFML/SFMLx64SourceAndCompile")
find_package(SFML 2.5.1 COMPONENTS system network REQUIRED)

add_executable(MORPH_Server)
target_sources(MORPH_Server
               PRIVATE Main.cpp 
               PRIVATE Game.cpp
               PRIVATE CommandManager.cpp
               PRIVATE ServerPlayer.cpp
               PRIVATE ServerProp.cpp
               PRIVATE ServerWorldState.cpp
               PRIVATE UDP_Server_CS.cpp
               )
add_library(MORPH_Shared_Functions STATIC IMPORTED)

set_target_properties(MORPH_Shared_Functions PROPERTIES IMPORTED_LOCATION C:/Users/dylan/Desktop/MORPH/MORPH_Shared_Functions/Build/Debug/MORPH_Shared_Functions.lib)

target_link_libraries(MORPH_Server
                      MORPH_Shared_Functions 
                      sfml-system
                      sfml-network
                      )

target_include_directories(MORPH_Server 
                           PRIVATE C:/Users/dylan/Desktop/MORPH/MORPH_Shared_Functions
                           PRIVATE C:/libraries/SFML/SFML-2.5.1/include
                           PRIVATE C:/libraries/GLM/glm
                           PRIVATE C:/libraries/CEREAL
                           )

【问题讨论】:

  • 您可能希望从删除 set(SFML_DIR "C:/libraries/SFML/SFMLx64SourceAndCompile") 和使用 c:/ 的其他部分开始。
  • 通过用 Linux 系统上的路径替换它们?所以我可以为 Linux 编译我的项目的唯一方法实际上是在 Linux 上执行 cmake?我无法按照我的要求从 Windows 执行此操作?
  • 您要求交叉编译。这与 CMake 关系不大……你需要一个交叉编译器设置。
  • 在 Windows 上执行此操作的一种方法是使用 WSL,尽管您仍然在 linux 上执行此操作。
  • 一般来说,这不值得麻烦。交叉编译器设置是棘手的问题。如果您想干净利落,您将需要一台 Linux 机器(或双启动或虚拟机)来测试您的二进制文件,那么为什么不将它也用于构建呢? 这样可以减少很多头痛。

标签: c++ linux cmake


【解决方案1】:

您很幸运,因为您使用的三个库都正确导出了它们的包。

在 linux 上,我假设库安装在 ~/workspace/libraries

诀窍是对所有依赖项使用find_package,并且必须删除每个自定义Findxyz.cmake 文件。

然后,您必须删除目录名称的 ALL_CAPS。这对于 CMake 正确查找包很重要。在 Windows 上可能无关紧要,因为文件系统不区分大小写,但在 linux 上是。

首先,为了让您的 CMakeLists 完全不知道在哪里可以找到包,我们将创建一个名为 linux-dependencies.cmake 的新文件,其中包含以下内容:

list(APPEND CMAKE_PREFIX_PATH "$ENV{HOME}/workspace/libraries")
list(APPEND CMAKE_PREFIX_PATH "path/to/morph/build") # can be relative

还有windows-dependencies.cmake:

list(APPEND CMAKE_PREFIX_PATH "C:/libraries/")
list(APPEND CMAKE_PREFIX_PATH "$ENV{HOMEPATH}/Desktop/MORPH/build")

当然,如果您将库安装目录设置为相对于项目或在两个平台上相对于主目录,则可以只有一个dependencies.cmake文件。

然后,在您下一次调用 cmake 时,只需告诉 cmake 包含该文件:

cmake .. -DCMAKE_PROJECT_INCLUDE=linux-dependencies.cmake

请注意,CMAKE_PROJECT_INCLUDE 仅在 CMake 3.15 之后可用。如果您不想升级 CMake 或只是提供默认值,您可以随时手动包含该文件。

您的 CMakeLists 文件应如下所示:

cmake_minimum_required(VERSION 3.15)

project(MORPH_Server VERSION 1.0)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

configure_file(CmakeConfig.h.in CmakeConfig.h)

find_package(SFML 2.5.1 COMPONENTS system network REQUIRED)
find_package(glm 0.9.9 REQUIRED)
find_package(cereal REQUIRED) # cereal don't export version info as far as I know

# You will have to export your target or write a MORPH-config.cmake file
find_package(MORPH REQUIRED)

add_executable(MORPH_Server)
target_sources(MORPH_Server
               PRIVATE Main.cpp 
               PRIVATE Game.cpp
               PRIVATE CommandManager.cpp
               PRIVATE ServerPlayer.cpp
               PRIVATE ServerProp.cpp
               PRIVATE ServerWorldState.cpp
               PRIVATE UDP_Server_CS.cpp
               )

# this take care of both linking and include dirs
target_link_libraries(MORPH_Server
                      MORPH_Shared_Functions 
                      sfml-system
                      sfml-network
                      glm
                      cereal
                      )

另一件有帮助的事情:在安装库时将CMAKE_PREFIX_PATH 设置为CMAKE_INSTALL_PREFIXCMAKE_INSTALL_PREFIX/libname


如果你想在 windows 上编译 linux 可执行文件,我建议简单地使用 WSL 或在 Docker 中运行编译器。 VSCode 实际上有一个很好的扩展,可以使用 docker 作为构建环境,据我所知,也可以使用 WSL 作为构建和运行环境。

【讨论】:

  • "然后,您必须删除目录名称的 ALL_CAPS。这对于 CMake 正确查找包很重要。" - 第一次听说这个 CMake 约束。为什么目录不以大写命名很重要?
  • @Tsyvarev 这是因为包名称(share/cmakexyz-config.cmake 文件中的目录名称)具有库定义的大小写。如果将安装目录放在与包名不对应的前缀中,并将CMAKE_PREFIX_PATH设置为父目录,则不会在其中搜索。
  • 因此,如果我们有 /install/ 的前缀路径并且在内部安装了库,例如 /install/glm /install/cereal,CMake 将在其中搜索。但是将它们全部大写会使它们在 linux 上技术上不同的目录名称。
【解决方案2】:

我的 2 美分,

1

根据find_package()搜索程序文档:

在所有情况下,<name> 都被视为不区分大小写,并对应于指定的任何名称(<PackageName>NAMES 给出的名称)。

参考:https://cmake.org/cmake/help/latest/command/find_package.html#search-procedure

注意:我个人更喜欢使用PackageNameConfig.cmake 而不是package-name-config.cmake ...

2

为什么不简单地在配置命令行中添加CMAKE_PREFIX_PATH 而不是通过您的dependencies.cmake 文件?

3

对于静态链接,我会使用FetchContent() 方法来控制“静态性”,因为大多数 Linux 发行版都倾向于将库构建为共享库,然后将它们放在 FHS 之后的目录中。

3.1

您还可以使用标志来允许用户根据需要使用系统范围的库(因此发行版维护者很高兴,并且您可以轻松创建独立的工件)。
警告:大多数库不打算在超级构建中使用,因此您可能需要修补它们(即PATCH_COMMAND git apply ...

# enable CMP0077 so superbuild can set it.
option(BUILD_THIRDPARTY "Build the third party" ON)
...
# to allow recursive superbuild (add_subdirectory(morph_server) etc...) 
# first check if third party target is not already present
# see: https://gitlab.kitware.com/cmake/cmake/-/issues/17735
if(NOT TARGET ThirdParty::ThirdParty)
  if(BUILD_THIRDPARTY)
   FetchContent_Declare(...)
   set(BUILD_SHARED_LIBS OFF)
   set(BUILD_EXAMPLES OFF) ...
   FetchContent_MakeAvailable(third_party)
   set(BUILD_SHARED_LIBS ${BUILD_SHARED_BCKP})
  else()
    find_package(ThirdParty REQUIRED)
  endif()
endif()

4

您也可以提供默认的FindThirdParty.cmake

if(UNIX)
  list(APPEND CMAKE_PREFIX_PATH "$ENV{HOME}/workspace/libraries")
  list(APPEND CMAKE_PREFIX_PATH "path/to/morph/build") # can be relative
else()
  list(APPEND CMAKE_PREFIX_PATH "C:/libraries/")
  list(APPEND CMAKE_PREFIX_PATH "$ENV{HOMEPATH}/Desktop/MORPH/build")
endif()

find_package(ThirdParty CONFIG REQUIRED)

因此您不必使用CMAKE_PREFIX_PATHdependencies.cmake 等,用户仍然可以提供自己的FindThirdParty.cmake 并在他们想要更改时添加CMAKE_PREFIX_PATH...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-03-16
    • 2017-06-14
    • 2021-06-24
    • 1970-01-01
    • 1970-01-01
    • 2021-09-14
    相关资源
    最近更新 更多