How can I package a Qt program built with CMake on Windows? - windows

I'm writing a cross-platform program which I'd like to package on Windows. I got it to run by putting three DLLs (Qt5Core, Qt5Widgets, Qt5Gui) in the build directory, then made a tarball and gave it to someone else with a Windows box, but it would not run on his Windows box. I searched for a way to get whatever else it needed and found Nathan Osman's post about windeployqt, so I copied windeployqt.cmake to the cmake/Modules directory, but couldn't figure out how to use it. Is there a way to use CPack to make a package (even if it's just a tarball) that has everything needed to run the program on a Windows box that doesn't have the compiler or Qt installed?
Here's the CMakeLists.txt file with some (hopefully irrelevant) stuff removed:
project(perfecttin)
cmake_minimum_required(VERSION 3.8.0)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_CXX_STANDARD 17) # appeared in CMake 3.8
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS ON)
set(SHARE_DIR ${CMAKE_INSTALL_PREFIX}/share/perfecttin)
add_definitions(-DBOOST_ALL_NO_LIB)
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
set(Boost_USE_STATIC_LIBS ON)
else ()
set(Boost_USE_STATIC_LIBS OFF)
endif ()
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
find_package(LibPLYXX)
find_package(Qt5 COMPONENTS Core Widgets Gui LinguistTools REQUIRED)
find_package(Boost COMPONENTS program_options)
find_package(Threads)
qt5_add_resources(lib_resources perfecttin.qrc)
qt5_add_translation(qm_files perfecttin_en.ts perfecttin_es.ts)
# To update translations, run "lupdate *.cpp -ts *.ts" in the source directory.
add_executable(perfecttin-gui adjelev.cpp angle.cpp binio.cpp
<snip lots of source files>
unitbutton.cpp ${lib_resources} ${qm_files})
target_link_libraries(perfecttin-gui ${CMAKE_THREAD_LIBS_INIT} Qt5::Widgets Qt5::Core)
target_link_libraries(fuzzptin ${CMAKE_THREAD_LIBS_INIT})
target_compile_definitions(fuzzptin PUBLIC _USE_MATH_DEFINES)
target_compile_definitions(perfecttin-gui PUBLIC _USE_MATH_DEFINES)
set_target_properties(perfecttin-gui PROPERTIES WIN32_EXECUTABLE TRUE)
install(TARGETS perfecttin-gui DESTINATION bin)
install(FILES ${qm_files} DESTINATION share/perfecttin)
include_directories(${PROJECT_BINARY_DIR})
configure_file (config.h.in config.h)
set(CPACK_PACKAGE_VERSION_MAJOR ${PERFECTTIN_MAJOR_VERSION})
set(CPACK_PACKAGE_VERSION_MINOR ${PERFECTTIN_MINOR_VERSION})
set(CPACK_PACKAGE_VERSION_PATCH ${PERFECTTIN_PATCH_VERSION})
set(CPACK_RESOURCE_FILE_LICENSE ${CMAKE_SOURCE_DIR}/COPYING)
set(CPACK_SOURCE_IGNORE_FILES /\\\\.git;.*~)
include(CPack)
include(CTest)

If you already have in your ${CMAKE_SOURCE_DIR}/cmake/Modules/ directory the file Windeployqt.cmake, you may use it like this:
install(TARGETS perfecttin-gui
DESTINATION "${INSTALL_BIN_PATH}"
)
if(WIN32)
include(Windeployqt)
windeployqt(nitroshare-cli ${INSTALL_BIN_PATH})
endif()
Here is the full example.

I did borrow a lot from this answer, however it was missing the necessary environmental setup which leads to Warning: Cannot find GCC installation directory. g++.exe must be in the path..
Qt has the following documentation on using windeployqt.exe
The tool can be found in QTDIR/bin/windeployqt. It needs to be run within the build environment in order to function correctly.
When using Qt Installer, the script QTDIR/bin/qtenv2.bat should be used to set it up.
Basically you need to run windeployqt.exe as a custom command within the correct environment.
# Retrieve the absolute path to qmake and then use that path to find
# the windeployqt executable
get_target_property(QMAKE_EXE Qt6::qmake IMPORTED_LOCATION)
get_filename_component(QT_BIN_DIR"${QMAKE_EXE }" DIRECTORY)
find_program(WINDEPLOYQT_ENV_SETUP qtenv2.bat HINTS "${QT_BIN_DIR}")
find_program(WINDEPLOYQT_EXECUTABLE windeployqt HINTS "${QT_BIN_DIR}")
# Run windeployqt immediately after build
add_custom_command(TARGET perfecttin-gui
POST_BUILD
COMMAND "${WINDEPLOYQT_ENV_SETUP}" && "${WINDEPLOYQT_EXECUTABLE}" \"$<TARGET_FILE:perfectin-gui>\"
)

"Since version 6.3, Qt offers a way to define additional deployment steps that are executed on installation."
- https://www.qt.io/blog/cmake-deployment-api
Among other things there is now a *deploy utility script generator already included.
install(TARGETS perfecttin-gui DESTINATION bin)
qt_generate_deploy_app_script(
TARGET perfecttin-gui
FILENAME_VARIABLE deploy_script
NO_UNSUPPORTED_PLATFORM_ERROR
)
install(SCRIPT ${deploy_script})
Documentation is at: generate-deploy-app-script
According to the docs so far this doesn't work on Linux
For some more options (and apparently Linux support?) one could use the function which the above convenience version actually invokes: qt-deploy-runtime-dependencies
For example qt_generate_deploy_app_script() puts the Qt plugins outside of the destination 'bin' directory, each in their own folder, which I don't like (I want them all under 'bin'). So I had to use the full version (this is all basically from the Qt docs linked above):
set(MY_APP perfecttin-gui)
if(APPLE)
set(executable_path "$<TARGET_FILE_NAME:${MY_APP}>.app")
else()
set(executable_path "\${QT_DEPLOY_BIN_DIR}/$<TARGET_FILE_NAME:${MY_APP}>")
endif()
set(deploy_script "${CMAKE_CURRENT_BINARY_DIR}/deploy_${MY_APP}.cmake")
file(GENERATE OUTPUT ${deploy_script} CONTENT "
include(\"${QT_DEPLOY_SUPPORT}\")
qt_deploy_runtime_dependencies(
EXECUTABLE \"${executable_path}\"
PLUGINS_DIR bin
)"
)
install(TARGETS ${MY_APP} DESTINATION bin)
install(SCRIPT ${deploy_script})
See https://www.qt.io/blog/cmake-deployment-api for more details about deployment in general
ADDED:
Found the source for qt_deploy_runtime_dependencies().
Looks like the next version adds a NO_TRANSLATIONS option, but it's not in my 6.4.1.
But I did find a hack to pass additional options to the deploy tool...
If you search that code for __qt_deploy_tool_extra_options you'll see the warning about how it may go away. But as a workaround...
My file(GENERATE OUTPUT) now looks like this (likely needs adjusting per platform):
file(GENERATE OUTPUT ${deploy_script} CONTENT "
include(\"${QT_DEPLOY_SUPPORT}\")
set(__qt_deploy_tool_extra_options --no-compiler-runtime --no-translations)
qt_deploy_runtime_dependencies(
EXECUTABLE \"${executable_path}\"
PLUGINS_DIR bin
# NO_TRANSLATIONS
)
")
Also the qt_generate_deploy_app_script() source if anyone is interested.

Related

Compile OCCT7.5 with rapidjson using FreeCad Libpack?

I'm looking to compile OCCT 7.5 in Windows 10 (x64 via VS2019) for use with FreeCAD, to enable exporting glTF files, which requires RapidJSON support (in OCCT). I've checked out OCCT 7.5.3 and RapidJSON 1.1.0 from their git repos, then grabbed the FreeCAD libpack 12.5.2 (for OCCT 7.5). I started from FreeCAD's build docs, then attempted to follow OCCT's build docs.
When configuring the OCCT project in CMake-GUI, I've been able to find what I think are correct values for some variables (e.g. those regarding FREETYPE) within the FreeCAD libpack, as well as RapidJSON, but still get some errors in the config, seemingly no matter what values I try:
Could not find headers of used third-party products:
3RDPARTY_TCL_INCLUDE_DIR 3RDPARTY_TK_INCLUDE_DIR
...
Could not find DLLs of used third-party products: 3RDPARTY_TCL_DLL_DIR
3RDPARTY_TK_DLL_DIR
I've tried using *.lib, *.h and *.dll files found within the FreeCAD Libpack (and their corresponding directories) for *_LIBRARY/INCLUDE/DLL variables, but nothing is found. I see
Info: TCL is used by OCCT
Could NOT find Tclsh (missing: TCL_TCLSH)
even though tclsh86t.exe exists in the libpack/bin directory.
What should the 3RDPARTY_TCL_* & 3RDPARTY_TK_* CMake variables be set to, to use the FreeCAD libpack?
The problem was my lack of familiarity with CMake and Cmake-gui: the gui opened a dialog for a FILEPATH when specifying a PATH variable. I naively thought, "I don't know CMake, so I'll trust the gui". Totally wrong.
I manually edited the 3RDPARTY_* variable entries to point to the correct directories (or libs, when needed) in the FreeCAD libpack, using the variable name and description/hint for each to determine what the variable's value should be. Below are my entries, for reference:
I did need to check "Advanced", to edit the FREETYPE_LIBRARY_DEBUG & FREETYPE_LIBRARY_RELEASE variables (CMake set them to separate libs found in a jdk directory, presumably because it was added to the system path at some point).

How to get SCons StaticLibrary store the library file in another folder

I'm using scons for a build project. I want to make a static library from object files that reside a different directory and surprisingly I'm not able to do that. Here is a snippet of the code I'm using:
OBJECT_FILES = env.Object('main.o', 'main.cpp')
env.StaticLibrary("../mylib", OBJECT_FILES)
StaticLibrary doesn't work if I put any directory above this directory even if I use absolute path. For the current directory or any other directory under the current directory, this works with no issues.
Here's how I'd do that..
env.StaticLibrary("../${LIBPREFIX}mylib${LIBSUFFIX}", 'main.cpp')
No need to explicitly request env.Object() SCons will figure out the correct thing to do here. Unless for some reason you want to later do something with that list of Object()'s.
When you specify
env.StaticLibrary("mylib", ['main.cpp'])
SCons will automatically prepend $LIBPREFIX and append $LIBSUFFIX to mylib and on a POSIX system you'd end up with libmylib.a on Windows you'd end up with mylib.lib
Please consider joining the SCons discord server for more "live" help and discussions.

How to correctly link the path of external library in CMake? [duplicate]

How to get CMake to link an executable to an external shared library that is not build within the same CMake project?
Just doing target_link_libraries(GLBall ${CMAKE_BINARY_DIR}/res/mylib.so) gives the error
make[2]: *** No rule to make target `res/mylib.so', needed by `GLBall'. Stop.
make[1]: *** [CMakeFiles/GLBall.dir/all] Error 2
make: *** [all] Error 2
(GLBall is the executable)
after I copied the library into the binary dir bin/res.
I tried using find_library(RESULT mylib.so PATHS ${CMAKE_BINARY_DIR}/res)
Which fails with RESULT-NOTFOUND.
arrowdodger's answer is correct and preferred on many occasions. I would simply like to add an alternative to his answer:
You could add an "imported" library target, instead of a link-directory. Something like:
# Your-external "mylib", add GLOBAL if the imported library is located in directories above the current.
add_library( mylib SHARED IMPORTED )
# You can define two import-locations: one for debug and one for release.
set_target_properties( mylib PROPERTIES IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/res/mylib.so )
And then link as if this library was built by your project:
TARGET_LINK_LIBRARIES(GLBall mylib)
Such an approach would give you a little more flexibility: Take a look at the add_library( IMPORTED) command and the many target-properties related to imported libraries.
I do not know if this will solve your problem with "updated versions of libs".
Set libraries search path first:
link_directories(${CMAKE_BINARY_DIR}/res)
And then just do
target_link_libraries(GLBall mylib)
I assume you want to link to a library called foo, its filename is usually something link foo.dll or libfoo.so.
1. Find the library
You have to find the library. This is a good idea, even if you know the path to your library. CMake will error out if the library vanished or got a new name. This helps to spot error early and to make it clear to the user (may yourself) what causes a problem.
To find a library foo and store the path in FOO_LIB use
find_library(FOO_LIB foo)
CMake will figure out itself how the actual file name is. It checks the usual places like /usr/lib, /usr/lib64 and the paths in PATH.
You already know the location of your library. Add it to the CMAKE_PREFIX_PATH when you call CMake, then CMake will look for your library in the passed paths, too.
Sometimes you need to add hints or path suffixes, see the documentation for details:
https://cmake.org/cmake/help/latest/command/find_library.html
2. Link the library
From 1. you have the full library name in FOO_LIB. You use this to link the library to your target GLBall as in
target_link_libraries(GLBall PRIVATE "${FOO_LIB}")
You should add PRIVATE, PUBLIC, or INTERFACE after the target, cf. the documentation:
https://cmake.org/cmake/help/latest/command/target_link_libraries.html
If you don't add one of these visibility specifiers, it will either behave like PRIVATE or PUBLIC, depending on the CMake version and the policies set.
3. Add includes (This step might be not mandatory.)
If you also want to include header files, use find_path similar to find_library and search for a header file. Then add the include directory with target_include_directories similar to target_link_libraries.
Documentation:
https://cmake.org/cmake/help/latest/command/find_path.html
and
https://cmake.org/cmake/help/latest/command/target_include_directories.html
If available for the external software, you can replace find_library and find_path by find_package.
Let's say you have an executable like:
add_executable(GLBall GLBall.cpp)
If the external library has headers, give the path to its include folder:
target_include_directories(GLBall PUBLIC "/path/to/include")
Add the library directory path:
target_link_directories(GLBall PUBLIC "/path/to/lib/directory")
Finally, link the library name
target_link_libraries(GLBall mylib)
Note that the prefix and extension of the library file are removed:
libmylib.a ➜ mylib
mylib.so ➜ mylib
One more alternative, in the case you are working with the Appstore, need "Entitlements" and as such need to link with an Apple-Framework.
For Entitlements to work (e.g. GameCenter) you need to have a "Link Binary with Libraries"-buildstep and then link with "GameKit.framework". CMake "injects" the libraries on a "low level" into the commandline, hence Xcode doesn't really know about it, and as such you will not get GameKit enabled in the Capabilities screen.
One way to use CMake and have a "Link with Binaries"-buildstep is to generate the xcodeproj with CMake, and then use 'sed' to 'search & replace' and add the GameKit in the way XCode likes it...
The script looks like this (for Xcode 6.3.1).
s#\/\* Begin PBXBuildFile section \*\/#\/\* Begin PBXBuildFile section \*\/\
26B12AA11C10544700A9A2BA \/\* GameKit.framework in Frameworks \*\/ = {isa = PBXBuildFile; fileRef = 26B12AA01C10544700A9A2BA \/\* GameKit.framework xxx\*\/; };#g
s#\/\* Begin PBXFileReference section \*\/#\/\* Begin PBXFileReference section \*\/\
26B12AA01C10544700A9A2BA \/\* GameKit.framework xxx\*\/ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameKit.framework; path = System\/Library\/Frameworks\/GameKit.framework; sourceTree = SDKROOT; };#g
s#\/\* End PBXFileReference section \*\/#\/\* End PBXFileReference section \*\/\
\
\/\* Begin PBXFrameworksBuildPhase section \*\/\
26B12A9F1C10543B00A9A2BA \/\* Frameworks \*\/ = {\
isa = PBXFrameworksBuildPhase;\
buildActionMask = 2147483647;\
files = (\
26B12AA11C10544700A9A2BA \/\* GameKit.framework in Frameworks xxx\*\/,\
);\
runOnlyForDeploymentPostprocessing = 0;\
};\
\/\* End PBXFrameworksBuildPhase section \*\/\
#g
s#\/\* CMake PostBuild Rules \*\/,#\/\* CMake PostBuild Rules \*\/,\
26B12A9F1C10543B00A9A2BA \/\* Frameworks xxx\*\/,#g
s#\/\* Products \*\/,#\/\* Products \*\/,\
26B12AA01C10544700A9A2BA \/\* GameKit.framework xxx\*\/,#g
save this to "gamecenter.sed" and then "apply" it like this ( it changes your xcodeproj! )
sed -i.pbxprojbak -f gamecenter.sed myproject.xcodeproj/project.pbxproj
You might have to change the script-commands to fit your need.
Warning: it's likely to break with different Xcode-version as the project-format could change, the (hardcoded) unique number might not really by unique - and generally the solutions by other people are better - so unless you need to Support the Appstore + Entitlements (and automated builds), don't do this.
This is a CMake bug, see http://cmake.org/Bug/view.php?id=14185 and http://gitlab.kitware.com/cmake/cmake/issues/14185
It has been a long time since the question was posted but I am leaving this one just for reference.
I have a blog post describing step-by-step almost what you (or anyone else) were trying to do.
Please check here: https://michae9.wordpress.com/2022/09/01/shared-lib-to-be-used-by-client-programs-with-cmake/

How do I generate loadable modules during Linux kernel build?

I seem to successfully build a kernel image, but I can not generate all the modules I expect. I expect more modules since I see them enabled in the gconfig window. Here is a copy of my make session. Seems like make goes into the devices directories. I can not figure out why it is not create the .ko files. I expect to see .ko files. I have checked the Makefile in /drivers directory, and I can see that it is configured with a number of lines like
obj-$(CONFIG_PCI) += pci/
Which directs make to build the pci module for instance. I think this implies that I should see a number .ko files. But I do not. I have seen just one .ko file for scsi module. I like to be able to build all of modules selected.
I also verified that a number of mudules are enabled when I issued:
make VARIANT_DEFCONFIG=msm8974_sec_hlte_spr_defconfig msm8974_sec_defconfig SELINUX_DEFCONFIG=selinux_defconfig gconfig
But as I said, I do not see any of them. What am I missing please?
#Subin - Thanks. I just tried make modules_install. I have to mention that I am cross compiling this for an arm target. I believe modules_install is for the purpose of installing the driver for the machine you are on? I got a message about needing to be in root, and I did not proceed. I have been wondering when I need to run it. What does it do exactly please?
Re: the make modules; I have run it before. I'll run it again and post the result. Since I got one .ko file I figured the issue is something different between that one module, and every other one enabled in my config. Here is what I got when I ran make modules:
sansari#ubuntu:~/WORKING_DIRECTORY$ make modules
CHK include/linux/version.h
CHK include/generated/utsrelease.h
make[1]: `include/generated/mach-types.h' is up to date.
CALL scripts/checksyscalls.sh
Building modules, stage 2.
MODPOST 1 modules
Re: your comment on the location of .ko files, I am doing a find to see if perhaps I am not looking at the right place, it only finds the one which was built. Not the other ones. Here is the output:
sansari#ubuntu:~/WORKING_DIRECTORY$ find . -type f -name "*.ko"
./drivers/scsi/scsi_wait_scan.ko
sansari#ubuntu:~/WORKING_DIRECTORY$
Should I perhaps run make v=1, in verbose mode that is? Would that provide more information on why the other modules are not built?
#Gil Hamilton - Thanks. You are right. Here is an excerpt of the .config file:
#
# SCSI support type (disk, tape, CD-ROM)
#
CONFIG_BLK_DEV_SD=y
# CONFIG_CHR_DEV_ST is not set
# CONFIG_CHR_DEV_OSST is not set
# CONFIG_BLK_DEV_SR is not set
CONFIG_CHR_DEV_SG=y
CONFIG_CHR_DEV_SCH=y
CONFIG_SCSI_MULTI_LUN=y
CONFIG_SCSI_CONSTANTS=y
CONFIG_SCSI_LOGGING=y
CONFIG_SCSI_SCAN_ASYNC=y
CONFIG_SCSI_WAIT_SCAN=m
This entry is the only one set to 'm'.
Most device driver modules in the linux kernel build system use a tristate (3-valued) configuration setting. The options are
'n' (don't build at all),
'y' (build and link statically into the main kernel object), and
'm' (build as module for dynamic loading).
The values are determined by the content of .config. The values in .config are usually generated from an existing config file (look in arch/<ARCH>/configs for your <ARCH>). Also check the output of 'make help' for interesting configuration targets.
If you're not seeing the .ko files being created, that indicates the corresponding configuration variable is either set to 'y' or 'n'.

Set environment variable GCOV_PREFIX (with arm-linux-gnueabi-gcc)

All
We are trying to use arm-linux-gnueabi-gcc 4.2.1.
We compile application with flags "-fprofile-arcs -ftest-coverage".
Then run on the board.
For the source file at location, /home/user/TestApp/HelloApp/, .gcda
files are generated correctly in the folder
/home/user/TestApp/HelloApp/ on the target.
Setting environment flags GCOV_PREFIX and GCOV_PREFIX_STRIP do not
seem any effect in relocating the .gcda files.
For example, on setting GCOV_PREFIX=/temp/app/ and
GCOV_PREFIX_STRIP=4, there is no change at all.
How to fix this issue ?
I found the solution: You need to set the environment flags GCOV_PREFIX and GCOV_PREFIX_STRIP on board instead of my compile pc.

Resources