While Vcpkg copy all dependency DLLs include one for Qt properly it's there is no option to do that for install. Such option would be very useful for most of projects using CMake that create their installers using CPack+NSIS.
Of course I can simply copy DLLs and Qt plugins from output directory to install directory, but this would be very ugly hack that will likely to break at any moment. Another alternative is using BundleUtilities, but it's not handle Qt plugins and winqtdeploy is pretty broken in Vcpkg.
You know what's funny, I made an issue exactly like this but then closed it.
When you CMake install something, it's ideally going to be in the users /usr/bin, /usr/lib, what have you.
Basically, your user should've already had all dependent DLLs/.sos installed so linking to them should be left as an exercise to the consumer, in my opinion.
@LeonineKing1199 I seen your issue and even thought to mention it, but after decide that don't make any sense. Basically what you talking about is deployment on Linux. It's my primary platform and on Linux you just prepare your binaries for package manager to come and pack them for you or install them appropriately. Also there are solutions for standalone apps such as snap or flatpack that do everything on their own and not require changes in CMakeLists.
Still when you need to deploy your application for Mac and Windows this is not the case. Mac expect all dependencies to be bundled inside DMG and Windows users expect installer and both can be created with CPack. This is why on these platforms install() used for completely different purposes: to prepare package structure that will be then packed into DMG or Windows installer (not yet checked UWP).
PS: Yeah to be fair it's not that different on Linux except for the dependencies management. CPack even have deb and rpm generators, but I never seen anyone actually using them and there no reason since each distribution have own way to delivery the software and packages not designed to be standalone anyway.
I'm awful with CMake, but here's a workaround I came up with. This my situation:
I just copied the add_executable macro from vcpkg.cmake. To get the environment right you can use a custom target. To run after installation you use install(CODE ...).
add_custom_target(InstallDLLs
COMMAND powershell -noprofile -executionpolicy Bypass -file ${_VCPKG_TOOLCHAIN_DIR}/msbuild/applocal.ps1
-targetBinary ${CMAKE_INSTALL_PREFIX}/yourapphere.exe
-installedDir "${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}$<$<CONFIG:Debug>:/debug>/bin"
-OutVariable out
)
set_target_properties(InstallDLLs PROPERTIES EXCLUDE_FROM_ALL TRUE)
# runs this target after installation
install(CODE "execute_process(COMMAND \"${CMAKE_COMMAND}\" --build . --target InstallDLLs --config RELEASE)")
Edit:
The previous way doesn't really work with cpack. Here's another hacky solution.
install(DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Release/
DESTINATION .
FILES_MATCHING PATTERN "*.dll")
Any updates on this? I'd really love to see this feature.
For Windows, my workaround is to have a template CopyDeps.bat which leverages the existing applocal.ps1 provided by vcpkg and looks like this:
@echo off
SET _TARGET_DIR=%1
SET _TARGET=@TARGET_OUTPUT_NAME@
SET _CONFIG=%2
if ("%_CONFIG%"=="Debug") (
SET _TARGET=@TARGET_OUTPUT_NAME@d
SET _VCPKG_ROOT=@VCPKG_INSTALL_ROOT@/debug/bin
) else (
SET _VCPKG_ROOT=@VCPKG_INSTALL_ROOT@/bin
)
echo [install]: Copying deps for (%_TARGET%, %_CONFIG%)
powershell -noprofile -executionpolicy Bypass -file "@VCPKG_APPLOCAL@" -targetBinary "%_TARGET_DIR%/%_CONFIG%/Bin/%_TARGET%.dll" -installedDir "%_VCPKG_ROOT%" -OutVariable out
Which is used in this macro to generate a specialized CopyDeps.bat for any given target:
macro(install_target_deps target)
# Get target output name
get_target_property(TARGET_OUTPUT_NAME ${target} OUTPUT_NAME)
if (TARGET_OUTPUT_NAME STREQUAL "TARGET_OUTPUT_NAME-NOTFOUND")
# It means no custom output name was specified, so just use target name
set(TARGET_OUTPUT_NAME "${target}")
endif()
configure_file(${CMAKE_CONFIGS_PATH}/CopyDeps.bat.in ${CMAKE_BINARY_DIR}/CopyDeps_${TARGET_OUTPUT_NAME}.bat @ONLY)
install(CODE "execute_process(COMMAND \${CMAKE_BINARY_DIR}/CopyDeps_${TARGET_OUTPUT_NAME}.bat \${CMAKE_INSTALL_PREFIX} \${CMAKE_INSTALL_CONFIG_NAME})")
endmacro()
And then invoke the macro after the target install() directive..
The only requirement then is that you detect and specify the:
applocal.ps1: VCPKG_APPLOCALVCPKG_INSTALL_ROOTHope this helps anyone having a similar problem.
I was really confused to see that this functionality is not included with vcpkg and I think this makes using vcpkg to create Windows installers more difficult than it should be. I would really appreciate if this functionality came with vcpkg.
Thanks for the inspiration on all of this. I think I have found a way to solve this by overloading the install command when VCPKG_APPINSTALL_DEPS is set to on (new feature flag)
Happy to hear your thoughts on this as I might have missed some edge cases.
Thanks! But I can't get it work.
It says:
CMake Error at C:/ProgramData/vcpkg/scripts/buildsystems/vcpkg.cmake:456 (_install):
Unknown CMake command "_install".
Call Stack (most recent call first):
CMakeLists.txt:181 (x_vcpkg_install_local_dependencies)
-- Configuring incomplete, errors occurred!
If I replace _install with install - it works.
It also not works with CPack. To make it work you should replace the following line in scripts\buildsystems\vcpkg.cmake:
-targetBinary \"${__VCPKG_APPINSTALL_DESTINATION}/$<TARGET_FILE_NAME:${TARGET}>\"
with this:
-targetBinary \"\${CMAKE_INSTALL_PREFIX}/${__VCPKG_APPINSTALL_DESTINATION}/$<TARGET_FILE_NAME:${TARGET}>\"
If I replace
_installwithinstall- it works.
Good catch. I've been testing it only with a custom install function as only then would this be transparent.
So I have this inside my own CMakeLists.txt - part of the original patch on splitting the aruguments for the install function.
function(install)
_install(${ARGV})
if(COMMAND x_vcpkg_install_local_dependencies)
if(${ARGV0} STREQUAL "TARGETS")
# Will contain the list of targets
set(PARSED_TARGETS "")
# Destination - [RUNTIME] DESTINATION argument overrides this
set(DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
# Parse arguments given to the install function to find targets and (runtime) destination
set(MODIFIER "") # Modifier for the command in the argument
set(LAST_COMMAND "") # Last command we found to process
foreach(ARG ${ARGN})
if(${ARG} MATCHES "ARCHIVE|LIBRARY|RUNTIME|OBJECTS|FRAMEWORK|BUNDLE|PRIVATE_HEADER|PUBLIC_HEADER|RESOURCE")
set(MODIFIER ${ARG})
continue()
endif()
if("${ARG}" MATCHES "TARGETS|DESTINATION|PERMISSIONS|CONFIGURATIONS|COMPONENT|NAMELINK_COMPONENT|OPTIONAL|EXCLUDE_FROM_ALL|NAMELINK_ONLY|NAMELINK_SKIP")
set(LAST_COMMAND ${ARG})
continue()
endif()
if("${LAST_COMMAND}" STREQUAL "TARGETS")
list(APPEND PARSED_TARGETS "${ARG}")
endif()
if("${LAST_COMMAND}" STREQUAL "DESTINATION" AND ("${MODIFIER}" STREQUAL "" OR "${MODIFIER}" STREQUAL "RUNTIME"))
set(DESTINATION "${CMAKE_INSTALL_PREFIX}/${ARG}")
endif()
endforeach()
x_vcpkg_install_local_dependencies(TARGETS ${PARSED_TARGETS} DESTINATION ${DESTINATION})
endif()
endif()
endfunction()
Unfortunately this part of the patch wasn't accepted by the VCPKG team.
It also not works with CPack. To make it work you should replace the following line in
scripts\buildsystems\vcpkg.cmake:-targetBinary \"${__VCPKG_APPINSTALL_DESTINATION}/$<TARGET_FILE_NAME:${TARGET}>\"with this:
-targetBinary \"\${CMAKE_INSTALL_PREFIX}/${__VCPKG_APPINSTALL_DESTINATION}/$<TARGET_FILE_NAME:${TARGET}>\"
Awesome. Glad you could test that as I have no experience with that part of cmake myself.
I'll make a new pull request with these fixes.
Unfortunately this part of the patch wasn't accepted by the VCPKG team.
Last time was the problem with CPack, but now it works correctly. Maybe they accept a new version with an option to override install command as you did?
I'll test it as soon as you open the PR.
After the new PR #14129 the custom install command should change to:
function(install)
_install(${ARGV})
if(COMMAND x_vcpkg_install_local_dependencies)
if(${ARGV0} STREQUAL "TARGETS")
# Will contain the list of targets
set(PARSED_TARGETS "")
# Destination - [RUNTIME] DESTINATION argument overrides this
set(DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
# Parse arguments given to the install function to find targets and (runtime) destination
set(MODIFIER "") # Modifier for the command in the argument
set(LAST_COMMAND "") # Last command we found to process
foreach(ARG ${ARGN})
if(${ARG} MATCHES "ARCHIVE|LIBRARY|RUNTIME|OBJECTS|FRAMEWORK|BUNDLE|PRIVATE_HEADER|PUBLIC_HEADER|RESOURCE")
set(MODIFIER ${ARG})
continue()
endif()
if("${ARG}" MATCHES "TARGETS|DESTINATION|PERMISSIONS|CONFIGURATIONS|COMPONENT|NAMELINK_COMPONENT|OPTIONAL|EXCLUDE_FROM_ALL|NAMELINK_ONLY|NAMELINK_SKIP")
set(LAST_COMMAND ${ARG})
continue()
endif()
if("${LAST_COMMAND}" STREQUAL "TARGETS")
list(APPEND PARSED_TARGETS "${ARG}")
endif()
if("${LAST_COMMAND}" STREQUAL "DESTINATION" AND ("${MODIFIER}" STREQUAL "" OR "${MODIFIER}" STREQUAL "RUNTIME"))
set(DESTINATION "${ARG}")
endif()
endforeach()
x_vcpkg_install_local_dependencies(TARGETS ${PARSED_TARGETS} DESTINATION ${DESTINATION})
endif()
endif()
endfunction()
Changing:
set(DESTINATION "${CMAKE_INSTALL_PREFIX}/${ARG}")
to:
set(DESTINATION "${ARG}")
Thanks! CPack just not works correctly if ${CMAKE_INSTALL_PREFIX} specified outside the x_vcpkg_install_local_dependencies.
But now it works :)
I also noticed that Qt translations (.qm files) are not copied. But I think that this is a vcpkg issue.
@sandercox, could you open the PR with install function override? I would be happy to test it.
Here you go. Initially I was a bit biased on your request as I was also providing some extra changes on my install command that do not make sense to be part of vcpkg, but I found a way to only optionally perform this override, meaning I can still copy this code as a base for my private projects and add whatever I need.
So now you can use it with:
set(VCPKG_APPLOCAL_DEPS_INSTALL ON)
set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_SOURCE_DIR}/../vcpkg/scripts/buildsystems/vcpkg.cmake CACHE STRING "Vcpkg toolchain file")
Enabling the option before including the toolchain, like you can also do for overlay ports and triplets.
Quite happy with this result - hoping this will also be accepted by the maintainers.
Most helpful comment
Any updates on this? I'd really love to see this feature.