使用CMake链接SDL2

3
我在将SDL2链接到项目中遇到了问题,可能是因为我对CMake不太熟悉,不知道如何使用它创建新项目。我尝试解决这个问题的每种方法都会出现以下其中一种问题:
  1. “无法找到WinMain@16”
  2. 多个未定义的引用错误
  3. 未定义引用至`SDL_main`
我曾尝试使用find_package、直接链接到文件、链接到库文件、按照教程操作(例如https://trenki2.github.io/blog/2017/06/02/using-sdl2-with-cmake/)、搜索答案(其中大多数都谈论使用find_package,但我无法使其正常工作)(例如Using SDL2 with CMake)。

项目依赖

SDL 2.0.12,CMake 3.17.0,7-Zip,mingw32-make,wget

该项目应该是跨平台的,但主要开发环境是Windows 10。所有脚本都从根文件夹“vivaria”运行。

项目结构

vivaria/
├── build/
│   └── [cmake build files]
├── buildtools/
│   └── SDL2/SDL2-2.0.12/lib/x86 (and x64)
│                            ├── SDL2.lib
│                            └── SDL2main.libs
├── deploy/
├── resources/
├── scripts/
│   ├── build_windows_debug_x86.bat
│   └── install_buildtools_windows.bat
└── src/
    ├── CMakeLists.txt
    └── vivaria.cpp

build_windows_debug_x86.bat

cmake -G "MinGW Makefiles" -S .\src\ -B .\build\ -DCMAKE_BUILD_TYPE=Debug
cd .\build\
mingw32-make

vivaria.cpp

#include <iostream>
#include "SDL.h"

int main() {
    if (SDL_Init(SDL_INIT_VIDEO) != 0){
        std::cout << "Hello world" << std::endl;
        return 1;
    }
    
    SDL_Quit();
    return 0;
}

CMakeLists.txt: undefined reference to SDL_main。
cmake_minimum_required(VERSION 3.17)
project(Vivaria VERSION 1.0.0)

set(CMAKE_CXX_GLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -lmingw32")
# set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++")

set(INCLUDE_DIR "${CMAKE_SOURCE_DIR}/../buildtools")
set(SDL2_DIR "${INCLUDE_DIR}/SDL2/SDL2-2.0.12")

set(SDL2_INCLUDE_DIRS "${SDL2_DIR}/include")
set(CMAKE_BINARY_DIR "${CMAKE_SOURCE_DIR}/../deploy")

# Support both 32 and 64 bit builds
if (${CMAKE_SIZEOF_VOID_P} MATCHES 8)
    set(SDL2_LIBRARIES "${SDL2_DIR}/lib/x64/SDL2main.lib;${SDL2_DIR}/lib/x64/SDL2.lib")
else ()
    set(SDL2_LIBRARIES "${SDL2_DIR}/lib/x86/SDL2main.lib;${SDL2_DIR}/lib/x86/SDL2.lib")
endif ()

# link dependencies
include_directories(${SDL2_INCLUDE_DIRS})
link_directories(${SDL2_LIBRARIES})

# Project files and linking
set(SOURCES vivaria.cpp)
add_executable(${PROJECT_NAME} vivaria.cpp)
target_link_libraries(${PROJECT_NAME} ${SDL2_LIBRARIES})

控制台输出

F:\Koodit\Vivaria\build>mingw32-make
[ 50%] Linking CXX executable Vivaria.exe
c:/mingw/bin/../lib/gcc/mingw32/9.2.0/../../../../mingw32/bin/ld.exe: F:/Koodit/Vivaria/src/../buildtools/SDL2/SDL2-2.0.12/lib/x86/SDL2main.lib(Win32/Release/SDL_windows_main.obj):(.text[_main_getcmdline]+0xd1): undefined reference to `SDL_main'
collect2.exe: error: ld returned 1 exit status
CMakeFiles\Vivaria.dir\build.make:104: recipe for target 'Vivaria.exe' failed
mingw32-make[2]: *** [Vivaria.exe] Error 1
CMakeFiles\Makefile2:91: recipe for target 'CMakeFiles/Vivaria.dir/all' failed
mingw32-make[1]: *** [CMakeFiles/Vivaria.dir/all] Error 2
Makefile:99: recipe for target 'all' failed
mingw32-make: *** [all] Error 2

install_buildtools_windows.bat

set SDL2=SDL2-devel-2.0.12-VC.zip
set DOWNLOAD_DIR=%cd%\buildtools
set OUTPUT_DIR=%cd%\buildtools\SDL2

wget "https://libsdl.org/release/%SDL2%" -P "%DOWNLOAD_DIR%"

7z x "%DOWNLOAD_DIR%\%SDL2%" -y -o%OUTPUT_DIR%

del /F /Q %DOWNLOAD_DIR%\%SDL2%

你可以看到,我对CMake还是有点新手,但我正在努力学习。


你有没有看过关于类似错误的这个问题?它的答案提到在源文件中设置SDL_MAIN_HANDLED宏。 - Tsyvarev
2个回答

8

哎呀,你的CMakeLists的所有内容都是错误的。

  1. 决不在你的CMakeLists.txt中设置CMAKE_CXX_FLAGS
  2. 决不使用include_directorieslink_directories
  3. 决不在没有可见性说明符的情况下使用target_link_libraries
  4. 决不手动设置库路径。
  5. 不要将目标名称设置为项目名称。这只会增加复杂度并导致不良风格。

以下内容足以满足需求:

cmake_minimum_required(VERSION 3.16)
project(Vivaria VERSION 1.0.0)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED YES)
set(CMAKE_CXX_EXTENSIONS OFF)

find_package(SDL2 REQUIRED)

add_executable(Vivaria vivaria.cpp)
target_link_libraries(Vivaria PRIVATE SDL2::SDL2)

你的代码也有错误。你需要在文件的顶部加入这行代码。
#define SDL_MAIN_HANDLED

首先,使用vcpkg安装SDL2。然后,通过上述更改,我使用以下命令编译并运行:

> mkdir build
> cd build
> cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=X:/path/to/vcpkg.cmake ..

我按照你说的安装了Ninja,将工具链改为vcpkg,并清除了我的cmakelists。但是在尝试运行构建后出现以下错误:“通过未在CMAKE_MODULE_PATH中提供“FindSDL2.cmake”,该项目已要求CMake查找由“SDL2”提供的软件包配置文件,但CMake没有找到任何文件”。如果我指定vcpkg安装为x64,则find_package将找到该软件包,但不会将其视为要使用的正确软件包。无论将-A设置为x64 / x86还是设置“-DCMAKE_GENERATOR_PLATFORM = x86”或x64都没有帮助(打印不支持的平台)。 - Nuubles
我不理解你的回答。如果我想将所需的库与我的源项目一起分发怎么办?我不想让用户承担安装额外依赖项的负担。我还想在更长的时间内保持外部依赖项的清洁,因为我的项目可能需要几年时间才能完成。你的回答中经常出现“从不”,但却没有解释原因。 - SuperTasche
1
@SuperTasche - 我不明白你的问题。我的任何指南都不会与将依赖项打包或将安装依赖项的负担转移给用户产生冲突。在CMake中,有很好的解决方案来处理依赖关系:ExternalProject(即超级构建),FetchContent,git子模块+ add_subdirectory等。 - Alex Reinking
1
@AlexReinking 感谢您的建议,我一定会好好检查!然而,当扩展您的指南时,这可能有助于cmake初学者理解他们的错误。在过去的几周里,我一直在处理cmake,并不断阅读诸如“您永远不应该做XYZ”之类的教条,但却没有任何合理的理由。对于那些主要目标是开始编写C++项目的人来说,这确实是一种痛苦。 - SuperTasche
1
@SuperTasche - 我认为这需要比适合于StackOverflow评论或回答不同问题的更多细节。如果您想让我解释理由,如果您开启一个新的SO问题,我很乐意这样做。 - Alex Reinking
显示剩余3条评论

1
感谢Tsyvarev!在源文件中设置宏SDL_MAIN_HANDLED解决了这个问题。
#define SDL_MAIN_HANDLED // insert this
#include <iostream>
#include "SDL.h"

int main() {
    if (SDL_Init(SDL_INIT_VIDEO) != 0){
        std::cout << "Hello world" << std::endl;
        return 1;
    }
    
        std::cout << "Hello world 2" << std::endl;

    SDL_Quit();
    return 0;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接