在CMake中,如何配置Windows共享库目标安装,以便链接到它的用户可执行文件在运行时始终可以找到它?

a64a0gku  于 12个月前  发布在  Windows
关注(0)|答案(1)|浏览(153)

我为一个共享库写了一个小的CMake项目:

add_library(MySharedTarget SHARED "public.h" "implementation.cpp")

target_include_directories(
    MySharedTarget 
    INTERFACE
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}> # when using within the build, the include directories is the current source dir
    $<INSTALL_INTERFACE:include> # when used from installation folder, the include directories is the exported path
)
# copy the public headers to the install directory
install(FILES "public.h" DESTINATION include)

# copy the .lib and dll to lib & bin directory
install(
    TARGETS MySharedTarget 
    EXPORT MySharedTarget 
    ARCHIVE DESTINATION lib/$<CONFIG>
    RUNTIME DESTINATION bin/$<CONFIG>
)

# create a targets file that will define the target for use by clients
install(EXPORT MySharedTarget 
  FILE MySharedTargetTargets.cmake
  DESTINATION cmake
)

include(CMakePackageConfigHelpers)

# generate the config file that includes the exports
# this is for find_package to work
configure_package_config_file(
  ${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in
  "${CMAKE_CURRENT_BINARY_DIR}/MySharedTargetConfig.cmake"
  INSTALL_DESTINATION "cmake"
  NO_SET_AND_CHECK_MACRO
  NO_CHECK_REQUIRED_COMPONENTS_MACRO
  )

install(
  FILES
  ${CMAKE_CURRENT_BINARY_DIR}/MySharedTargetConfig.cmake
  DESTINATION cmake
  )

字符串
所有这些都在安装路径中创建了一个很好的文件夹层次结构。如果将此文件夹添加到不同项目的CMAKE_PREFIX_PATH中,find_packagetarget_link_libraries将按预期工作。

list(PREPEND CMAKE_PREFIX_PATH
    "my_shared_library_folder"
)

find_package(MySharedLibrary CONFIG REQUIRED)


编译链接此共享库的其他项目可以正常工作。但是,在运行时,它无法找到共享库(DLL)。
那么,CMake处理这种情况的惯用方法是什么?我可以告诉CMake将运行时工件复制到可执行路径吗?有没有其他推荐的方法?

tgabmvqs

tgabmvqs1#

免责声明:我对DLLs和Windows平台的经验有限。我的结论很可能是错误的。
据我所知,没有什么东西是不需要库的使用者付出很多努力就能“正常工作”的。要了解DLL搜索在Windows上是如何工作的,请参阅https://learn.microsoft.com/en-us/windows/win32/dll/dynamic-link-library-search-order#standard-search-order-for-unpackaged-apps,其中指出:
[...]搜索顺序如下:

  1. DLL重定向。
  2. API集。
  3. SxS信息清单重新导向。
    1.已加载模块列表。
    1.已知的DLL。
    1.Windows 11,版本21 H2(10.0; Build 22000)与更高版本.进程得程序包依赖关系图.这是应用程序得程序包加上在应用程序程序包清单得<Dependencies>部分中指定为<PackageDependency>得任何依赖关系.将按照依赖关系在清单中得出现顺序搜索它们.
    1.从中加载应用程序的文件夹。
    1.系统文件夹。请使用GetSystemDirectory函数来撷取此文件夹的路径。
  4. 16位系统文件夹。没有函数可以获取此文件夹的路径,但会进行搜索。
  5. Windows文件夹。请使用GetWindowsDirectory函数来取得此文件夹的路径。
    1.目前的文件夹。
    1.在PATH环境变量中列出的目录。这不包括由App Paths注册表项指定的每个应用程序的路径。计算DLL搜索路径时不使用App Paths注册表项。
    [...]要更改系统使用的标准搜索顺序,可以使用LOAD_WITH_ALTERED_SEARCH_PATH调用LoadLibraryEx函数。也可以通过调用SetDllDirectory函数更改标准搜索顺序。
    如果我的理解正确的话,您当前提出的问题的每一个解决方案都需要库的用户做一些非常复杂的事情,以至于CMake只能后退一步,不会或不能自动做任何事情来减轻您的选择和实现的负担(您的存储库)和存储库的用户:修改其可执行文件以使用备用搜索顺序、修改其启动可执行文件的方式以给予修改后的PATH变量或CWD、修改其注册表项等。
    我可以告诉cmake将运行时工件复制到可执行文件路径吗?
    据我所知,这超出了你的库的安装配置的控制范围。软件需要获得预见未来的能力才能工作。这是你的库的 * 用户 * 可以做的事情,他们可能会使用file(COPY)$<TARGET_FILE:tgt>生成器表达式。我想你可以创建一个助手脚本,为它定义一个助手函数,并使该助手脚本是存储库安装的一部分。
    在支持RPATH机制的平台上,CMake可以尽可能地使用它来减轻人们的工作负担(更多信息请参见the dedicated CMake wiki page),但Windows不是这些平台之一(请参见Is there a Windows/MSVC equivalent to the -rpath linker flag?)。
    根据上面链接的Q&A中人们的说法,一种常见的模式是使用一个.bat文件来修改PATH环境变量 *,然后 * 调用可执行文件。我想您可以尝试设置一种模式,在该模式中,您的库安装附带一个.bat脚本,该脚本将DLL的安装目录添加到PATH,然后库的使用者有自己的.bat脚本来调用.bat并运行它的可执行文件,这仍然需要用户的一些操作,但看起来相对较少。
    或者你可以告诉你的用户修改他们的系统PATH,但是在某些情况下这是不希望的。但是如果这是一个可以接受的解决方案,你甚至可以把它作为安装程序/脚本的一部分来自动化。唉,安装程序超出了我的知识范围,而且我很肯定每个安装程序的配置实现细节会有所不同。
    如果您的库的使用者的维护者愿意从安装树而不是构建树对他们自己的可执行文件进行所有本地运行和测试,我相信他们可以考虑使用install(IMPORTED_RUNTIME_ARTIFACTS ...)install(RUNTIME_DEPENDENCY_SET ...),但同样,这将超出您的库及其安装配置的控制范围。
    我有点希望我错了,希望比我更懂的人来纠正我,写一个更好的答案。

相关问题