CMake and Config/Modules find_package - c++

I'm developing a lib and now trying to make it usable through cmake find_package keyword.
Using the config mode works fine.
I've put the CPackConfig.cmake generated by cmake inside the installation folder of my lib by naming it log++Config.cmake and it can be found in my project. Fine.
But this method is not satisfying. I need to define my own variables such as lib dependencies needed to link and some custom macros, and didn't found any way to embed this inside the CPackConfig.cmake.
So now I'm trying to use the module mode of find_package. My concern is that I can't find any way to have cmake use my Findlog++.cmake without putting it inside cmake installation folders (which is awfully ugly).
I've found some posts telling to just put this file inside my project root but it's almost as ugly as modifying cmake itself.
Is there a way to have my Findlog++.cmake file somewhere inside my lib folder and have cmake finding it without setting the CMAKE_MODULE_PATH variable?

You seem to be confused between CPack and 'config file packages'. First, understand that they are separate and different. Then read
http://www.cmake.org/cmake/help/v3.0/manual/cmake-packages.7.html#creating-packages
http://www.cmake.org/cmake/help/v3.0/manual/cmake-buildsystem.7.html
to learn about usage requirements and inclusion of macros etc.

Related

How to idiomatically add external library dependencies that use git and CMake to a git and CMake project?

I would like to know how to add external libraries into my project. Is there a standard way of doing so?
The way I do it and that I don't like is:
Have a folder called vendors where I add submodules e.g. boost, openssl...
I build the external libraries (as they come with a cmake to build in general).
I add a premake (I could have used a cmake) to each external library and I configure so I can see the project in VS as well as the cpp and the hpp files.
I don't like this because I do copy the binaries of the external libraries manually, hence if I delete the bin folder I can't build my solution just by clicking build but I have to build the external libraries first using there cmake and then I copy the binaries manually to the bin/ folder of my solution.
Could you please give me a "standard" way I can do this? I feel like there could be better ways by just using to the max the CMake that comes with the external library. Also, I don't like changing the external libs too much, I just want to be able to update them anytime and everything works without me touching stuff.
How can this be done?
One approach is to use CMake's FetchContent functionality.
The FetchContent module allows specifying Git repositories of CMake projects to fetch at configure time. The default setting assumes that that repository has a CMakeLists.txt file at the repo's root directory. It clones the repo to a default (but configurable) location, and then just calls add_subdirectory() on the cloned directory.
You can read about how to use it in the reference documentation, and you can read about how that approach compares with some other CMake-supported approaches for using dependencies in the official Using Dependencies Guide. Do brace yourself when reading the reference docs, though. They're not designed to be like a beginner-friendly tutorial, and since FetchContent is built upon another module called ExternalProject, some of the docs for FetchContent just point you to go read sections from the ExternalProject docs. Be prepared to do a bit of digging.
Here's a basic example I used in a project at one point.
include(FetchContent)
FetchContent_Declare(
range-v3
GIT_REPOSITORY git#github.com:ericniebler/range-v3.git
GIT_TAG "0.12.0" # https://github.com/ericniebler/range-v3/releases
GIT_SHALLOW TRUE
GIT_PROGRESS ON
SYSTEM
)
# more `FetchContent_Declare`s (if any). They should all be declared
# before any calls to FetchContent_Make_available (see docs for why).
FetchContent_MakeAvailable(range-v3)
# more `FetchContent_MakeAvailable`s (if any).
FetchContent in some ways is designed to be a little bit "low level". It has a lot of machinery and customization points. If your project is super simple, you might find it useful to try out a CMake-external wrapper module called "CPM" (CMake Package Manager) that attempts to cater to sensible defaults for more common, simple use-cases.
If you use FetchContent or CPM, be aware that since they eventually just call add_subdirectory, you might need to take some steps to avoid naming conflicts of target names and CMake variable names between your CMake configs and the CMake configs of the dependencies you pull in. For more info, see How can I avoid clashes with targets "imported" with FetchContent_MakeAvailable?.
As others have mentioned, you can also look into using package managers like vcpkg or Conan. I don't know much about those so I can't comment.

How to install the C++20 range library from github

I would like to use the range-v3 library in my project, but i don't understand how. The installation description says the following:
This library is header-only. You can get the source code from the
range-v3 repository on github. To compile with Range-v3, just #include
the individual headers you want.
Does that mean I can copy and paste the needed header files and add the filepath to my CMake file? I am a bit confused, because I never included third party library.
Note: please see hythis' answer for a better solution.
Does that mean I can copy and paste the needed header files and add the filepath to my CMake file?
Basically, yes. First git clone to <path_to_range_v3>. Then include these lines into CMakeLists.txt:
add_library(range_v3 INTERFACE IMPORTED)
set_target_properties(range_v3 PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES <path_to_range_v3>/include)
target_link_libraries(your_target PUBLIC range_v3)
I'm not sure why Evg suggested what they did, I don't even think in 2019 you were forced to create your own CMake interface with ranges-v3 (see here, the file existed way before hand). Regardless, don't use Evg's solution, ranges-v3 is a good header only library, and in order for a header only library to be good it must provide CMake integration.
Header only libraries do not mean the authors do not provide CMake support, or even avoid CMake themselves. Bad authors do this, as you've probably experienced by having to ask this question in the first place.
To properly integrate with Ranges V3, use a packagemanager (though some package managers screw the process up depending on how their custom CMake files are configured) such as Conan or vcpkg and integrate with their respective CMake solutions, or add the project as a git submodule (though you could git clone it as well) then in your CMakeLists.txt:
add_subdirectory([path to submodules]/range-v3)
...
target_link_libraries(my_target [SCOPE] range-v3::range-v3)
It can be hard to figure out actual targets for libraries if the authors don't spell out how to use their projects in a CMake project, and I don't blame any one making an SO post about it, it's a pain.
Generally if the project has a CMakeLists.txt file, it probably has static-library/sub_directory cmake integration, but some libraries only have install targets, thus are unusable when baked into your source code with out editing their CMakeLists.txt
To check if this is the case, or if you can actually use the targets, if you can't find any CMake documentation about how to use the library (which I couldn't) do the following:
Look inside the CMakeLists.txt file
Find a project alias usually in the form of project_name::project_name. This is how I found range-v3's project alias.
If you can't find alias (via searching for ::), find the actual target name for the library (and try to use this directly), sometimes this isn't exported though (hidden in a sub directory from the top cmake file). OpenCV doesn't use :: for example, and instead each component target is marked with opencv_[component name] but still exported.
If the project has not been configured to be properly used as a submodule and is otherwise meant to be used as a static library, submit an issue or PR to the given repository, this is a bug.

How to include source of external library for IntelliSense in a CMake project?

I am working on a C++ application in Visual Studio, using the Visual Studio's CMake project template.
To build my application I only need the header and the external library and I can link it like this:
# CMakeList.txt : CMake project for myapp, include source and define project specific logic here.
cmake_minimum_required (VERSION 3.8)
# Add source to this project's executable.
add_executable (myapp "myapp.cpp" "myapp.h")
# Add TIFF library
set(TIFF_INCLUDE_DIR "C:\\libs\\tiff\\out\\install\\x64-Debug\\include")
set(TIFF_LIBRARY "C:\\libs\\tiff\\out\\install\\x64-Debug\\lib\\tiffd.lib")
find_package(TIFF REQUIRED)
target_link_libraries(myapp PRIVATE TIFF::TIFF)
So far, so good. I can use the tiff library to open, read, write tiff files, etc., and IntelliSense is catching up with declarations in the header files. But, I would like IntelliSense to also be aware of the full source code of the TIFF library. For example, if I am in myapp.cpp and I ctrl+click on TIFFOpen it opens the header corresponding to TIFFOpen, but when I ctrl+click TIFFOpen in the header file, it doesn't go to the corresponding source file, which is the normal behaviour for source files in myapp. This is understandable, since I never told Visual Studio where to find the source files of the external library.
CMake doesn't need to know where the source files of the external libraries are, since it won't build the external library, therefore I guess I don't/shouldn't change anything in CMakeLists.txt
One option (I haven't tried yet, but I'm fairly sure it would work), would be to just include the entire tiff library as a sub-project of myapp. I do have some problems with this solution though:
The external library is not conceptually an integral part of the project, and I don't plan to modify the external library. This is more of a principle issue.
Simply having it as a subfolder in my project makes it a risk of changing something I didn't intend to change.
I don't want to rebuild the external library when I do a rebuild all. I know Visual Studio / CMake is smart enough to figure out that nothing changed and doesn't rebuild, but I would rather have Visual Studio / CMake not even try.
The way I see it, I have to set the directory with the source files somewhere in Visual Studio settings, but still related to the project. My best guess is that the .vs/ProjectSettings.json is the file I need to edit somehow, but honestly, I have no clue.
Alternatively, maybe I could write some command in CMakeLists.txt that doesn't do anything, but triggers the IntelliSense to look in the folder with source files. Again, I have no clue how should I go about this.
In a nutshell, I want IntelliSense to see all source files of an external library, the same way it sees the source files of myapp, without including all source files of the external library as a sub-project of myapp. How should I go about it, if even possible?
If relevant, I use Visual Studio 2019 Community and the CMake it comes with it (3.15).
Regarding your last comment, writing code in comment section is inconvenient so I'll just post it here although not an answer.
libtiff-4.pc is for pkg-config, not cmake, and find_package() can't deal with it directly on Windows and would take some work if you really want to. It might be easier to just write everything manually. Remember to set the tiff.lib and tiffd.lib according to your configuration. You can use CMAKE_BUILD_TYPE variable and if() command such as:
# set build type to release if not specified
if(NOT CMAKE_BUILD_TYPE OR CMAKE_BUILD_TYPE STREQUAL "")
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build Type" FORCE)
endif()
# switch lib to link according to build type
if(CMAKE_BUILD_TYPE STREQUAL "Release")
# for release
set(TIFF_LIBRARY "<your_path_to_installed_lib>/tiff.lib")
else()
# for debug. If you have other configs like relwithdebinfo you can add more
set(TIFF_LIBRARY "<your_path_to_installed_lib>/tiffd.lib")
endif()
Also, remove find_package() and use target_link_libraries() and target_inlude_directories():
set(TIFF_INCLUDE_DIR "<your_path_to_installed_headers>/include")
target_link_libraries(myapp PRIVATE ${TIFF_LIBRARY})
target_include_directories(myapp PRIVATE ${TIFF_INCLUDE_DIR})
You can also skip setting TIFF_LIBRARY and TIFF_INCLUDE_DIR and pass the string directly if you like.
I use Visual Studio a lot and it's my favorite IDE. But package management with cmake on Windows is not as smooth as Linux. Always remember to set environment variables after compiling and installing external libraries.
Normally find_package() will look for a system environment variable named <libname>_DIR (for example TIFF_DIR, which is not found in your case), which is used to store path to installed lib, then look for <libname>Config.cmake and <libname>ConfigVersion.cmake in that folder (and would fail for TIFF since it doesn't have them).
It also searches other places, check https://cmake.org/cmake/help/v3.15/command/find_package.html?highlight=find_package for details.
So for those libs with cmake exported files, add a variable with correct name and value. And that's only for compiling.
If you want your application to run after compiling, you also need to add the path of installed lib's binaries (usually *.dll) to system's Path variable. In your case you should find something like tiff.dll and tiffd.dll after compiling and installation, add that folder to Path and you are good to go.
Actually, showing the source in the IDE is easy enough.
Just add the source with add_library(tiff_for_ide EXCLUDE_FROM_ALL ${SOURCES}) but don't link with it in the main program. You'll want to use a different target name for the library. For this you'd need the source in your project directly or as a submodule (if using Git, something else if available by you VCS).
Other options are using ExternalModule_Add; or FetchContent_MakeAvailable with the add_library as above, to avoid adding third party deps into the repository directly.
Now, just cross your fingers and hope that the IDE is not intelligent enough to restrict searching for sources that are linked against the target which compiles the current file. Or that it's intelligent enough to detect this situation but fallback to searching the whole project files anyway when it's linking against a binary.

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.

Issue with CLion and find_package: Generated CMake files are placed in some odd location

So basically I have the following setup:
A small test library called mylib with an according CMake file. The CMake file creates all the necessary information so that another project (which is going to be my binary) can use find_package to add the library. The library also has a install target.
A small binary called mybin again wih according CMake file which is depended on the mylib project. Using find_package I can add mylib either by specifying the location of the according myLibConfig.cmake (and myLibTargets.cmake etc.) files or by executing the install target of mylib and then letting CMake find the library itself.
Using CMake and XCode everything works like a charm. First, I configure and build the library. Second, I configure my binary project, specify the location of the library and then build it without any problems.
Now I want to do the same using CLion. My problem now is that CLion puts all the generated CMake file (which usually are placed in the build folder) in some cryptic location which can't be changed in the IDE. Now, when I want to build the mybin project I have to specify this cryptic location which seems kinda odd to me (especially because you have to find out first where CLion actually places those files).
So my question is: Is there a more convenient way to handle the described configuration? I guess this is more or less the standard use case which makes me wonder if I'm missing out on something. Being able to specify where CLion should put the generated CMake files would solve my problem I guess.
I know that i can install the library (using the install target) and then let CMake find it. The problem here is that CLion (to my understanding) doesn't support install targets and therefore I have to use (in my case) XCode to build and install the library first.
I was misunderstsanding the intention of find_package(as Tsyvarev pointed out). By using the solution proposed in this question I managed to add an install target to CLion which now allows me to conveniently build "mylib" and use it in the "mybin" project without having to configure anything manually.