使用CMake时,为什么我的共享库没有正确链接?

4

这是一个非常微不足道的问题,可能是由于我对CMake缺乏经验。我遵循了http://fabzter.com/blog/cmake-tutorial-1中的教程,并且正在处理链接问题。

基本上,我有一个名为MathFuncs的库和一个使用MathFuncs的可执行文件。 MathFuncs的CMakeLists如下:

cmake_minimum_required(VERSION 2.8)
project(MathFuncs)

include_directories(${PROJECT_SOURCE_DIR})

SET (HEADER_FILES mysqrt.hpp)
add_library(MathFuncs SHARED mysqrt.cpp ${HEADER_FILES})

可执行的CMakeLists文件如下:

cmake_minimum_required (VERSION 2.6)
project (Tutorial)

set (Tutorial_VERSION_MAJOR 1)
set (Tutorial_VERSION_MINOR 0)
set (Tutorial_VERSION_BUGFIX 0)

#configure header file to pass some of the CMake settings 
#to the source code
configure_file(
    "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
    "${PROJECT_BINARY_DIR}/TutorialConfig.h"
) 

# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
include_directories("${PROJECT_BINARY_DIR}")

# add the include directories necessary to build library
include_directories("${PROJECT_SOURCE_DIR}/MathFuncs}")
add_subdirectory(MathFuncs)

SET (MATHFUNCTIONS_DIR ${PROJECT_SOURCE_DIR}/MathFuncs)
add_executable(tutorial tutorial.cpp ${MATHFUNCTIONS_DIR}/mysqrt.hpp)

target_link_libraries(tutorial MathFuncs)

CMake运行良好。然而,当我尝试使用Visual Studio编译时,出现链接器错误,说明它无法打开MathFuncs.lib。当我从MathFuncs CMakeList中删除'SHARED'选项时,它可以运行,因为它正在构建一个静态库,但是对于我的应用程序,我想要一个共享库DLL。
如何让CMake将库引用设置为共享?
谢谢,

在这行代码中:include_directories("${PROJECT_SOURCE_DIR}/MathFuncs}") 多了一个 } 吗? - Javi
1
你已经验证了MathFuncs的构建吗?(它无法构建不一定会阻止教程尝试构建,但链接文件将不存在)。 - IdeaHat
MathFuncs只能构建为dll,没有任何问题。根据Fraser的回答,我现在明白了首先需要导出定义,但是我仍然无法生成.lib文件。谢谢。 - krames
1个回答

6

看起来你没有正确地从你的DLL中导出类/函数。请参阅这篇文章以获取详细说明。

这并不是完全直接的,但幸运的是CMake可以在这里提供一些帮助。你可以使用GenerateExportHeader模块自动生成一个头文件,其中包含了所有预处理器定义,你应该需要正确地导出库的公共函数。

这是我如何更改你的文件的示例。每个都有足够的注释,让你理解正在发生什么。如果没有,请添加一条评论,我会进行扩展。

顶层CMakeLists.txt:

cmake_minimum_required (VERSION 3.0)
project(Tutorial)

add_subdirectory(MathFuncs)

set(Tutorial_VERSION_MAJOR 1)
set(Tutorial_VERSION_MINOR 0)
set(Tutorial_VERSION_BUGFIX 0)

# Configure header file to pass some of the CMake settings to the source code.
set(TutorialConfigHeader "${PROJECT_BINARY_DIR}/TutorialConfig.h")
configure_file(TutorialConfig.h.in "${TutorialConfigHeader}")

# Include the output file of 'configure_file' to ensure it gets configured.
add_executable(tutorial tutorial.cpp "${TutorialConfigHeader}")

# Add TutorialConfig.h's path to include dirs for 'tutorial'.
target_include_directories(tutorial PRIVATE "${PROJECT_BINARY_DIR}")

target_link_libraries(tutorial MathFuncs)

# If 'MathFuncs' is a shared lib, copy it to 'tutorial's bin dir so
# it can be found at runtime.
get_target_property(MathFuncsType MathFuncs TYPE)
if(MathFuncsType STREQUAL "SHARED_LIBRARY")
  add_custom_command(TARGET tutorial POST_BUILD
                     COMMAND ${CMAKE_COMMAND} -E copy_if_different
                         $<TARGET_FILE:MathFuncs> $<TARGET_FILE_DIR:tutorial>
                     COMMENT "Copying MathFuncs shared lib alongside tutorial."
                     VERBATIM)
endif()


MathFuncs/CMakeLists.txt:

cmake_minimum_required(VERSION 3.0)
project(MathFuncs)

add_library(MathFuncs SHARED
            mysqrt.cpp
            mysqrt.hpp
            "${PROJECT_BINARY_DIR}/mathfuncs_export.h")

# Write appropriate export PP definitions in a file called
# 'mathfuncs_export.h' in the current binary dir
include(GenerateExportHeader)
generate_export_header(MathFuncs)

# Add the current source dir as a PUBLIC include dir
# (to allow mysqrt.hpp to be found by dependent targets)
# Add the current binary dir as a PUBLIC include dir
# (to allow mathfuncs_export.h to be found by dependent targets)
target_include_directories(MathFuncs PUBLIC
                           ${PROJECT_SOURCE_DIR} ${PROJECT_BINARY_DIR})


然后,要使用生成的导出头文件,您只需要让mysqrt.hpp包含类似于以下内容:

#ifndef MYSQRT_HPP_
#define MYSQRT_HPP_

#include "mathfuncs_export.h"

double MATHFUNCS_EXPORT MySqrt(double input);

#endif  // MYSQRT_HPP_

这将会让VS创建一个导出库${CMAKE_BINARY_DIR}/MathFuncs/Debug/MathFuncs.lib(修复链接问题)实际的DLL${CMAKE_BINARY_DIR}/MathFuncs/Debug/MathFuncs.dll

谢谢您的帮助!不幸的是,VS没有生成.lib文件。一切都编译得很好,.dll仍在生成,但我仍然得到相同的链接器错误。 - krames
我很确定如果VS没有生成.lib文件,那是因为你的库中没有导出任何内容。你至少需要将一个函数或类声明为__declspec(dllexport),这就是生成的"mathfuncs_export.h"文件的作用——它应该包含#define MATHFUNCS_EXPORT __declspec(dllexport),这意味着你只需要在你的函数/类声明中使用MATHFUNCS_EXPORT即可。你的构建树中是否有"mathfuncs_export.h"文件,是否包含此定义? - Fraser
mathfuncs_export.h在构建树中 - 同时MATHFUNCS_EXPORT被定义为__declspec(dllexport)。我甚至在mathfuncs.cpp中实现了导出的MySqrt,但仍然没有生成lib文件。 - krames
我的错误 - 我的意思是mysqrt.hpp/cpp。 - krames
不必将共享库复制到可执行文件的文件夹中,可以设置CMAKE_RUNTIME_OUTPUT_DIRECTORY(例如在这里使用cmake构建可执行文件和共享库,运行时链接器找不到dll)。 - Liviu
显示剩余4条评论

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