Setting up 3rd party libraries environment for CMake on Windows 10 - c++

I'm having problems with the "proper" way of installation third-party libraries for use with CMake projects (C++). For most of the time I haven't been bothering myself with this stuff and just hardcoding library paths in my CMakeLists.txt, but I really want to start using this properly.
I ultimately want to have something where I can just type, for example, find_package(SDL2 REQUIRED) in a project and the library is there, found, and works perfectly.
What I have right now is I have a folder C:\CMakeLibs where I --install all 3rd party libraries, so for example there is C:\CMakeLibs\SDL2 and in it, there are folders like cmake, include, lib, etc. Then I include the folder C:\CMakeLibs\SDL2 in CMAKE_PREFIX_PATH in system environment variables, so that CMake can find the SDL2Config.cmake file and there's no need for any FindSDL2.cmake. This way I can just type following code and it straight works, and is also probably the most portable for open-source projects.
find_package(SDL2 REQUIRED)
add_executable(app main.cpp)
target_link_libraries(app PRIVATE SDL2::SDL2main SDL2::SDL2-static)
My only concern here is that I have to manually add any new libraries to CMAKE_PREFIX_PATH. I'd like just have only C:\CMakeLibs as my CMAKE_PREFIX_PATH and everything working fine. I could technically --install the libraries directly to C:\CMakeLibs but I fear of any possible incompatibilities when having all the different files in one shared folders, it doesn't feel like the best solutions.
I know this question might be one of those "without the best answers/based on opinion". I've been looking for solutions on internet for long time already, ever since I started using CMake, so I decided to just write this post here. I hope I described my problem clearly, English isn't my native language.

Related

Adding include paths and lib files in CMake

Recently, I have begun wanting to learn OpenGl, and in the tutorial they are using a static GLFW library to make the code concise and cross platform, but I use VS code, and they use Visual Studio. I use CMake to link my libraries so how would I do that with a lib file and the .h files respectively?
I thought you could achieve this as so
cmake_minimum_required(VERSION 3.0.0)
project(openGL VERSION 1.0.0)
include_directories(${workspaceFolder}dev\dependencies\GLFW\include)
link_directories(${workspaceFolder}dev\dependencies\GLFW\lib-vc2019)
add_executable(main main.cpp)
target_link_libraries(${workspaceFolder}dev\dependencies\GLFW\include)
but this does not seem to work
If anyone knows how I could complete this, please show me. I have respect for all of you. I am quite a young programmer, but I want to be able to do big things, and I would like to start by working on nice graphical interfaces.

CMakeLists C++ Beginner

I've started playing a little bit with C++ and to make it happen I decided to write a simple game engine.
For this purpose, I'm using CLion as my IDE and it works all good but adding libraries is just a nightmare. First I've installed all required libraries like glew, glfw or glm using brew, all went fine. Then I spent almost 2 hours to get it to work on my project.
My biggest mystery is the reason why it works, I've worked with build systems in java, python or golang and everything was always clear to me. However, I have no idea why it works the way it works and I'd love to know!
Here is my CMakeLists file.
cmake_minimum_required(VERSION 3.10)
project(untitled2)
find_package(GLEW REQUIRED)
find_package(GLFW3 REQUIRED)
set(CMAKE_CXX_STANDARD 17)
add_executable(untitled2 main.cpp)
target_link_libraries(untitled2 ${GLEW_LIBRARIES})
target_link_libraries(untitled2 glfw)
Now I have a few questions:
1. Why am I able to use GLM library without including it in the CMakeLists?
2. Why do I need to include glfw and glew but not glm?
3. Why do I need to use ${GLEW_LIBRARIES} and not some name like glew? (I tried different names, but nothing worked.)
btw. I'm using macOS.
The first thing to remember is that C++ doesn't (yet) have a real module system like newer languages. It just has a list of directories that it searches for header files, a list of directories that it searches for libraries, and a list of libraries to search for symbols when linking. The target_link_libraries directive just adds compiler flags that add to those three lists.
Now, on to this specific scenario. Most of the magic happens in the find_package directive. That directive really just ends up running cmake scripts. Sometimes those are packaged with cmake, sometimes they're installed along with the package you're finding. In the end, those scripts can do basically whatever they want. They all have the same objective, to give you a way to add the appropriate compiler flags to use the package, but there are a couple common ways they do that.
The older way is to set variables that you can use to tell the compiler what directories to search for headers and libraries and what libraries to link to. That's the approach GLEW seems to have taken. It sets the variables GLEW_LIBRARIES and GLEW_INCLUDE_DIRS and you then have to use link_libraries and include_directories to tell the compiler what to do. This was the only approach available in older versions of cmake (pre 2.8 IIRC), so while it's not as nice to use, it is still how many libraries' find_package scripts work.
The newer way is to create imported targets. Those targets have appropriate properties set so that any targets that link to the imported target inherit the appropriate include directories and library flags. This is the approach GLFW took. It creates an imported target named glfw that has the INTERFACE_INCLUDE_DIRECTORIES and INTERFACE_LINK_LIBRARIES properties set. When you pass that to target_link_libraries, your untitled2 target will inherit those include directories and libraries.
Finally, GLM is a header-only library. There are no library files to link to, so as long as the appropriate directory is added to the compilers header search path you'll be able to include and use GLM.
Since you used homebrew to install your libraries, all of their headers are likely under the same base directory; most likely "/usr/local/include". All of their library files are similarly likely under the same directory; probably something "/usr/local/lib". That means that your compiler will be able to find any of their headers and libraries is you tell it to search "/usr/local/include" for headers and "/usr/local/lib" for libraries.
So, to finally answer the question: Thing's work because the glfw target told cmake that it should set the compiler flags to add "/usr/local/include" to its list of include directories. Since that's the same directory it needs to search for GLM and GLEW, the compiler is able to find the headers for all of your libraries. The compiler is also able to find the library files it need to link to because cmake told it to look for them explicitly via the list GLEW_LIBRARIES and the inherited properties from the glfw target. GLM doesn't have any library files to link to, so there's nothing to tell it about.
You really shouldn't rely on everything being in the same place though. You should be able to tell the compiler about everything like this (note that I haven't actually tested this):
cmake_minimum_required(VERSION 3.10)
project(untitled2)
set(CMAKE_CXX_STANDARD 17)
add_executable(untitled2 main.cpp)
# This will fill the variables GLEW_INCLUDE_DIRES and GLEW_LIBRARIES
# that you can use to add the appropriate compiler flags
find_package(GLEW REQUIRED)
# This will create an imported target named glfw that you can link to
# to inherit the appropriate include directories and libraries
find_package(GLFW3 REQUIRED)
# This also creates an imported target named glm that you can "link to"
# to inherit the appropriate include directories
find_package(glm REQUIRED)
# GLEW uses an old-style find_package script, so you have to
# explicitly tell cmake about GLEW's include directories
target_include_directories(untitled2 PUBLIC ${GLEW_INCLUDE_DIRS})
# And the library files to link to
target_link_libraries(untitled ${GLEW_LIBRARIES})
# cmake will automatically add the appropriate include directories
# and library files that the imported glfw target tells it about
target_link_libraries(untitled2 glfw)
# You use the target_link_libraries directive with the glm imported target
# even though you're not actually linking to any libraries. It's just how
# you tell cmake you want your untitled2 target to inherit the appropriate
# include directories from the imported glm target
target_link_libraries(untitled2 glm)

Convert from Xcode to CMake

I have a C++ project that I initially developed using Xcode on my Mac and I would like to set up the very same project using CMake. The project makes use of some external libraries, including eigen and boost. Xcode has a rather long list of Build Settings, including paths to external libraries, specification of the compiler version, additional linker flags, compiler optimization level, etc... and I am wondering where all of this information goes in the CMakeLists.txt file. I've searched extensively for help on this but have found precious little. I am new to CMake and have never written make files before. If there were a utility that could convert my Xcode project into a CMake project, that would be ideal. But I would be very glad to know of a tutorial on this, or to have some specific guidance. Any help on this would be greatly appreciated.
Conversion Strategy
I am pretty sure that the easiest and fastest way to move to CMake is a mixture of looking at comparable projects that link the same dependencies, and maybe copying a few relative file paths to your source files from XCode.
A well-written CMakeLists.txt that uses Eigen and Boost can be very small (and platform-independent).
One reason for this is CMake has a very different way to use dependencies.
For well-known libraries such as Boost, CMake includes scripts to include headers and link libraries.
Minimal CMakeLists.txt
A minimal example may look like this:
find_package(Eigen3 REQUIRED)
find_package(Boost REQUIRED COMPONENTS system)
add_executable(
app
src/main.cpp
)
target_include_directories(
app
${Eigen3_INCLUDE_DIRS}
${Boost_INCLUDE_DIR}
)
target_link_libraries(
app
${Boost_LIBRARIES}
)
Example Projects
For Eigen, there is no such pre-installed script that can be called by find_package.
Here are two real-world example projects that link Eigen and Boost: https://github.com/ompl/ompl/blob/master/CMakeLists.txt
https://github.com/roboticslibrary/rl
Here is an example for a find script for Eigen.
As a piece of advice, some CMake projects are out there are convoluted and use old syntax, newer project files tend to use small letters in commands.
If it happens that directly target linking libraries doesn't work, you'll have to write your own Find*.cmake files by yourself.
Here's a spec of what you should cover to make it (this is just one approach):
Imagine you have a library XYZ:
Your file
FindXYZ.cmake should have as a result a set of variables to use:
XYZ_LIBRARIES
XYZ_INCLUDE_DIR
For example:
set(XYZ_LIBRARIES ${SOME_PATH_TO_LIBRARY1} ${SOME_PATH_TO_LIBRARY2})
find_path(XYZ_INCLUDE_DIR NAMES xyz/xyz.h HINTS ${PLACE_WHERE_INCLUDE_SHOULD_BE}
PATH_SUFFIXES include
)
From your main cmake maybe you 'use' it:
find_package(XYZ REQUIRED)
# Now we use both _INCLUDE_DIR and _LIBRARIES
include_directories(SYSTEM "${XYZ_INCLUDE_DIR}")
# TARGET_NAME is the current target, the 'client' of XYZ
# append XYZ to our list of libraries to link
target_link_libraries(${TARGET_NAME} ${XYZ_LIBRARIES})
I'm surely missing details but that's the general idea. Here are some cmake scripts that I know work in a big project:
Example with OpenSSL
Find:
https://github.com/highfidelity/hifi/blob/Android/cmake/modules/FindOpenSSL.cmake
Client (in this case is a library that indeed needs OpenSSL):
https://github.com/highfidelity/hifi/blob/Android/domain-server/CMakeLists.txt

Using CMake to find dependencies in an application-specific subfolder

In spite of many years of coding large-scale C++ applications, I do not understand how find_package is supposed to work in a medium-size CMake project, ASSUMING that I want to build the source to dependent packages myself and not simply rely on large systems like opencv, pcl or boost being installed somewhere in a system folder. I can't can't believe that I'm the only person in the world who has shipped multiple OpenCV and other open-source apps, has worked with meta-build systems like NAnt and SCons on major game projects, yet can't understand the most basic things about how CMake works or find a tutorial answering these questions.
In the past, I have essentially hacked around not understaning find_package by setting all the foo_DIR values by hand as CMake complains until I get a working folder.
I would like to run through a simple example which I'm working on right now, and dearly hope someone can explain what I'm doing so wrong.
Firstly, some assumptions:
I want to build everything for both MacOS and Windows, ideally via CMakeGUI. MacOS should build XCodeProjects and Windows should build Visual Studio Solutions.
Where there are dependencies, I want to compile them myself, so I have debug symbols and can modify the dependency source (or at least debug into it.)
No installation of pre-built binaries into system folders, i.e. no use of sudo port install opencv/pcl, etc on mac.
I have multiple projects, and prefer to keep a project and its dependencies in a single folder.
For the purposes of a concrete example, suppose I am building this project, although it's an arbitrary choice to illustrate the process and confusion I suffer:
https://github.com/krips89/opendetection
This lists dependencies, which I have intentionally reordered here so that I can take them in order, as follows:
find_package(OpenCV REQUIRED)
find_package(Eigen REQUIRED)
find_package(Boost 1.40 COMPONENTS program_options REQUIRED )
find_package(PCL REQUIRED)
find_package(VTK REQUIRED)
I would like to have all of these dependencies downloaded and configured in a single path (let's say c:\src on Windows, and ~\src on Mac for simplicity), NOT in a system path. Assume that the actual folder is a sub-folder for this project, and no a sub-folder for all projects. This should also allow for side-by-side installation of multiple projects on the same computer.
Taking this one step at a time:
(1) I clone openCV from https://github.com/opencv/opencv, sync to tag 3.1, configure into the folder opencv_build folder, build and install into opencv_install. I've done this so many times it's pretty straightforward.
(2) As above, but for eigen (although building for eigen doesn't actually do anything s it's a template library. I install to a folder eigen_install
Taking directory shows a series of folders for downloaded dependencies. I have assumed a convention where , and are source repos, and their following _build folders are the "WHere to build the binaries" folders in CMakeGui.
$ ls
boost_1_40_0 opencv opendetection_build
eigen opencv-build opendetection_data
eigen_build opencv_contrib pcl
eigen_install opendetection
All good so far, now let's try to configure opendetection and generate a solution into opendetection_build, and find pendetection's dependencies from within the ~/src folder, that is for the first two dependencies, I hope to find opencv and eigen in the opencv-build and eigen-build folders.
OpenCV immediately fails, as expected, saying:
Could not find a package configuration file provided by "OpenCV" with any of the following names:
OpenCVConfig.cmake
opencv-config.cmake
Add the installation prefix of "OpenCV" to CMAKE_PREFIX_PATH or set "OpenCV_DIR" to a directory containing one of the above files. If "OpenCV" provides a separate development package or SDK, be sure it has been installed.
That's good, because I want to explicitly tell CMake to look for dependent packages under my ~/src folder. Question: Is the use of CMAKE_PREFIX_PATH=/users/foo/src the recommended way to accomplish what I want - looking for all sub-packages under a specific path?
Following this, CMake finds OpenCV (good), and sets OpenCV_DIR = /Users/foo/src/opencv-build.
Question: Given that I have made an "install" to opencv-install (using CMAKE_INSTALL_PREFIX and building the Install Target Of OpenCV, shouldn't it find OpenCV in the opencv-install folder not opencv-build?
Moving on to eigen, I have configured and built eigen, and installed it to ~/src/eigen-install, which since it is a subfolder of CMAKE_PREFIX_PATH (~/src) I might expect to be found. But it doesn't seem to be. Can somebody explain to me what I'm not understanding? Particularly given that Eigen in a template library, and that there are at least three folders (eigen, eigen_build and eigen_install) under CMAKE_PREFIX_PATH which I would have thought CMake would find something in, I assume I must be doing something wrong here. I KNOW from past experience, I can set EIGEN_INCLUDE_DIR by hand in CMakeGUI by hand, and continue hacking forth, but that just seems wrong.
I'm more than willing to write up a web page explaining this for future people as dumb as me if one does not already exist, although I can't understand how use of CMake for basic project configuration and generation is apparently so obvious to everyone but so opaque for me. I have actually been using CMake for some years, usually by just manually setting Boost_INCLUDE_Dir, Foo_INCLUDE_PATH etc manually, but clearly this is not the right solution. Generally, after spending a couple of days fighting through the various packages to generate a solution by manually setting INCLUDE PATHS, LIBRARY PATHS and other options, I just deal with the solution and don't touch CMake again. But I would love to understand what I'm missing about find_package for my (surely not uncommon) use case of wanting to control my project dependencies rather than just using sudo port install * and installing random versions of projects to my global system folders.
As error message says, CMAKE_PREFIX_PATH should be set to installation prefix of the package. E.g., if the package has been built using CMake, this is CMAKE_INSTALL_PREFIX variable's value, if the package has been build using Autotools, this is value of --prefix option used for configure it, and so on.
CMake doesn't search every directory under CMAKE_PREFIX_PATH. That is why specifying it as /users/foo/src is useless if you have the package installed at /users/foo/src/eigen-install.
Instead, you may install all 3d-party packages into /users/foo/src/install, and use that path as CMAKE_PREFIX_PATH in your main project.

How to easily include headers from library dependency in cmake circa v2.8.8

I'm experimenting with CMake a bit for a C++ repository, but I'm running into some trouble trying to make it easy to build applications against libraries in the same source tree without a lot of extra CMake code.
The layout of the source tree is basically the following:
ROOT
libs/
lib1/
lib2/
lib3/
apps/
app1/
app2/
app3/
The libraries are independent of one another, and the applications may link against one or more of the libraries.
Currently I have a root CMakeLists.txt that lists out each application and library as a subdirectory so that if the library is changed and the application is rebuilt, so is the library. This works fine and CMake links it in without me having to specify where the library lives, but I don't see a way to do something similar for include directories.
Is there a common way to handle this? I'd prefer not to have each application's CMakeLists.txt have to manually list out the path to the libraries it needs.
If you are not afraid of making more headers available than you actually need for each application, you could list all lib directories in an INCLUDE_DIRECTORIES statement e.g. in the CMakeListst.txt adding all application sublists. But there is no such concept of managing "belonging" include folders per target built-in.
This question is pretty old. It was asked on 2012-07-18. I'm adding an answer here to attempt to explain some history. Warning: I may have gotten it wrong or misunderstood the history.
At that time, the latest non-release-candidate relase was CMake v2.8.8 (released on 2012-04-18). The INCLUDE_DIRECTORIES target property already existed in v2.8.8, but there was no corresponding INTERFACE_INCLUDE_DIRECTORIES target property yet. Its target_link_libraries documentation didn't say anything about a target automatically adding the include directories of the targets it depended upon.
About ten months after this question was asked (2012-07-18), CMake v.2.8.11 was released. It added the target_include_directories command signature with the INTERFACE|PUBLIC|PRIVATE argument, and added the INTERFACE_INCLUDE_DIRECTORIES target property.
So you can create a target B, specify public/interface include directories for it, and then make a target A depend on it via target_linK_libraries, and then A will automatically add/use the include directories of B. That can be made transitive by making the link public/interface, or non-transitive by making the link private.
You can find Kitware's list of software releases here, and a list of the documentation revisions here.