【问题标题】:Trying to statically link SDL in Visual Studio尝试在 Visual Studio 中静态链接 SDL
【发布时间】:2014-10-31 12:17:58
【问题描述】:

我正在使用 Microsoft Visual Studio 2013 Express for Windows Desktop,使用 C++ 编写并使用 SDL 2.0.3 库。我正在尝试构建一个可以在其他计算机上运行而无需安装任何东西的独立 .exe。

我已经按照TwinklebearDev Lesson 0: Setting Up SDL 设置了我的项目。我正在链接到 x86 版本的库,并将 x86 版本的 SDL2.dll 放在 Debug 文件夹和 Release 文件夹中。以下是源代码:

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

int main(int argc, char **argv){
    if (SDL_Init(SDL_INIT_EVERYTHING) != 0){
        std::cout << "SDL_Init Error: " << SDL_GetError() << std::endl;
        return 1;
    }
    SDL_Quit();

    return 0;
}

这完美地工作并返回 0,直到我尝试静态链接。我试图通过右键单击项目并单击属性来静态链接,然后设置以下内容:

配置属性 > C/C++ > 代码生成 > 运行时库 > 多线程 (/MT)

然后我得到一堆错误:

警告 1 警告 LNK4098:defaultlib 'msvcrt.lib' 与使用冲突 其他库;采用 /NODEFAULTLIB:库 C:\SDLtest\Project1\Project1\LIBCMT.lib(crt0init.obj) Project1

错误 2 错误 LNK2005: "private: __thiscall type_info::type_info(class type_info const &)" (??0type_info@@AAE@ABV0@@Z) 已经定义在 LIBCMT.lib(typinfo.obj) C:\SDLtest\Project1\Project1\msvcrt.lib(ti_inst.obj) Project1

错误 3 错误 LNK2005: "private: class type_info & __thiscall type_info::operator=(class type_info const &)" (??4type_info@@AAEAAV0@ABV0@@Z) 已经定义在 LIBCMT.lib(typinfo.obj) C:\SDLtest\Project1\Project1\msvcrt.lib(ti_inst.obj) Project1

错误 4 错误 LNK2005:_exit 已在 LIBCMT.lib(crt0dat.obj) C:\SDLtest\Project1\Project1\msvcrt.lib(MSVCR120.dll) Project1

错误 5 错误 LNK2005:___iob_func 已在 LIBCMT.lib(_file.obj) C:\SDLtest\Project1\Project1\msvcrt.lib(MSVCR120.dll) Project1

错误 6 错误 LNK1169:一个或多个多重定义符号 找到 C:\SDLtest\Project1\Release\Project1.exe 1 1 Project1

如何静态链接 SDL 2.0.3 以构建可在大多数计算机上运行而无需安装任何东西的独立可执行文件?

【问题讨论】:

  • 您是尝试仅静态链接 Visual Studio 运行时还是静态链接它和 SDL?从您显示的内容来看,它看起来像是第一个选项。
  • 其实我只需要静态链接Visual Studio运行时,只要在程序的文件夹中包含SDL2.dll就可以了。
  • 我会尽量为您省去很多麻烦,并告诉您让人们安装 Visual Studio 运行时或通过安装程序自己提供。无论如何,他们最终都会安装它,除非他们不使用任何其他程序,并且除非您想从源代码构建 SDL 以确保 RTL 与分配器不匹配,否则您只会走上一条糟糕的道路。
  • 如果您不匹配 DLL 的运行时,我相信除了遵守该选择之外别无选择
  • This question 似乎表明您可以静态链接 VS 运行时,只要您“告诉链接器显式忽略动态链接的 CRT”。这会是一个好方法吗(它会冒堆管理问题的风险)吗?如何告诉链接器显式忽略动态链接的 CRT?

标签: visual-c++ static-linking sdl-2


【解决方案1】:

我已经为此工作了一段时间,终于设法在 Windows 上静态链接 SDL2。您可以选择使用 Cmake 或 Visual Studio 的 IDE。我更喜欢 Cmake,但使用 Visual Studio 会稍微简单一些。

先决条件

  • CMake(必需)
  • Visual Studio 2019(不需要,但更简单)

第 1 步:构建 SDL2
here 下载 SDL2 源代码。如果您不想将其保存在您的下载文件夹中,我建议您将其解压缩到您的文档文件夹或除下载之外的其他文件夹中。解压缩文件后,在文件夹中打开 cmd 提示符。确保您进入解压缩文件夹内的文件夹。 (您应该看到一个文件夹和文件列表。如果您只看到一个文件夹,请进入该文件夹)您的目录应该类似于~\Documents\SDL2-2.0.14\SDL2-2.0.14。到达这里后,在此目录中打开一个 cmd 提示符。执行命令mkdir build &amp;&amp; cd build。接下来,执行命令cmake ..。假设您有默认的 Windows 10 生成器 Visual Studio 16 2019,解决方案将在您的构建文件夹中创建。如果您使用的是不同的生成器,请像使用该生成器一样构建 SDL2。您现在可以关闭命令提示符。在您的构建文件夹中导航,然后打开 ALL_BUILD.vcxproj 文件。这将打开 Visual Studio。确保将您的配置更改为Release。现在您已经打开了它,进入 SDL2 解决方案的属性(右键单击 SDL2,然后单击对话框底部的属性)。现在您已经打开了属性面板,请确保您在GeneralConfiguration Properties。这应该是您自动打开的位置。将Configuration Type 属性从Dynamic Library (.dll) 更改为Static Library (.lib)。之后,转到Librarian,然后转到General。将Output File 的结尾从$(TargetExt) 更改为.lib。现在我们已经告诉 Visual Studio 创建一个静态库。单击应用,然后按确定。在构建之前,我们需要在源代码中添加一个定义,让 SDL2 我们要使用 C 库函数。如果你不这样做,那么什么都无法编译。您需要找到并打开 SDL_config.h。重要提示:确保您正在编辑正确的 SDL_config.h!那里有两个!确保您正在编辑构建文件夹中的那个。如果你打开的 SDL_config.h 只有 56 行,那你就错了。找到正确的 SDL_config.h 后,将行 #define HAVE_LIBC 1 somwehere 添加到行 #if HAVE_LIBC 上方。确保保存文件。现在右键单击 SDL2 解决方案,然后单击构建。完成后,右键单击 SDL2main,然后单击构建。这将在~\SDL2-2.0.14\SDL2-2.0.14\build\Release 中生成文件SDL2.libSDL2main.lib。现在您已经有了静态库,您可以将它们包含在您的项目中。

第 2 步选项 1:使用 Visual Studio IDE 链接静态库
这种方法要简单得多,对于不熟悉 cmake 的人来说,我会推荐这种方法。请注意,您可以按照相同的说明使用任何 c/c++ IDE,但会有所不同。打开要静态链接的 Visual Studio 解决方案。现在像我们之前所做的那样转到解决方案属性。转到链接器->常规->附加库目录。通过按顺序添加以下库来编辑此列表:SDL2main.lib SDL2.lib winmm.lib version.lib Imm32.lib Setupapi.lib libcmt.lib libucrtd.lib 注意:如果您不使用 Visual Studio 提供的编辑输入法(单击 Linker->Input->Additional Dependencies 最右侧的向下箭头),那么你需要直接输入这些库,中间有分号。您可能需要也可能不需要库 libucrtd.liblibcmt.lib,这取决于您的版本。确保包含 SDL2 头文件。完成后,点击应用,然后确定。重要提示:确保您的main 函数具有签名int main(int, char**),否则您的程序将无法编译。

第 2 步选项 2:使用 Cmake 链接静态库
现在我们将使用 Cmake 静态链接 SDL2。我将使用 msvc,因为 msvc 比 MinGW 更容易访问运行时二进制文件。对于 msvc,我们将通过 Visual Studio 使用 Cmake。没有必要使用 Visual Studio。您真正需要的只是记事本和编译器。如果您要使用 MinGW 而不是 msvc,那么您需要在 Windows SDK 中找到运行时二进制文件并告诉 MinGW 在哪里可以找到它们。
首先,在 Visual Studio 中新建一个 Cmake 项目。如果您看到介绍性屏幕,请随时按下按钮以创建 CMakeSettings.json 文件。所有这些都是帮助为 cmake 配置不同的构建类型。这不是必需的,但它可能很有用。如果您想要一个 CMakeSettings.json 文件,但没有选择制作一个,那么您可以右键单击解决方案资源管理器中的空白区域,然后选择 Cmake Settings for &lt;ProjectName&gt;。进入您的源目录,然后打开CmakeLists.txt 文件。确保它在父目录中——有两个CmakeLists.txt 文件,但现在我们关心的是父目录。将以下代码复制并粘贴到该 CMakeLists.txt 中:

cmake_minimum_required(VERSION 3.8)

set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MDd")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD")

project("tut")

add_subdirectory("src")

在行中

set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MDd")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD")

我们告诉 CMake 使用静态运行时库。 如果你想让你的程序更小,那么你可以使用“/MD”。请注意,它需要运行时 dll 才能运行,这意味着所有这些。 编辑:您需要使用 /MD 和 /MDd。 SDL2 需要它。你仍然可以静态链接。

add_subdirectory("src")

这告诉 CMake 在子目录“src”中可以找到更多信息。如果您将 src 文件夹命名为其他任何名称,请确保将“src”替换为您的文件夹名称。现在进入子目录“src”(或者你叫什么)并打开那个 CMakeLists.txt。将以下代码复制并粘贴到该 CMakeLists.txt 中:

cmake_minimum_required (VERSION 3.8)

add_executable(tut2 WIN32 "tut2.cpp")

target_include_directories(tut2 PRIVATE "${CMAKE_SOURCE_DIR}/include")

target_compile_definitions(tut2 PRIVATE "$<$<CONFIG:Debug>:" "_DEBUG" ">" "$<$<CONFIG:Release>:" "NDEBUG" ">" "WIN32;" "_CONSOLE;" "UNICODE;" "_UNICODE")
target_compile_options(tut2 PRIVATE /Oi; /Gy; /permissive-; /sdl; /W3; $<$<CONFIG:Debug>:/Z7>; ${DEFAULT_CXX_DEBUG_INFORMATION_FORMAT}; ${DEFAULT_CXX_EXCEPTION_HANDLING})

target_link_options(tut2 PRIVATE $<$<CONFIG:Debug>: /INCREMENTAL> $<$<CONFIG:Release>: /DEBUG; /OPT:REF; /OPT:ICF; /INCREMENTAL:NO> /NODEFAULTLIB:LIBCMT)

target_link_libraries(tut2 PRIVATE SDL2main SDL2 winmm version Imm32 Setupapi)

target_link_directories(tut2 PRIVATE "${CMAKE_SOURCE_DIR}/lib")

在行中

target_include_directories(tut2 PRIVATE "${CMAKE_SOURCE_DIR}/include")

我们告诉 CMake 在哪里寻找我们的包含文件。在此示例中,包含目录位于我们项目的源目录中。 (与我们原来的 CMakeLists.txt 所在的位置相同)。如果您觉得需要在目录中进行硬编码,则只需输入文件的绝对路径即可。但是请注意,这会使其他人难以构建您的代码。

target_compile_definitions(tut2 PRIVATE "$<$<CONFIG:Debug>:" "_DEBUG" ">" "$<$<CONFIG:Release>:" "NDEBUG" ">" "WIN32;" "_CONSOLE;" "UNICODE;" "_UNICODE")
target_compile_options(tut2 PRIVATE /Oi; /Gy; /permissive-; /sdl; /W3; $<$<CONFIG:Debug>:/Z7>; ${DEFAULT_CXX_DEBUG_INFORMATION_FORMAT}; ${DEFAULT_CXX_EXCEPTION_HANDLING})

在这些行中,我们告诉 cmake 我们希望它编译的所有标志。如果您想知道每个标志的作用,那么this 是您应该查看的位置。

target_link_options(tut2 PRIVATE $<$<CONFIG:Debug>: /INCREMENTAL> $<$<CONFIG:Release>: /DEBUG; /OPT:REF; /OPT:ICF; /INCREMENTAL:NO> /NODEFAULTLIB:LIBCMT)

在这些行中,我们告诉 cmake 我们想要链接我们的库的所有标志。如果您想知道每个标志的作用,那么您应该查看this

target_link_libraries(tut2 PRIVATE SDL2main SDL2 winmm version Imm32 Setupapi)
target_link_directories(tut2 PRIVATE "${CMAKE_SOURCE_DIR}/lib")

在这些行中,我们告诉 Cmake 我们需要链接的所有库,以及查找这些库的位置。同样,${CMAKE_SOURCE_DIR}/lib 是一个相对路径,指向我放置 SDL2.lib 和 SDL2main.lib 的位置,但您可以放置​​任何相对或绝对路径来替换它。您现在可以构建项目,如果您做的一切正确,它应该可以成功编译。如果您收到“无法打开 projectName.exe”的错误消息,则只需重新配置您的 CMakeList.txt 文件即可。

如果这看起来比它应该的要难,那是因为它是。我最近不得不切换回 Windows 10,并且在 Linux 上花了我 2 分钟的时间度过了美好的一周。如果您可以选择,我强烈建议您尝试一下 Linux。您所要做的就是将--static-libs 传递给sdl2-config,然后!您的程序是静态链接的。如果您有任何问题,或者有什么我忘了介绍,请在 cmets 中告诉我。

【讨论】:

  • 感谢您为此付出的时间,帮了大忙。
【解决方案2】:

您遇到的问题是您已将编译器配置为使用静态 C 运行时库LIBCMT.lib,但您链接的某些目标文件要求将其链接到默认库。特别是 msvcrt.lib,动态 C 运行时库。因为这两个库定义了相同的符号集,所以您会得到多重定义的错误。

错误列表开头的警告消息中建议了一个可能的解决方案:use /NODEFAULTLIB:librarydocumentation for /NODEFAULTLIB 解释了如何在 Visual Studio 2013 IDE 中设置此选项:

  1. 打开项目的 Property Pages 对话框。 [...]
  2. 单击 链接器 文件夹。
  3. 单击 输入 属性页。
  4. 选择 Ignore All Default Libraries 属性或在 Ignore Specific Library 属性中指定要忽略的库列表。命令行属性页面将显示您对这些属性所做的更改的效果。

您要排除的默认库的名称是msvcrt.lib

【讨论】:

  • 我尝试排除msvcrt.lib并收到10个错误,这是前4个:错误1错误LNK2001:未解析的外部符号@__security_check_cookie@4错误2错误LNK2001:未解析的外部符号__imp__memmove错误3错误LNK2001:未解析的外部符号 _atexit 错误 4 错误 LNK2001:未解析的外部符号 __purecall
  • __imp__memmove 符号表示其中一个目标文件是以需要与动态 C 运行时库链接的方式编译的。尝试从头开始重建您的项目,以确保它不是您自己的目标文件之一。如果这不起作用,那么您似乎需要重新编译 SDL 库,以便将其与静态 C 运行时库一起使用。
猜你喜欢
  • 2011-06-15
  • 1970-01-01
  • 2012-09-30
  • 2014-11-15
  • 2018-08-09
  • 1970-01-01
  • 2016-02-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多