Using third-party libraries with CMake - cmake

I want anyone who cloned the repository can build it immediately, and don't need to install the dependencies.
Therefore, I found several ways:
Use git submodule and add_subdirectory.
Use find_package to find the built libraries and the headers.
The first one takes much time to build, so I think the second might be better. To make people be able to build the project instantly, I put the the files in the project, but it saied it doesn't know the linker language. What's this? And how to solve?
Direstories:
Project Root
lib
SDL2
(generated files when install)
include
(headers)
src
(sources)
CMakeLists.txt
CMakeLists.txt:
# ...
list(APPEND CMAKE_PREFIX_PATH lib)
find_package(SDL2)
# ...

Related

How to build namespaced subprojects in cmake that find each other

I am trying to get a basic cmake project to work with 2 subprojects (each a library) where one depends on the other.
As such I have a self-contained project for lib1 and a self-contained project for lib2 (but that depends on lib1). Some lib2 authors could while others will not have access to lib1 source. If no access is desired, it seems that building and installing one after the other is the way to go. Is there a way to build them in one go using cmake? Currently, I am trying using an overall project, libs.
However, as the libraries are related I want to call them in a boost / poco like fashion. That is for lib2 I would like to write:
find_package(libs COMPONENTS lib1 REQUIRED)
target_link_libraries(lib2 PUBLIC libs::lib1)
I have created config files for both lib1 and lib2 and libs. However, I keep getting the same error
CMake Error at libs/lib2/CMakeLists.txt:67 (find_package):
By not providing "Findlibs.cmake" in CMAKE_MODULE_PATH this project has
asked CMake to find a package configuration file provided by "libs", but
CMake did not find one.
Could not find a package configuration file provided by "libs" with any of
the following names:
libsConfig.cmake
libs-config.cmake
Add the installation prefix of "libs" to CMAKE_PREFIX_PATH or set
"libs_DIR" to a directory containing one of the above files. If "libs"
provides a separate development package or SDK, be sure it has been
installed.
I don't understand where to put these files. It searches for them under prefix/ etc but at the time of building none of these libraries is yet installed. What is the proper place to put the -config.cmake files? I have put them all in
${CMAKE_BINARY_DIR}/libs
which seems similar to Poco, but the error remains. How can the libraries find each other?
// update:
From the comments I understand that lib1 first needs to be build in order to use find_package. Is there a way (in cmake) to
build lib1, install it, build lib2
or is this better to do using a bash / python / etc script?

How can I use CMake and CPack with a dependency that overwrites my variables?

I'm trying to use CPack to package my project, but I've run into an issue with one of my dependencies.
First, I am trying to use modern CMake style by relying on targets and dependencies modeled between targets using target_link_libraries. My project uses the CMocka library as a dependency. I have added the CMocka repo as a git submodule and I have add_subdirectory(cmocka_dir) in my own CMakeLists.txt file.
I also use CPack with the TGZ generator to gather all my files into an archive. I simply set(CPACK_GENERATOR TGZ) and include(CPack) in my CMakeLists.txt. I have found that this takes care of everything, that is, until I started using CMocka. CMocka sets a lot of CPACK_xxx variables inside its root CMakeLists.txt. I believe that when I include CMocka's subdirectory in my own project, these variables propagate inside my CMake scope and mess with my packaging process.
My question is how I can fix this. Specifically:
Is there a way to isolate CMocka in its own environment, or otherwise prevent it from breaking my packaging process?
Am I doing it wrong by including CMocka using add_subdirectory?
Am I doing CPack wrong by just setting CPACK_GENERATOR and doing include(CPack)?
Thanks for your thoughts.

How do I specify a specific CMake target as a dependency?

Our CMake project, hosted on GitHub, has a CMake git submodule as a dependency. The file structure, then, is roughly:
project/
CMakeLists.txt
extern/
big_lib/
CMakeLists.txt
include/
*.hpp
static/
CMakeLists.txt
shared/
CMakeLists.txt
We have authorship of both project and big_lib.
The top level CMakeLists.txt for project includes something like:
add_subdirectory(${PROJECT_SOURCE_DIR}/extern/big_lib)
target_link_libraries(${PROJECT_NAME} big_lib::static)
big_lib::static is a library we don't install/publish; it's not specified as such in the big_lib configuration, it's for internal consumption only - namely for tests. We deliver a client facing shared library, but the shared library is not appropriate for project.
This is why ExternalProject_Add may not be the most appropriate solution for satisfying our dependency - as it is my understanding installing the dependency in the build directory won't install the specific build target we need. Also, I haven't had luck getting it to work yet.
What I've also noticed is that we're building all targets in big_lib, of which there are hundreds - mostly tests, and that shared library I don't want or need. I suspect this is because we're including the entire library from it's base directory.
I've tried:
add_subdirectory(${PROJECT_SOURCE_DIR}/extern/big_lib/static)
But it seems there's configuration from the big_lib base directory that now goes unspecified, which is why I'm including the base directory in project instead of this.
So my questions are:
Is there a better way to specify the static build target so only that gets built?
Is there a better way to organize the configuration of big_lib so I can add only the static library as the dependency folder, and not duplicate configuration from the base directory between static and shared?
What options am I not aware of? Maybe I should use ExternalProject_Add and specify some sort of custom build command where I issue just the static lib as the build target and install target, and then link against that artifact?

How to include a library using CMAKE in a cross-platform way

I am trying to use the assimp library in a cross platform C++ project. I include the repo as a git submodule, so, effectively, if someone downloads my project they will also download the ASSIMP project.
After I go through the assimp build / CMAKE instructions and (on Linux) type make install and from then on in my project I can use:
target_link_libraries(${PROJECT_NAME} assimp)
However, there is no make install on Windows.
The only other way I have been able to include the library on Linux is to put (in my CmakeLists.txt file):
target_link_libraries(${PROJECT_NAME} ${CMAKE_SOURCE_DIR}/build/assimp/code/libassimp.so)
This is not cross platform as it hardcodes the name and location of the .so file which will not work on Windows.
How can I expose the library so that I can do something like target_link_libraries(${PROJECT_NAME} assimp) on all platforms?
My directory tree looks like:
- src
- include
- assimp
- bin
Where the assimp directory in the include directory is the git submodule
I think you're going about this the wrong way. You don't need to build assimp in a separate step from your project, and you don't need to make install to make it available.
There are a number of ways of handling third party dependencies in Cmake, since you've already chosen to submodule the assimp repository, we'll start there. Assuming assimp is located in the root of your repository in a directory called assimp/ this would be a barebones project including it:
cmake_minimum_required(VERSION 3.0)
project(Project myassimpproj)
# include your directories
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}
)
# set any variables you might need to set for your app and assimp
set(BUILD_ASSIMP_TOOLS ON)
set(ASSIMP_BUILD_STATIC_LIB ON)
# add assimp source dir as a subdirectory, effectively making
# assimp's CMakeLists.txt part of your build
add_subdirectory(/path/to/assimp ${CMAKE_BINARY_DIR}/assimp)
add_executable(assimp_target main.cpp)
# be sure to link in assimp, use platform-agnostic syntax for the linker
target_link_libraries(assimp_target assimp)
There may be a better way of phrasing this using generator expressions syntax, but I haven't looked at assimp's CMakeLists.txt to know if it's supported (and this is a more generic way anyway.)
Not every project uses Cmake, so you may not be able to just add_subdirectory(). In those cases, you can effectively "fake" a user call to build them using their build commands on respective platforms. execute_process() runs a command at configure time add_custom_command() and add_custom_target() run commands at build time. You then create a fake target to make integration and cross your fingers they support Cmake someday.
You can also use the ExternalProject commands added to Cmake to create a custom target to drive download, update/patch, configure, build, install and test steps of an external project, but note that this solution and the next download the dependency rather than using the submodule'd source code.
Finally, I prefer to work with prebuilt dependencies, cuts down on build time, and they can be unit tested on their own outside of the project. Conan is an open source, decentralized and multi-platform package manager with very good support for C++ and almost transparent support for Cmake when used the right way. They have grown very stable in the last year. More information on how to use Conan with Cmake can be found here.

forcing cmake install through add_dependencies?

I'm sorry if this is duplicate. I see a lot of post that are similar, but I am either not "getting" it or it is not exactly the same issue.
I need to build two shared libraries. They are in the same directory structure, and they both get but in to bin. One is dependent on the other.
I've been reading up on add_dependencies, but that seems to deal with project builds outside of cmake. These are both inside
Directory structure
src
CMakeLists.txt
bin
xml
CMakeLists.txt
XML.c
utils.c
XMLSPCL.c
src/CMakelists.txt
set (BIN_DIR "${CMAKE_CURRENT_SOURCE_DIR}/bin")
add_subdirectory(xml/shrxml)
xml/CMakeLists.txt (AFTER the suggestion below)
set(CMAKE_BUILD_TYPE Release)
project(shrxml)
add_library(shrxml SHARED XML.c utils.c)
install(TARGETS shrxml LIBRARY DESTINATION ${BIN_DIR})
project(shrxmlSPCL)
add_dependencies(shrxmlSPCL shrxml)
add_library(shrxmlSPCL SHARED XMLSPCL.c)
target_link_libraries(shrxmlSPCL LINK_PUBLIC shrxml)
install(TARGETS shrxmlSPCL LIBRARY DESTINATION ${BIN_DIR})
I can get it to work, but I know there has to be a correct way to do this.
To get it to work, I:
comment out the xmlSPCL project
cmake ..
make shrxml
run make install so I can get shrxml.so in to the bin directory
un-comment out xmlSPCL
make (again)
make install (again)
What I want is to run make once, have the shrxml.so go into the bin dir so that when shrxmlSPCL is linked, it sees the dependent lib. Can that be done with the extra steps?
to make an example project run I had to make 3 things different
1.
target_link_libraries(shrxmlSPCL LINK_PUBLIC shrxml)
-> So neither lib nor .so as prefix/suffix
2.
add_dependencies(shrxmlSPCL shrxml)
-> somewhere below the add_library() having the dependency
3.
install(TARGETS shrxmlSPCL LIBRARY DESTINATION ${BIN_DIR})
-> add LIBRARY into your install command doc