Reputation: 11
How Can I link to external library that I have packed with my project?
Here is how my project is set up:
I have a folder called App and that is where my main.cpp sits.
C:\Raph\src\App
main.cpp
I also have a folder called "ExternalLibrary" and that is where I have bundled Qt Library that I need to use in my project:
C:\Raph\src\ExternalLibrary\Platform\Windows\Qt5.6\VS2013_64bit
It contains 3 folders:
**bin**
moc.exe
rcc.exe
uic.exe
......
bunch of Qt dll files
**include**
bunch of Qt header files
**lib**
bunch of Qt lib files
I need to setup Cmake to do three things:
Link to Qt library that I have packed in "ExternalLibrary" dynamically.
Automatically performs moc.exe, uic.exe, rcc.exe every time I add a Qt class or a resource in the project. I also dont need any of the generated moc_myClassname.cpp to show up in the project.
I want All the Qt dlls to end up in my bin folder where the executable sits. e.g:
C:\Raph\build\Raph\Windows\x64\Debug\bin
Raph.exe + all necessary Qt Dlls
Upvotes: 1
Views: 978
Reputation: 4092
(1) You can find the Qt Library the following way:
find_package(Qt5Core [...] 5.6 REQUIRED)
add_executable(someExe file1.cpp)
target_link_libraries(someExe Qt5::Core)
Note that you should keep the original directory stucture of Qt, also containing the Qt cmake scripts, e.g. lib/cmake/Qt5/Qt5Config.cmake
If you want a specific directory to be included when searching for Qt, you can do (before you search for Qt):
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} "./your/path")
This can for example be the directory you supply.
(2) You can use cmake's automoc feature. Just add
set(CMAKE_AUTOMOC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
at the beginning of your cmake script. CMake also supports AUTOUIC
and AUTORCC
. I haven't tried them yet, but the probably work similar.
(3) You can for example add a custom target to your project, that copies the dlls. When you build that target, all the dlls will be copied. The paths of the dlls you can get from the targets that the Qt find script defines (like Qt5::Core
).
get_target_property(LOC_R Qt5::Core LOCATION_RELEASE)
get_target_property(LOC_D Qt5::Core LOCATION_DEBUG)
However, you should also scan these target for dependencies (other dlls they depend on). You could write a macro that scans a whole list of targets for the correspondig dlls and adds them to a list, let's call these RELEASE_DLLS
and DEBUG_DLLS
:
macro(copydlls RELEASE_DLLS DEBUG_DLLS MODULELIST)
foreach(ELEMENT ${${MODULELIST}})
get_target_property(LOC_R ${ELEMENT} LOCATION_RELEASE)
get_target_property(LOC_D ${ELEMENT} LOCATION_DEBUG)
list(APPEND ${RELEASE_DLLS} ${LOC_R})
list(APPEND ${DEBUG_DLLS} ${LOC_D})
get_target_property(DEPENDENCIES_RELEASE ${ELEMENT} IMPORTED_LINK_DEPENDENT_LIBRARIES_RELEASE)
foreach(DEPENDENCY ${DEPENDENCIES_RELEASE})
if(TARGET ${DEPENDENCY})
get_target_property(LOC_R ${DEPENDENCY} LOCATION_RELEASE)
if(${LOC_R} MATCHES ".dll$")
list(APPEND ${RELEASE_DLLS} ${LOC_R})
endif()
endif()
endforeach()
get_target_property(DEPENDENCIES_DEBUG ${ELEMENT} IMPORTED_LINK_DEPENDENT_LIBRARIES_DEBUG)
foreach(DEPENDENCY ${DEPENDENCIES_DEBUG})
if(TARGET ${DEPENDENCY})
get_target_property(LOC_D ${DEPENDENCY} LOCATION_DEBUG)
if(${LOC_D} MATCHES ".dll$")
list(APPEND ${DEBUG_DLLS} ${LOC_D})
endif()
endif()
endforeach()
get_target_property(DEPENDENCIES_RELEASE ${ELEMENT} IMPORTED_LINK_INTERFACE_LIBRARIES_RELEASE)
foreach(DEPENDENCY ${DEPENDENCIES_RELEASE})
if(TARGET ${DEPENDENCY})
get_target_property(LOC_R ${DEPENDENCY} LOCATION_RELEASE)
if(${LOC_R} MATCHES ".dll$")
list(APPEND ${RELEASE_DLLS} ${LOC_R})
endif()
endif()
endforeach()
get_target_property(DEPENDENCIES_DEBUG ${ELEMENT} IMPORTED_LINK_INTERFACE_LIBRARIES_DEBUG)
foreach(DEPENDENCY ${DEPENDENCIES_DEBUG})
if(TARGET ${DEPENDENCY})
get_target_property(LOC_D ${DEPENDENCY} LOCATION_DEBUG)
if(${LOC_D} MATCHES ".dll$")
list(APPEND ${DEBUG_DLLS} ${LOC_D})
endif()
endif()
endforeach()
endforeach()
endmacro()
Then you could get all the Qt dlls in a list, by calling this macro with:
IF(MSVC)
set(QT_MODULES "Qt5::Core" "Qt5::Gui" "Qt5::Widgets")
set(RELEASE_DLLS)
set(DEBUG_DLLS)
copydlls(RELEASE_DLLS DEBUG_DLLS QT_MODULES)
ENDIF(MSVC)
When you've retrieved that information, you can create your custom target the following way. Let's assume you have all your dll paths in the lists RELEASE_DLLS
and DEBUG_DLLS
and your executable names as a list in TARGETS
. Then you could do something like this:
if(MSVC)
set(COPY_COMMAND_RELEASE "-E copy_if_different ")
set(COPY_COMMAND_DEBUG "-E copy_if_different ")
list(REMOVE_DUPLICATES RELEASE_DLLS)
list(REMOVE_DUPLICATES DEBUG_DLLS)
foreach(DLL ${RELEASE_DLLS})
string(CONCAT COPY_COMMAND_RELEASE "${COPY_COMMAND_RELEASE} \"${DLL}\" ")
endforeach()
foreach(DLL ${DEBUG_DLLS})
string(CONCAT COPY_COMMAND_DEBUG "${COPY_COMMAND_DEBUG} \"${DLL}\" ")
endforeach()
string(CONCAT COPY_COMMAND_RELEASE ${COPY_COMMAND_RELEASE} "\"${CMAKE_CURRENT_BINARY_DIR}/Release\" ")
string(CONCAT COPY_COMMAND_DEBUG ${COPY_COMMAND_DEBUG} "\"${CMAKE_CURRENT_BINARY_DIR}/Debug\" ")
add_custom_target(COPY_DLLS)
foreach(EXECUTABLE ${TARGETS})
add_custom_command(TARGET COPY_DLLS PRE_BUILD COMMAND ${CMAKE_COMMAND} ${COPY_COMMAND_RELEASE} COMMENT "Copying dlls to executable directory...")
add_custom_command(TARGET COPY_DLLS PRE_BUILD COMMAND ${CMAKE_COMMAND} ${COPY_COMMAND_DEBUG} COMMENT "Copying dlls to executable directory...")
endforeach()
endif()
If you want this target to be executed every time one of your other targets is built, you can do:
foreach(EXECUTABLE ${TARGETS})
add_dependencies(${EXECUTABLE} COPY_DLLS)
endforeach()
Don't foget that you must also copy the platforms
directory from plugins
to your executable folder, for example by adding it to the copy dll target:
add_custom_command(TARGET COPY_DLLS PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory ${PATH_QT_ROOT}/plugins/platforms ${CMAKE_CURRENT_BINARY_DIR}/Debug/platforms
COMMAND ${CMAKE_COMMAND} -E copy_directory ${PATH_QT_ROOT}/plugins/platforms ${CMAKE_CURRENT_BINARY_DIR}/Release/platforms
COMMENT "Copying Qt platforms to executable directory...")
Upvotes: 1