Cmake building two projects - c++

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)

Related

How to get a VS solution with all the projects and solutions for single projects with CMake

TL;DR I basically want a modular solution of this question. Not only one solution with everything in it, but also a solution for each executable alone.
In my question here I wanted to know how I should split my CMakeLists.txt among different folders. Some folders contain code that will be a static library and some code will be executables or dynamic libraries that build on those static libraries.
Folder structure looks like this:
/path/to/base/
CMakeLists.txt
app1/
<src files for app1>
CMakeLists.txt
app2/
<src files for app2>
CMakeLists.txt
lib/
<src files for lib>
CMakeLists.txt
If I take the approach of having a CMakeLists.txt in the parent folder to all my libraries and executables and from the include all the directories I have the problem that when I generate VS projects/solutions that my projects for each target contains all the projects - not only the ones necessary for my intended target.
Contents of CMakeLists.txt in the base folder:
cmake_minimum_required(VERSION 3.1)
add_subdirectory(lib)
add_subdirectory(app1)
add_subdirectory(app2)
and the CMakeLists.txt in the app1 folder (app2 is equivalent)
cmake_minimum_required(VERSION 3.1)
project(app1)
add_executable(app1 <src of app1>)
target_link_libraries(app1 lib)
and CMakeLists.txt of lib:
add_library(lib <src of lib>)
If I run cmake on the CMakeLists.txt from base the solution contains all the projects. And even if I open just one of the projects; it also contains everything. VS project of app1 also builds app2 - which I don't want.
If I only run cmake on the CMakeLists.txt of app1 I get a solution which doesn't contain app2 but it also doesn't contain lib, because that's only mentioned for linking inside the cmake file and not as a target (it's in the base file)
CMake will create a solution for each call to project, which will then include all the targets of the current directory and its subdirectories (regardless whether they were defined before or after the actual call to project).
Here's what a well-behaved CMake script should do: Each CMakeLists that you want to be able to act as a root of its own sub-tree within the larger project should start with a cmake_minimum_required call, followed by the project call. Writing project in a CMakeLists basically means: This is a fully self-contained component that can be built independently, even if you delete all the files from its parent directories. As such, it makes sense to have a separate solution for each such project.
With this in mind we can see why this doesn't work in your case: Your apps are not fully self-contained, they depend on lib. The solution to this is to write the build scripts for the apps as if lib was provided as a third-party component, with find_package calls and everything.
In the compound build, you already have a target for lib, so the script invoked by the find_package call should be made to short-circuit to using that target and only actually go scavenging the system if it cannot find that existing target.
Also read up on how CMake's packaging system works, which provides some automation to handle such cases pretty elegantly.

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.

How can I use CMake to both build wxwidgets on-demand and link with it

I have the following situation:
I'm working on an application that depends on a number of third party libs, among them wxwidgets
I build the application for multiple target configurations (x86, arm, Linux, Windows) using Linux as my build host system
Due to the above mentioned multiple target configurations, I have chosen to build those third-party libs from source, using CMake's ExternalProject_Add function.
The third-party libs are built 'on-demand' at a location separate from my application's CMAKE_BINARY_DIR so that I can wipe the build tree for my application without having to rebuild the third-party libs (takes a looooong time).
The location of the third-party libs is different depending on what target configuration I build them for (obviously).
I'm quite new to CMake and the problem I currently face is this:
The source files in my application can't find the wx include files and I need to set the correct linker flags to be able to link my application against wxwidgets.
This seems to be handled by a utility 'wx-config' that provides exactly that info as output when run with either the --cppflags or --libs flag. I can not however, figure out how to catch that output and append it to the include dirs and linked libraries I setup from my CMakeLists.txt files.
So basically what I want is.
Build wxwidgets (if it doesn't exist) for the current target configuration
Run wx-config --cppflags and --libs to find out the correct include dirs and linker flags for the current target configuration
Use the info from step 2 when building targets that are my own application
So far I've tried something like this:
# Set a target-configuration-specific location
set(wxwidgetsTop ${MYPROJECT_EXTERNAL_DIR}/wxwidgets/wxwidgets_${MYPROJECT_CURRENT_TARGET_CFG})
# Build the project
ExternalProject_Add( wxWidgetsExternal
PREFIX ${wxwidgetsTop}
URL ${MYPROJECT_EXTERNAL_DIR}/tarballs/wxWidgets-3.0.2.tar.bz2
SOURCE_DIR ${wxwidgetsTop}/src/wxwidgets
CONFIGURE_COMMAND ${configure_cmdline}
BUILD_COMMAND make -j${MYPROJECT_NCPU}
INSTALL_COMMAND make install
)
# Create a wxwidgets target to be used as a dependency from other code
add_library(wxWidgets IMPORTED STATIC GLOBAL)
add_dependencies(wxWidgets wxWidgetsExternal)
# (non-working) attempt to get the correct include dirs and linker
# flags for wxwidgets
add_custom_command(TARGET wxWidgetsExternal
POST_BUILD
COMMAND ${INSTALL_DIR}/bin/wx-config ARGS --cppflags
COMMENT "Running wx-config"
)
but the above does not provide a way to actually use the result from the custom command to append the cppflags and linker options when building the targets that make up my application.
What is a good way to achieve what I want?
I see three different ways of doing this:
Method 1: use find_package
Use wxWidgets as a standalone requirement for your project, and expect the devs to install it before building your project. In your CMakeLists.txt you will need to call find_package(wxWidgets), like this:
find_package(wxWidgets COMPONENTS net gl core base)
if(wxWidgets_FOUND)
include(${wxWidgets_USE_FILE})
# and for each of your dependent executable/library targets:
target_link_libraries(<YourTarget> ${wxWidgets_LIBRARIES})
endif()
This has the advantage of not rebuilding the lib if you rebuild your project, however it requires some work for your user (they need to handle the installation of wxWidgets by hand) and for you (you need to setup include paths / compile definitions / ... by hand).
Method 2: embed wxWidgets
The second option is to bundle wxWidgets in your repo (svn external or git submodule) and usually (re)write the CMakeLists.txt of this lib to be target-oriented. Then, in your top-most CMakeLists.txt, you can do the following:
# for example, if you just need core and net:
target_link_librairies(my_app PUBLIC wxWidgetsCore wxWidgetsNet)
# No need to manually setup include dirs, etc...
To make a CMakeLists.txt target-oriented, you define include directories and other compilation properties for a target, not a directory. Example:
# When defining wxWidgetsCore, for example
add_library(wxWidgetsCore ...)
target_include_directories(wxWidgetsCore PUBLIC someDir)
target_compile_definitions(wxWidgetsCore PUBLIC -pedantic)
target_link_libraries(wxWidgetsCore PUBLIC someLib)
The drawback of this approach is that rebuilding your project will trigger a rebuild of wxWidgets. However, it is possible to trick this by not using "rebuild" but "clean just my app, then build". Here is some insight on how to achieve this.
Method 3: some sort of hybrid
The big drawback of method 2 leads to the third approach: don't put wxWidgets in your project, but create a CMakeLists.txt that will "import" the lib. The idea: you ask your user for the directory where wxWidgets is installed, then this script will setup everything for your project. First, put the CMakeLists.txt here:
/your-project-root
/thirdparty
/wxWidgets
CMakeLists.txt
/dir-where-wxwidgets-is-installed
...
Now, you define an imported target:
# When defining wxWidgetsCore, for example
set(WX_INCLUDE_DIR ${USER_SPECIFIED_WX_ROOT}/include)
add_library(wxWidgetsCore IMPORTED GLOBAL)
set_property(TARGET wxWidgetsCore APPEND PROPERTY
INTERFACE_INCLUDE_DIRECTORIES ${WX_INCLUDE_DIR})
See INTERFACE_INCLUDE_DIRECTORIES and INTERFACE_LINK_LIBRARIES. You need your user to have build wxWidgets somewhere in his system, but from your point of view you just do target_link_libraries(your_app PUBLIC wxWidgets...), as in method 2. The advantage is that this approach is interchangeable with method 2 transparently, and you don't put the whole dependency in your project.
Setting cppflags and linker flags has to be done at CMake time, but you are trying to run wx-config at build time and you are not capturing its output anyway, so your add_custom_command() isn't doing anything useful other than printing things to the build tool's output.
Ideally, you would use the FindwxWidgets module CMake already provides. It requires wxWidgets to already be built (but see further below). Have a look at the CMake documentation for it and see if that at least sounds like what you are trying to achieve manually by using wx-config. If you can get FindwxWidgets to do the job for you, that would be a much cleaner approach.
Getting something to build at configure time so you can use it later on in your CMakeLists.txt file is a bit more tricky. ExternalProject_Add() downloads and builds things at build time, but you need wxWidgets to be built earlier at configure time. I wrote an article recently for how to do at least the downloading part at configure time and you should be able to adapt it to do the whole build at configure time instead. The article uses Google Test as its example and can be found here:
https://crascit.com/2015/07/25/cmake-gtest/
It would be trivial to make it put the wxWidgets build wherever you like, not just in the CMAKE_BINARY_DIR area. That would allow you to have different wxWidgets builds for each build configuration and to be able to wipe out your application's build tree independently of the wxWidgets builds.
Hope that points you in the right direction.
The solution I use checks for wxWidgets installation in the system using find_package, if it's not found, then the script downloads wxWidgets from github and links the program against downloaded library. The lib is installed in the build directory, so only the first build is slow - subsequent builds do not even check wxWidgets sources timestamps, so the process is as fast as building using preinstalled wxWidgets library.
Here's how my script does it:
It quietly checks for wxWidgets installation using find_package(wxWidgets QUIET),
If it's found, the script adds a dummy library wxWidgets_external,
If it's not, then it creates an ExternalProject named wxWidgets_external which downloads, builds and installs the library in the build dir, setting wxWidgets_ROOT_DIR to point to the wxWidgets installation dir,
Then we add another ExternalProject pointing to a folder with the main program's source files and CMakeLists.txt build script. This external projects depends on wxWidgets_external which is either a dummy library in case wxWidgets is preinstalled in the system, or an external project set up to download the library from github,
In the aforementioned CMakeLists.txt we again call find_package, this time with REQUIRED parameter and use the library the standard way (https://docs.wxwidgets.org/trunk/overview_cmake.html). Because we set up the dependencies and variables correctly, this call will use either preinstalled wxWidgets (if it's available) or the one downloaded from github.
There are more quirks to it, but that's the gist of it. The full sample code (tested on Linux, Windows and Mac) is available on github (https://github.com/lszl84/wx_cmake_template).
Also see full blog post which explains this in more detail: https://justdevtutorials.medium.com/wxwidgets-cmake-multiplatform-superbuild-4ea86c4e6eda

CMAKE for modular c++ framework

I'm refactoring a large platform independent c++ framework so that it's libraries and execuables no longer have to be in the same directory (or even repository) and it is proving quite challenging. The framework was currently used only by me and it should now address our whole working group so I have to keep it as modular and as automatized as possible.
My basic structure looks like this
apps/
app1, app2, ...
libs/
core, lib1, lib2, ...
bin:
app1, app2, libcore, liblib1, liblib2, ... (on NIX)
app1.exe, app2.exe, core.dll, lib1.dll, lib2.dll, ... (on Windows)
Apps depend on libs, and all libs depend on the core lib. This all works fine in the same root directory and with the add_subdirectory mechanism.
Project dependencies were handeled by the order in which I was calling add_subdirectory: first libs (core being the first), then apps. Cmake was kind enough to set ${core_SOURCE_IR} to the respective directory and all binaries (libs and apps) were generated in the same directory.
What I need advice with is:
should I move to a find_package based approach (write for each lib and FindLib.cmake file)
how the apps and libs will find each other
where to put the binaries
how to pass along include directories of dependent targets
ExternalProject_Add ?
Thank you
I'd recommend using INSTALL(TARGETS... along with INSTALL(EXPORT...
For full details, run:
cmake --help-command INSTALL
FIND_PACKAGE is generally used to find an external project which is already installed (and which wasn't installed with CMake's INSTALL(EXPORT... command) and ExternalProject_Add is used to download, configure, build and install an external project.
If you use INSTALL(EXPORT... with each of your libs and exes, and then just INCLUDE the installed <target>.cmake in your main CMakeLists.txt, these will become available as proper CMake targets, with their dependencies per configuration already set.
As to where to install these export files - that's up to you, but as long as the path you specify is the same place you use when you INCLUDE the file later, it should work fine.
First, you can add_subdirectory()es in any order without caring about dependencies. CMake handles them when all CMakeLists.txt are parsed.
Second, if you wish any your lib or app to be buildable stand-alone, you certainly should use something like find_package(). It can be find_library() and find_file(lib.h) for simple cases.
In this case after find_package() invocation you will have LIB_INCLUDE_DIRS, LIB_LIBRARY_DIRS, LIB_LIBRARIES variables defined, which can be passed to include_directories(), link_directories() and target_link_libraries() respectively. That's how apps will find libs.
As for where to put binaries - in *NIX it's wide practice to place them into ${CMAKE_INSTALL_PREFIX}/bin.
Please comment on this answer, if there is something unclear for you.