project(..), cmake_minimum_required(..)

Hello,

I’m concerned about cmake version-race and project maintainability especially in deeply embedded project.

I have inherited certain habits/practices since cmake 2.6 which I’m reluctant to change. One of the reasons is because I don’t want people stuck with an older cmake-version disabled by their systems ability (or own willingness) to upgrade if not necessary. But another is personal strong unwillingness to mess with stuff that works. Once done, stuff should be left alone (as per old UN*X philosophy). But things aren’t always as easy as they seem and one can’t always be as anal as one would like…

So in short my question is about the “proper” (or best practice) use of project(…) and cmake_minimum_required(…). In addition, also the habit of having complete CMakeLists.txt in each source sub-directory. (I.e. I have a strong preference towards molecularity, yet some people fear the perceived extra maintenance).

Modularity:

To me modularity even beyond the obvious scope for coarse level big project, makes sense also for minuscule levels. It makes re-use even for wrap-around debugging much easier, especially in cross-build environments where setting up debugging can be a real pain, the ability to wrap-and-debug even a small parts can makes life much easier and save tons of time. What do you think of this practice? I’ll personally take the small extra lines penalty (mostly adding include_directories) as these lines don’t usually change anyway and can furthermore be abstracted using common_functions.cmake files. There is ONE irritating drawback however. Cmake isn’t very good at composing higher level libraries out of libraries from sub-project (think static, cross built ones. These are usually preferred to be ONE library, not a plethora). The ability to have system-specific ldtool re-composition easily

would be a deeply appreciated ability to add to Cmake.

Project(), version and VERSION:

project(…) first argument is “name”. This used to be sensitive to the same level directory-name and I’ve always used the exact same name, spelling and capitals and all. I don’t remember if issue was when build out-of-source, when cmake projects included each other or combination of both, but I’m 100% certain there was one. Confirming or correcting is appreciated as having several cmake projects called “src” when using cmake recursively seems… stupid.

project(…) can also take take the option VERSION which has caused much confusion, especially with recursive project files. I therefore avoid it altogether, as setting explicit VERSION* is just as easy and will make the CMakeLists.txt both future proof and cmake_minimum_required downgradeable if needed.

As mentioned, I usually make an effort to keep cmake_minimum_required as low as possible to not disable users, unless there IS an actual feature that is essential. Hence, my projects can include sub-modules where cmake_minimum_required is different. Is there any drawbacks with that?

I’ve assumed that cmake_minimum_required not only requires a certain version, but if set lower than the actual cmake version being in use, it will also set that version specific policies. Is that correct? I.e. the VERSION behavior and project name issue above will differ not only on which actual version of cmake itself, but also with how cmake_minimum_required is set. I have also assumed that when used recursively, the right policies will follow accordingly to each CMakeList.txt setting. Is this correct? I.e. policies change with CMakeLists.txt and aren’t defined on top-level.

Comments, discussion or references to best-practices deeply appreciated
Regards -Gajah

If you declare a minimum version of 2.6, that should still work. However, I think everything in the past ~10 years distributes at least 2.8.12 (RHEL 7 being the main holdout, though EPEL has 3.14 leaving Ubuntu 16.04 and Ubuntu 18.04 as the “oldest” ones in typical use today (AFAIK)).

This sounds quite unnecessary. There’s a lot of boilerplate that goes into top-level projects (at least to make “good” packages) I would be loathe to duplicate. Every dependency would need a find_package() alternative to all-together builds, so “good” packages would be really useful in this case. I recommend just picking a few places in the tree to support such things and I think you’d greatly reduce the amount of busy work you have to deal with.

I think you’d have to balance the find_package() load versus the benefit of building each library individually. Of course, if the libraries never depend on each other, that’s not such a problem. But then, I’d just build that directory/target set individually if I was debugging just that one (though I don’t tend to do cross-compilation things).

I think you want OBJECT libraries with STATIC libraries at various levels making components available individually while also being able to combine everything into a single library at the end as well. I don’t know how well this works with symbol exports on Windows, but if you don’t care about it or shared builds, that’s likely a moot point.

Using OBJECT libraries in a manner like this would need CMake 3.12 to really get their full power (they were exempted from various CMake mechanisms before that).

Pretty sure there hasn’t been. Seems like a convention to me. But maybe 2.6 was overly strict here. I recommend dropping 2.6 support if that’s the issue.

I recommend leaving project() out of any non-top-level directories. As above, I wouldn’t make every directory top-level.

Policy scopes are…weird. Here, they’ll line up with directories nicely, but be careful of cross-project CMake function and macro calls since they get executed with the policy scope of their definition, not use.

Yes.

It should only differ based on cmake_minimum_required.

cmake_minimum_required should Just Work properly, but any cmake_policy calls affect the current policy scope. You may need cmake_policy({PUSH,POP}) around those regions.

Thanks Ben for your thoughtful advice and comments.

I agree with most but would just like to add some aspects:

I don’t practice having top-level C Make projects in every source directory, only in those where it makes sense.

The boiler-plate code is not a big penalty in deeply embedded cross-development, but agreed - for OS based targets that would indeed be a penalty - not so much for performance perhaps, as the cache will cover for test already made, but for complexity.

“Using OBJECT libraries…” that may be a real motive for updating cmake_minimum_required(…). Tried this with lesser version and couldn’t get it working without defining (non-portable) functions. Great advice!

Policy scopes comment and cmake_policy({PUSH,POP}) also greatly appreciated.

Thanks //G