CMakePresets.json vs CMakeSettings.json vs CMakeLists.txt - cmake

I've googled around a bit but I've not found anything really satisfactory. There are a lot of tutorials on how to use them, but I'm struggling to understand which one best fits a particular project.
It's not very clear to me, for each of them, what should I use for. As far as I understood (and that might not be right), all of them overlap in some features, such as defining environment or CMake variables.
What I've generally understood is that CMakePresets and CMakeSettings are Microsoft tools (maybe tool is not the right word, but I couldn't come up with a better one) for integration of a CMake project in MS IDE (VS and VSCode). How they cope with CMakeLists and what are the scope of each one?

Every project needs to have a CMakeLists.txt file that configures the CMake project. You can learn more step-by-step by following the official CMake tutorial. Here's a tutorial on CMakeLists.txt files by CLion.
CmakePresets.json is an optional CMake feature. It is not (as you guessed) a Microsoft-specific tool. It is a tool to allow writing up presets for configuration, build, test, and packaging settings that are commonly used together. That's what it's useful for. Ex. filling in option() variables (that are defined in the project's CMakeLists.txt file). Presets are basically a tool to not have to write many commonly-used-together commandline arguments for common user scenarios and instead have a shortcut/alias for them.
CMakeSettings.json is specific to Visual Studio. You can use it when building projects specifically with Visual Studio. It has some similar capabilities as CMakePresets.json files, but is much more oriented to work with Visual Studio IDE configuration.
Here's an analogy for what presets are and when they're useful: Imagine a project is like a sandwich shop where you pick what ingredients you want in the sandwich (what options you want to choose when configuring the project build). Lets say one sandwich shop has (among many other ingredients), bacon, lettuce, tomato, and cucumber, which are the ingredients you like to have in your sandwich. The list of all the possible ingredients to choose from is the CMakeLists.txt file. The shop notices that many people like to order sandwiches with bacon, lettuce, and tomato together, so they make a preset: "B.L.T.". So now, you can tell them what you want faster: Instead of bacon, lettuce, tomato, and cucumber, you can say: "I want a B.L.T. with onion". Now imagine a different shop with many more ingredients, and you often ordering the same thing with many more ingredients. Can you see how that would be useful? Now- not all customers will commonly want the same thing, and the "official" presets are determined by the shop owner (the project maintainers commit a CMakePresets.json file to their project repo), but you might want to have some custom presets. That's what the CMakeUserPresets.json file is for (never committed to project repo. In .gitignore).
So how do you choose what settings to put in the CMakeLists.txt versus a CMakePresets.json if you choose to create one? If you are 100% certain that one specific value for something (a CMake variable, an environment variable, etc.) will always be the desired value to be used and nobody will want anything different, then hardcode it in the CMakeLists.txt. Otherwise, use the appropriate mechanism (Ex. option() or if(DEFINED ...)) to define the setting with an overridable default value in the CMakeLists.txt.

Related

What's deprecated in KitWare's guide to Finding libraries in CMake?

I need to write a CMake FindXYZ-type module. Googling, I've found this guide:
https://cmake.org/Wiki/CMake:How_To_Find_Libraries
from Kitware, but there's a disclaimer about it being deprecated. Which significant changes, if any, have been made to how these modules are written over the past, say, 6-7 years?
Yes, the CMake Wiki's content now officially moved inside CMake's documentation, so the "deprecated" warning is more a general one that the Wiki is no longer looked after.
In your case the main part of CMake Wiki: How To Find Libraries moved to CMake's documentation cmake-packages chapter.
What has changed?
I think the major change over the last years is what Stephen Kelly in his "Embracing Modern CMake" talk called:
Modern CMake packages define IMPORTED targets
find_package(Foo REQUIRED)
add_executable(hello main.cpp)
target_link_libraries(hello
Foo::Core
)
The same basic tint is found in CMake's documentation cmake-developer - Find Modules chapter:
The traditional approach is to use variables for everything, including libraries and executables. This is what most of the existing find modules provided by CMake do.
The more modern approach is to behave as much like config file packages files as possible, by providing imported target. This has the advantage of propagating Transitive Usage Requirements to consumers.
Details
You can see this "modern approach" as an extension of the previous methods (like in "FindZLIB: Add imported target and documentation" commit).
What should definitely be there (the core of all "Find Modules" for years now) is the find_package_handle_standard_args() macro.
This macro is build around the ..._FOUND cached variable handling.
My recommendation would be to concentrate on the imported targets and the ..._INCLUDE_DIRS and ..._LIBRARIES variables are just a side effect of having to cache your find results somewhere.
No, I don't think there's any significant changes. I still use it.
I think they are just trying to get you to look at their other documentation, like find_package.
In writing new Find modules, I normally just look at the other
FindXXX.cmake
as examples/templates and go from there.

where is the list of names that cmake reserves?

Everything is in the title, but for more context informations:
I am creating a library, where all components are independent (it's only because it's easier to manage 1 git repo, really).
In that library's root folder, I have 1 sub-folder for each part of the library's components, with exactly 3 "interesting folders" (src,tests,include/components_name). I have hardcoded those folders in a foreach loop so that all actions will be done for all modules by default.
The problem seems to be that, one of the modules is named "option_parser" which is, indeed, relatively generic, and also seems to be "reserved" by cmake, and same for everything derived from it. I've tried "option_parser_test", "option_parser_tests", and other random names based on "option_parser_" root.
So, here is my question: where I can learn how to avoid names that cmake reserves?
And how can I affect them anyway to my binaries (because, I feel like it's stupid to change a project's name because of a build system. Might be a strong enough reason to switch it.)
It's really quite simple. Use these three commands to see all reserved words:
cmake --help-command-list
cmake --help-variable-list
cmake --help-property-list
The answer of Cinder Biscuits above should probably already help you.
Additionally, you should probably read CMake's own documentation regarding the CMake language and in particular the note in the "Variables" section:
Note: CMake reserves identifiers that:
begin with CMAKE_ (upper-, lower-, or mixed-case), or
begin with _CMAKE_ (upper-, lower-, or mixed-case), or
begin with _ followed by the name of any CMake Command.

In cmake, what is a "project"?

This question is about the project command and, by extension, what the concept of a project means in cmake. I genuinely don't understand what a project is, and how it differs from a target (which I do understand, I think).
I had a look at the cmake documentation for the project command, and it says that the project command does this:
Set a name, version, and enable languages for the entire project.
It should go without saying that using the word project to define project is less than helpful.
Nowhere on the page does it seem to explain what a project actually is (it goes through some of the things the command does, but doesn't say whether that list is exclusive or not). The cmake.org examples take us through a basic build setup, and while it uses the project keyword it also doesn't explain what it does or means, at least not as far as I can tell.
What is a project? And what does the project command do?
A project logically groups a number of targets (that is, libraries, executables and custom build steps) into a self-contained collection that can be built on its own.
In practice that means, if you have a project command in a CMakeLists.txt, you should be able to run CMake from that file and the generator should produce something that is buildable. In most codebases, you will only have a single project per build.
Note however that you may nest multiple projects. A top-level project may include a subdirectory which is in turn another self-contained project. In this case, the project command introduces additional scoping for certain values. For example, the PROJECT_BINARY_DIR variable will always point to the root binary directory of the current project. Compare this with CMAKE_BINARY_DIR, which always points to the binary directory of the top-level project. Also note that certain generators may generate additional files for projects. For example, the Visual Studio generators will create a .sln solution file for each subproject.
Use sub-projects if your codebase is very complex and you need users to be able to build certain components in isolation. This gives you a very powerful mechanism for structuring the build system. Due to the increased coding and maintenance overhead required to make the several sub-projects truly self-contained, I would advise to only go down that road if you have a real use case for it. Splitting the codebase into different targets should always be the preferred mechanism for structuring the build, while sub-projects should be reserved for those rare cases where you really need to make a subset of targets self-contained.

CMake: Best method for "subprojecting" files

I'm learning/vetting CMake at the moment as I'm thinking of migrating our code to it. One thing we do a lot of with our current make system is to "subproject" common code files. For example, we have a lot of shared, generic headers (plus some c/cpp files) which get included in every project we create. I want to replicate this in CMake but I don't see an easy way of doing it. To be precise, I want to do something like:
Parent CMakeLists.txt
add_subdirectory(shared_folder shared_build_folder)
#Next line should somehow add in the files reference in the shared_folder
add_executable([specific files for this project] build_folder)
Child CMakeLists.txt (shared_folder)
#Somehow have a list of files here that get added to the parent project
So far I've found various "ways" of doing this, but all seem a little hacky. I'm coming to the conclusion that this is in fact the way I have to do things and CMake isn't really geared towards this style of development. For clarity, most of my solutions involve doing something like creating a variable at the parent level which consists of a list of files. This variable (via some shenanigans) can get "passed" to/from any children, filled in and then when I call add_exectuable I use that variable to add the files.
All my solutions involve quite a few macros/functions and seemingly quite a bit of overhead. Is this something other people have tried? Any clues on the best approach for doing this?
Thanks
Andrew
We were facing the exact same problem and after some time of crying we accepted the CMake-way and it resulted in a better structured project even if it meant to change some parts of our structure.
When using sub-directories the targets are automatically exported throughout the whole project (even in subsequent other add_subdirectory-calls) once the add_subdirectory-statement was processed: sub-projects which contain common code are creating libraries.
There is also the PARENT_SCOPE which you can use to export variables to parent CMakeLists.txt
For "other" things we simulated the FindPackage-mechanism by including .cmake-files into the main CMakeLists.txt with include. In doing so we can provide variables easily, change the include_directories and do other fancy things global to the project.
As there are no dependencies between cmake-variables, we don't use cmake to configure the source (features of the project), but only the build (compiler, includes, libraries...). This split was the key element of our build-system-refactoring.

Can CMake recognize CMakeLists.txt with another name (CMakeLists_nightly.txt)?

I am wanting to create CMakeLists.txt files that are more specifically named such as "CMakeLists_nightly.txt", "CMakeLists_weekly.txt" and so forth. The reason I want to do this is to cut down on the folder hierarchy clutter of my project. I could easily put each of these files in their own folder with the postfix I showed above but I do not want to do this.
Can I tell cmake to take a CMakeLists.txt file by another name? I have seen this question asked before on another forum (http://www.cmake.org/pipermail/cmake/2007-August/016036.html) but it was back in 2007 and the answer was no. Does the current version of CMake provide this capability?
Not really, but you can emulate this by putting CMakeLists.txt in separate directories, e.g. continous/CMakeLists.txt and nightly/CMakeLists.txt. Use INCLUDE to include the appropriate scripts for each of the build configs.
Consider if this really is the right approach - completely separating the nightly and continuous script is a really bad idea as that will lead to duplication and a very bug prone build setup.
Answer, which came into my mind, while I was reading an answer from larsmoa and thinking about it little bit longer:
(this is not exactly the answer to the question about different name for CMakeLists.txt, but rather, to "how to have two different CMake configuraiton files in the same directory")
You can avoid creating multiple directories and storing there CMakeLists.txt (it may also be problematic, if you want your script to be the parent of everything). My Idea is, that you can have two "include" cmake files with any names you like. And then in CMakeLists.txt you may have an set(CACHE), which controlls, which include-script should be actually included.
With this setup you can have two build directories: one configured with one value of the option, and another - with another. Depending on that, in which build-directory you do the build, corresponding build definition will be used.
It can look something like this:
CMakeLists.txt:
set(
MY_BUILD_KIND BUILD_A CACHE STRING
"Select build kind: BUILD_A or BUILD_B"
)
if ( MY_BUILD_KIND strequal "BUILD_A" )
include(build_a.cmake)
elseif (MY_BUILD_KIND strequal "BUILD_B")
include(build_b.cmake)
else ()
message ( FATAL_ERROR "Unknown build kind: ${MY_BUILD_KIND}" )
endif ()
Background (why do I need it?): My situation is kind of exotic, I guess. I have a C++ project, different parts of which use two different compilers. And there is a part of it, which needs to be built by each of them. So the directory structure is like this:
Projects
CompilerAProjects
CompilerBProjects
CommonProjects
Here "CommonProjects" are included as Part of "CompilerAProjects" and also as part of "CompilerBProjects". Now we try to integrate cmake and I was thinking, how can we keep the structure, but do the build with CMake. If I put CMakeLists.txt in the root directory, then I don't understand, how to differentiate between two compilers. And if I don't have the root project, then it is not clear, how to refer to "sibling" project. So I came to the idea, that I can included sub-directories basing on the current compiler. And then I decided, that actually it is not necessary, that compiler is the driving factor, we can use set(CACHE) instead. And we are not restricted to select, which sub-directory we select, but can also include ".cmake" files.