How to idiomatically package dependencies for a QT application using CPack? - linux

I have a simple QT project. I'm developing on Linux. But it's meant to be deployed to Linux, Mac and Windows ultimately.
I'm attempting to package it for distribution. I'm running into problems locating the dependencies and packaging them up and doing this in an idiomatic way (IOW: No hardcoded paths to DLLs or including the DLLs in my source repo)
For the Windows port, I'm using MinGW and compiling like this:
mingw64-cmake -G "Unix Makefiles" .. -DCMAKE_INSTALL_PREFIX=../install -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=/usr/share/mingw/toolchain-mingw64.cmake
make && ctest && make install && cpack -G "TGZ" && cpack -G "NSIS64"
I've set it to product a tar.gz file and an NSIS installer. There's no particular reason for NSIS and not Wix at the moment. This is just to figure things out.
It compiles a Windows executable, but it does not include the DLLs necessary to run the program. It's these:
Libgcc_s_seh-1.dll
Qt5Core.dll
Qt5Gui.dll
A quick find on my computer shows me that those DLLs are present here:
/usr/x86_64-w64-mingw32/sys-root/mingw/bin/Qt5Widgets.dll
/usr/x86_64-w64-mingw32/sys-root/mingw/bin/libgcc_s_seh-1.dll
...
Is there a way to automatically get CPack to dig up the DLLs and include them in the installer?
Here is my CMakeLists.txt file:
cmake_minimum_required(VERSION 2.8.11)
project(myapp)
enable_testing()
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
find_package(Qt5Core REQUIRED)
find_package(Qt5Gui REQUIRED)
find_package(Qt5Widgets REQUIRED)
add_executable(myapp WIN32 main.cpp mainwindow.cpp resources.qrc)
target_link_libraries(myapp Qt5::Widgets)
target_link_libraries(myapp Qt5::Core)
target_link_libraries(myapp Qt5::Gui)
INSTALL(TARGETS myapp
BUNDLE DESTINATION .
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
INSTALL(FILES ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS} DESTINATION bin COMPONENT Libraries)
IF(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS)
INSTALL(PROGRAMS ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS} DESTINATION bin COMPONENT System)
ENDIF(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS)
INCLUDE(CPack)
I've looked around for some help on this. The best thing I came across was
this link
But it's not looking so idiomatic. If we look closer at the CMakeLists.txt file, it's got machine-specific hard coded paths that are certain to change in the future:
IF( WIN32 AND ${ARCH_32BIT})
SET(QT_INSTALLED_PATH "C:/QtMSVCX86/Qt5.5.0/5.5/msvc2013" )
ELSEIF(WIN32 AND ${ARCH_64BIT})
SET(QT_INSTALLED_PATH "C:/QtMSVCX64/Qt5.5.0/5.5/msvc2013_64" )
ELSEIF(UNIX AND NOT MINGW AND ${ARCH_32BIT})
SET(QT_INSTALLED_PATH "/opt/Qt5.5.0/5.5/gcc/" )
ELSEIF(UNIX AND NOT MINGW AND ${ARCH_64BIT})
SET(QT_INSTALLED_PATH "/opt/Qt5.5.0/5.5/gcc_64/" )
ENDIF()
SET(CMAKE_AUTOMOC ON)
SET(CMAKE_AUTOUIC ON)
SET(CMAKE_AUTORCC ON)
FIND_PACKAGE(Qt5Widgets PATHS ${QT_INSTALLED_PATH} NO_DEFAULT_PATH)
FIND_PACKAGE(Qt5Qml PATHS ${QT_INSTALLED_PATH} NO_DEFAULT_PATH)
FIND_PACKAGE(Qt5Quick PATHS ${QT_INSTALLED_PATH} NO_DEFAULT_PATH)

Have a look at CMake's Bundle Utilities It contains a FIXUP_BUNDLE macro which collects all the necessary dependencies of an executable, including Qt. It works basically the same way on Windows, Linux and Mac. You could start by adding FIXUP_BUNDLE(myapp) to your CMake file. the actual dependency resolving and copying happens during the CPack run. Depending on project size and complexity, some tweaks are necessary, but in general I have seen it used successfully in larger cross-platform Qt projects with a CMake based build system

Related

CMake - Compile in Linux, Execute in Windows

I have a large codebase with Linux dependencies, and I would like to use CMake to compile my code into an executable that can be run on Windows, i.e. I want CMake to produce an ".exe" file or something of that nature.
I have tried using the solution provided on the CMake website: https://cmake.org/cmake/help/v3.4/manual/cmake-toolchains.7.html#cross-compiling
however it has not worked...
Here is my CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(myProject VERSION 1.0 LANGUAGES C CXX)
set(CMAKE_CROSSCOMPILING true)
set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_SYSTEM_VERSION 10.0)
set(CMAKE_SYSTEM_PROCESSOR arm)
find_package(... *all my required packages* REQUIRED)
include(... *required include files*)
add_executable(${PROJECT_NAME} ...)
target_link_libraries(${PROJECT_NAME} ...)
It compiles and will execute on Linux, however I want it to produce a Windows compatible executable.
You need a mingw-w64 toolchain in Linux to do this, for example on Arch Linux you can get all the necessary mingw-w64-... packages through AUR, including mingw-w64-cmake. These packets should get you going:
mingw-w64-binutils-symlinks
mingw-w64-gcc
mingw-w64-cmake
Install others to fulfill any dependencies of your software.
Then you can just run mingw-w64-cmake instead of cmake using your regular CMakeLists.txt. E.g.:
mkdir build-mingw; cd build-mingw
x86_64-w64-mingw32-cmake ../
make
However typically it is a good idea to use a static build so your executable will work standalone. Here is how I do it:
# STATIC stuff (Windows)
if (${CMAKE_SYSTEM_NAME} MATCHES "Windows")
set(BUILD_FOR_WIN TRUE)
endif()
option(STATIC_BUILD "Build a static binary." ${BUILD_FOR_WIN})
if (STATIC_BUILD)
set(CMAKE_EXE_LINKER_FLAGS "-static")
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a" CONFIG)
set(BUILD_SHARED_LIBS OFF)
endif()
Which creates a variable, STATIC_BUILD, that the user can set, and is defaulted to ON if compiling for Windows.
There is not much more you need to adapt in your CMake files. For example I need to include extra Qt platform plugins when building Qt:
if (STATIC_BUILD AND ${CMAKE_SYSTEM_NAME} MATCHES "Windows")
# include plugins into static build on windows
# (we lack support for static on other platforms right now)
set(QT_PLUGINS SvgIcon WindowsIntegration WindowsVistaStyle)
endif()
The key takeaway here for you is first to get the proper environment on your system.

Project with shared lib and test application using that lib

I've built a shared lib and a test application with CMake (2.8)
Now, after make install, I get a tree like this:
root/
lib/
mylib.so
samples/
test1
test2
...
Now when I run my test app, it can't find the shared library file to run:
error while loading shared libraries: mylib.so: cannot open shared object file: No such file or directory
How can I solve this?
EDIT
Here is the relevant part of my CMakeLists.txt
For the mylib
add_library(mylib SHARED ${LIB_SOURCES})
target_link_libraries(mylib ${VTK_LIBRARIES} ${OpenCV_LIBS})
install(TARGETS mylib DESTINATION lib)
install(FILES include/mylib.h DESTINATION include)
install(DIRECTORY models DESTINATION .)
add_subdirectory(samples)
For the executables folder:
file(GLOB APP_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp)
foreach( samplesourcefile ${APP_SOURCES} )
string( REPLACE ".cpp" "" samplename ${samplesourcefile} )
add_executable( ${samplename} ${samplesourcefile} )
target_link_libraries( ${samplename} mylib )
install(TARGETS ${samplename} DESTINATION samples)
endforeach( samplesourcefile )
This may be caused because your library was installed in a non default directory in your Linux operating system.
File /etc/ld.so.conf contains the directories used by the dynamic loader for searching shared libs in Linux, see ldconfig manpage.
You can always check if all shared dependencies of a dynamic linked executable like your test application are found with command ldd.
So a solution to your missing library problem would be to install your library under a directory that your system uses as a search path for finding shared libraries.
A proper place to install development shared libraries in Linux distros is /usr/local/lib; this is almost certainly included as a search path for shared libraries by default in all Linuxes. Likewise, development binaries like your test app would be better placed under /usr/local/bin (however having it elsewhere is not the cause of the missing lib problem).
For the record, here follows an excerpt about /usr/local as documented by The Linux Documentation Project:
[...] It copies the structure of '/usr'. These days, '/usr/local' is
widely regarded as a good place in which to keep
self-compiled or third-party programs. The /usr/local hierarchy is for
use by the system administrator when installing software locally. It
needs to be safe from being overwritten when the system software is
updated. It may be used for programs and data that are shareable
amongst a group of hosts, but not found in /usr. Locally installed
software must be placed within /usr/local rather than /usr unless it
is being installed to replace or upgrade software in /usr.

CMake install can't find library and points to wrong compiler version

linux noob here- will appreciate any kind of help.
Some background:
I'm trying to build a program from source on RHEL 6.5, the dependencies for this program are specifically:
GCC 4.7 and above (for C++ 11 support)
CMake 2.8.9+
we already had GCC 4.4.7 installed in /usr/libexec/gcc, so our linux person built and installed the new version in /usr/local/libexec/gcc (version 4.9)
We didn't have CMake so I installed in from scratch by unizipping the source in /usr/local and following the directions from here:
http://www.cmake.org/install/
./bootstrap
make
make install
so far so good and in the CMakeOutput.log of the CMake it is correctly pointing to the new GCC's path, [COMPILER PATH=/usr/local/libexec/gcc/.../4.9.2/
and I did have to copy a .so file from /usr/lib64 to /usr/local/lib64 in order to successfully bootstrap/make it but I don't think that's the source of my problem.
The Problem:
Now here's what i'm having trouble with: so when I finally try to build this program using "cmake ."
I get the following issues:
-- The C compiler identification is GNU 4.4.7
-- Performing Test COMPILER_SUPPORTS_CXX11 - Failed
The compiler identification should be version 4.9 and the Test should've succeeded but it did not...
-- Could NOT find ZLIB (missing: ZLIB_LIBRARY ZLIB_INCLUDE_DIR)
-- Could NOT find PNG (missing: PNG_LIBRARY PNG_PNG INCLUDE_ DIR)
Cmake has the FindPNG cmake module file in /usr/local/cmake-3.0.2/Modules but it doesn't seem to know where it is, I tried copying just the FindPNG.cmake file into the local cmake directory of the program and I just kept getting missing module files one after another...
Now- I think all these errors could just be a result of something not pointing to something correctly, maybe not setting environment variables for something, missing or wrong CMake commands / variables in the CMakeList file or whatever but I have spent a quite amount of time trying to fix it trying different approaches but just couldn't figure it out...any help will be greatly appreciated!!!
Here's the top level CMakeLists.txt of the program I'm trying to build:
cmake_minimum_required(VERSION 2.8)
project(COLLADA2GLTF)
if (NOT WIN32)
#http://www.guyrutenberg.com/2014/01/05/enabling-c11-c0x-in-cmake/
include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
if(COMPILER_SUPPORTS_CXX11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
message("-- C++11 Enabled")
elseif(COMPILER_SUPPORTS_CXX0X)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
message("-- C++0x Enabled")
else()
message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.")
endif()
endif()
set(USE_OPEN3DGC "ON")
set(WITH_IN_SOURCE_BUILD "ON")
set(COLLADA2GLTF_BINARY_DIR, COLLADA2GLTF_SOURCE_DIR)
set(BUILD_SHARED_LIBS "OFF")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
include(GetGitRevisionDescription)
get_git_head_revision(GIT_REFSPEC GIT_SHA1)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/GitSHA1.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/GitSHA1.cpp" #ONLY)
set(TARGET_LIBS GeneratedSaxParser_static OpenCOLLADABaseUtils_static UTF_static ftoa_static MathMLSolver_static OpenCOLLADASaxFrameworkLoader_static OpenCOLLADAFramework_static buffer_static)
if (NOT WIN32)
set(CMAKE_FIND_LIBRARY_SUFFIXES .so .a .dylib)
endif()
# Lets libxml2 work in a shared library
add_definitions(-DLIBXML_STATIC_FOR_DLL)
IF(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
ADD_DEFINITIONS(-fPIC)
ENDIF(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
include_directories(${COLLADA2GLTF_SOURCE_DIR})
.......
include_directories(${COLLADA2GLTF_SOURCE_DIR}/dependencies/OpenCOLLADA/COLLADABaseUtils/include)
include_directories(${COLLADA2GLTF_SOURCE_DIR}/dependencies/OpenCOLLADA/COLLADASaxFrameworkLoader/include)
include_directories(${COLLADA2GLTF_SOURCE_DIR}/dependencies/OpenCOLLADA/GeneratedSaxParser/include)
if (WIN32)
include_directories(${COLLADA2GLTF_SOURCE_DIR}/dependencies/misc)
endif()
if (USE_OPEN3DGC)
add_definitions( -DUSE_OPEN3DGC )
include_directories(${COLLADA2GLTF_SOURCE_DIR}/extensions/o3dgc-compression)
include_directories(${COLLADA2GLTF_SOURCE_DIR}/dependencies/o3dgc/src)
include_directories(${COLLADA2GLTF_SOURCE_DIR}/dependencies/o3dgc/src/o3dgc_common_lib/inc)
include_directories(${COLLADA2GLTF_SOURCE_DIR}/dependencies/o3dgc/src/o3dgc_encode_lib/inc)
include_directories(${COLLADA2GLTF_SOURCE_DIR}/dependencies/o3dgc/src/o3dgc_decode_lib/inc)
endif()
find_package(PNG)
if (PNG_FOUND)
include_directories(${PNG_INCLUDE_DIR})
include_directories(${ZLIB_INCLUDE_DIR})
add_definitions(-DUSE_LIBPNG)
else()
message(WARNING "libpng or one of its dependencies couldn't be found. Transparency may not be correctly detected.")
endif()
link_directories(${COLLADA2GLTF_BINARY_DIR}/lib)
if (WIN32)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
add_definitions(-DWIN32)
add_definitions(-EHsc)
endif()
add_subdirectory(dependencies/OpenCOLLADA)
if (USE_OPEN3DGC)
add_subdirectory(dependencies/o3dgc/src)
endif()
set(GLTF_SOURCES
COLLADA2GLTFWriter.h
COLLADA2GLTFWriter.cpp
......
assetModifiers/GLTFFlipUVModifier.cpp
${CMAKE_CURRENT_BINARY_DIR}/GitSHA1.cpp
GitSHA1.h)
if (USE_OPEN3DGC)
LIST(APPEND GLTF_SOURCES
extensions/o3dgc-compression/GLTF-Open3DGC.cpp
extensions/o3dgc-compression/GLTF-Open3DGC.h)
endif()
option(CONVERT_SHARED "CONVERT_SHARED" OFF)
if (CONVERT_SHARED)
add_library(collada2gltfConvert SHARED ${GLTF_SOURCES})
#Make sure the dll is in the same directory as the executable
if (WIN32)
set_target_properties(collada2gltfConvert PROPERTIES RUNTIME_OUTPUT_DIRECTORY "bin")
endif()
else()
add_library(collada2gltfConvert STATIC ${GLTF_SOURCES})
add_definitions(-DSTATIC_COLLADA2GLTF)
endif()
if (PNG_FOUND)
LIST(APPEND TARGET_LIBS ${PNG_LIBRARY} ${ZLIB_LIBRARY})
endif()
if (USE_OPEN3DGC)
LIST(APPEND TARGET_LIBS o3dgc_common_lib o3dgc_enc_lib o3dgc_dec_lib)
endif()
IF("${CMAKE_SYSTEM}" MATCHES "Linux")
LIST(APPEND TARGET_LIBS rt)
endif("${CMAKE_SYSTEM}" MATCHES "Linux")
target_link_libraries (collada2gltfConvert ${TARGET_LIBS})
set(GLTF_EXE_SOURCES main.cpp
${CMAKE_CURRENT_BINARY_DIR}/GitSHA1.cpp
GitSHA1.h)
if (WIN32)
LIST(APPEND GLTF_EXE_SOURCES ${COLLADA2GLTF_SOURCE_DIR}/dependencies/misc/getopt_long.c ${COLLADA2GLTF_SOURCE_DIR}/dependencies/misc/getopt.c ${COLLADA2GLTF_SOURCE_DIR}/dependencies/misc/getopt.h)
endif()
add_executable(collada2gltf ${GLTF_EXE_SOURCES})
target_link_libraries (collada2gltf collada2gltfConvert)
I guess that you're just facing the problem of unsatisfied dependencies. RHEL/CentOS 6 is pretty old distribution with an old kernel and applications; its recent releases are distributed with deprecated packages (like in your case) due to the needs of the backward compatibility. So is there any significant reason for you to use the one?
I spend one year in the project where we were developing software under RHEL 6.4 and I would suggest that you set yourself an impossible goal: you need to install to your OS not just a compiler, but also all of its libraries (for instance, actual C++ standard library .so etc.) and headers. Since these versions are not provided in you distro, you'll face difficulties due to the presence of same but differing libraries. Since all system software relies on to the old libraries, your manipulation can crash your system.
However, in case if you want to continue, you can try to install missing dependencies ZLIB and PNG with these commands (just a suggestion):
yum install zlib-devel libpng-devel
But definitly I would suggest you to save your time and deploy some fresh distro: CentOS 7 (if you want server) or Fedora 21 (will be released tommorow).

build c file with openssl by NDK under cygwin

I am trying to build c file included by ndk under cygwin
In Android.mk, I add -I/usr/include to LOCAL_FLAGS like
LOCAL_FLAGS := -I/usr/include
And I have checked that openssl does under /usr/include
But when I run ndk-build under by project dir, it output
"fatal error: openssl/ssl.h: No such file or directory"
I think I have specified the include directory, but not solve this problem.
Is there any other way can I try?
You seem to have some gaps in your knowledge:
C code compiles to processor's native instruction set. Your desktop/build machine probably has a different architecture from your Android device(thus a different instruction set).
NDK doesn't just compile, it cross-compiles. It means that the NDK runs on the build machine, but the executable it produces cannot run on the build machine(different instruction sets).
All libraries on your desktop are in your desktop's processoer's instruction set. Thus, you cannot link any program build by the NDK using the desktop's libraries. This means:
No includes from '/usr/include/'
No libs from /lib, /usr/lib, /lib64 or /usr/lib64
No Cygwin packages under on Windows
What you need to do is build your own openssl using the NDK and use that to link against when you build your executable.
Please note that the answer is missing a lot of information (at least 3 Bachelor's level Computer Science courses worth of information).

CMake to create a exe and lib with (prepackaged headers and dlls)

cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
project(ImageProc)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
find_package(PCL 1.2 REQUIRED)
find_package(OpenCV REQUIRED)
add_definitions( -fPIC -Wall -O3)
include_directories(${PCL_INCLUDE_DIRS})
#link_directories(${PCL_LIBRARY_DIRS}) Dont think neccesary..
add_definitions(${PCL_DEFINITIONS})
add_executable (ImageProc svm.cpp ImageProc.cpp testImageProc.cpp)
target_link_libraries (ImageProc ${OpenCV_LIBS} ${PCL_LIBRARIES})
add_library(ImageProcLib STATIC svm.cpp ImageProc.cpp)
target_link_libraries (ImageProcLib ${OpenCV_LIBS} ${PCL_LIBRARIES})
Currently, I run this and open with VS C++ and generate an exe and lib.
The exe runs on my machine.
Current limitations:
When I pass the exe to my friend, he cant run it on his machine as he gets hit by host of missing dlls.
When I use the lib files, to create a new project in VS C++, there is a fatal error in not finding a header file.
I know, I can manually add all the dlls and or package all the header and library files for the lib. But it is definitely cumbersome and ugly also.
Question:
Does CMake offer a way, so that when compiling into
An exe (it will automatically find all the necessary dlls into bin directory)
Into a lib (it will automatically source out all the header files and also the neccesary library for the linking part into the lib directory)
Preparing your package for deployment on other development environment can still be a tough task. You will need to setup your own interfaces/API correctly and will need to deal with cross-dll issues, different runtimes, etc.
However, there are a few tools from CMake at your disposal:
Check out the Install and Export. You can use it to specify (in your CMakeLists.txt) which files are necessary for deployment. This way, you can mark which header-files, but also which targets (libs and exes) should be deployed.
Also take a look at CPack in combination with NSIS, which can be used to build NSIS installers of your project.
Together with the InstallRequiredSystemLibraries and BundleUtilities you can then prepare an install or package target. See also this and this question demonstrating how third-party dlls can be added to such a deployment package.
InstallRequiredSystemLibraries is very useful for appending vcredist to your own package installer, which you will surely need if you are installing your project on a non-development pc.

Resources