dll - CMake GET_RUNTIME_DEPENDENCIES通过lib(导入库)链接时找不到dll库?
问题描述
构建操作系统:Windows 10、Cmake 3.16.3。
我target_link_libraries
用来将第 3 方.lib
文件链接到我的.dll
库。
但是当我GET_RUNTIME_DEPENDENCIES
用来安装我的dll
时,没有找到依赖项。
它只发生在 Windows 上,在 Linux 上安装是可以的。
是否有任何线索如何解决这个问题,或者至少如何调试它?
什么确切的命令在 Windows 上使用 CMake 来确定依赖关系?
我GET_RUNTIME_DEPENDENCIES
这样称呼:
file(GET_RUNTIME_DEPENDENCIES
RESOLVED_DEPENDENCIES_VAR RES
UNRESOLVED_DEPENDENCIES_VAR UNRES
CONFLICTING_DEPENDENCIES_PREFIX CONFLICTING_DEPENDENCIES
EXECUTABLES ${EXECS}
LIBRARIES ${LIBS} ${MODULES} ${QTPLUGINS_LIBS}
DIRECTORIES ${RUNTIME_DEPENDENCIES_DIRECTORIES}
POST_EXCLUDE_REGEXES ${PLATFORM_POST_EXCLUDE_REGEXES}
)
WhereLIBS
包含 my dll
but no RES
no UNRES
contains path to 3rd paty dll
。
解决方案
因此,在较新的 CMake 中,所有这些运行时依赖查找魔法都存在严重的问题,这根本不是他们的错。问题是你、我和大约 90% 的其他 CMake 用户世界一直在查找错误的模块#THISWHOLETIME ,现在我们的鸡已经回家栖息,因为正如你可能发现的那样,GET_RUNTIME_DEPENDENCIES
/将如果您尝试将它们与未正确设置它们的查找模块创建的具有(我现在知道的)损坏的依赖关系的目标一起使用,那么所有这些都完全被抛弃了。所以,上个月我在 CMake Discourse 论坛上发布了这篇文章(我之前的链接):RUNTIME_DEPENDENCY_SET
$<TARGET_RUNTIME_DLLS>
IMPORTED
Windows 库、查找模块和 TARGET_RUNTIME_DLLS
Windows DLL Question™ 以前以一种或另一种形式出现过无数次,但它被 以新$<TARGET_RUNTIME_DLLS>
的视角呈现,所以这里有一个新的看法。
如果你和我一样(根据我在公共项目的源代码树中观察到的情况,大约 90% 的 CMake 用户/开发人员都和我一样),你在 Windows 上编写 Find 模块的方法可能是这样的:
- 在所有三个桌面平台上使用相同的代码
- 让 CMake 发现
.lib
/.dll.a
导入库而不是实际的 DLL,使用find_library()
. - 最终将您的目标创建为 UNKNOWN IMPORTED,因为如果您尝试仅使用导入库创建 SHARED IMPORTED 库目标,它将无法正常工作,但 UNKNOWN IMPORTED 工作得很好,所以嗯。
- 将导入库设置为目标
IMPORTED_LOCATION
,因为这似乎工作正常。 - 收工,因为嘿-一切都编译了。
这已经为我们所有人服务了多年(实际上是几十年),所以我们大多只是接受它作为 CMake 在 Windows 上的工作方式。
但现在来了$<TARGET_RUNTIME_DLLS>
。如果您尝试在 Windows 上实际使用它,您可能已经发现,虽然您的所有 CONFIG 模式包依赖项的 DLL 都被很好地捕获,但生成器表达式将愉快地忽略从 Find 模块创建的任何目标就像我上面描述的那样。......这可能是他们中的大多数。(在我自己的库构建中,所有这些,甚至是我没有编写的。)
为了$<TARGET_RUNTIME_DLLS>
工作,IMPORTED 目标必须正确定义为共享库目标,并且需要IMPORTED_
正确设置其属性:import lib path in IMPORTED_IMPLIB
,DLL path in IMPORTED_LOCATION
。
所以,现在我有了这个新模块,它使用DLLTOOL.EXE
它及其方便-I
的标志来获取导入库的 DLL 的名称,然后使用find_program()
. (只是因为find_library()
不匹配 DLL,我想查看 PATH。我可以使用find_file()
但我很确定我必须明确地给它更多的搜索路径。)
宏接受一个参数,即您已配置变量的名称<prefix>_IMPLIB
。(或者<prefix>_IMPLIBS
,它与复数无关,并且在命名其输出变量时将遵循您的输入使用的任何形式。)
您传递给其名称的变量应该已经包含导入库的有效路径。通常这是由 设置的find_library()
,即使我们都将它们视为运行时库 (DLL),但实际上它们并非如此。
有了find_library(<prefix>_IMPLIB ...)
输出,implib_to_dll(<prefix>_IMPLIB)
将尝试发现并<prefix>_LIBRARY
使用导入库的关联运行时 DLL 的路径自动填充相应的变量。
将所有正确的变量设置为正确的值后,现在可以在 Windows 上正确配置 SHARED IMPORTED 库目标。$<TARGET_RUNTIME_DLLS>
然后可用于发现和操作由这些目标定义的 DLL 集。
Find 有点痛苦,而且确实有点像 CMake 至少可以半自动地做的事情。但是,至少现在它有效。
现在我只需要重写我所有的 find 模块来使用它。叹。
ImplibUtils.cmake
#[=======================================================================[.rst:
IMPLIB_UTILS
------------
Tools for CMake on WIN32 to associate IMPORTED_IMPLIB paths (as discovered
by the :command:`find_library` command) with their IMPORTED_LOCATION DLLs.
Writing Find modules that create ``SHARED IMPORTED`` targets with the
correct ``IMPORTED_IMPLIB`` and ``IMPORTED_LOCATION`` properties is a
requirement for ``$<TARGET_RUNTIME_DLLS>`` to work correctly. (Probably
``IMPORTED_RUNTIME_DEPENDENCIES`` as well.)
Macros Provided
^^^^^^^^^^^^^^^
Currently the only tool here is ``implib_to_dll``. It takes a single
argument, the __name__ (_not_ value!) of a prefixed ``<prefix>_IMPLIB``
variable (containing the path to a ``.lib`` or ``.dll.a`` import library).
``implib_to_dll`` will attempt to locate the corresponding ``.dll`` file
for that import library, and set the variable ``<prefix>_LIBRARY``
to its location.
``implib_to_dll`` relies on the ``dlltool.exe`` utility. The path can
be set by defining ``DLLTOOL_EXECUTABLE`` in the cache prior to
including this module, if it is not set implib_utils will attempt to locate
``dlltool.exe`` using ``find_program()``.
Revision history
^^^^^^^^^^^^^^^^
2021-11-18 - Updated docs to remove CACHE mentions, fixed formatting
2021-10-14 - Initial version
Author: FeRD (Frank Dana) <ferdnyc@gmail.com>
License: CC0-1.0 (Creative Commons Universal Public Domain Dedication)
#]=======================================================================]
include_guard(DIRECTORY)
if (NOT WIN32)
# Nothing to do here!
return()
endif()
if (NOT DEFINED DLLTOOL_EXECUTABLE)
find_program(DLLTOOL_EXECUTABLE
NAMES dlltool dlltool.exe
DOC "The path to the DLLTOOL utility"
)
if (DLLTOOL_EXECUTABLE STREQUAL "DLLTOOL_EXECUTABLE-NOTFOUND")
message(WARNING "DLLTOOL not available, cannot continue")
return()
endif()
message(DEBUG "Found dlltool at ${DLLTOOL_EXECUTABLE}")
endif()
#
### Macro: implib_to_dll
#
# (Win32 only)
# Uses dlltool.exe to find the name of the dll associated with the
# supplied import library.
macro(implib_to_dll _implib_var)
set(_implib ${${_implib_var}})
set(_library_var "${_implib_var}")
# Automatically update the name, assuming it's in the correct format
string(REGEX REPLACE
[[_IMPLIBS$]] [[_LIBRARIES]]
_library_var "${_library_var}")
string(REGEX REPLACE
[[_IMPLIB$]] [[_LIBRARY]]
_library_var "${_library_var}")
# We can't use the input variable name without blowing away the
# previously-discovered contents, so that's a non-starter
if ("${_implib_var}" STREQUAL "${_library_var}")
message(ERROR "Name collision! You probably didn't pass "
"implib_to_dll() a correctly-formatted variable name. "
"Only <prefix>_IMPLIB or <prefix>_IMPLIBS is supported.")
return()
endif()
if(EXISTS "${_implib}")
message(DEBUG "Looking up dll name for import library ${_implib}")
execute_process(COMMAND
"${DLLTOOL_EXECUTABLE}" -I "${_implib}"
OUTPUT_VARIABLE _dll_name
OUTPUT_STRIP_TRAILING_WHITESPACE
)
message(DEBUG "DLLTOOL returned ${_dll_name}, finding...")
# Check the directory where the import lib is found
get_filename_component(_implib_dir ".." REALPATH
BASE_DIR "${_implib}")
message(DEBUG "Checking import lib directory ${_implib_dir}")
# Add a check in ../../bin/, relative to the import library
get_filename_component(_bindir "../../bin" REALPATH
BASE_DIR "${_implib}")
message(DEBUG "Also checking ${_bindir}")
find_program(${_library_var}
NAMES ${_dll_name}
HINTS
${_bindir}
${_implib_dir}
PATHS
ENV PATH
)
set(${_library_var} "${${_library_var}}" PARENT_SCOPE)
message(DEBUG "Set ${_library_var} to ${${_library_var}}")
endif()
endmacro()
推荐阅读
- java - springboot h2 无法创建数据库并登录到 h2-console
- google-maps-react - 如何使用 google-maps-react 加载 GeoJSON?
- terraform - Terraform 获取安全组的私有子网
- python - 如何处理管道中 OneHotEncoder 的不同类别?
- c++ - POD 结构(相同类型的成员):成员是否位于连续的内存位置?
- c# - 具有不同字体的 Xfinium 多行单元格
- c# - 从 .NET Core 应用程序使用 IBMMQ amqmdnet.dll
- ionic-framework - ionic 4 如何动态添加 svg 图标 - {title: 'About', url: '/about' , icon:'person'}
- mysql - 当数量等于0时如何隐藏药物?
- r - 如何使用带有geom =“line”的stat_summary在ggplot2中绘制NA间隙?