How to specify CMake Preset for subdirectories? - cmake

I have a big library with a lot of options that I recently converted to CMake from make, with a lot of options. I use configuration presets to have multiple profiles. The library is developed by my team, while the applications building over it are developed by other teams. The library provides common functionalities and is a must to be consumed by the apps. We were seeking to provide sane default options via presets.
Right now, I want to build a toy app over it.
I have the following directory structure where a CMakePresets.json file is located in the subdirectory (my_lib/, which is actually a git submodule).
main.cpp
CMakeLists.txt // Top-Level CMakeLists
my_lib/
------> lib.c // simplified view of the functionalities here
------> lib.h
------> CMakeLists.txt
------> CMakePresets.json
# Top level CMakeLists.txt
project(toy_app)
add_subdirectory(my_complex_lib)
set(SOURCES main.cpp)
add_executable(toy_app ${SOURCES})
target_link_libraries(toy_app my_lib)
Running cmake --list-presets at the top level won't detect sub-directories presets.
The question is how to specify the preset from the top-level invocation or even provide a default one in the CMakelists inside the library?
Trial #1
I have found the idea of default preset was rejected ( https://gitlab.kitware.com/cmake/cmake/-/issues/21417 )
Trial #2
I tried to add CMakePresets.json at the top level and included the one inside my_lib and it worked.
Running cmake --list-presets on the top level can find presets right now.
I wonder if there are other solutions?

As you indicate in your question post "Trial #2", yes, you can include other json files following the CMakePresets.json schema into CMakePresets.json files. See the docs on includes for more info. Here's an example from the docs:
{
...
"include": [
"otherThings.json",
"moreThings.json"
],
...
}
So as you found out, you can keep your main CMakePresets.json file in the subdirectory and create another one in the project's root directory that includes the subdirectory one, or you can do it the other way around: Put your main one in the project's root directory and create one that just includes it in the subdirectory.
This could be useful if you wanted to generate buildsystems for both the root directory and the subdirectory. I.e. you want your subdirectory's CMake config to be able to be used as a standalone project. In that case you might be making it a git submodule of the root project, or using a monorepo structure for the users of various parts of your project.
If you're going with a git submodule, then you couldn't put a main CMakePresets.json file in the project's root directory and include it in the submodule because the submodule repo should stand alone without knowing about what uses it as a submodule. In that case, you could put the main CMakePresets.json in the repo that gets used as a submodule, or just make two "copies" of the same CMakePresets.json file- one for each repo.
If you wanted to have a utility CMakePreset.json file with comon things used by multiple repos, you could create a repo with just that utility file and include that repo as a submodule to all your other repos and then use the CMakePresets.json's "include" mechanism to pull in those utility preset configurations into the repo's own CMakePresets.json file.

Related

Building cmake project with dozens of package dependencies in different subdirectories

I am trying to build an example project that requires dozens of packages contained in different subdirectories, for example
/home/Olumide/src/project/Release/modules/module1/CMakeFiles/Export/lib/cmake/module1
/home/Olumide/src/project/Release/modules/module2/CMakeFiles/Export/lib/cmake/module2
/home/Olumide/src/project/Release/modules/module3/CMakeFiles/Export/lib/cmake/module3
...
Given that the application that I'm trying to build is in the location /home/Olumide/src/project/applications/tutorial, is it possible to build the application without explicitly specifying the paths to all each package that the project requires, or modifying the CMakeLists.txt file, for example by specify a common root path to be searched for all the packages, for example:
cmake -DCMAKE_MODULE_PATH=/home/Olumide/src/project/Release/modules .
Unfortunately this does not work and cmake complains that it cannot find package configuration files with the names Modul1eConfig.cmake or module1-config.cmake etc.
If all of the modules adhere tho this exact pattern you should be able to provide the "root path" via cache variable and pass a the info where to look for the libs to find_package via PATHS parameter:
# maybe a more reasonable default here?
set(MYPROJECT_MODULE_PATH "/home/Olumide/src/project/Release/modules" CACHE PATH "the path to the modules we're looking for")
set(MYPROJECT_MODULE_NAMES
module1
module2
...
)
foreach(_MODULE IN LISTS MYPROJECT_MODULE_NAMES)
find_package(${_MODULE} REQUIRED CONFIG
PATHS "${MYPROJECT_MODULE_PATH}/${_MODULE}/CMakeFiles/Export"
)
endforeach()
In general if these packages logically belong to the same software package, you should provide a way of finding those modules by passing them as components for find_package but since this seems to be code not under your own control you may not have that luxury. (E.g. boost provides this functionality: find_package(boost COMPONENTS test mpl ...).) However it may be worth some investigation if there is a configuration script somewhere that's supposed to automatically include the modules, if listed as components, e.g. somewhere in Release or Release/modules.

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 generation requiring generated files

I'm building a project using CMake in multiple subdirectories. There is a parent directory containing a parent CMakeLists file, and each subdirectory contains its own CMakeLists file. As such, I'm using the add_subdirectory command to run the subdirectories.
My issue is that one of the subdirectories generates code that another subdirectory needs in order to build. Specifically, it's Google Protocol Buffers. The CMakeLists file in that subdirectory will generate the pb.cc and pb.h files if run independently, but until I do so, I cannot generate the cache of the parent CMake file as it complains it's missing those source files.
Directory structure is as follows:
/
--CMakeLists.txt
--protobuf/
----CMakeLists.txt
----src/
--main/
----CMakeLists.txt
----src/
Where the main subdirectory requires files generated by the protobuf subdirectory.
Is there a way I can have the parent CMakeLists file build the subdirectory as part of its generation step? Or somehow mark the protobuf files as required, but missing, so the generation does not fail?
You can try running this particular cmake file as execute_process before including the main file. But this goes against the designed cmake usage.
The correct answer would be to make your generation step a part of build process. If you can post a minimal, reproducible example of your problem I could give you concrete ways to solve your problem directly instead of making your workaround work.

How to set a specific package path for CMakeLists.txt

My colleague wrote a CMakeLists.txt, which contains things as below:
find_package(OpenCV 3 REQUIRED
COMPONENTS
opencv_core
opencv_imgproc
opencv_imgcodecs
CONFIG
)
As the project needs these components of Opencv3, my colleague downloaded the whole Opencv3, of course, it works.
But the whole Opencv3 is too big, so I get only the necessary lib files: libopencv_core.so, libopencv_imgproc.so and libopencv_imgcodecs.so and try to replace the whole Opencv3. The three so files are put here: /opt/opencv3/.
I don't know how to tell the CMakeLists.txt to look for the components of Opencv3 at the specific path instead of the path by default.
I'm totally a newbie on writing CMakeLists.txt...
CMake find_package() finds and configure project dependencies in CMake using two modes of operation, Module and Config, as described in its documentation.
In Module mode (not your case), it looks for a Find<package>.cmake file inside CMAKE_MODULE_PATH. This file searches for header files and libraries and set the necessary CMake variables in case of success.
In your case it is using Config, as requested by the CONFIG keyword. In Config mode CMake looks for for a <name>Config.cmake file or <lowercase-name>-config.cmake.
These config files describe the version of the dependency and the location of header files and library modules.
So you should look for OpenCVConfig.cmake or opencv-config.cmake in CMAKE_PREFIX_PATH or in OpenCV_DIR.
Please note that you have to set(OpenCV_DIR ...) before calling find_package().

Which file organization for CMake? Should CMakeLists.txt include other files?

I use CMake to configure, build and install a C++ code.
I have defined some user options.
I have added an option to let CMake download external depedencies.
Etc, etc.
For the moment, everything concerning CMake is written in a single file CMakeLists.txt. There is also a configuration file 'config.cmake' which allows users to define various parameters such as verbosity level, paths to libraries, flags for compilers, etc.
What is a good file organization for CMake?
Should CMakeLists.txt file be divided into subfiles?
Thank you very much for your help!
A good file organization depends on how large your CMake logic is, or how large you expect it to become. Generally speaking:
Functionality that may get reused a lot you probably will want to organize into "per topic" .cmake files which contain macros, and include them in your other CMakeLists.txt files to load that functionality. You'll find lots of these already out there on the web (custom Find*.cmake files for locating particular system libraries are quite common, for example.) A common convention is to have a directory "CMake" in your toplevel source directory, although other configurations are possible.
If you have a lot of different build targets with their own unique source files (multiple libraries in a single project, for example) it is good practice to separate those into subdirectories and have one CMakeLists.txt file per subdirectory, with the toplevel CMakeLists.txt file using add_subdirectory to "hook them in" to the main build.
A "typical" structure for a project could look like this:
project/
CMakeLists.txt
README
CMake/
FindSpecialtyLib1.cmake
FindSpecialtyLib2.cmake
CustomCMakeMacroA.cmake
etc...
include/
CMakeLists.txt (for installing headers, etc. if needed.)
src/
CMakeLists.txt (top level switching logic per user settings, etc.)
lib1/
CMakeLists.txt
src1.cxx
src2.cxx
lib2/
CMakeLists.txt
src1.cxx
src2.c
private_header.h
That's just a hypothetical example, of course - you'll want to shape your logic to your specific project. Since you mention a config.cmake file, it's probably worth mentioning that the majority of variable setting of that sort is usually done in the cmake-gui or from -D definitions at the CMake command line, in my experience. A config.cmake works as a way to group options, but most users are probably going to start with the gui when it comes to setting things. I also recommend checking the CMake email archives for specific questions - I've found it to be an extremely helpful resource.