使用CMake进行C++ SDL2独立开发和跨平台开发

myzjeezk  于 2023-03-02  发布在  其他
关注(0)|答案(3)|浏览(342)

我的目标是使用SDL 2构建一个C++应用程序,该应用程序可以在Linux和Windows上运行,但可能没有安装SDL 2(我知道有很多关于它的帖子,我稍后会介绍)*。
所以我回顾了我的小项目结构,我记得使用CMake使构建更有效率。所以我记录了很多,并总结了这个结构:

- app (contains the main function)
  |- main.cpp
  |- CMakeLists.txt
- build
- libs (contains externals libraries such as: "spdlog", "googletest")
- src
  |- all the source files
  |- CMakeLists.txt
- tests
  |- tests files
  |- CMakeLists.txt
CMakeLists.txt
  • 结构不是讨论的重点 *

现在,我的项目结构已经完成,我将关注于我的构建文件。
感谢许多帖子,我明白我应该通过静态链接。(我知道缺点,如重新编译静态库,没有最后更新自动,更大的文件,...)。我想分发我的程序作为一个“文件夹”,所以我排除了解决方案(软件包)安装程序。我的程序必须能够使用自己的资源(文件夹)运行。另外,请记住SDL 2附带了允许静态链接的“zlib许可证”。
所以我写了我的
src/CMakeLists.txt

set(PROJ_LIB_NAME "projectlib")

configure_file(
    ${CMAKE_CURRENT_SOURCE_DIR}/config/cmake_config.hpp.in 
    ${CMAKE_CURRENT_SOURCE_DIR}/config/cmake_config.hpp
)

set(PROJ_LIB_SOURCES
    core.cpp
    utils/logs_utils.cpp
    gui/gui.cpp
)

add_library(${PROJ_LIB_NAME} STATIC ${PROJ_LIB_SOURCES})
target_include_directories(${PROJ_LIB_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

### ====================================
### LIBRARIES
### ====================================

### spdlog
### ====================================
if(NOT TARGET spdlog)
    # Stand-alone build
    find_package(spdlog REQUIRED)
endif()
target_link_libraries(${PROJ_LIB_NAME} PRIVATE spdlog::spdlog)

### SDL2
### ====================================
find_package(SDL2 REQUIRED)
target_include_directories(${PROJ_LIB_NAME} PRIVATE ${SDL2_INCLUDE_DIRS})
target_link_libraries(${PROJ_LIB_NAME} PRIVATE -static ${SDL2_LIBRARIES})
  • 请注意最后一行的-static *

还有我的CMakeLists.txt(根目录):

cmake_minimum_required(VERSION 3.16.3 FATAL_ERROR)
enable_testing()

set(PROJ_NAME "progr")
set(PROJ_VERSION "1.0.0")

# set the project name
project(${PROJ_NAME} VERSION ${PROJ_VERSION})

# specify the C++ standard
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)

# specify compiler flags
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Werror -Wpedantic")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")

add_subdirectory(${CMAKE_SOURCE_DIR}/libs/googletest-1.11.0)
add_subdirectory(${CMAKE_SOURCE_DIR}/libs/spdlog-1.8.5)
add_subdirectory(${CMAKE_SOURCE_DIR}/src)
# add_subdirectory(${CMAKE_SOURCE_DIR}/tests)
# add_subdirectory(${CMAKE_SOURCE_DIR}/app)

最后,应用程序/CMakeLists.txt

set(PROJ_EXE_NAME "main")

add_executable(${PROJ_EXE_NAME} main.cpp)
target_link_libraries(${PROJ_EXE_NAME} PUBLIC projectlib)

如果我取消add_subdirectory(${CMAKE_SOURCE_DIR}/app)的注解来构建我的应用程序,我会得到一堆未定义的引用,这些引用来自“/usr/lib/gcc/x86_64-linux-gnu/9/libstdc ++. a”、“../libs/spdlog-1.8.5/libspdlogd.a”、“/usr/lib/gcc/x86_64-linux-gnu/9/../../x86_64-linux-gnu/libSDL2.a”..

到目前为止,我为使我的应用程序独立、可移植所做的尝试:

  • 下载SDL 2源代码并将其放在libs/中,告诉CMake将其视为子目录-〉这是愚蠢的,因为源代码旨在将SDL安装在您的计算机上,而不是用于您的项目...(按照SDL安装指南,您可以)
  • 正在寻找findsdl2.cmake文件-〉我真的需要那些无法理解的文件吗?而且,它对sdl 1很好,但对sdl 2不再是了。
  • 玩了这么多不同的参数在CMake因为2天...
  • 还有很多我现在记不起来了。
    没有一个单一的解决方案能让所有人都称赞

我不明白为什么没有教程来指导如何实现制作C++ SDL 2便携式应用程序的目标,即使是在没有安装SDL 2的机器上......每个人似乎都同意,像Steam这样的应用程序肯定会被安装,这取决于他们来管理SDL。那么不通过Steam的游戏呢?但话又说回来,我想知道这是不是一个好主意。作为一个JS开发者,当我使用NPM依赖项构建应用程序时,我会屏蔽我的软件包版本,以便所有开发人员使用相同的工具工作,并且我的生产环境稳定。我不理解这个动态库逻辑,所以我很乐意得到解释:)

**EDIT:**忘了提到我的项目库(src/CMakeLists.txt)构建得很好,但当我尝试将其与主函数(app/CMakeLists.txt)链接时,一切都出错了。
**EDIT 2:**有一个工作静态链接,但必须下载一些其他依赖项...我已经做了什么:

  • 我下载了SDL源代码并将其放在我的/libs文件夹(https://www.libsdl.org/download-2.0.php)中(不需要在中创建构建目录并运行“make”或“configure”命令。CMakeLists将为您处理此问题。另外,不要“make install”)
  • 在构建源代码之前,修改根目录CMakeLists.txt以添加add_subdirectory(${CMAKE_SOURCE_DIR}/libs/SDL2-2.0.14)
  • 修改我的src/CMakeLists.txt以注解所有SDL find_package内容(等...),并仅添加库链接。请参见:
### SDL2
### ====================================
## Comments
#set(SDL2_DIR ${CMAKE_SOURCE_DIR}/libs/SDL2-2.0.14)
#find_package(SDL2 REQUIRED)
#target_include_directories(${PROJ_LIB_NAME} PRIVATE ${SDL2_INCLUDE_DIRS})
#target_link_libraries(${PROJ_LIB_NAME} PRIVATE -static ${SDL2_LIBRARIES})
## end Comments

## the only needed link to SDL2
target_link_libraries(${PROJ_LIB_NAME} PRIVATE SDL2main SDL2-static)
  • 之后,运行我的应用程序给予我一些错误,如“没有可用的视频设备”在初始化的SDL,但我的应用程序正在建设!
  • 我发现我需要一些更多的依赖项,这些依赖项是在你执行sudo apt install libsdl2-dev时安装的(我猜)(我们想避免的)。你可以在这里找到所有的依赖项:https://github.com/libsdl-org/SDL/blob/main/docs/README-linux.md#build-dependencies
  • 我已经安装了依赖项...一切正常!

我想我的新问题现在是“* 如果我试图在没有这些依赖项的计算机上运行我的应用程序怎么办?*"。问这个问题是因为我设法在**安装它们之前编译和运行我的应用程序。我得出结论,我的可执行文件中有SDL代码,但没有依赖项代码。
我仍然想知道为什么静态链接总是如此妖魔化,而对我来说,它允许控制依赖项的版本。我同意“bug修复”的说法,但这些新版本可能与您的应用程序不兼容。在这种情况下,您应该向您的用户解释,您正在非常努力地工作,以发布修复时,您的代码不会导致回归...

ffx8fchx

ffx8fchx1#

回答标题中的问题,忽略问题中的错误。我将建议一个不同的解决方案。
在Windows上(假设MinGW)这很容易,静态链接并不重要:如果不这样做,您只需随可执行文件一起提供所有必要dll。
所需DLL的列表确定如下:

  • 确保MinGW和库附带的dll文件不与C:\Windows中的文件重叠(包括子目录)。如果你看到重叠,删除C:\Windows中匹配的dll文件。一些蹩脚的安装程序喜欢把自定义的dll文件放在那里,这往往会导致问题。
  • 打开一个shell,在其中将MinGW的bin/目录前置到PATH
  • 使用ntldd -R <filename.exe>获得一个dll列表。在MinGW的bin/中的那些你必须发布。

在Linux上,有几个突出的解决方案:一对一对一,一对二对一,一对三对一。
我没有太多的经验,但这是我一直在做的:

  • 确保你运行的是你想支持的最老的Linux版本(我用的是Ubuntu 18.04),你可以在Docker下运行它。
  • 从源代码构建SDL2,不要从软件包管理器获取。至少在Ubuntu上,我听说打包的版本在运行时不能动态查找可用的依赖项,降低了可移植性。
  • 使用ldd来确定依赖项列表。必须通过实验来确定需要提供哪些依赖项。首先将所有依赖项复制到当前目录。
  • 在所有这些库和您的可执行文件上使用patchelf --set-rpath '$ORIGIN' <filename>'很重要,您不希望$ORIGIN被扩展为shell变量)。
  • 将整个目录复制到另一个系统(或多个系统),看看它是否可以运行。在它开始运行之前,您必须删除一些复制的共享库。最后我只得到了libstdc++.so.6libgcc_s.so.1和我使用的库。

或者,您可以使用设置LD_LIBRARY_PATH而不是patchelf的启动程序脚本。

xqkwcwgp

xqkwcwgp2#

所有这一切都是以最新版本的CMake为前提的-如果这对您不起作用,您可能需要安装一个更新的版本。
首先,您需要编译SDL 2。在Linux上,对于最新版本,这意味着需要下载并构建如下所示的源代码:

wget https://www.libsdl.org/release/SDL2-2.0.14.tar.gz
tar -xvf SDL2-2.0.14.tar.gz
cd SDL2-2.0.14
mkdir build
cd build
cmake .. -DCMAKE_INSTALL_PREFIX=~/SDL2
make -j8
make install

这意味着您现在在机器上的~/SDL 2文件夹(您可能想更改此位置)中有一个构建的SDL 2副本。CMake有一个内置的FindSDL模块,该模块非常过时,但要使用它,请将以下内容添加到CMakeLists.txt中

find_package(SDL 2 REQUIRED)
# When building an application:
include_directories(${SDL_INCLUDE})
target_link_libraries(myapplication SDL::SDL)

然后,当您在命令行中调用以生成项目时,请执行以下操作:

cmake .. -DSDL_LIBRARY=~/SDL2/lib -DSDL_INCLUDE_DIR=~/SDL2/include

对于Windows,这与使用CMake的过程基本相同,只是如果您使用VS 2019,您可以通过GUI使用CMake,因此可以在GUI中设置参数,而不是在命令行上。如果您从开始菜单打开开发人员命令提示符,则可以在命令行上进行设置(这是必需的,以便为CMake设置要查找的编译器变量)。
至于其他库-在Linux上,您将始终需要依赖C标准库(libc)由系统提供(除非你使用musl)。通常,这是向前兼容的,但不是向后兼容的,所以许多开发人员在一个非常旧的操作系统上编译他们的代码,以确保它能在任何地方运行。你还需要在libstdc中进行静态链接。在Windows上,存在可再发行包(例如“Visual C Redistributable for Visual Studio 2019”),这些包通常随应用程序一起分发或假定已安装。

qij5mzcb

qij5mzcb3#

您可以使用我在项目中使用的方法,我使用SDL2和SLD_Image,并通过FetchContent将它们获取到其存储库上的特定TAG

cmake_minimum_required(VERSION 3.24)
project(sdl_test)

include(FetchContent)
Set(FETCHCONTENT_QUIET FALSE)

FetchContent_Declare(
        SDL2
        GIT_REPOSITORY https://github.com/libsdl-org/SDL.git
        GIT_TAG release-2.26.3
        GIT_SHALLOW TRUE
        GIT_PROGRESS TRUE
)
FetchContent_MakeAvailable(SDL2)

FetchContent_Declare(
        SDL2_image
        GIT_REPOSITORY https://github.com/libsdl-org/SDL_image.git
        GIT_TAG release-2.6.3
        GIT_SHALLOW TRUE
        GIT_PROGRESS TRUE
)

set(SDL2IMAGE_INSTALL OFF)
set(BUILD_SHARED_LIBS FALSE)

FetchContent_MakeAvailable(SDL2_image)

add_executable(sdl_test main.cpp)
target_link_libraries(sdl_test SDL2::SDL2main SDL2::SDL2-static SDL2_image::SDL2_image-static)

有了这个,您将拥有它静态和跨平台工作。
包含目录将照常工作。

#include <SDL.h>

请记住将您的main设置为这种格式

int main(int, char *[]) {
....
}

相关问题