CMake linking to external project - c++

I am new to cmake and want to use the following external project in my program. (https://github.com/mfontanini/cppkafka)
The idea is to download from git and build using cmake, which I have managed already. At the end of it, I am left with a .so file which I then have to link back to the main project. Any ideas on how I can achieve this ? Also, would like some suggestion on how to manage dependencies in a project with cmake that would possibly be carried forward to production.

Use ADD_SUBDIRECTORY to add the external project to your project. Then you can use the named tragets from this project while linking with TARGET_LINK_LIBRARIES.

You can use the ExternalProject of cmake.
External project are downloaded in ${CMAKE_CURRENT_BINARY_DIR}/third-party
You can perform actions on them as target.

Related

cMake run build step of ExternalProject at cmake configure time

I have a cmake project which includes an external not cmake based project with ExternalProject_Add. The external project is a library used in my application.
Is there a way to run the build step of the external project during the cmake configure time?
Right now, I have to call the build step of the external project before I'm able to see the library header files in my project. So far not that big issue, but it would be convenient to have all headers ready after the configuration step...
Thanks

Does CMake has a "find-or-download-and-run-build-command" mechanism?

CMake has a find_package() backed by a bunch of FindXYZ scripts (which you can also add to).
What mechanism, if any, is available to me to tell cmake: "Find this package, and if you haven't found it, download it and trigger its build" - with the downloading and building part also backed by per-package scripts or settings (so that downloading could be with wget or git clone, building could be with cmake or maven or a package-specific command, etc.) ?
Yeah, I was bitten by that Friday.
So, CMake has an ExternalProject directive, meant for exactly that, get/update if necessary, configure, build and install this and that external project. Awesome!
Sadly, CMake isn't that awesome.
You can't use the target defined by ExternalProject as a library in target_link_libraries. I've really tried to.
The basic problem is that the updating, building and installation of the external project happens at build time, whereas CMake insists on only using libraries that it found during pre-build (i.e. during the CMake run); you can't re-detect stuff while running make/ninja/msvc… .
You can define a custom target, tell it where the .so you'd want to link against later will be, and try to coerce CMake into believing you without checking at pre-build. Sadly, at least in the CMake versions I had, that broke dependency tracking, so that it simply didn't build the external library, because nothing needed it.
From the error messages you get when trying to use an external project in target_link_library, it seems CMake assumes you'd only want to install tools you need at build time that way, not libraries. A bummer.
You can roll your own version of download-on-demand using execute_process() (which runs on the CMake configure step) with ${CMAKE_COMMAND} as the command invoked on a CMakeLists.txt containing ExternalProject_Add().
You could even either configure_file() the CMakeLists.txt to fill out custom variables or dynamically create the CMakeLists.txt file.

Embedding library and it's includes via CMake

I'm creating a very small project that depends on the following library: https://github.com/CopernicaMarketingSoftware/AMQP-CPP
I'm doing what i always do with third-party libraries: i add their git repo as a submodule, and build them along with my code:
option(COOL_LIBRARY_OPTION ON)
add_subdirectory(deps/cool-library)
include_directories(deps/cool-library/include)
target_link_libraries(${PROJECT_NAME} coollib)
This has worked perfectly for libraries like Bullet, GLFW and others. However, this AMQP library does quite an ugly hack. Their include directory is called include, but in their CMake install() command, they rename it to amqpcpp. And their main header, deps/cool-library/amqpcpp.h, is referencing all other headers using that "fake" directory.
What happens is: when CMake tries to compile my sources which depend on deps/cool-library/amqpcpp.h, it fails because it's not finding deps/cool-library/amqpcpp/*.h, only deps/cool-library/include.
Does anyone have any idea how i can fix this without having to bundle the library into my codebase?
This is not how CMake is supposed to work.
CMake usually builds an entire distributive package of a library once and then installs it to some prefix path. It is then accessible for every other build process on the system by saying "find_package()". This command finds the installed distibution, and all the libs, includes etc. automagically. Whatever weird stuff library implementers did, the resulting distros are more or less alike.
So, in this case you do a lot of unnecessary work by adding includes manually. As you see it can also be unreliable.
What you can do is:
to still have all the dependencies source distributions in submodules (usually people don't bother doing this though)
build and install each dependency package into another (.gitignored) folder within the project or outside by using their own CMakeLists.txt. Let's say with a custom build step in your CMakeLists.txt
use "find_package()" in your CMakeLists.txt when build your application
Two small addition to Drop's answer: If the library set up their install routines correctly, you can use find_package directly on the library's binary tree, skipping the install step. This is mostly useful when you make changes to both the library and the dependent project, as you don't have to run the INSTALL target everytime to make library changes available downstream.
Also, check out the ExternalProject module of CMake which is very convenient for having external dependencies being built automatically as part of your project. The general idea is that you still pull in the library's source as a submodule, but instead of using add_subdirectory to pull the source into your project, you use ExternalProject_Add to build it on its own and then just link against it from your project.

Configuring a CMake project with dependencies on multiple other CMAKE projects

I have a CMAKE project that depends on other projects built with CMAKE. These are : glfw, oglplus, portaudio etc.
How should I set up my project to work well in a cross platform fashion? What is the recommended way to go about it? I have been trying to read the CMAKE documentation but could only find examples to simple scenarios.
Just add the dependencies to your project README and expect the user stored them (already compiled) in system scope.
Add CMake options to request the path to dependency files.
Use add_subdirectory to chain your project with dependencies.

prevent code from compiling if an external project fails to build and install correctly

Is there a way to tell the CMake system that I don't want further compilation until the dependencies (external projects) are built and installed correctly?
Say a project depends on other libraries, which may or may not be present in the system. In case they're not, I use the ExternalProject_Add to download and install it. Yet, even though the configure part goes fine, building the external project doesn't happen until you type make. Now it doesn't make sense to compile the code if building the dependencies failed. I found in CMake ExternalProject_Add() and FindPackage() that maybe adding some dependencies would help, but that answer defines the dependency at configuration time, so it's not relevant to my situation.
Any ideas?
ExternalProject_Add will generate a custom target for building the dependency. If you don't want a target to be built in case building the dependency failed, add a dependency from that target to the external project's target.
ExternalProject_Add(my_external_lib [...])
add_executable(my_program [...])
add_dependencies(my_program my_external_lib)