[CMake] DLL handling under CMake

Louis-Paul CORDIER lp.cordier at dynamixyz.com
Tue Jul 4 04:19:55 EDT 2017


Hi,

Thank you very much for this code snippet. However I don't like the 
fixup_bundle function, as it takes the first dll that it found to be 
linked against.

I also did a try with a dependency scanning function. It is quiet long 
to write, but I guess it is the cleanest way to handle DLL under Windows.
Note: I still have an issue with this function. Indeed, if user uses 
Generator expressions for library dependencies, it will not work.
e.g:
add_library(Foo_lib IMPORTED GLOBAL)
# ... set location properties
target_link_libraries(${PROJECT_NAME} optimized 
$<$<CONFIG:Release_Production>:Foo_lib>)

Any idea for a workaround? What do you think about this CMake code?

Also, I would see a real benefit to add a LINK_DEPENDENT_LIBRARIES 
property (inspired of IMPORTED_LINK_DEPENDENT_LIBRARIES) to each target 
that could be automatically filled by each target_link_libraries() calls.



# This function scan all dependencies of a project recursively, and 
retrieve all shared
# library associated with it.
# Prerequisite: your upstream CMakeLists.txt must make use of 
add_library(foo SHARED IMPORTED GLOBAL),
# and fill the following properties on the imported target:
# set_target_properties(foo PROPERTIES IMPORTED_IMPLIB "path_to_foo.lib")
# set_target_properties(foo PROPERTIES IMPORTED_LOCATION "path_to_foo.dll")
# set_target_properties(foo PROPERTIES IMPORTED_LINK_DEPENDENT_LIBRARIES 
"path_to_dll_on_which_foo_depends.dll")
# GLOBAL keyword is important as it allows downstream CMakeLists.txt to 
scan dependencies.

# Input parameters:
# dep_to_scan: your downstream project
# config_to_scan: configuration to use for the scanning.
# output_variable: variable in which the function stores the result.

# Usage:
# RECURSIVE_SCAN(my_app Release DLLS)
#  install(FILES ${DLLS}
#     DESTINATION release
#     CONFIGURATIONS Release)

set(COUNT 0)
function(RECURSIVE_SCAN dep_to_scan config_to_scan output_variable)

   MATH(EXPR COUNT "${COUNT}+1")
   string(RANDOM LENGTH ${COUNT} ALPHABET "-" SPACES)

   message("${SPACES} Scanning ${dep_to_scan}")
   if(NOT TARGET ${dep_to_scan})
     MATH(EXPR COUNT "${COUNT}-1")
     #message("${dep_to_scan} Is not target")
     return()
   endif()


   get_target_property(_is_imported ${dep_to_scan} IMPORTED)
   if(_is_imported)

     # We need to check if the imported library rely on other shared 
libraries.
     get_target_property(_dependent_dll ${_lib} 
IMPORTED_LINK_DEPENDENT_LIBRARIES_${config_to_scan})
     if(NOT _dependent_dll)
       get_target_property(_dependent_dll ${_lib} 
IMPORTED_LINK_DEPENDENT_LIBRARIES)
     endif()

     if(_dependent_dll)
       list(APPEND ${output_variable} ${_dependent_dll})
     endif()


     #Otherwise, check if it is a shared library. (LOCATION variable can be
     # either .lib or DLL regarding of the type of library.)
     get_target_property(_TYPE ${dep_to_scan} TYPE)

     if(NOT _TYPE STREQUAL STATIC_LIBRARY)
       get_target_property(_dll_found ${dep_to_scan} 
LOCATION_${config_to_scan})
       if(_dll_found)
         list(APPEND ${output_variable} ${_dll_found})
       endif()

     endif()

     message("${SPACES}- DLL found: (${${output_variable}})")

   endif(_is_imported)

   get_target_property(_libraries ${dep_to_scan} INTERFACE_LINK_LIBRARIES)

   if(_libraries)
       foreach(_lib ${_libraries})
         RECURSIVE_SCAN(${_lib} ${config_to_scan} ${output_variable})
       endforeach()
   endif()

   # If we reach our first recursion, we need to clean the list of
   # DLL in order to remove duplicates.
   MATH(EXPR COUNT "${COUNT}-1")

   if(${COUNT} EQUAL 0)
     list(REMOVE_DUPLICATES ${output_variable})
   endif()

   set(${output_variable} ${${output_variable}} PARENT_SCOPE)

endfunction(RECURSIVE_SCAN)


Best regards,

Louis-Paul CORDIER

Le 04/05/2017 à 09:51, lectem at gmail.com a écrit :
>
> I managed to get it working by using an intermediate script.
>
> One might want to generate the script instead of using the « RUN_IT » 
> variable trick.
>
> This was only tested on Windows, but seems to work fine.
>
> Put the following code in a xxxxxx.cmake file, include it from your 
> CMakeLists.txt and enjoy.
>
> # This is a helper script to run BundleUtilities fixup_bundle as postbuild
>
> # for a target. The primary use case is to copy .DLLs to the build 
> directory for
>
> # the Windows platform. It allows generator expressions to be used to 
> determine
>
> # the binary location
>
> #
>
> # Usage : run_fixup(TARGET LIBS DIRS)
>
> # - TARGET : A cmake target
>
> # - See fixup_bundle for LIBS and DIRS arguments
>
> if(RUN_IT)
>
> # Script ran by the add_custom_command
>
>                 include(BundleUtilities)
>
> fixup_bundle("${TO_FIXUP_FILE}" "${TO_FIXUP_LIBS}" "${TO_FIXUP_DIRS}")
>
> # End of script ran by the add_custom_command
>
> else()
>
> set(THIS_FILE ${CMAKE_CURRENT_LIST_FILE})
>
> message(${THIS_FILE})
>
> function(run_fixup _target _libs _dirs)
>
>                 message(${THIS_FILE})
>
>                 add_custom_command(
>
>                                TARGET ${_target} POST_BUILD
>
>                                COMMAND ${CMAKE_COMMAND} 
> -DRUN_IT:BOOL=ON -DTO_FIXUP_FILE=$<TARGET_FILE:${_target}> 
> -DTO_FIXUP_LIBS=${_libs} -DTO_FIXUP_DIRS=${_dirs}  -P ${THIS_FILE}
>
>                                COMMENT "Fixing up dependencies for 
> ${_target}"
>
>                                VERBATIM
>
>                 )
>
> endfunction()
>
> endif()
>
> *De : *Clément Gregoire <mailto:lectem at gmail.com>
> *Envoyé le :*jeudi 4 mai 2017 08:37
> *À : *Hendrik Sattler <mailto:post at hendrik-sattler.de>; Louis-Paul 
> CORDIER <mailto:lp.cordier at dynamixyz.com>; Cmake Mailing List 
> <mailto:cmake at cmake.org>
> *Objet :*Re: [CMake] DLL handling under CMake
>
> I'd also be interested in this. I saw an old mail in the ML about 
> this, but it seems fixup_bundle is old and cant use generator 
> expressions, making it hard to use (I don't want to hardcode the 
> executable path).
>
> Do you have a sample for this ?
>
> CMake would really benefit from having those features made more 
> accessible instead of everyone having to write its own script
>
> Le sam. 29 avr. 2017 22:10, Hendrik Sattler <post at hendrik-sattler.de 
> <mailto:post at hendrik-sattler.de>> a écrit :
>
>
>
> Am 27. April 2017 10:43:50 MESZ schrieb Louis-Paul CORDIER 
> <lp.cordier at dynamixyz.com <mailto:lp.cordier at dynamixyz.com>>:
> >This steps are tedious and I'm wondering if there is a mechanism that
> >exists or that have to be imagined to make the DLL nightmare end.
>
> I use BundleUtilities to achieve the copying of DLL files to the 
> installation directory. The main problem for this is to enumerate the 
> needed directories.
>
> I use the same for copying DLL files to the output directory to ease 
> debugging.
>
> The advantage is the inspection of the exe for really needed DLL 
> files. This AUTOMATICALLY handles the case debug vs. release.
>
> HS
>
> --
> Diese Nachricht wurde von meinem Android-Mobiltelefon mit K-9 Mail 
> gesendet.
> --
>
> Powered by www.kitware.com <http://www.kitware.com>
>
> Please keep messages on-topic and check the CMake FAQ at: 
> http://www.cmake.org/Wiki/CMake_FAQ
>
> Kitware offers various services to support the CMake community. For 
> more information on each offering, please visit:
>
> CMake Support: http://cmake.org/cmake/help/support.html
> CMake Consulting: http://cmake.org/cmake/help/consulting.html
> CMake Training Courses: http://cmake.org/cmake/help/training.html
>
> Visit other Kitware open-source projects at 
> http://www.kitware.com/opensource/opensource.html
>
> Follow this link to subscribe/unsubscribe:
> http://public.kitware.com/mailman/listinfo/cmake
>

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://public.kitware.com/pipermail/cmake/attachments/20170704/57fe10e6/attachment.html>


More information about the CMake mailing list