尝试在Visual Studio中静态链接SDL

时间:2014-09-06 20:48:33

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

我正在使用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。我试图通过右键单击项目并单击“属性”,然后设置以下内容来静态链接:

  

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

然后我收到了一堆错误:

  

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

     

错误2错误LNK2005:“private:__ thiscall type_info :: type_info(class   type_info const&amp;)“(?? 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&amp; __thiscall   type_info :: operator =(class type_info const&amp;)“   (?? 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以构建可在大多数计算机上运行的独立可执行文件,而无需安装任何内容?

2 个答案:

答案 0 :(得分:3)

我已经研究了一段时间,我终于设法在 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 && cd build。接下来,执行命令 cmake ..。假设您拥有默认的 Windows 10 生成器 Visual Studio 16 2019,解决方案将在您的构建文件夹中创建。如果您使用不同的生成器,则像使用该生成器一样构建 SDL2。您现在可以关闭命令提示符。在您的构建文件夹中导航,然后打开 ALL_BUILD.vcxproj 文件。这将打开 Visual Studio。确保将您的配置更改为 Release。现在您已打开它,进入 SDL2 解决方案的属性(右键单击 SDL2,然后单击对话框底部的属性)。现在您已打开属性面板,请确保您位于 General 中的 Configuration 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 行添加到 #if HAVE_LIBC 行上方。确保保存文件。现在右键单击 SDL2 解决方案,然后单击构建。完成后,右键单击 SDL2main,然后单击构建。这将在 SDL2.lib 中生成文件 SDL2main.lib~\SDL2-2.0.14\SDL2-2.0.14\build\Release。现在您有了静态库,您可以将它们包含在您的项目中。

步骤 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 <ProjectName>。进入您的源目录,并打开 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 在哪里寻找我们的包含文件。在这个例子中,include 目录位于我们项目的源目录中。 (与我们原来的 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,然后!您的程序是静态链接的。如果您有任何问题,或者有什么我忘记介绍的地方,请在评论中告诉我。

答案 1 :(得分:-1)

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

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

  1. 打开项目的属性页对话框。 [...]
  2. 单击链接器文件夹。
  3. 单击输入属性页。
  4. 选择忽略所有默认库属性,或在忽略特定库属性中指定要忽略的库列表。 “命令行”属性页将显示您对这些属性所做的更改的效果。

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