c++ - 无法在 CMake 中创建动态库并将其链接到另一个动态库?

标签 c++ windows dll cmake

我目前正在努力在 Windows 10 下开发一个使用不同库的小型应用程序。首先我想说的是,我作为一名专业开发人员已经有好几年了,但我总是在不同的环境(主要是 GNU/Linux 和 MacOS)上进行开发,而对于我来说,Windows 总是更像是一个游戏操作系统而不是一个工作操作系统;现在 Windows 10 似乎对开源更加友好,我想试一试,看看通过结合 WSL-2 和 vcpkg 是否可以让某些东西正常工作。所以我认为使用 allegro 库开发一个简单的跨平台 2D 应用程序可能是一个好主意。

我使用 Visual Studio 2019 社区版创建了一个新的 CMake 项目,并在项目本身内部设置了一个 vcpkg 克隆(我觉得这不是一个好主意,但我有点喜欢将所有东西都放在一个单处)。使用 vcpkg 我安装了我希望使用的 2 个库:allegro 和 sqlite3。两个安装都很好,我实际上对 vcpkg 本身感到非常惊讶。

我设置了我的项目结构:因为这将是一个模块化项目,我开始通过 add_library(lib_name SHARED sources) 在 CMake 中创建一堆共享库。函数,我最终得到了一个类似于以下结构的结构:

project_root/CMakeLists.txt
project_root/src/main.cpp

project_root/core
project_root/core/CMakeLists.txt
project_root/core/include/core/bunch_of_headers.hpp
project_root/core/src/bunch_of_sources.cpp

project_root/model
project_root/model/CMakeLists.txt
project_root/model/include/model/bunch_of_headers.hpp
project_root/model/src/bunch_of_sources.cpp

等等..
project_root 中的每个文件夹包含一个模块,所有 CMakeLists 文件如下所示:
add_library(model SHARED all_the_source_files.cpp)

include(DeclareNewSharedLibrary)
declare_new_shared_library(model)

find_package(sqlite3 CONFIG REQUIRED)
target_link_libraries(model sqlite3)

在哪里 declare_new_shared_library()在单独的 cmake 模块中定义为以下函数
function(declare_new_shared_library lib_name)
    message(STATUS "Declaring new library ${lib_name}")
    include(GenerateExportHeader)
    generate_export_header(${lib_name})

    target_include_directories(${lib_name}
        PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include"
        PUBLIC "${CMAKE_CURRENT_BINARY_DIR}"
    )

    set_target_properties(${lib_name} PROPERTIES
        IMPORTED_IMPLIB "${CMAKE_CURRENT_BINARY_DIR}/${lib_name}.lib"
        IMPORTED_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/${lib_name}.dll"
    )
endfunction(declare_new_shared_library)

到目前为止没有什么花哨的。

现在,每个模块都可以正常编译,即使我将共享库链接到通过 vcpkg 下载的库(如上面的示例中那样)。 ld在 Windows 上正确输出所需文件( .lib.dll )并将它们复制到 ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}正如预期的那样。

我在这里遇到的问题是 一个 不编译的库。 CMake 文件与其他文件相同,除了我链接到 allegro 库(与所有其他库一样,它已通过 vcpkg 安装)。这是用于比较的 CMake 文件:
add_library(core SHARED all_the_sources.cpp)

include(DeclareNewSharedLibrary)
declare_new_shared_library(core)

find_package(allegro CONFIG REQUIRED)
target_link_libraries(core allegro)


当我尝试编译此目标(直接或通过依赖系统)时,我收到以下错误

TL;博士:
C:\Dev\ConsultantLife\msvcrtd.lib(exe_main.obj) : error LNK2019: unresolved external symbol main referenced in function "int __cdecl invoke_main(void)" (?invoke_main@@YAHXZ)
C:\Dev\ConsultantLife\bin\core.dll : fatal error LNK1120: 1 unresolved externals

这是勇敢者的长版本:
  Cleaning... 3 files.
  [1/2] C:\PROGRA~2\MICROS~1\2019\COMMUN~1\VC\Tools\Llvm\80D306~1.0\bin\clang-cl.exe  /nologo -TP -Dcore_EXPORTS -I..\..\..\core\include -Icore -I..\..\..\vcpkg\installed\x64-windows\include -m64 -fdiagnostics-absolute-paths  /DWIN32 /D_WINDOWS /W3 /GR /EHsc /MDd /Zi /Ob0 /Od /RTC1 /showIncludes /Focore\CMakeFiles\core.dir\Game.cpp.obj /Fdcore\CMakeFiles\core.dir\ -c ..\..\..\core\Game.cpp
  [2/2] cmd.exe /C "cd . && "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe" -E vs_link_dll --intdir=core\CMakeFiles\core.dir --rc=C:\PROGRA~2\WI3CF2~1\10\bin\100183~1.0\x64\rc.exe --mt=C:\PROGRA~2\WI3CF2~1\10\bin\100183~1.0\x64\mt.exe --manifests  -- C:\PROGRA~2\MICROS~1\2019\COMMUN~1\VC\Tools\MSVC\1422~1.279\bin\Hostx64\x64\link.exe /nologo core\CMakeFiles\core.dir\Game.cpp.obj  /out:bin\core.dll /implib:core\core.lib /pdb:bin\core.pdb /dll /version:0.0 /machine:x64 /debug /INCREMENTAL  ..\..\..\vcpkg\installed\x64-windows\debug\lib\allegro-debug.lib kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib  && cmd.exe /C "cd /D C:\Dev\ConsultantLife\out\build\x64-Debug\core && powershell -noprofile -executionpolicy Bypass -file C:/Dev/ConsultantLife/vcpkg/scripts/buildsystems/msbuild/applocal.ps1 -targetBinary C:/Dev/ConsultantLife/out/build/x64-Debug/bin/core.dll -installedDir C:/Dev/ConsultantLife/vcpkg/installed/x64-windows/debug/bin -OutVariable out""
  FAILED: bin/core.dll core/core.lib 
  cmd.exe /C "cd . && "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe" -E vs_link_dll --intdir=core\CMakeFiles\core.dir --rc=C:\PROGRA~2\WI3CF2~1\10\bin\100183~1.0\x64\rc.exe --mt=C:\PROGRA~2\WI3CF2~1\10\bin\100183~1.0\x64\mt.exe --manifests  -- C:\PROGRA~2\MICROS~1\2019\COMMUN~1\VC\Tools\MSVC\1422~1.279\bin\Hostx64\x64\link.exe /nologo core\CMakeFiles\core.dir\Game.cpp.obj  /out:bin\core.dll /implib:core\core.lib /pdb:bin\core.pdb /dll /version:0.0 /machine:x64 /debug /INCREMENTAL  ..\..\..\vcpkg\installed\x64-windows\debug\lib\allegro-debug.lib kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib  && cmd.exe /C "cd /D C:\Dev\ConsultantLife\out\build\x64-Debug\core && powershell -noprofile -executionpolicy Bypass -file C:/Dev/ConsultantLife/vcpkg/scripts/buildsystems/msbuild/applocal.ps1 -targetBinary C:/Dev/ConsultantLife/out/build/x64-Debug/bin/core.dll -installedDir C:/Dev/ConsultantLife/vcpkg/installed/x64-windows/debug/bin -OutVariable out""
  LINK Pass 1: command "C:\PROGRA~2\MICROS~1\2019\COMMUN~1\VC\Tools\MSVC\1422~1.279\bin\Hostx64\x64\link.exe /nologo core\CMakeFiles\core.dir\Game.cpp.obj /out:bin\core.dll /implib:core\core.lib /pdb:bin\core.pdb /dll /version:0.0 /machine:x64 /debug /INCREMENTAL ..\..\..\vcpkg\installed\x64-windows\debug\lib\allegro-debug.lib kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib /MANIFEST /MANIFESTFILE:core\CMakeFiles\core.dir/intermediate.manifest core\CMakeFiles\core.dir/manifest.res" failed (exit code 1120) with the following output:
     Creating library core\core.lib and object core\core.exp
C:\Dev\ConsultantLife\msvcrtd.lib(exe_main.obj) : error LNK2019: unresolved external symbol main referenced in function "int __cdecl invoke_main(void)" (?invoke_main@@YAHXZ)
C:\Dev\ConsultantLife\bin\core.dll : fatal error LNK1120: 1 unresolved externals
  ninja: build stopped: subcommand failed.

现在,据我了解,链接器正在尝试将我的库链接为可执行文件,因为它无法找到 main函数(是的,这是一个库,亲爱的..)它会提示并给出错误。

项目中其他库的编译完全相同link.exe命令(除了系统库以外的其他库)并且它不会失败..

现在我想问一下:我做错了什么,allegro-5.2 中是否有错误? (即使我看不到库如何寻找主要方法以及链接器如何......好吧,你明白我的意思)还是我应该放弃一切?

会不会是 Allegro.cmake 文件的问题?我不得不承认我从未将 CMake 用于专业项目(我们是一群仍在使用 GNU Autotools&Co. 的老手),所以我可能错过了一些东西。这是我创建的文件(从我在 vcpkg 文件夹中找到的其他 cmake 文件复制/粘贴)
### Allegro-config.cmake
include(${CMAKE_CURRENT_LIST_DIR}/Allegro-targets.cmake)


### Allegro-targets.cmake
# Generated by CMake

if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" LESS 2.5)
   message(FATAL_ERROR "CMake >= 2.6.0 required")
endif()
cmake_policy(PUSH)
cmake_policy(VERSION 2.6)
#----------------------------------------------------------------
# Generated CMake target import file.
#----------------------------------------------------------------

# Commands may need to know the format version.
set(CMAKE_IMPORT_FILE_VERSION 1)

# Protect against multiple inclusion, which would fail when already imported targets are added once more.
set(_targetsDefined)
set(_targetsNotDefined)
set(_expectedTargets)
foreach(_expectedTarget sqlite3)
  list(APPEND _expectedTargets ${_expectedTarget})
  if(NOT TARGET ${_expectedTarget})
    list(APPEND _targetsNotDefined ${_expectedTarget})
  endif()
  if(TARGET ${_expectedTarget})
    list(APPEND _targetsDefined ${_expectedTarget})
  endif()
endforeach()
if("${_targetsDefined}" STREQUAL "${_expectedTargets}")
  unset(_targetsDefined)
  unset(_targetsNotDefined)
  unset(_expectedTargets)
  set(CMAKE_IMPORT_FILE_VERSION)
  cmake_policy(POP)
  return()
endif()
if(NOT "${_targetsDefined}" STREQUAL "")
  message(FATAL_ERROR "Some (but not all) targets in this export set were already defined.\nTargets Defined: ${_targetsDefined}\nTargets not yet defined: ${_targetsNotDefined}\n")
endif()
unset(_targetsDefined)
unset(_targetsNotDefined)
unset(_expectedTargets)


# Compute the installation prefix relative to this file.
set(_IMPORT_PREFIX "${PROJECT_SOURCE_DIR}/vcpkg/installed/${VCPKG_TARGET_TRIPLET}")

# Create imported target allegro
add_library(allegro SHARED IMPORTED)

set_target_properties(allegro PROPERTIES
  INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include"
)

# Load information for each installed configuration.
file(GLOB CONFIG_FILES "${CMAKE_CURRENT_LIST_DIR}/Allegro-targets-*.cmake")
foreach(f ${CONFIG_FILES})
  include(${f})
endforeach()

# Cleanup temporary variables.
set(_IMPORT_PREFIX)

# Loop over all imported files and verify that they actually exist
foreach(target ${_IMPORT_CHECK_TARGETS} )
  foreach(file ${_IMPORT_CHECK_FILES_FOR_${target}} )
    if(NOT EXISTS "${file}" )
      message(FATAL_ERROR "The imported target \"${target}\" references the file
   \"${file}\"
but this file does not exist.  Possible reasons include:
* The file was deleted, renamed, or moved to another location.
* An install or uninstall procedure did not complete successfully.
* The installation package was faulty and contained
   \"${CMAKE_CURRENT_LIST_FILE}\"
but not all the files it references.
")
    endif()
  endforeach()
  unset(_IMPORT_CHECK_FILES_FOR_${target})
endforeach()
unset(_IMPORT_CHECK_TARGETS)

# This file does not depend on other imported targets which have
# been exported from the same project but in a separate export set.

# Commands beyond this point should not need to know the version.
set(CMAKE_IMPORT_FILE_VERSION)
cmake_policy(POP)



### Allegro-targets-debug.cmake
#----------------------------------------------------------------
# Generated CMake target import file for configuration "Debug".
#----------------------------------------------------------------

# Commands may need to know the format version.
set(CMAKE_IMPORT_FILE_VERSION 1)

# Import target "allegro" for configuration "Debug"
set_property(TARGET allegro APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG)
set_target_properties(allegro PROPERTIES
    IMPORTED_IMPLIB_DEBUG "${_IMPORT_PREFIX}/debug/lib/allegro-debug.lib"
    IMPORTED_LOCATION_DEBUG "${_IMPORT_PREFIX}/debug/bin/allegro-debug-5.2.dll"
  )

list(APPEND _IMPORT_CHECK_TARGETS allegro )
list(APPEND _IMPORT_CHECK_FILES_FOR_allegro 
    "${_IMPORT_PREFIX}/debug/lib/allegro-debug.lib" 
    "${_IMPORT_PREFIX}/debug/bin/allegro-debug-5.2.dll"
)

# Commands beyond this point should not need to know the version.
set(CMAKE_IMPORT_FILE_VERSION)


由于过去几天这种行为让我发疯,我在 Linux 环境(vcpkg,cmake,...)上测试了相同的配置,它就像一个魅力,我编译了所有东西,它工作了。我看到的唯一区别是 Linux 上的 vcpkg 仅使用静态库,而在 Windows 上使用 dll。

对不起文字墙!

谢谢您的帮助!

最佳答案

而且通常情况下,在寻求帮助后几分钟,我就能自己找到答案。

似乎 liballegro 试图找到一个主要方法,这就是链接失败的原因。

解决方案是将以下行添加到 Allegro-config.cmake文件

target_compile_definitions(allegro INTERFACE ALLEGRO_NO_MAGIC_MAIN)

关于c++ - 无法在 CMake 中创建动态库并将其链接到另一个动态库?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58085848/

相关文章:

windows - 有没有办法在 Windows 上的 Ubuntu 上的 Bash 中使用 Notepad++ 打开文件?

python - Pyinstaller 创建的 exe 在一台机器上运行,但在另一台机器上出错

windows - 如何在 Windows 上为 CMake 自定义命令设置运行时路径

c++ - Mac OS X 上的高性能代码

php - 如何修复 CLI 使用的 Windows PHP 版本?

c++ - C/C++ 结构,在缓冲区中包含位字段

java - 在 64 位 Windows 上编译的 JAR 不能在 32 位上运行

c# - 创建要在 C# 中编码的 C++ Dll 的最佳实践

c++ - 在我的程序中使用for循环打印出矩阵的一个错误

c++ - C++,在C++中管理文件的问题