Building third party library as part of CMake project - cmake

I have a top level CMakeLists.txt file which builds a library called Camellia and several executables in subdirectories which link with Camellia. I would like to add a third party library called XDMF as a dependency for Camellia under Camellia/TPL/Xdmf. Luckily, XDMF is already on a CMake build system, but when I use
add_subdirectory(TPL/Xdmf)
and then
add_library(Camellia ${LIB_SOURCES} ${HEADERS})
it builds Camellia in the build/TPL/Xdmf/bin directory rather than build as it does without the add_subdirectory(TPL/Xdmf). Why does adding a subdirectory change the build directory for Camellia, and how do I fix it? Also how do I make sure Camellia is linking with Xdmf once I get that figured out?

I think that ExternalProject_Add(...) is what you want.
Please see CMake: How to build external projects and include their targets which describes a similar issue.

Related

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?

CMake package configuration files for upstream projects using Qt5 problems

I am working on a larger C++ library that is using CMake and depends on Qt.
We moved from Qt4 to Qt5 and now I encounter a problem when using our lib
in an upstream project. As a minimal working example demonstrating the problem please have a look at this repo:
https://github.com/philthiel/cmake_qt5_upstream
It contains two separate CMake projects:
MyLIB: a tiny library that uses QString from Qt5::Core.
It generates and installs package configuration files
MyLIBConfig.cmake, MyLIBConfigVersion.cmake, and MyLIBTargets.cmake
in order to be searchable by CMake find_package()
MyAPP: a tiny executable depending on MyLIB
The project uses find_package(MyLIB) and creates an executable that uses MyLIB
The problem is that CMake gives me the following error message when configuring the MyAPP project:
CMake Error at CMakeLists.txt:11 (add_executable):
Target "MyAPP" links to target "Qt5::Core" but the target was not found.
Perhaps a find_package() call is missing for an IMPORTED target, or an
ALIAS target is missing?
The reason for this behaviour is that in the automatically generated MyLIBTargets.cmake file the INTERFACE_LINK_LIBRARIES entry for Qt5 Core is the Qt5::Core symbol. Using Qt4, the absolute path to the Qt core lib was specified here.
Now, I simply can resolve this by using
find_package(Qt5Core 5.X REQUIRED)
in the MyAPP project.
However, I would like to know if this is the intended/generic way to go, i.e. requesting upstream projects of our lib to search for the required transitive Qt5 dependencies themselves, or if I probably misuse CMake here and need to change my configuration procedure?
The CMake docu on package file generation
https://cmake.org/cmake/help/v3.0/manual/cmake-packages.7.html
mentions that macros can be provided by the package configuration files to upstream. Maybe this would be the correct place to search for imported targets like Qt5 and break upstream configuration runs when these dependencies are not found?
Best,
Philipp
[edit of the edit] Full Source Example
You need to deliver a CMake config file for your project, and probably the ConfigFile should be generated via CMake itself (because you cannot know for shure where the user will install your software).
Tip, use the ECM cmake modules to ease the creation of that:
find_package(ECM REQUIRED NO_MODULE)
include(CMakePackageConfigHelpers)
ecm_setup_version(${PROJECT_VERSION}
VARIABLE_PREFIX ATCORE
VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/atcore_version.h"
PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KF5AtCoreConfigVersion.cmake"
SOVERSION 1
)
configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/KF5AtCoreConfig.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/KF5AtCoreConfig.cmake"
INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR}
)
and the KF5AtCoreConfig.cmake.in:
#PACKAGE_INIT#
find_dependency(Qt5Widgets "#REQUIRED_QT_VERSION#")
find_dependency(Qt5SerialPort "#REQUIRED_QT_VERSION#")
find_dependency(KF5Solid "#KF5_DEP_VERSION#")
include("${CMAKE_CURRENT_LIST_DIR}/KF5AtCoreTargets.cmake")
This will generate the correct FindYourSortware.cmake with all your dependencies.
[edit] Better explanation on what's going on.
If you are providing a library that will use Qt, and that would also need to find the Qt5 library before compilling the user's source, you need to provide yourself a FindYourLibrary.cmake code, that would call
find_package(Qt5 REQUIRED COMPONENTS Core Gui Widgets Whatever)
Now, if it's your executable that needs to be linked, use the Components instead of the way you are doing it now.
find_package(Qt5 REQUIRED COMPONENTS Core)
then you link your library with
target_link_libraries(YourTarget Qt5::Core)

How to include external libraries in CMakeLists?

I have been trying to build my project with CMakeLists for a couple of days now with some success but I'm not sure on what it is exactly that I am doing.
I am currently using 5 external libraries. (Assimp, glew, glfw, glm, DevIL)
And my main platform is windows but platform independence is a big plus.
So the question I have is how to proceed when I want to include an external library to my project?
This is what I think I know so far.
(1) If you are "lucky" you can just use find_library and link it with target_link_libraries you are good to go.
(2) Since glm is a header only library, all I need is an include_directories.
(3) If the external library has its own CMakeLists, you do add_subdirectory and set include_directories to where the header files are.
This is what I know I don't understand.
(4) If the external library has .h files and .cpp files but no CMakeLists. How do I include (and build?) this library?
(5) (This is the most important question for me!) If the external library has .h files and either .lib or .dll files, how do I include this library? In both cases! (lib/dll)
Thanks in advance for any and all replies!
Best regards
Edvin
If you want to do the quick-and-dirty way you can just
create a super-repo with all your dependencies (or alternatively, write a script that downloads or clones them)
write a shell script that builds all the source-only dependencies
hardcode the include dirs, libraries and compile flags of the dependencies into the CMakeLists.txt of you main project (well, relative to super-repo root)
build the dependencies before you configure your main project
If a dependency has no CMakeLists.txt or any other build script suitable for your system, you need to find or write one.
If you want to do it elegantly and reusable you need to study the topics of CMake config-modules. For a quick introduction please check out this Stackoverflow answer: How to use CMake to find and link to a library using install-export and find_package?
You will need to write config-modules for the dependencies (or find a fork which has it). Your main project will contain only a list of find_package commands and you list your dependencies with the target_link_libraries command and all other details will be taken care by the config modules.
Generally you don't need to include third-party projects directly into your main project's CMakeLists (add_subdirectory or ExternalProject_add) since you will not be working on them so they will not change. There's no need to clutter your IDE workspace with them. It's best to download/clone/build/install them with a separate script before you configure your main project.

linking failed when building my own project using LLVM

I'm learning to build a llvm project, this is the reference: http://llvm.org/docs/Projects.html. I use the "llvm/projects/sample" directory as the primary project skeleton, and it works. Then I want to build tools from "llvm/examples" to my project, such as Fibonacci, it can't work. I do it this way: first copy the "llvm/examples/Fabonacci" directory to "MyProj/tools" ("MyProj" is top level of my project) and change Makefile to contain Fabonacci target, then configure & make. but the Fabonacci tool seems can't be built. It depends on some libs when linking. So what can I do if I want to build the source code from "llvm/projects/example" in my project?
You need to provide LLVM libraries to linker when building your own project. This means adding some flags, library directories and libraries themselves to link command. Build script probably needs some editing.
llvm-config tool can be used for providing necessary options to compiler/linker. Check documentation and examples.

Having trouble getting CMake to work with third party libraries

I'm trying to make a small game using both SFML and Box2D. I have the following directory structure:
/
src/
game/ # my code
thirdparty/ # other libraries' code
box2d/
sfml/
bin/
etc...
I'm trying to set it up so that I can run make and have box2d or sfml compile as well if they need, since I might make some changes to the libraries.
I've tried putting this in my CMkaeLists.txt:
find_package(Box2D)
find_package(sfml-window)
find_package(sfml-graphics)
find_package(sfml-system)
as well as other things, but I keep getting errors and I'm not sure how to get around them. for example:
CMake Error at CMakeLists.txt:20 (find_package):
Could not find module Findsfml-window.cmake or a configuration file for
package sfml-window.
Adjust CMAKE_MODULE_PATH to find Findsfml-window.cmake or set
sfml-window_DIR to the directory containing a CMake configuration file for
sfml-window. The file will have one of the following names:
sfml-windowConfig.cmake
sfml-window-config.cmake
But I can't find any of the files it lists there.
The find_pacakge command is for finding packages that are defined in for cmake as modules or configurations. There is probably not a cmake module or config defined for these libraries. So, if you want to use the find package command to find these libraries then you will need to create a cmake module that knows how to find them. Given your stated requirements I would not think that this is easiest way to do it.
If you are statically linking you libraries then set up a custom target to invoke make on each of the libraries. Add the include directories to your include path. Use find_library command to find the libraries.
If you intend to dynamically link your libraries then create a custom target to build and install your libraries and you should be good as long as you install them in one of the normal places.
Have a gander here:
http://www.itk.org/Wiki/CMake:How_To_Find_Libraries Writing find modules
Take a look at the "Writing find modules" section. Be sure to read the document all the way through.
If you want to make redistributable and portable cmake projects, I think this is the right direction for you to go.