I think the issue with CMake is exactly the issue with C++.
There's a 'modern' way to do things, and a 'legacy' way to do things, and the respective designers of CMake and C++ have decided that it was—for several reasons—better to leave the 'legacy' stuff in the languages, rather than pull a Python and make a clean split.
In 'modern' CMake, there are targets, and properties on said targets, i.e. compile and link options, C/C++ versions, libraries, headers, other custom dependencies, etc.
There are also functions and generator expressions[1] to make control flow a little easier. On top of these, CMake's built-in `find-package` and `FetchContent` make package management a lot easier than it used to be. Want Boost? Just do `find_package(Boost)`, and then `target_link_libraries(<my_target> Boost::boost)`. It gets easier still with a proper C++ dependency manager like vcpkg or Conan.
In legacy CMake, all these were set with global variables and there was no unified way to handle packages. I fully foresee going forward that at least a plurality of C++ developers will coalesce on a CMake + vcpkg (which has more packages than Conan) workflow.
Bash cannot handle figuring out all my dependencies and how to run them in across howeverany CPUs I have. Nor can it handle all the different cross compile, static analysis, address sanitizer, and so on.builds I run.
I do this for personal projects and honestly, considering the alternatives, I think it rules.