Configuring a CMake project with dependencies on multiple other CMAKE projects - c++

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.

Related

Cmake building two projects

I want to build two projects using cmake (A library and sandbox application using that library).
I'm currently having the following folder structure:
-- yanthra_engine
|
-- CMakeLists.txt
-- lib
-- ...
-- sandbox
|
-- CMakeLists.txt
-- out
-- ...
The yantra_engine builds a library where as sandbox builds an executable(using the above mentioned library).
Should I keep full fledged CMakeLists files for both the projects? Is there any efficient folder structure to follow?
I would like the library to build automatically when building my sandbox application, but not vice-versa.
You should keep seperate CMakeLists.txt files. It's good practice to use one CMakeLists.txt file per unrelated target. (Unrelated in the sense of not being different builds of the same library, e.g. one shared one static.)
Whether to add both targets to the same project is basically up to you. If the library is properly set up, you could create a seperate project for the library and use it's install target to install the library including cmake configuration files on your machine making it easy to import the installed library as a target to the sandbox project. This requires you to add the appropriate install commands to the library.
If you want to be able to build both the library and the sandbox via the same build files, basically all you need to do is to make sure both CMakeLists.txt files are reachable from a CMakeLists.txt file.
Without location of any of the files you could e.g.
Create a CMakeLists.txt file in the parent folder adding both subdirectories
cmake_minimum_required(VERSION 3.0)
project(CommonProject)
add_subdirectory(yanthra_engine)
add_subdirectory(sandbox EXCLUDE_FROM_ALL) # targets in sandbox not built by default.
You could also include the yanthra_engine directory from sandbox/CMakeLists.txt
...
add_subdirectory(../yanthra_engine "${CMAKE_BINARY_DIR}/yanthra_engine_build")
...
Both approaches allow you to either set up a build project for the lib on its own or set up a build project for both. In approach 1 the source directory for building both would be the parent directory of yanthra_engine and sandbox and in approach 2 it would be sandbox.
For both approaches you don't need to wory about unnecessarily building the sandbox project though as long as you specify the target you want to build. Since sandbox links the lib but there is no dependnecy established the other way round building sandbox makes sure the lib is up to date, but building the lib only builds the lib and its dependencies which excludes the sandbox.
One thing you can try is to let yanthra be a subdirectory of sandbox. Then you can do this in sandbox:
add_subdirectory(yanthra_engine)

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.

How do I write system-independent code when there are paths involved?

Say I am creating a project that uses a certain library and I have to provide the path for that library while linking. In the command line or makefile I might have:
g++ ... -L/path/to/mylibrary
I'm also going to send this project to someone else who wants to use it. The path on their system might not necessarily be the same as mine. They could be using a different file path all together.
How do I make sure that the path to the library works for both my computer and the recipient of my project?
This is the role of a build system or build configuration tool. There are many of those around. The main one is probably CMake as it has a very extensive feature set, cross-platform, and widely adopted. There are others like Boost.Jam, autoconf, and others.
The way that these tools will be used is that they have automated scripts for looking into the file-system and finding the headers or libraries that you need, i.e., the dependencies required to compile your code. They can also be used to do all sorts of other fancy things, like checking what features the OS supports and reconfiguring the build as a consequence of that. But the point is, you don't hard-code any file-paths into the build configuration, everything is either relative to your source folder or it is found automatically by the build script.
Here is an example CMake file for a project that uses Boost:
cmake_minimum_required (VERSION 2.8)
project (ExampleWithBoost)
find_package(Boost 1.46 COMPONENTS thread program_options filesystem REQUIRED)
# Add the boost directory to the include paths:
include_directories(SYSTEM ${Boost_INCLUDE_DIR})
# Add the boost library directory to the link paths:
link_directories(${Boost_LIBRARY_DIRS})
# Add an executable target (for compilation):
add_executable(example_with_boost example_with_boost.cpp)
# Add boost libraries to the linking on the target:
target_link_libraries(example_with_boost ${Boost_LIBRARIES})
The find_package cmake function is simply a special script (specialized for Boost, and installed with CMake) that finds the latest version of boost (with some minimal version) installed on the system, and it does so based on the file-name patterns that the library uses. You can also write your own equivalents of find_package, or even your own package finders, using the functions that CMake provides for searching the file system for certain file-name patterns (e.g., regular expressions).
As you see, the build configuration file above only refer directly to your source files, like "example_with_boost.cpp", and it's only relative to the source folder. If you do things right, the configuration scripts will work on virtually any system and any OS that CMake supports (and that the libraries you depend on support). This is how most major cross-platform projects work, and when you understand how to work with these systems, it's very powerful and very easy to use (in general, far easier to use and trouble-free than build configurations that you do by point-and-click within IDE menus like in Visual Studio).
You can use premake that generates cross platform makefiles: Visual Studio, Gcc and others
http://industriousone.com/what-premake
CMake is another alternative
http://www.cmake.org/
I'm not sure if there's a single universal way of doing this, but people often provide different config files and let the main Makefile detect which one to include: linux.make, darwin.make, cygwin.make etc.
there are also various tools like CMake that allow to automate this, but all in all it's just scripting that hides the system-dependency from the developer.

Generate Find Cmake automatically and install it for importing

Is there any way to create a FindXXX.cmake automatically where XXX is my Cmake project? I see many projects that they created their FindXXX.cmake manually but I believe it's possible to create it automatically.
And, where I should install my project on Linux?
Thanks!
Take a look at CMake's project config file mechanism (along with the CMakePackageConfigHelper module; you might also want to take a look at this wiki page).
Find scripts are most useful for locating dependencies that are not aware of CMake themselves. If on the other hand the dependency was also built using CMake, you can let CMake auto-generate a project config file for you as part of that project's build process. This config file will allow you to refer to the targets of that project from an enclosing project as if they were being built as part of the enclosing project's CMake run. This is even more powerful than using find scripts, as it allows for example distinct handling of configurations beyond the debug/optimized options available to traditional find scripts.
On Windows, projects generating config files this way will register themselves with CMake, so that depending projects building on the same machine can find them automatically without any additional configuration. If you are building on non-Windows platforms (or you are building the two libraries on different machines) you will have to place the config file in a default directory (the docs for find_package describe which directories are searched) or explicitly point CMake to the location using CMAKE_MODULE_PATH.
Modern CMake-aware libraries should always prefer this approach over traditional find scripts. A prominent example of a library that does this already is Qt5.
CMake supports templating with configure_file() command.
Standard dirs where CMake searches for FindXXX.cmake modules are listed in the documentation of find_package() command.

How to Integrate a CMake Project with GYP-Based Libraries

I've been building cross-platform applications using C++ and CMake. Unfortunately many libraries do not include CMake project descriptions. For example Google supported projects frequently use GYP.
Here are some of the approaches I've taken for this problem:
Find a CMake project description for the library on the web
Write a project description using CMake
Finding or writing CMakeLists.txt descriptions for libraries is a maintenance problem. As the library is updated, so too must my CMake description. Also the project files created by the library's authors are more likely to be correct.
How specifically should I integrate GYP projects into my CMake-based software? Is duplicating the project description in CMake the best approach, or is there some feature of CMake that I am missing?
What are some of the general approaches to dealing with this class of problems in CMake? What about libraries that include a set of project files for various platforms/IDEs? Does CMake provide a way to integrate these projects into mine?
Use the ExternalProject module
http://www.cmake.org/cmake/help/v3.0/module/ExternalProject.html
Not sure if the output is usable for your purposes but gyp can generate Cmake projects gyp -f cmake mygypfile.gyp. Your CMakeLists.txt will be in out/[Configuration] (configuration can be Debug, Release, etc)