# Effective Modern CMake ## Getting Started For a brief user-level introduction to CMake, watch C++ Weekly, Episode 78, [Intro to CMake](https://www.youtube.com/watch?v=HPMvU64RUTY) by Jason Turner. LLVM’s [CMake Primer](https://llvm.org/docs/CMakePrimer.html) provides a good high-level introduction to the CMake syntax. Go read it now. After that, watch Mathieu Ropert’s CppCon 2017 talk [Using Modern CMake Patterns to Enforce a Good Modular Design](https://www.youtube.com/watch?v=eC9-iRN2b04) ([slides](https://github.com/CppCon/CppCon2017/blob/master/Tutorials/Using%20Modern%20CMake%20Patterns%20to%20Enforce%20a%20Good%20Modular%20Design/Using%20Modern%20CMake%20Patterns%20to%20Enforce%20a%20Good%20Modular%20Design%20-%20Mathieu%20Ropert%20-%20CppCon%202017.pdf)). It provides a thorough explanation of what modern CMake is and why it is so much better than “old school” CMake. The modular design ideas in this talk are based on the book [Large-Scale C++ Software Design](https://www.amazon.de/Large-Scale-Software-Addison-Wesley-Professional-Computing/dp/0201633620) by John Lakos. The next video that goes more into the details of modern CMake is Daniel Pfeifer’s C++Now 2017 talk [Effective CMake](https://www.youtube.com/watch?v=bsXLMQ6WgIk) ([slides](https://github.com/boostcon/cppnow_presentations_2017/blob/master/05-19-2017_friday/effective_cmake__daniel_pfeifer__cppnow_05-19-2017.pdf)). This text is heavily influenced by Mathieu Ropert’s and Daniel Pfeifer’s talks. If you are interested in the history and internal architecture of CMake, have a look at the article [CMake](http://www.aosabook.org/en/cmake.html) in the book [The Architecture of Open Source Applications](http://aosabook.org/en/index.html). ## General ### Use at least CMake version 3.0.0. Modern CMake is only available starting with version 3.0.0. ### Treat CMake code like production code. CMake is code. Therefore, it should be clean. Use the same principles for `CMakeLists.txt` and modules as for the rest of the codebase. ### Define project properties globally. For example, a project might use a common set of compiler warnings. Defining such properties globally in the top-level `CMakeLists.txt` file prevents scenarios where public headers of a dependent target causing a depending target not to compile because the depending target uses stricter compiler options. Defining such project properties globally makes it easier to manage the project with all its targets. ### Forget the commands `add_compiler_options`, `include_directories`, `link_directories`, `link_libraries`. Those commands operate on the directory level. All targets defined on that level inherit those properties. This increases the chance of hidden dependencies. Better operate on the targets directly. ### Get your hands off `CMAKE_CXX_FLAGS`. Different compilers use different command-line parameter formats. Setting the C++ standard via `-std=c++14` in `CMAKE_CXX_FLAGS` will brake in the future, because those requirements are also fulfilled in other standards like C++17 and the compiler option is not the same on old compilers. So it’s much better to tell CMake the compile features so that it can figure out the appropriate compiler option to use. ### Don’t abuse usage requirements. As an example, don’t add `-Wall` to the `PUBLIC` or `INTERFACE` section of `target_compile_options`, since it is not required to build depending targets. ## Modules ### Use modern find modules that declare exported targets. Starting with CMake 3.4, more and more find modules export targets that can be used via `target_link_libraries`. ### Use exported targets of external packages. Don’t fall back to the old CMake style of using variables defined by external packages. Use the exported targets via `target_link_libraries` instead. ### Use a find module for third-party libraries that do not support clients to use CMake. CMake provides a collection of find modules for third-party libraries. For example, Boost doesn't support CMake. Instead, CMake provides a find module to use Boost in CMake. ### Report it as a bug to third-party library authors if a library does not support clients to use CMake. CMake dominates the industry. It’s a problem if a library author does not support CMake. ### Write a find module for third-party libraries that do not support clients to use CMake. It’s possible to retrofit a find module that properly exports targets to an external package that does not support CMake. ### Export your library’s interface, if you are a library author. See Daniel Pfeifer’s C++Now 2017 talk [Effective CMake](https://youtu.be/bsXLMQ6WgIk?t=37m15s) ([slide](https://github.com/boostcon/cppnow_presentations_2017/blob/master/05-19-2017_friday/effective_cmake__daniel_pfeifer__cppnow_05-19-2017.pdf) 24ff.) on how to do this. Keep in mind to export the right information. Use `BUILD_INTERFACE` and `INSTALL_INTERFACE` generator expressions as filters. ## Projects ### Avoid custom variables in the arguments of project commands. t.b.d. (see beginning of Daniel Pfeifer’s talk) ### Don't use `file(GLOB)` in projects. CMake is a build system generator, not a build system. It evaluates the `GLOB` expression to a list of files when generating the build system. The build system then operates on this list of files. Therefore, the build system cannot detect that something changed in the file system. CMake cannot just forward the `GLOB` expression to the build system, so that the expression is evaluated when building. CMake wants to be the common denominator of the supported build systems. Not all build systems support this, so CMake cannot support it neither. ### Put CI-specific settings in CTest scripts, not in the project. It just makes things simpler. See Dashboard Client via CTest Script for more information. ### Follow a naming convention for test names. This simplifies filtering by regex when running tests via CTest.