我的目标是使用 SDL2 构建一个 C++ 应用程序,该应用程序可以在 Linux 和可能未安装 SDL2 的 Windows 上运行。 (我知道有很多关于它的帖子,我稍后会来)。
所以我正在浏览我的小项目结构,我记得使用 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
结构不是本次讨论的重点。
现在我的项目结构已经完成,我专注于我的构建文件。
多亏了很多帖子,我明白我应该通过静态链接。 (我知道一些缺点,例如重新编译静态库、没有自动进行上次更新、较大的文件等)。我想将我的程序作为“文件夹”分发,所以我排除了(包)安装程序的解决方案。 我的程序必须能够使用他自己的资源(文件夹)运行。另外,请记住,SDL2 附带了允许静态链接的“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)
最后,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"...>
到目前为止,我已经尝试使我的应用独立便携:
没有一种解决方案让每个人都为之鼓掌。
我不明白为什么没有关于如何在没有安装 SDL2 的机器上实现制作 C++ SDL2 便携式应用程序的目标的教程......每个人似乎都同意像 Steam 这样的应用程序肯定会被安装和由他们来管理 SDL。那么没有通过 Steam 的游戏呢?但话又说回来,我想知道这是否是一个好主意。作为 JS 开发人员,当我构建具有 NPM 依赖项的应用程序时,我会阻止我的包版本,以便所有开发人员都使用相同的工具并且我的生产环境稳定。我不明白这个动态库逻辑,所以我很高兴有解释:)
编辑:忘记提及我的项目库 (src/CMakeLists.txt) 构建良好,当我尝试将它与我的主要功能 (app/CMakeLists.txt) 链接时,一切都出错了
编辑 2: 有一个有效的静态链接,但必须下载其他一些依赖项...到目前为止我所做的:
add_subdirectory(${CMAKE_SOURCE_DIR}/libs/SDL2-2.0.14)
### 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)
sudo apt install libsdl2-dev
(我们想要避免的)时,我需要安装更多的依赖项(我猜)。您可以在此处找到所有依赖项:https://github.com/libsdl-org/SDL/blob/main/docs/README-linux.md#build-dependencies我想我的新问题现在是“如果我尝试在没有这些依赖项的计算机上运行我的应用怎么办?”。之所以这么问,是因为我在安装它们之前 设法编译并运行了我的应用程序。我得出结论,我的可执行文件中包含 SDL 代码,但没有依赖项代码。
我仍然想知道为什么静态链接总是如此妖魔化,因为它允许对依赖项的版本进行控制。我同意“错误修复”的说法,但这些新版本可能与您的应用程序不兼容。在这种情况下,您应该向您的用户解释,当您的代码没有导致回归时,您正在非常努力地发布修复程序......
答案 0 :(得分:0)
回答标题中的问题,忽略问题中的错误。我将建议一个不同的解决方案。
在 Windows 上(假设为 MinGW)这很容易。静态链接并不重要:如果您不这样做,您只需随可执行文件一起提供所有必要的 dll。
所需的dll列表确定如下:
确保您的 MinGW 和库附带的 dll 与 C:\Windows
(包括子目录)中的 dll 不重叠。如果您看到重叠,请删除 C:\Windows
中匹配的 dll。一些蹩脚的安装程序喜欢将自定义 dll 放在那里,这往往会导致问题。
打开一个 shell,并在其中将 MinGW 的 bin/
目录前置到 PATH
中。
使用 ntldd -R <filename.exe>
获取 dll 列表。 MinGW 的 bin/
中的那些你必须发货。
在 Linux 上,有几个突出的解决方案:Appimage、Flatpak、Snap。
我对此没有太多经验,但这是我一直在做的事情:
ldd
确定依赖项列表。哪些需要运送必须通过实验来确定。首先将它们全部复制到当前目录。patchelf --set-rpath '$ORIGIN' <filename>
。 ('
很重要,您不希望将 $ORIGIN
扩展为 shell 变量。)libstdc++.so.6
、libgcc_s.so.1
和我使用的库。或者,您可以使用设置 LD_LIBRARY_PATH
而不是 patchelf
的启动器脚本。
答案 1 :(得分:-1)
所有这些都假定 CMake 是最新版本 - 如果这对您不起作用,您可能需要安装较新版本。
所以,首先,您要编译 SDL2。在 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
这意味着您现在在计算机上的 ~/SDL2 文件夹中拥有了 SDL2 的构建副本(您可能想要更改此位置)。 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 的过程大致相同,不同之处在于,如果您使用 VS2019,则通过 GUI 使用 CMake,因此在其中而不是在命令行上设置参数。如果您从开始菜单打开开发人员命令提示符,则可以在命令行上执行此操作(这是必需的,以便为 CMake 设置编译器变量以供查找)。
就其他库而言 - 在 Linux 上,您将始终需要依赖系统提供的 C 标准库 (libc)(除非您使用 musl)。通常,这是向前兼容但不向后兼容,因此许多开发人员在非常旧的操作系统上编译他们的代码,以确保它“无处不在”。您还需要在 libstdc++ 中静态链接。在 Windows 上,存在可再分发的包(例如“Visual C++ Redistributable for Visual Studio 2019”),它们通常随您的应用程序一起分发或假定已安装。