Lesson 30 · Reference

What not to use

Every bad habit from old CMake tutorials, explained. What it is, why it hurts, what to use instead, and what CMake 4.x actually deprecated.

This is the “skip the old tutorials” lesson. Every item here appears in old blog posts, and every one has a replacement with a concrete reason. Some items are officially deprecated. Others are officially discouraged. We mark the difference.

1. Global commands, discouraged

Old, globalPer-targetReason
include_directories(dir)target_include_directories(t PRIVATE/PUBLIC dir)Leaks into every target, including third-party
add_definitions(-DFOO)target_compile_definitions(t ...)Same leak
add_compile_options(...)target_compile_options(t ...)Same leak
link_libraries(lib)target_link_libraries(t ...)Same leak, plus no transitivity control

Why. Lesson 7’s whole point. Global settings apply to targets you do not own, and cannot express “who needs what”.

2. file(GLOB) for sources, discouraged

file(GLOB SRCS src/*.cpp)      # do not
add_executable(app ${SRCS})

The official docs warn against it. Adding a file does not trigger regeneration, and the result is silently missing files. CONFIGURE_DEPENDS is unreliable on some generators and costs a scan every rebuild.

Use instead. Explicit lists (lesson 19).

3. Global CMAKE_CXX_STANDARD, discouraged

set(CMAKE_CXX_STANDARD 20)     # global. Hides incompatibilities.

The official tutorial “cautions against globally setting CMAKE_<LANG>_STANDARD”.

Use instead. target_compile_features(t PUBLIC cxx_std_20) (lesson 12).

4. cmake_minimum_required below 3.5, an error on CMake 4.x

cmake_minimum_required(VERSION 3.4)   # error on CMake 4.x

Since CMake 4.0, a policy version below 3.5 is an error. The OLD behaviors of the oldest policies were removed.

Use instead. VERSION 4.4, or 3.28...4.4 (lesson 6).

5. Policies set to OLD, deprecated by definition

cmake_policy(SET CMP0079 OLD)    # silences warnings by choosing removed behavior

The docs. “The OLD behavior of a policy is deprecated by definition” and may be removed in a future version. OLD behaviors get removed on a schedule.

Use instead. NEW behavior and updated code (lesson 6).

6. Deprecated in CMake 4.4, the current release

From the 4.4 release notes:

Deprecated in 4.4Replacement
CMAKE_WARN_DEPRECATED / CMAKE_ERROR_DEPRECATED variablescmake_diagnostic()
write_basic_package_version_file(... COMPATIBILITY ExactVersion)AnyNewerVersion / SameMajorVersion / SameMinorVersion
-Wdev / --warn-uninitialized / --no-warn-unused-cli flags-Wauthor / -Wuninitialized / -Wno-unused-cli
MACROS directory property (removed, policy CMP0217)none

7. Deprecated in other 4.x releases

  • 4.3. CMAKE_ENABLE_EXPORTS, replaced by CMAKE_EXECUTABLE_ENABLE_EXPORTS and CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS. Also export(EXPORT) without arguments.
  • 4.2. The Visual Studio 14 2015 generator, “will be removed in a future version”. Also uppercased <Package>_FOUND variables.
  • 4.1. FindGTest result variables, GTEST_INCLUDE_DIRS and so on. Use GTest::gtest. Also FindProtobuf’s protobuf_generate_cpp() and protobuf_generate_python().
  • 4.0. The undocumented declarative form of ctest -S scripting was removed, the one triggered by a script that calls no CTest commands. Normal ctest -S dashboard scripts still work. Also FindGDAL was deprecated.

8. Style traps that cost you

  • if(MSVC) versus if(CMAKE_CXX_COMPILER_FRONTEND_VARIANT MATCHES "MSVC"). The second catches clang-cl. Use it (lesson 11).
  • No .gitignore for build/. Commit build dirs, lose your mind (lesson 4).
  • Branch-pinned FetchContent. GIT_TAG main breaks reproducibility. Hash-pin (lesson 18).
  • install() with absolute destinations. CPack silently ignores them (lesson 24).

Tip

The rule behind every entry. The tool gives you a way to say what and who. Use it. Global settings, GLOB and OLD policies exist because they were the only tools once. CMake grew better tools. The old ones stayed for compatibility, not because they are good.

Read the docs