Deplying a C++ application on Linux- linking everything statically to simplify deployment? - c++

I am building a C++ project from Github and want to deploy the code to a remote Linux machine. This is all new to me.
The project has a main.cpp, which includes the various headers/sources like a library.
The CMake outputs an executable (to represent main.cpp) AND a separate static library. The project also uses OpenSSL, which I have linked statically.
I presume the OpenSSL functions are included within the static library? So when I deploy, I don't need to copy-over or install any OpenSSL on the remote machine?
Is it possible to modify the CMake so the application and the library are merged in to one file?
I am trying to make deployment as simple as copying over a single file, if this is possible.
Any additional advice/references are most-welcome.
UPDATE the CMake script:
cmake_minimum_required(VERSION 3.20)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake;${CMAKE_MODULE_PATH}")
project(helloworld C CXX)
set (CMAKE_CXX_STANDARD 20)
set (CMAKE_BUILD_TYPE Release)
set (BUILD_MAIN TRUE)
set (BUILD_SHARED_LIBS FALSE)
set (OPENSSL_USE_STATIC_LIBS TRUE)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set( HELLOWORLD_HEADERS helloworld/File1.h helloworld/File2.h )
set( HELLOWORLD_SOURCES helloworld/File1.cpp helloworld/File2.cpp )
# Static library
add_library( helloworld ${HELLOWORLD_SOURCES} ${HELLOWORLD_HEADERS} )
# Rapidjson
include_directories(/tmp/rapidjson/include/)
# OpenSSL
if (NOT OPENSSL_FOUND)
find_package(OpenSSL REQUIRED)
endif()
add_definitions(${OPENSSL_DEFINITIONS})
target_include_directories(helloworld PUBLIC $<BUILD_INTERFACE:${OPENSSL_INCLUDE_DIR}>)
target_link_libraries(helloworld PRIVATE ${OPENSSL_LIBRARIES})
set( HELLOWORLD_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR})
include(GNUInstallDirs)
target_include_directories(helloworld PUBLIC
$<BUILD_INTERFACE:${HELLOWORLD_INCLUDE_DIRS}/>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/helloworld>
)
set_target_properties(helloworld PROPERTIES PUBLIC_HEADER "${HELLOWORLD_HEADERS}")
add_library(helloworld::helloworld ALIAS helloworld)
option(HELLOWORLD_INSTALL "Install HelloWorld" TRUE)
if (HELLOWORLD_INSTALL)
install(TARGETS helloworld
EXPORT helloworld
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/helloworld/
)
configure_file("${CMAKE_CURRENT_LIST_DIR}/helloworld-config.cmake.in" "${CMAKE_BINARY_DIR}/helloworld-config.cmake" #ONLY)
install(FILES "${CMAKE_BINARY_DIR}/helloworld-config.cmake" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/helloworld")
install(EXPORT helloworld
FILE helloworld-targets.cmake
NAMESPACE helloworld::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/helloworld
)
endif()
if (BUILD_MAIN)
add_executable(main main.cpp)
target_link_libraries(main helloworld)
endif()

ITNOA
I it is very helpful to make URL of your GitHub's project, but I write some public notes about that
In generally in CMake for static linking your library to your executable, you can write simple like below (from official CMake example)
add_library(archive archive.cpp zip.cpp lzma.cpp)
add_executable(zipapp zipapp.cpp)
target_link_libraries(zipapp archive)
In above example your executable file is just work without needing .a library file and you can simple copy single file.
if you want to make all of thing static, you make sure all dependencies make static link to your project, like CMake: how to produce binaries "as static as possible"
if you want to prevent library creation, Probably in your CMake file, you can find add_library command, and add_executable command. you can remove add_library command and add all sources to add_executable command.
for example add_executable(a.out main.cpp lib.cpp)

Related

cmake - Header files of shared library not found

I am making a custom library that I want to be installable for users. However, when I try to use my own library in a cmake executable, I get a build error saying that the library headers were not found.
The library CMakeLists.txt:
cmake_minimum_required(VERSION 3.16)
project(mylibrary)
include(GNUInstallDirs)
set(CMAKE_CXX_STANDARD 14)
# Register a library - This will created lib[xxx].so
add_library(mylibrary SHARED src/library.cpp)
configure_file(mylibrary.pc.in mylibrary.pc #ONLY)
# List the /include directory
target_include_directories(mylibrary PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
install(TARGETS mylibrary
EXPORT mylibraryConfig
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
export(TARGETS mylibrary
FILE "${CMAKE_CURRENT_BINARY_DIR}/mylibraryConfig.cmake")
install(EXPORT mylibraryConfig
DESTINATION "${CMAKE_INSTALL_LIBDIR}/mylibrary/cmake"
NAMESPACE mylibraryConfig::)
install(
DIRECTORY include
DESTINATION ${CMAKE_INSTALL_PREFIX})
install(FILES ${CMAKE_BINARY_DIR}/mylibrary.pc
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig)
Which I successfully build and install with:
$ cmake .. -DCMAKE_INSTALL_PREFIX=~/libraries/local # Use non-standard destination
$ make && make install
The executableCMakeLists.txt:
cmake_minimum_required(VERSION 3.16)
project(myexecutable)
set(CMAKE_CXX_STANDARD 14)
find_package(mylibrary REQUIRED)
add_executable(myexecutable src/main.cpp)
target_link_libraries(myexecutable PUBLIC mylibrary)
target_include_directories(myexecutable PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
I can prepare cmake for this project:
$ cmake .. -DCMAKE_PREFIX_PATH=~/libraries/local # Use non-standard location
However, building it fails:
$ make
fatal error: mylibrary/library.h: No such file or directory
2 | #include <mylibrary/library.h>
To my understanding the location of the library (binaries and headers) is embedded in the installed package. And through find_package() that information retrieved, so why isn't it working here?
Similar questions:
I largely based my library cmake on: How to create a shared library with cmake?
Same problem but I am already using target_include_directories: Cmake Linking Shared Library: "No such file or directory" when include a header file from library
When a shared library target is namespaced in the config file you need to reference it with the full name in the downstream packages when using find_package, i.e. you need to use
target_link_libraries(myexecutable PUBLIC mylibraryConfig::mylibrary)
Alternatively, remove the namespace from the install by replacing
install(EXPORT mylibraryConfig
DESTINATION "${CMAKE_INSTALL_LIBDIR}/mylibrary/cmake"
NAMESPACE mylibraryConfig::)
...with:
install(EXPORT mylibraryConfig
DESTINATION "${CMAKE_INSTALL_LIBDIR}/mylibrary/cmake")

Require shared library with dependencies in cmake

I'm new to cmake, so I'm having trouble figuring out how to model the following:
My program requires a third party library that I only have access to in the form of headers and *.so files. It also ships many of its dependencies as *.so files. How do I make sure that everything compiles and links correctly, ideally in the "correct" cmake way?
I've tried this
cmake_minimum_required (VERSION 3.8)
project ("Test")
add_executable (MyApp "MyApp.cpp")
link_directories("/path/to/lib")
target_include_directories(MyApp PUBLIC "/path/to/headers")
This compiles but fails at the linking stage.
Then I tried
cmake_minimum_required (VERSION 3.8)
project ("Test")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
add_executable (MyApp "MyApp.cpp")
find_package(Library REQUIRED)
target_link_libraries(MyApp PUBLIC Library::Library)
And cmake/FindLibrary.cmake following https://pabloariasal.github.io/2018/02/19/its-time-to-do-cmake-right/ (truncated):
...
if(Library_FOUND AND NOT TARGET Library::Library)
add_library(Library::Library SHARED IMPORTED)
set_target_properties(Library::Library PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${Library_INCLUDE_DIR}"
)
set_target_properties(Library::Library PROPERTIES
IMPORTED_LOCATION "/path/to/library.so"
)
endif()
This compiles and links, but the required other *.so files are not found at runtime. I suppose I need to add them also as libraries somehow, although I don't want them to be exported in FindLibrary.cmake. How do I do this correctly?
You can use IMPORTED libraries:
add_library(externalLib SHARED IMPORTED GLOBAL)
set_property(TARGET externalLib PROPERTY IMPORTED_LOCATION "/path/to/lib.so")
target_include_directories(externalLib INTERFACE "/path/to/headers")
target_link_directories(externalLib INTERFACE /path/to/libs)
target_link_libraries(externalLib INTERFACE -ldep1 -ldep2)
Then you can just depend on it:
target_link_libraries(MyApp PRIVATE externalLib)

How to make ExternalProject_Add acting like ADD_SUBDIRECTORY command with cmake?

I'm creating a native library under Android Studio, and I'm hurting the following problem.
In my AndroidStudio project CMakeLists.txt, I have this:
cmake_minimum_required(VERSION 3.7.0)
add_library(native-lib
SHARED
src/main/cpp/native-lib.cpp )
IF(USE_EXTERNAL)
include(ExternalProject)
ExternalProject_Add(
project_mylib
DOWNLOAD_COMMAND ""
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/mylib/"
CMAKE_ARGS
-DCMAKE_INSTALL_PREFIX=${PROJECT_BINARY_DIR}
)
add_dependencies(native-lib project_mylib)
ELSE()
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/mylib/)
ENDIF()
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
target_link_libraries(native-lib
${log-lib}
mylib
)
A self-made library is located in src/main/cpp/mylib/, with a CMakeLists.txt:
cmake_minimum_required(VERSION 3.7.0)
project(lib)
add_library(mylib SHARED lib.cpp)
INSTALL(TARGETS mylib
RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin
LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib
)
When I use the "traditional" add_subdirectory(...) everything goes well. But, if I use the ExternalProject_Add(...) version, linker is skipping the compiled libmylib.so library and so cannot link mylib to native-lib.
I have the following message: skipping incompatible /home/.../app/.externalNativeBuild/cmake/debug/arm64-v8a/lib/libmylib.so when searching for -lmylib
My guess is that all the flags set by AndroidStudio for the root CMakeLists.txt are not set when the ExternalProject is compile leading to an incompatible shared library.
So, I wonder if there is a way to compile a cmake ExternalProject like it was part of the root project (sharing the same compile flags etc) ?
Thanks for any advice

Install and export INTERFACE only library, CMake

new CMake user here.
I've made a simple header only library, with the following CMake file:
cmake_minimum_required(VERSION 3.7)
project(mylib VERSION 0.1 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 14)
#add_compile_options(-Wa -aslh)
# Define the library target
add_library(mylib INTERFACE)
target_include_directories(mylib INTERFACE include/)
add_executable(mytest test/basic_checks.cpp)
target_link_libraries(mytest mylib)
From http://foonathan.net/blog/2016/03/03/cmake-install.html
I've learned there are several steps to getting a library installed and to have it useable from another CMake project. First it must be "installed" and then it must be "exported". Then there's find_package, but I'll cross that bridge after I've gotten over the first two steps.
So following the example, I figure that I could add the following to the bottom of my CMake file:
# Install the header file.
install(FILES include/mylib.hpp DESTINATION "include/mylib-${PROJECT_VERSION}")
But the guide talks about also using install on TARGETS, and then adding EXPORT to the target install commands.
How much of this applies to a header only library in which I have no other compiled code of files other than the header?
How do I apply the steps described in : http://foonathan.net/blog/2016/03/03/cmake-install.html
To an INTERFACE only library? The lack of .cpp files in my project is leading me to question which instructions apply and don't apply.
try this:
cmake_minimum_required(VERSION 3.7)
project(mylib VERSION 0.1 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 14)
#add_compile_options(-Wa -aslh)
# Define the library target
add_library(mylib INTERFACE)
target_include_directories(mylib INTERFACE
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
"${PROJECT_BINARY_DIR}/mylibConfigVersion.cmake"
VERSION 0.1
COMPATIBILITY AnyNewerVersion
)
install(TARGETS mylib
EXPORT mylibTargets
LIBRARY DESTINATION lib COMPONENT Runtime
ARCHIVE DESTINATION lib COMPONENT Development
RUNTIME DESTINATION bin COMPONENT Runtime
PUBLIC_HEADER DESTINATION include COMPONENT Development
BUNDLE DESTINATION bin COMPONENT Runtime
)
include(CMakePackageConfigHelpers)
configure_package_config_file(
"${PROJECT_SOURCE_DIR}/cmake/mylibConfig.cmake.in"
"${PROJECT_BINARY_DIR}/mylibConfig.cmake"
INSTALL_DESTINATION lib/cmake/mylib
)
install(EXPORT mylibTargets DESTINATION lib/cmake/mylib)
install(FILES "${PROJECT_BINARY_DIR}/mylibConfigVersion.cmake"
"${PROJECT_BINARY_DIR}/mylibConfig.cmake"
DESTINATION lib/cmake/mylib)
install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/ DESTINATION include)
add_executable(mytest test/basic_checks.cpp)
target_link_libraries(mytest mylib)
the content of cmake/mylibConfig.cmake.in should only be this
#PACKAGE_INIT#
include("${CMAKE_CURRENT_LIST_DIR}/mylibTargets.cmake")
check_required_components("#PROJECT_NAME#")
if you do all of this, not only that it makes your header-only library 'installable', but it also makes it 'findable'. users will be able to import your library like so:
find_package(mylib CONFIG REQUIRED)
target_link_libraries(MyApp mylib) # installed include/ path automatically added
Just a small addition to https://stackoverflow.com/a/49143782/2119944
Probably cmake/mylibConfig.cmake.in should look like this:
#PACKAGE_INIT#
include("${CMAKE_INSTALL_PREFIX}/lib/cmake/mylib/mylibTargets.cmake")
check_required_components("#PROJECT_NAME#")
Otherwise it will point to local project dir.

Right way of creating a library, installing it and linking to another project using CMake

I have a set of files that I want to make into a library and then use that library in another project. This it how it looks like right now
cmake_minimum_required(VERSION 2.8)
aux_source_directory(. SRC_LIST)
SET(CMAKE_CXX_FLAGS "-std=c++11 -fopenmp -fPIC")
add_library (helperlibs lib1.cpp lib2.cpp lib3.cpp lib4.cpp )
INSTALL(TARGETS helperlibs
DESTINATION "${HOME}/lib"
)
INSTALL(FILES lib1.h lib2.h lib3.h lib4.h helperheader.h
DESTINATION "${HOME}/include/helperlibs"
)
In this code Lib4 depends on Lib1-3 and Lib3 depends on Lib1-2 and Lib2 depends on Lib1. Each of these cpp files also depend on a helperheader.h file that contains some definitions and structs.
In my project I have the following CMake file
cmake_minimum_required(VERSION 2.8)
aux_source_directory(. SRC_LIST)
SET( CMAKE_CXX_FLAGS "-std=c++11 -fopenmp -fPIC")
SET(MYINCS ${HOME}/include/helperlibs)
SET(MYLIBDIR ${HOME}/lib)
SET(MYLIBS ${MYLIBDIR}/libhelperlibs.a )
include_directories(${MYINCS})
add_executable(main main.cpp)
target_link_libraries(main ${MYLIBS})
So what I am wondering if you want to create a static library and link to from a another project using cmake is this the way you should write?
Embed the search paths into the library target as properties and create an export.
This way executables in the same build tree will find the library and its include files without you having to specify paths (they become implicit).
I needed to read the cmake documentation carefully a few times before it dawned on me how it should work.
http://www.cmake.org/cmake/help/v3.0/manual/cmake-packages.7.html#creating-packages
http://www.cmake.org/cmake/help/v3.0/manual/cmake-buildsystem.7.html
excerpt from a live example:
add_library(trustportal-util ${CMAKE_CURRENT_LIST_FILE} ${_source_files} ${_disabled_source_files} )
target_link_libraries(trustportal-util ${Boost_LIBRARIES})
if(APPLE)
find_library(SECURITY_FRAMEWORK Security)
target_link_libraries(trustportal-util ${SECURITY_FRAMEWORK})
else()
find_library(LIB_SSL ssl)
find_library(LIB_CRYPTO crypto)
target_link_libraries(trustportal-util ${LIB_SSL} ${LIB_CRYPTO})
endif()
target_compile_definitions(trustportal-util PUBLIC BOOST_MOVE_USE_STANDARD_LIBRARY_MOVE)
target_include_directories(trustportal-util PUBLIC ${Boost_INCLUDE_DIRS})
target_include_directories(trustportal-util PRIVATE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
target_include_directories(trustportal-util SYSTEM PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
install(TARGETS trustportal-util
EXPORT trustportal-utilExport
DESTINATION lib
INCLUDES DESTINATION include
)
INSTALL(EXPORT trustportal-utilExport DESTINATION lib)
One option is to do what you are currently doing where you place the includes and libs in a common location, perhaps /usr/include and /usr/lib on linux, or ${HOME} on both Windows/Linux, up to you.
Another option is available if you use git. You can include the project inside another using submodules. Then use include_directory(${submodule}) and build and link directly for every project. The advantage of this approach is that dependencies are more localised. One problem with this method is if you recursively do this, you may end up with duplicates of some projects and cmake will complain that two libraries have the same name.