cmake: install/deploy find-module for multiple projects - cmake

I have a custom (complex) Find-cmake module. Everything works just fine.
My problem is that i need this find-module in many projects to detect my libraray (like the QT cmake stuff).
Is it possible to install my module in a central directory where i can "find" it?
I know that i have to set the CMAKE_MODULE_PATH to the directory, but for a central install location this approach seems to be strange.
So what is the preferred way to use a find module in multiple projects?

Actually there is a registry for CMake packages:
https://cmake.org/cmake/help/latest/manual/cmake-packages.7.html#user-package-registry
Note that I never used it, because where I work we do both compilation and cross-compilation on the same box, thus we have to have different cmake files

Related

What is the one-step process to clone a repo and run a CMake+vcpkg project, not assuming vcpkg exists?

I'm missing something in my understanding of CMake+vcpkg, and I'm also missing proper keywords to search for a solution. (Plus I'm new to both CMake and vcpkg, unfortunately.)
I want to have a public repo for a C++ project that uses CMake as its build system and vcpkg as its package manager.
At my currently level of understanding the user needs to have CMake and vcpkg already installed before he can type cmake and build the repo. I'd like to make it as simple as possible to build the repo and not have a bunch of instructions telling him how to get set up even before he can build.
Is this right?
I'd like a one-step solution: After cloning the repo user types ... something ... and the repo gets built.
I am willing in this day-and-age to assume he's got CMake installed ... plus that it can find the right toolchain. So maybe all he needs to type is 'cmake' ...
Is it a reasonable assumption that the user has CMake installed and configured with his preferred toolchain?
I am not willing to assume he's got vcpkg installed.
Is it a reasonable assumption that the user does not have vcpkg installed and configured?
(TBH, I don't even know if it is CMake or vcpkg that configures the toolchain - I assumed CMake but one of the suggested questions suggests it is vcpkg ...)
What are the reasonable assumptions today, and what is the minimal-step solution?
There's nothing wrong in assuming that the user has certain tools installed.
Let's say you are developing libfoo which depends on libbar and you want to make it as easy as possible for your users to install libfoo.
With a package manager
If libfoo and libbar are available via the same package manager all your users have to do is:
vcpkg install libbar libfoo
You don't have to do anything special in libfoo for this, just instruct the user to install all dependencies in your readme.
It doesn't really matter what package manager is used.
Without a package manager
You will still want to make it easy for people to build and install your project directly. It may seem that invoking a package manager during the build or configuration phase of your project and solving all dependencies is user friendly because the user no longer has to deal with installing those, but it isn't for a number of reasons, including:
you or someone else may want to add your project to another package manager (like conan, spack, etc)
someone may want to consume libfoo with FetchContent, CPM, directly with add_subdirectory, etc
someone may not be a user of vcpkg - there's no need to force them to use it, if possible
you may want to add another dependency, libbaz, which is not available on vcpkg
a user may have the right version of libbar already installed (not necessarily through vcpkg)
This list is not exhaustive. If you're not writing a library some points don't really apply.
This means that someone who has all the dependencies already installed should be able to use libfoo like this:
git clone your-repo
cd your-repo
cmake -Bbuild
cmake --build build
cmake --install build
Resolving dependencies without a package manager
However, it may be desirable to solve dependencies automatically. If your dependencies are using CMake the easiest way of doing this is with FetchContent. For some of the reasons outlined above you should provide an escape hatch so people can still use the already installed dependencies. This can be done with an option. For example, something like FOO_USE_EXTERNAL_BAR. This can be set either to yes or no by default, there's no right answer. As long as the user can control this I don't think it matters that much. You should namespace your options to avoid possible conflicts with options used by other projects.
In this case your build script could do this:
if (FOO_USE_EXTERNAL_BAR)
find_package(bar REQUIRED)
else ()
FetchContent_Declare(
bar
GIT_REPOSITORY bar-repo
GIT_TAG release-tag
)
FetchContent_MakeAvailable(bar)
endif ()
target_link_libraries(foo PRIVATE bar::bar)
Depending on how libbar's CMakeLists.txt is written and organized the if and else branches may get more complicated. See Effective CMake for some details and tips.
Now I can either let libfoo resolve the libbar by setting FOO_USE_EXTERNAL_BAR to ON when I configure your project, or I can set it to OFF to have more control over how it is resolved. I may even use libfoo as a dependency for a project that already depends on libbar. If you always pull it in I can't avoid conflicts in this case.
Using CMake to update dependencies
You may still find it easy for you to be able to update all the project's dependencies using CMake without downloading them via FetchContent. While this will probably raise some eyebrows you could add a custom target for solving dependencies with a package manager. This should also be controllable by an option. Unlike in the above case I strongly believe that if you do this the option should be set to off by default:
if (FOO_AUTO_USE_VCPKG)
add_custom_target(
update_deps
COMMAND vcpkg install libbar
)
add_dependencies(foo update_deps)
endif ()
This will invoke vcpkg every time you build foo so it will make your builds slower. If you remove the add_dependencies call you would have to manually run the update_deps target whenever you need to (which shouldn't be that often anyway).
Notes
Using options is a great way of providing options to your users. It should be noted that they increase the cognitive load, so picking strong defaults can help with that.
FetchContent is a nice way of taking the care away from the user, but at the same time multiple projects that use it will end up re-downloading the same libraries over and over again. It is still more user friendly than invoking a package manager at build time and as long as the users can disable this behavior there's nothing to worry about.
Some parts of this answer may be regarded more as opinion and less as facts. As I said, there is no one right way of doing this, different people will have different ways of solving this problem. Different projects and different environments will have different constraints.
I already recommended the Effective CMake talk above, other useful recourses are available here. If you're a library author you may also want to take a look at Deep CMake for Library Authors.
I had this same question. For my part, I am not willing to assume that the user has either CMake or vcpkg preinstalled.
Here is my solution so far, as a Windows batch file:
#REM Bootstrap...
set VCKPG_PARENT_DIR=C:\Projects
set CMAKE_VERSION="3.20.2"
mkdir "%VCKPG_PARENT_DIR%"
pushd "%VCKPG_PARENT_DIR%"
git clone https://github.com/Microsoft/vcpkg.git
.\vcpkg\bootstrap-vcpkg.bat -disableMetrics
set PATH=%PATH%;%VCKPG_PARENT_DIR%\vcpkg\downloads\tools\cmake-%CMAKE_VERSION%-windows\cmake-%CMAKE_VERSION%-windows-i386\bin
set VCPKG_DEFAULT_TRIPLET=x64-windows
set PYTHONHOME=%VCKPG_PARENT_DIR%\vcpkg\packages\python3_x64-windows\tools\python3
popd
#REM Build the project...
cmake -B build -S .\engine\ -DCMAKE_TOOLCHAIN_FILE=%VCPKG_ROOT%\scripts\buildsystems\vcpkg.cmake -DCMAKE_BUILD_TYPE=Release -DUSE_PYTHON_3=ON
cmake --build .\build\ --config Release
mkdir bin
xcopy .\build\Release\*.* .\bin\
xcopy .\build\objconv\Release\*.* .\bin\
xcopy .\build\setup\Release\*.* .\bin\
It could use some improvement, but hopefully this gives you an idea of one route you could take.

Package vs Library

I've just started working with CMake and I noticed that they have both a find_package and a find_library. And this confuses me. Can somebody explain the difference between a package and a library in the world of programming? Or, in the world of CMake?
Appreciate it, guys!
Imagine you want to use zlib in your project, you need to find the header file zlib.h, and the library libz.so (on Linux). You can use the low-level cmake commands find_path and find_library to find them, or you can use find_package(ZLIB). The later command will try to find out all what is necessary to use zlib. It can be extra macro definitions, or dependencies.
Update, more detail about find_package: when the CMake command find_package(SomeThing) is called, as in the documentation, there are two possible modes that cmake can run:
the module mode (that searches for a file FindSomeThing.cmake)
or the config mode (that searches for a file named SomeThingConfig.cmake)
For ZLIB, there is a module named FindZLIB, shipped with CMake itself (on my Linux machine that is the file /usr/share/cmake/Modules/FindZLIB.cmake). That module is a CMake script that uses the CMake API to search for ZLIB files in default locations, or ask the user for the location if it cannot be found automatically.

What is the default search path for find_package in windows using cmake?

I am porting some code over to windows and my cmake checks for the package Libavahi using
find_package(Libavahi)
I have the headers, dll, etc. but I'm not sure where to place these such that cmake will find them.
Where can I put these files to be found by cmake? They're in a folder called usr.
I see that the module path is specified using:
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
but I'm wondering if there is a default location that will be searched as well
The CMake manual fully specifies the rather complicated search order for the different find_* commands. Unfortunately, since Windows lacks a default directory structure à la /usr/local/lib, it is hard to come up with reasonable defaults here.
One of the most reliable ways of managing directories is through environment variable hints. You simply add an $ENV{MY_VAR} to the HINTS section of the find command and then document that environment variable in your project's readme. Most users that are capable of compiling a C++ program know how to use environment variables, and it is way more convenient than having to give the path on the command line every time (although it never hurts to leave that as an additional option).
For find_package CMake offers a special mechanism on Windows called the package registry. CMake maintains a list of package information in the Windows registry under HKEY_CURRENT_USER\Software\Kitware\CMake\Packages\. Packages build from source can register there using the export command. Other projects build later on the same machine will then be able to find that package without additional configuration. This is quite powerful if you need to build a lot of interdependent projects from source on the same machine.
Update: Starting with version 3.12, CMake now implicitly considers the <PackageName>_Root environment variable a HINT for every find_package call.
In the newer versions of cmake, you can use the --debug-find option to list the directories that cmake is searching through. Somethin like:
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_TOOLS=ON --debug-find .

Is there any interactive shell for module development in cmake?

CMake is awesome, especially with lots of modules (FindOOXX). However, when it comes to write a FindXXX module, a library XXX which your project depends, it's not that easy to handle for non-cmake-expert. I sometimes encounter a library without support to CMake, and I like to make one for it. I'm wondering if there is any interactive shell while writing/testing cmake modules?
Are you writing FindXXX for project "XXX" or is "XXX" a dependency of your project that you're trying to find? If the former, you should instead write a file called XXX-config.cmake (or XXXConfig.cmake) and install it into one of the directories mentioned in the docs for find_package. In general, XXX-config.cmake files are for projects which are expected to be found by CMake (and installed by the project itself) and FindXXX.cmake files are for projects which don't support CMake (and usually have to support finding any version of XXX).
That said, for FindXXX.cmake, usually you just need a few find_file (e.g., for headers), some find_library calls, or even just a single pkg_check_module from FindPkgConfig.cmake followed by a find_package_handle_standard_args call (use include(FindPackageHandleStandardArgs) to get it). FPHSA makes writing proper Find modules a breeze.
For XXX-config.cmake files, I have typically used configure_file to generate two versions: one for the install (which includes your install(EXPORT) file) and one for the build tree (generated by export() calls). Using this, other useful variables can be accurately set such as things like "which exact version of Boost was used" or "was Python support compiled in" so that dependent projects can get a better picture of what the dependency looks like.
I have also recently discovered that CMake ships with the CMakePackageConfigHelpers module which is supposed to help with making these files. There looks to be quite a bit of documentation for it.

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.