[CMake] DLL handling under CMake

Michael Jackson mike.jackson at bluequartz.net
Sat Apr 29 10:00:37 EDT 2017


Our project uses some custom cmake functions to gather the DLLs from 3rd 
party libs (HDF5, TBB, Qt) and create custom targets to copy them to the 
build directory and create install rules to copy them to the package 
when it is created. Yes, they were tedious to write and get correct but 
they seem to work. The Qt version was the easiest after we moved to Qt 5 
since it is straight forward to get the locations. The HDF5 folks 
recently updated their config file to have the same type of information. 
TBB is odd, but relatively easy to guess the correct pattern.

I had a discussion about this topic yesterday and my pain point is that 
a lot of the "Find**" modules really _need_ to also define the DLL 
location instead of just where the link libs are at. From my little 
world on Windows all the .dll files seem to be in a bin directory. So I 
think that for each "Find***" those should define a few additional items:

FOO_IS_SHARED
FOO_BINARY_DIR

assuming of course that the proper FooConfig.cmake is not available.

--
Mike Jackson

PS:: http://www.github.com/bluequartzsoftware/cmp has our custom 
scripts. It is mainly Qt focused but may have some gems in there that 
you find valuable.

Louis-Paul CORDIER wrote:
> Hi,
>
> I'm using CMake for a while now for cross-platform project.
>
> In opposition to Linux, Windows does not have a library system
> management through a repository system. I would say that 99% of
> applications that have common libraries between them does not share the
> runtimes. Each time, the .dll files are duplicated for each application.
> We have to live with that, and to create proper CMakeLists.txt that can
> handle DLL integration.
>
> I think many of CMake users have the following pipeline on Windows:
>
> 1. Run CMake (this will execute some find_library to get the correct
> .lib files)
> 2. Compile the application
> 3. Run the INSTALL target
> 4. Copy the .dll files into the output binary folder.
> 5. Package the application with the DLL inside (e.g innosetup)
>
> Currently find_library does not search for runtime, but only for .lib.
> So even if a developer is using find_library, he/she has to implement
> some additional CMake code to retrieve the path to .dll files and copy
> them using the install or the file CMake commands. I added my current
> code for handling Qt library in my project at the end of this email. (I
> put a huge part of it if someone want to reuse it). You will notice that
> this code is handling the case where you are debugging using Visual
> Studio, to avoid the missing .DLL issue.
>
> 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.
>
> All the best
>
> Louis-Paul Cordier
>
>
> ...
>
> #Folder where compiled files for release/debug install will be placed
> set(G3_RELEASE_DIR "release")
> set(G3_DEBUG_DIR "debug")
>
> find_package(Qt5 ${QT5VC2015_VERSION} EXACT COMPONENTS Core OpenGL
> Concurrent REQUIRED)
>
> QT5_WRAP_UI(...)
> QT5_WRAP_CPP(...)
> target_include_directories(...)
> target_compile_definitions(...)
>
> #Add Qt libraries to the linker
> target_link_libraries(${PROJECT_NAME}
> ${Qt5Widgets_LIBRARIES}
> ${Qt5OpenGL_LIBRARIES}
> ${Qt5Concurrent_LIBRARIES}
> )
>
> if( WIN32 )
> SET(QT_DLL
> Qt5Core
> Qt5Gui
> Qt5Widgets
> Qt5OpenGL
> Qt5Concurrent
> )
>
> foreach( _file ${QT_DLL} )
> list( APPEND DLL_LIBRARIES "${QT5_DIR}/bin/${_file}.dll" )
> list( APPEND DLL_LIBRARIES_D "${QT5_DIR}/bin/${_file}d.dll" )
> endforeach( _file ${QT_DLL} )
>
> #TODO: add the platform libraries.
>
> endif( WIN32 )
>
> # I add other DLLs of other project's library by appending to
> DLL_LIBRARIES and DLL_LIBRARIES_D
>
> #Handle DLLs under Windows.
> if(WIN32)
>
> set(DLL_LIBRARIES_PATH "")
> set(DLL_LIBRARIES_D_PATH "")
>
> #Process Release libraries.
> foreach( _file ${DLL_LIBRARIES} )
>
> # Convert path to CMake path to avoid escaping issues.
> file(TO_CMAKE_PATH ${_file} _file_cmake_path)
>
> #check file existance
> if(NOT EXISTS ${_file_cmake_path})
> message(FATAL_ERROR "Missing dll file: ${_file_cmake_path}")
> endif(NOT EXISTS ${_file_cmake_path})
>
> # Add the DLL to the installation process.
> install(FILES ${_file_cmake_path}
> DESTINATION ${G3_RELEASE_DIR}
> CONFIGURATIONS Release RelWithDebInfo MinSizeRel Release_CMT Release_Net)
>
> # Extract the folder path of the DLL. It allows to add the folder where the
> # DLLs are stored to the PATH environment of Visual Studio, in order to
> avoid
> # copying DLL after each builds.
> if(MSVC)
> get_filename_component(_dll_folder ${_file} DIRECTORY)
> list(APPEND DLL_LIBRARIES_PATH ${_dll_folder})
> endif(MSVC)
>
> endforeach( _file ${DLL_LIBRARIES} )
>
> #Process Debug libraries.
> foreach( _file ${DLL_LIBRARIES_D} )
>
> # Convert path to CMake path to avoid escaping issues.
> file(TO_CMAKE_PATH ${_file} _file_cmake_path)
>
> #check file existance
> if(NOT EXISTS ${_file_cmake_path})
> message(FATAL_ERROR "Missing dll file: ${_file_cmake_path}")
> endif(NOT EXISTS ${_file_cmake_path})
>
> # Add the DLL to the installation process.
> install(FILES ${_file_cmake_path}
> DESTINATION ${G3_DEBUG_DIR}
> CONFIGURATIONS Debug)
>
> # Extract the folder path of the DLL. It allows to add the folder where the
> # DLLs are stored to the PATH environment of Visual Studio, in order to
> avoid
> # copying DLL after each builds.
> if(MSVC)
> get_filename_component(_dll_folder ${_file} DIRECTORY)
> list(APPEND DLL_LIBRARIES_PATH_D ${_dll_folder})
> endif(MSVC)
>
> endforeach( _file ${DLL_LIBRARIES_D} )
>
> if(MSVC)
> #Remove duplicate entries
> list(REMOVE_DUPLICATES DLL_LIBRARIES_PATH)
> list(REMOVE_DUPLICATES DLL_LIBRARIES_PATH_D)
>
> #Set architecture
> if(ARCH_X64)
> set(DLL_LIBRARIES_ARCH "x64")
> else(ARCH_X64)
> set(DLL_LIBRARIES_ARCH "Win32")
> endif(ARCH_X64)
>
> # The output file goes in the build dir.
> # @ONLY means only variables of the form @VAR@ will be substituted.
> # This method need DLL_LIBRARIES_ARCH, DLL_LIBRARIES_PATH and
> DLL_LIBRARIES_PATH_D
> # variables to be set. DONT FORGET TO RESTART VISUAL STUDIO if DLL paths
> changes, as
> # vcxproj.user files are loaded only once at Visual Studio startup.
>
> configure_file(project.vcxproj.user.in
> ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.vcxproj.user @ONLY)
> endif(MSVC)
>
> endif(WIN32)
>
> -------------------
>
> NOTE:
>
> For those who are wondering the content of project.vcxproj.user.in:
>
> <?xml version="1.0" encoding="utf-8"?>
> <Project ToolsVersion="4.0"
> xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
> <PropertyGroup
> Condition="'$(Configuration)|$(Platform)'=='Release|@DLL_LIBRARIES_ARCH@'">
> <LocalDebuggerEnvironment>PATH=@DLL_LIBRARIES_PATH@;%PATH%</LocalDebuggerEnvironment>
>
> <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
> </PropertyGroup>
>
> <PropertyGroup
> Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|@DLL_LIBRARIES_ARCH@'">
>
> <LocalDebuggerEnvironment>PATH=@DLL_LIBRARIES_PATH@;%PATH%</LocalDebuggerEnvironment>
>
> <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
> </PropertyGroup>
>
> <PropertyGroup
> Condition="'$(Configuration)|$(Platform)'=='MinSizeRel|@DLL_LIBRARIES_ARCH@'">
>
> <LocalDebuggerEnvironment>PATH=@DLL_LIBRARIES_PATH@;%PATH%</LocalDebuggerEnvironment>
>
> <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
> </PropertyGroup>
>
> <PropertyGroup
> Condition="'$(Configuration)|$(Platform)'=='Debug|@DLL_LIBRARIES_ARCH@'">
> <LocalDebuggerEnvironment>PATH=@DLL_LIBRARIES_PATH_D@;%PATH%</LocalDebuggerEnvironment>
>
> <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
> </PropertyGroup>
>
>
> </Project>
>
>


More information about the CMake mailing list