CMake GET_RUNTIME_DEPENDENCIES 通过 lib(导入库)链接时找不到 dll 库?

2023-12-31

构建操作系统:Windows 10,Cmake 3.16.3。

I use target_link_libraries链接第 3 方.lib文件到我的.dll图书馆。

但是当我使用GET_RUNTIME_DEPENDENCIES安装我的dll,没有发现依赖关系。

仅在Windows上出现这种情况,在Linux上安装是可以的。

有没有任何线索如何解决这个问题,或者至少如何调试它?

在 Windows 上使用 CMake 来确定依赖关系的具体命令是什么?

I call 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}
)

Where LIBS包含我的dll but no RES no UNRES包含第三方的路径dll.


因此,在较新的 CMake 中,所有这些运行时依赖项查找魔法都非常令人讨厌,而且这根本不是他们的错。问题是你、我以及大约 90% 的 CMake 用户世界都已经查找模块wrong #这段时间 https://discourse.cmake.org/t/windows-libraries-find-modules-and-target-runtime-dlls-re-re-revisited/4286,现在我们的鸡已经回家了,因为,正如你可能已经发现的那样,GET_RUNTIME_DEPENDENCIES / RUNTIME_DEPENDENCY_SET, $<TARGET_RUNTIME_DLLS>如果你尝试将它们与已经(我现在知道)损坏的目标一起使用,将会完全把床弄脏IMPORTED查找未正确设置的模块创建的依赖项。因此,上个月我在 CMake Discourse 论坛上发布了这篇文章(我之前的链接):

Windows 库、查找模块和 TARGET_RUNTIME_DLLS

Windows DLL Question™ 之前已经以这样或那样的形式出现过多次,但它被赋予了新的视角:$<TARGET_RUNTIME_DLLS>,所以这里有一个新的看法。

如果您像我一样(从我在公共项目的源代码树中观察到的情况来看,大约 90% 的 CMake 用户/开发人员都像我一样),那么您在 Windows 上编写 Find 模块的方法可能已经是这样的:

  1. 在所有三个桌面平台上使用相同的代码
  2. 让 CMake 发现.lib / .dll.a导入库而不是实际的 DLL,使用find_library().
  3. 最终将目标创建为 UNKNOWN IMPORTED,因为如果您尝试仅使用导入库创建共享导入库目标,它将无法工作,但 UNKNOWN IMPORTED 工作得很好,所以嗯。
  4. 将导入库设置为目标IMPORTED_LOCATION因为这似乎工作正常。
  5. 今天到此为止,因为嘿——一切都编译好了。

这已经为我们所有人服务了很多年(实际上是几十年),所以我们基本上已经接受它作为 CMake 在 Windows 上的工作方式。

但现在出现了$<TARGET_RUNTIME_DLLS>。如果您尝试在 Windows 上实际使用它,您可能会发现,虽然所有 CONFIG 模式包依赖项的 DLL 都被很好地捕获,但生成器表达式将愉快地忽略从编写的 Find 模块创建的任何目标就像我上面描述的那样。 ……这可能是其中的大多数。 (在我自己的库构建中,它是所有的,甚至是我没有写的。)

For $<TARGET_RUNTIME_DLLS>为了工作,导入的目标必须被正确定义为共享库目标,并且它需要有它的IMPORTED_属性设置正确:导入lib路径IMPORTED_IMPLIB,DLL路径在IMPORTED_LOCATION.

所以,现在我有了这个使用的新模块DLLTOOL.EXE而且它很方便-I标志来获取导入库的 DLL 的名称,然后使用find_program()。 (仅仅是因为find_library()与 DLL 不匹配,我想查看 PATH。我本来可以用find_file()但我很确定我必须明确地给它更多的搜索路径。)

该宏有一个参数,即name已经配置的变量<prefix>_IMPLIB. (Or <prefix>_IMPLIBS,它与复数无关,并且将遵循您在命名其输出变量时输入使用的任何形式。)

您传递给它的名称的变量应该已经包含导入库的有效路径。通常由以下设置find_library(),尽管我们一直将它们视为运行时库 (DLL),但实际上它们并非如此。

武装find_library(<prefix>_IMPLIB ...)输出,implib_to_dll(<prefix>_IMPLIB)将尝试发现并自动填充相应的变量<prefix>_LIBRARY以及导入库的关联运行时 DLL 的路径。

将所有正确的变量设置为正确的值后,现在可以在 Windows 上正确配置共享导入库目标。$<TARGET_RUNTIME_DLLS>然后可用于发现和操作由这些目标定义的一组 DLL。

Find 有点痛苦,而且确实感觉像是 CMake 至少可以半自动完成的事情。但是,至少目前它是有效的。

现在我只需重写所有查找模块即可使用它。叹。

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) <[email protected] /cdn-cgi/l/email-protection>
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()
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

CMake GET_RUNTIME_DEPENDENCIES 通过 lib(导入库)链接时找不到 dll 库? 的相关文章

随机推荐