eph*_*err 6 dll cmake dllimport
构建操作系统:Windows 10,Cmake 3.16.3。
我用来target_link_libraries将第三方.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}
)
Run Code Online (Sandbox Code Playgroud)
其中LIBS包含我的dll但不RES包含UNRES通往第三个帕蒂的路径dll。
因此,在较新的 CMake 中,所有这些运行时依赖查找魔法都存在严重的肮脏之处,而这根本不是他们的错。问题是你、我和大约 90% 的 CMake 用户世界的其他人一直在做错误的find module #THISWHOLETIME ,现在我们的鸡已经回家了,因为,正如你可能已经发现的那样,GET_RUNTIME_DEPENDENCIES/ RUNTIME_DEPENDENCY_SET,$<TARGET_RUNTIME_DLLS>如果您尝试将它们与具有(我现在知道的)由IMPORTED未正确设置它们的 Find 模块创建的损坏依赖项的目标一起使用,那么它们将完全崩溃。因此,上个月我在 CMake Discourse 论坛上发布了这篇文章(我之前的链接):
Windows DLL 问题\xe2\x84\xa2 之前已经以某种形式出现过多次,但它\xe2\x80\x99s 带来了新的视角$<TARGET_RUNTIME_DLLS>,所以这里\xe2\x80\x99s 对其进行了全新的处理。
如果你\xe2\x80\x99像我一样(并且大约90%的所有CMake用户/开发人员都像我一样,从我\xe2\x80\x99已经能够在公共项目\xe2\x80\x99源中观察到树),您在 Windows 上编写查找模块的方法可能是这样的:
\n.lib/.dll.a导入库而不是实际的 DLL,使用find_library().IMPORTED_LOCATION因为这似乎工作正常。\xe2\x80\x99s 为我们所有人服务了很多年(实际上是几十年),所以我们\xe2\x80\x99s 基本上只是接受它作为 CMake 在 Windows 上的工作方式。
\n但现在来了$<TARGET_RUNTIME_DLLS>。如果您尝试在 Windows 上实际使用它,您可能会发现,虽然所有 CONFIG 模式包依赖项 xe2x80x99 DLL 都被很好地捕获,但生成器表达式将愉快地忽略从 Find 模块创建的任何目标,这些目标像我上面描述的那样编写。\xe2\x80\xa6 这可能是其中的大部分。(在我自己的库构建中,它是所有的,甚至是我没有写的\xe2\x80\x99t 的。)
为了$<TARGET_RUNTIME_DLLS>工作,IMPORTED 目标必须正确定义为共享库目标,并且需要IMPORTED_正确设置其属性:import lib 路径在 中IMPORTED_IMPLIB,DLL 路径在IMPORTED_LOCATION.
所以,现在我有了这个新模块,它使用DLLTOOL.EXE它及其方便的-I标志来获取导入库\xe2\x80\x99s DLL 的名称,然后使用find_program(). (仅仅因为find_library()不会\xe2\x80\x99t匹配DLL,并且我想查看路径。我可以\xe2\x80\x99已经使用过,find_file()但我\xe2\x80\x99m非常确定我\xe2\x80\x99d必须明确地给它更多的搜索路径。)
该宏采用一个参数,即已配置的变量的名称<prefix>_IMPLIB。(或者<prefix>_IMPLIBS, it\xe2\x80\x99s 与复数无关,并且将遵循命名其输出变量时输入使用的任何形式。)
您传递给它的名称的变量应该已经包含导入库的有效路径。通常,\xe2\x80\x99 由 设定find_library(),尽管我们\xe2\x80\x99 都将它们视为运行时库 (DLL),但实际上它们并非如此。
配备find_library(<prefix>_IMPLIB ...)输出后,implib_to_dll(<prefix>_IMPLIB)将尝试发现并使用<prefix>_LIBRARY导入 lib\xe2\x80\x99s 关联的运行时 DLL 的路径自动填充相应的变量。
将所有正确的变量设置为正确的值后,现在可以在 Windows 上正确配置共享导入库目标。$<TARGET_RUNTIME_DLLS>然后可用于发现和操作由这些目标定义的一组 DLL。
Find 有点痛苦,而且确实感觉像是 CMake 至少可以半自动完成的事情。但是,至少目前它是有效的。
\n现在我只需重写所有查找模块即可使用它。叹。
\n#[=======================================================================[.rst:\nIMPLIB_UTILS\n------------\n\nTools for CMake on WIN32 to associate IMPORTED_IMPLIB paths (as discovered\nby the :command:`find_library` command) with their IMPORTED_LOCATION DLLs.\n\nWriting Find modules that create ``SHARED IMPORTED`` targets with the\ncorrect ``IMPORTED_IMPLIB`` and ``IMPORTED_LOCATION`` properties is a\nrequirement for ``$<TARGET_RUNTIME_DLLS>`` to work correctly. (Probably\n``IMPORTED_RUNTIME_DEPENDENCIES`` as well.)\n\nMacros Provided\n^^^^^^^^^^^^^^^\n\nCurrently the only tool here is ``implib_to_dll``. It takes a single\nargument, the __name__ (_not_ value!) of a prefixed ``<prefix>_IMPLIB``\nvariable (containing the path to a ``.lib`` or ``.dll.a`` import library).\n\n``implib_to_dll`` will attempt to locate the corresponding ``.dll`` file\nfor that import library, and set the variable ``<prefix>_LIBRARY``\nto its location.\n\n``implib_to_dll`` relies on the ``dlltool.exe`` utility. The path can\nbe set by defining ``DLLTOOL_EXECUTABLE`` in the cache prior to\nincluding this module, if it is not set implib_utils will attempt to locate\n``dlltool.exe`` using ``find_program()``.\n\nRevision history\n^^^^^^^^^^^^^^^^\n2021-11-18 - Updated docs to remove CACHE mentions, fixed formatting\n2021-10-14 - Initial version\n\nAuthor: FeRD (Frank Dana) <ferdnyc@gmail.com>\nLicense: CC0-1.0 (Creative Commons Universal Public Domain Dedication)\n#]=======================================================================]\ninclude_guard(DIRECTORY)\n\nif (NOT WIN32)\n # Nothing to do here!\n return()\nendif()\n\nif (NOT DEFINED DLLTOOL_EXECUTABLE)\n find_program(DLLTOOL_EXECUTABLE\n NAMES dlltool dlltool.exe\n DOC "The path to the DLLTOOL utility"\n )\n if (DLLTOOL_EXECUTABLE STREQUAL "DLLTOOL_EXECUTABLE-NOTFOUND")\n message(WARNING "DLLTOOL not available, cannot continue")\n return()\n endif()\n message(DEBUG "Found dlltool at ${DLLTOOL_EXECUTABLE}")\nendif()\n\n#\n### Macro: implib_to_dll\n#\n# (Win32 only)\n# Uses dlltool.exe to find the name of the dll associated with the\n# supplied import library.\nmacro(implib_to_dll _implib_var)\n set(_implib ${${_implib_var}})\n set(_library_var "${_implib_var}")\n # Automatically update the name, assuming it\'s in the correct format\n string(REGEX REPLACE\n [[_IMPLIBS$]] [[_LIBRARIES]]\n _library_var "${_library_var}")\n string(REGEX REPLACE\n [[_IMPLIB$]] [[_LIBRARY]]\n _library_var "${_library_var}")\n # We can\'t use the input variable name without blowing away the\n # previously-discovered contents, so that\'s a non-starter\n if ("${_implib_var}" STREQUAL "${_library_var}")\n message(ERROR "Name collision! You probably didn\'t pass "\n "implib_to_dll() a correctly-formatted variable name. "\n "Only <prefix>_IMPLIB or <prefix>_IMPLIBS is supported.")\n return()\n endif()\n\n if(EXISTS "${_implib}")\n message(DEBUG "Looking up dll name for import library ${_implib}")\n execute_process(COMMAND\n "${DLLTOOL_EXECUTABLE}" -I "${_implib}"\n OUTPUT_VARIABLE _dll_name\n OUTPUT_STRIP_TRAILING_WHITESPACE\n )\n message(DEBUG "DLLTOOL returned ${_dll_name}, finding...")\n\n # Check the directory where the import lib is found\n get_filename_component(_implib_dir ".." REALPATH\n BASE_DIR "${_implib}")\n message(DEBUG "Checking import lib directory ${_implib_dir}")\n\n # Add a check in ../../bin/, relative to the import library\n get_filename_component(_bindir "../../bin" REALPATH\n BASE_DIR "${_implib}")\n message(DEBUG "Also checking ${_bindir}")\n\n find_program(${_library_var}\n NAMES ${_dll_name}\n HINTS\n ${_bindir}\n ${_implib_dir}\n PATHS\n ENV PATH\n )\n set(${_library_var} "${${_library_var}}" PARENT_SCOPE)\n message(DEBUG "Set ${_library_var} to ${${_library_var}}")\n endif()\nendmacro()\nRun Code Online (Sandbox Code Playgroud)\n
GET_RUNTIME_DEPENDENCIES不知道您的配置时间变量,因此您需要手动指定它们。这个答案指出您可以将变量传递给安装步骤,但到目前为止我还无法使其工作。幸运的是,它确实支持生成器表达式。
您的代码片段中的另一个问题是它必须在安装时调用。例如在一个install(CODE ...)块中。
因此,考虑到所有这些,这应该可以帮助您开始。
install(CODE [[
file(GET_RUNTIME_DEPENDENCIES
RESOLVED_DEPENDENCIES_VAR RES
UNRESOLVED_DEPENDENCIES_VAR UNRES
CONFLICTING_DEPENDENCIES_PREFIX CONFLICTING_DEPENDENCIES
EXECUTABLES $<TARGET_FILE:your_executable_target_name>
LIBRARIES $<TARGET_FILE:a_lib_target_name>
)
message("\n\nFound dependencies :")
foreach(DEP ${RES})
message("${DEP}")
endforeach()
message("\n\nNot found dependencies :")
foreach(DEP ${UNRES})
message("${DEP}")
endforeach()
]])
Run Code Online (Sandbox Code Playgroud)
构建您的安装目标以查看结果。
cmake ..
cmake --build . --target install
Run Code Online (Sandbox Code Playgroud)