cmake
Use when writing CMakeLists.txt, out-of-source builds, target_link_libraries, target properties, find_package/FetchContent, toolchain files, CPack, CMake presets, or cmake configure errors.
Install
npx skills add https://github.com/OutlineDriven/outline-driven-development/tree/main/.devin/skills/cmake
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install outlinedriven-outline-driven-development@llmmart
git clone https://github.com/OutlineDriven/outline-driven-development.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole outlinedriven/outline-driven-development collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
CMake
Modern target-first CMake for C/C++ projects. Grounded against CMake 4.4.3; the examples floor at CMake 3.25 so the preset schema version 6 works.
Contract
| Field | Bound contract |
|---|---|
| Trigger | The task writes or refactors CMakeLists.txt, configures out-of-source builds, selects generators, manages targets with target_link_libraries, integrates packages via find_package or FetchContent, enables sanitizers, writes toolchain files, sets up presets, or resolves a cmake configure error. |
| Authority | Reversible local: writes only CMakeLists.txt, CMakePresets.json, toolchain files, and the build directory; rollback is version control plus deleting the build directory. No remote mutation. |
| Side effect | cmake --build and cmake --install write local build outputs and install trees; FetchContent downloads declared dependencies. |
| Done | The project configures and builds with the requested options, or the failing command and its error are reported. |
Inputs
- Project layout (required): sources, headers, and where the root
CMakeLists.txtlives. - CMake version (required if not inferrable): run
cmake --version. Preset schema version 6 needs CMake 3.25 or newer. - Dependencies (optional): system packages, FetchContent sources, or vendored trees.
- Target platform (optional): a cross-compilation target needs a toolchain file.
Procedure
Apply modern CMake rules. Define targets, not variables. Use
target_*commands withPUBLIC/PRIVATE/INTERFACEto control propagation. Never useinclude_directories()orlink_libraries(); they are legacy global state. Done when: every property attaches to a target.Write the minimal project. Done when:
cmake -S . -B buildconfigures cleanly.
cmake_minimum_required(VERSION 3.25)
project(MyApp VERSION 1.0 LANGUAGES C CXX)
set(CMAKE_C_STANDARD 23)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # clang-tidy and IDEs read this
add_executable(myapp src/main.c src/utils.c)
target_include_directories(myapp PRIVATE include)
target_compile_options(myapp PRIVATE -Wall -Wextra)
- Add libraries and link them. Done when: each library target carries its own include paths and the executable links against it.
add_library(mylib STATIC lib/foo.c lib/bar.c)
target_include_directories(mylib
PUBLIC include # consumers inherit this
PRIVATE src # only mylib sees this
)
add_library(myshared SHARED lib/foo.c)
set_target_properties(myshared PROPERTIES VERSION 1.0.0 SOVERSION 1)
target_link_libraries(myapp PRIVATE mylib)
- Configure and build out of source. Done when: the build completes in a separate directory.
cmake -S . -B build -G Ninja
cmake --build build -- -j"$(nproc)"
cmake -S . -B build-debug -DCMAKE_BUILD_TYPE=Debug
cmake -S . -B build-release -DCMAKE_BUILD_TYPE=Release
cmake --install build-release --prefix /usr/local
Build types: Debug, Release, RelWithDebInfo, MinSizeRel.
- Integrate external dependencies. Prefer
find_packagefor system libraries,FetchContentfor source builds,pkg_check_modulesas a fallback. Done when: the dependency resolves and links.
find_package(OpenSSL REQUIRED)
find_package(Threads REQUIRED)
find_package(ZLIB REQUIRED)
target_link_libraries(myapp PRIVATE OpenSSL::SSL OpenSSL::Crypto Threads::Threads ZLIB::ZLIB)
include(FetchContent)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG v1.17.0
GIT_SHALLOW TRUE
)
FetchContent_MakeAvailable(googletest)
find_package(PkgConfig REQUIRED)
pkg_check_modules(LIBFOO REQUIRED libfoo>=1.2)
target_link_libraries(myapp PRIVATE ${LIBFOO_LIBRARIES})
target_include_directories(myapp PRIVATE ${LIBFOO_INCLUDE_DIRS})
- Set per-configuration flags with generator expressions. Done when: each flag applies only to its intended configuration or compiler.
target_compile_options(myapp PRIVATE
$<$<CONFIG:Debug>:-g -Og -fsanitize=address>
$<$<CONFIG:Release>:-O2 -DNDEBUG>
$<$<CXX_COMPILER_ID:GNU>:-fanalyzer>
)
target_link_options(myapp PRIVATE
$<$<CONFIG:Debug>:-fsanitize=address>
)
- Gate sanitizers behind an option. Done when:
cmake -DENABLE_ASAN=ONproduces an instrumented build.
option(ENABLE_ASAN "Enable AddressSanitizer" OFF)
if(ENABLE_ASAN)
target_compile_options(myapp PRIVATE -fsanitize=address -fno-omit-frame-pointer -g -O1)
target_link_options(myapp PRIVATE -fsanitize=address)
endif()
- Write a toolchain file for cross-compilation. Done when:
cmake -DCMAKE_TOOLCHAIN_FILE=...configures for the target.
# toolchain-aarch64.cmake
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)
set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++)
set(CMAKE_SYSROOT /opt/aarch64-sysroot)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
- Capture common configurations in
CMakePresets.json. Schema version 6 requires CMake 3.25 or newer. Done when:cmake --preset <name>configures andcmake --build --preset <name>builds.
{
"version": 6,
"configurePresets": [
{
"name": "release",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/release",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
}
},
{
"name": "debug",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/debug",
"cacheVariables": { "CMAKE_BUILD_TYPE": "Debug", "ENABLE_ASAN": "ON" }
}
],
"buildPresets": [
{ "name": "release", "configurePreset": "release" },
{ "name": "debug", "configurePreset": "debug" }
]
}
- Diagnose configure errors against this table. Done when: the error maps to a cause and a fix.
| Error | Cause | Fix |
|---|---|---|
Could not find package Foo |
Package missing or wrong prefix | Install the dev package; set CMAKE_PREFIX_PATH |
No CMAKE_CXX_COMPILER |
No C++ compiler found | Install g++ or clang++; check PATH |
target_link_libraries called with wrong number of arguments |
Missing PUBLIC/PRIVATE/INTERFACE |
Add the keyword |
Cannot find source file |
Typo or wrong relative path | Check the path relative to CMakeLists.txt |
Compatibility error on old cmake_minimum_required |
CMake 4 dropped compatibility with versions below 3.5 | Raise the floor to 3.25 |
For complete project templates see references/templates.md.
Failure and recovery
find_packagefails: install the-devpackage or setCMAKE_PREFIX_PATH; fall back toFetchContentwhen no system package exists.- FetchContent fails offline: pre-seed
FETCHCONTENT_SOURCE_DIR_<NAME>with a local copy. - Generator expression error: check the expression name against
cmake --help-manual cmake-generator-expressions. - Preset rejected: confirm
cmake --versionmeets the schema version's floor. - Stale cache after option changes: delete the build directory or pass
-Uto clear a cache entry; do not editCMakeCache.txtby hand.
Output
A working CMakeLists.txt or build configuration, verified by a successful cmake -S . -B build and cmake --build build. For debugging tasks, the diagnosed error with its cause and applied fix.
Files (outline-driven-development)
-
agents
-
openai.yaml 291 B
interface: display_name: "Cmake" short_description: "Use when writing CMakeLists.txt, out-of-source builds, target_link_libraries, target properties, find_package/FetchContent, toolchain files, CPack, CMake presets, or cmake configure errors." policy: allow_implicit_invocation: false
-
-
references
-
templates.md 3.2 KB
# CMake project templates Floors at CMake 3.25 so preset schema version 6 and current commands apply. Standards follow the repo floor: C23 and C++23. ## Minimal C library plus executable ```text myproject/ ├── CMakeLists.txt ├── include/mylib/mylib.h ├── src/mylib.c ├── src/main.c └── test/test_mylib.c ``` ```cmake cmake_minimum_required(VERSION 3.25) project(MyProject VERSION 1.0 LANGUAGES C) set(CMAKE_C_STANDARD 23) set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) add_library(mylib STATIC src/mylib.c) target_include_directories(mylib PUBLIC include) target_compile_options(mylib PRIVATE -Wall -Wextra) add_executable(myapp src/main.c) target_link_libraries(myapp PRIVATE mylib) enable_testing() add_executable(test_mylib test/test_mylib.c) target_link_libraries(test_mylib PRIVATE mylib) add_test(NAME mylib_tests COMMAND test_mylib) install(TARGETS myapp RUNTIME DESTINATION bin) install(TARGETS mylib ARCHIVE DESTINATION lib) install(DIRECTORY include/ DESTINATION include) ``` ## C++ project with GoogleTest via FetchContent ```cmake cmake_minimum_required(VERSION 3.25) project(MyProject VERSION 1.0 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED ON) add_library(mylib STATIC src/engine.cpp src/parser.cpp) target_include_directories(mylib PUBLIC include PRIVATE src) target_compile_options(mylib PRIVATE -Wall -Wextra $<$<CONFIG:Debug>:-g -Og> $<$<CONFIG:Release>:-O2 -DNDEBUG> ) add_executable(myapp src/main.cpp) target_link_libraries(myapp PRIVATE mylib) include(FetchContent) FetchContent_Declare( googletest GIT_REPOSITORY https://github.com/google/googletest.git GIT_TAG v1.17.0 GIT_SHALLOW TRUE ) set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) FetchContent_MakeAvailable(googletest) enable_testing() add_executable(unit_tests test/test_engine.cpp test/test_parser.cpp) target_link_libraries(unit_tests PRIVATE mylib GTest::gtest_main) include(GoogleTest) gtest_discover_tests(unit_tests) ``` ## Sanitizer option pattern ```cmake option(SANITIZE_ADDRESS "Enable ASan" OFF) option(SANITIZE_THREAD "Enable TSan" OFF) option(SANITIZE_UNDEFINED "Enable UBSan" OFF) set(SANITIZER_FLAGS "") if(SANITIZE_ADDRESS) list(APPEND SANITIZER_FLAGS -fsanitize=address -fno-omit-frame-pointer) endif() if(SANITIZE_THREAD) list(APPEND SANITIZER_FLAGS -fsanitize=thread) endif() if(SANITIZE_UNDEFINED) list(APPEND SANITIZER_FLAGS -fsanitize=undefined) endif() if(SANITIZER_FLAGS) add_compile_options(${SANITIZER_FLAGS}) add_link_options(${SANITIZER_FLAGS}) endif() ``` ASan and TSan are mutually exclusive; do not enable both in one build. ## Dependency management patterns System package with a FetchContent fallback: ```cmake find_package(ZLIB QUIET) if(NOT ZLIB_FOUND) include(FetchContent) FetchContent_Declare(zlib GIT_REPOSITORY https://github.com/madler/zlib.git GIT_TAG v1.3.1 ) FetchContent_MakeAvailable(zlib) set(ZLIB_TARGET zlibstatic) else() set(ZLIB_TARGET ZLIB::ZLIB) endif() target_link_libraries(myapp PRIVATE ${ZLIB_TARGET}) ``` Vendored in-tree library: ```cmake add_subdirectory(third_party/json) target_link_libraries(myapp PRIVATE nlohmann_json::nlohmann_json) ```
-
-
SKILL.md 7.6 KB
--- name: cmake description: 'Use when writing CMakeLists.txt, out-of-source builds, target_link_libraries, target properties, find_package/FetchContent, toolchain files, CPack, CMake presets, or cmake configure errors.' disable-model-invocation: true --- # CMake Modern target-first CMake for C/C++ projects. Grounded against CMake 4.4.3; the examples floor at CMake 3.25 so the preset schema version 6 works. ## Contract | Field | Bound contract | |---|---| | Trigger | The task writes or refactors `CMakeLists.txt`, configures out-of-source builds, selects generators, manages targets with `target_link_libraries`, integrates packages via `find_package` or `FetchContent`, enables sanitizers, writes toolchain files, sets up presets, or resolves a cmake configure error. | | Authority | Reversible local: writes only `CMakeLists.txt`, `CMakePresets.json`, toolchain files, and the build directory; rollback is version control plus deleting the build directory. No remote mutation. | | Side effect | `cmake --build` and `cmake --install` write local build outputs and install trees; `FetchContent` downloads declared dependencies. | | Done | The project configures and builds with the requested options, or the failing command and its error are reported. | ## Inputs - Project layout (required): sources, headers, and where the root `CMakeLists.txt` lives. - CMake version (required if not inferrable): run `cmake --version`. Preset schema version 6 needs CMake 3.25 or newer. - Dependencies (optional): system packages, FetchContent sources, or vendored trees. - Target platform (optional): a cross-compilation target needs a toolchain file. ## Procedure 1. Apply modern CMake rules. Define targets, not variables. Use `target_*` commands with `PUBLIC`/`PRIVATE`/`INTERFACE` to control propagation. Never use `include_directories()` or `link_libraries()`; they are legacy global state. Done when: every property attaches to a target. 2. Write the minimal project. Done when: `cmake -S . -B build` configures cleanly. ```cmake cmake_minimum_required(VERSION 3.25) project(MyApp VERSION 1.0 LANGUAGES C CXX) set(CMAKE_C_STANDARD 23) set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # clang-tidy and IDEs read this add_executable(myapp src/main.c src/utils.c) target_include_directories(myapp PRIVATE include) target_compile_options(myapp PRIVATE -Wall -Wextra) ``` 3. Add libraries and link them. Done when: each library target carries its own include paths and the executable links against it. ```cmake add_library(mylib STATIC lib/foo.c lib/bar.c) target_include_directories(mylib PUBLIC include # consumers inherit this PRIVATE src # only mylib sees this ) add_library(myshared SHARED lib/foo.c) set_target_properties(myshared PROPERTIES VERSION 1.0.0 SOVERSION 1) target_link_libraries(myapp PRIVATE mylib) ``` 4. Configure and build out of source. Done when: the build completes in a separate directory. ```bash cmake -S . -B build -G Ninja cmake --build build -- -j"$(nproc)" cmake -S . -B build-debug -DCMAKE_BUILD_TYPE=Debug cmake -S . -B build-release -DCMAKE_BUILD_TYPE=Release cmake --install build-release --prefix /usr/local ``` Build types: `Debug`, `Release`, `RelWithDebInfo`, `MinSizeRel`. 5. Integrate external dependencies. Prefer `find_package` for system libraries, `FetchContent` for source builds, `pkg_check_modules` as a fallback. Done when: the dependency resolves and links. ```cmake find_package(OpenSSL REQUIRED) find_package(Threads REQUIRED) find_package(ZLIB REQUIRED) target_link_libraries(myapp PRIVATE OpenSSL::SSL OpenSSL::Crypto Threads::Threads ZLIB::ZLIB) include(FetchContent) FetchContent_Declare( googletest GIT_REPOSITORY https://github.com/google/googletest.git GIT_TAG v1.17.0 GIT_SHALLOW TRUE ) FetchContent_MakeAvailable(googletest) find_package(PkgConfig REQUIRED) pkg_check_modules(LIBFOO REQUIRED libfoo>=1.2) target_link_libraries(myapp PRIVATE ${LIBFOO_LIBRARIES}) target_include_directories(myapp PRIVATE ${LIBFOO_INCLUDE_DIRS}) ``` 6. Set per-configuration flags with generator expressions. Done when: each flag applies only to its intended configuration or compiler. ```cmake target_compile_options(myapp PRIVATE $<$<CONFIG:Debug>:-g -Og -fsanitize=address> $<$<CONFIG:Release>:-O2 -DNDEBUG> $<$<CXX_COMPILER_ID:GNU>:-fanalyzer> ) target_link_options(myapp PRIVATE $<$<CONFIG:Debug>:-fsanitize=address> ) ``` 7. Gate sanitizers behind an option. Done when: `cmake -DENABLE_ASAN=ON` produces an instrumented build. ```cmake option(ENABLE_ASAN "Enable AddressSanitizer" OFF) if(ENABLE_ASAN) target_compile_options(myapp PRIVATE -fsanitize=address -fno-omit-frame-pointer -g -O1) target_link_options(myapp PRIVATE -fsanitize=address) endif() ``` 8. Write a toolchain file for cross-compilation. Done when: `cmake -DCMAKE_TOOLCHAIN_FILE=...` configures for the target. ```cmake # toolchain-aarch64.cmake set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR aarch64) set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc) set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++) set(CMAKE_SYSROOT /opt/aarch64-sysroot) set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) ``` 9. Capture common configurations in `CMakePresets.json`. Schema version 6 requires CMake 3.25 or newer. Done when: `cmake --preset <name>` configures and `cmake --build --preset <name>` builds. ```json { "version": 6, "configurePresets": [ { "name": "release", "generator": "Ninja", "binaryDir": "${sourceDir}/build/release", "cacheVariables": { "CMAKE_BUILD_TYPE": "Release", "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" } }, { "name": "debug", "generator": "Ninja", "binaryDir": "${sourceDir}/build/debug", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug", "ENABLE_ASAN": "ON" } } ], "buildPresets": [ { "name": "release", "configurePreset": "release" }, { "name": "debug", "configurePreset": "debug" } ] } ``` 10. Diagnose configure errors against this table. Done when: the error maps to a cause and a fix. | Error | Cause | Fix | |---|---|---| | `Could not find package Foo` | Package missing or wrong prefix | Install the dev package; set `CMAKE_PREFIX_PATH` | | `No CMAKE_CXX_COMPILER` | No C++ compiler found | Install g++ or clang++; check `PATH` | | `target_link_libraries called with wrong number of arguments` | Missing `PUBLIC`/`PRIVATE`/`INTERFACE` | Add the keyword | | `Cannot find source file` | Typo or wrong relative path | Check the path relative to `CMakeLists.txt` | | Compatibility error on old `cmake_minimum_required` | CMake 4 dropped compatibility with versions below 3.5 | Raise the floor to 3.25 | For complete project templates see `references/templates.md`. ## Failure and recovery - `find_package` fails: install the `-dev` package or set `CMAKE_PREFIX_PATH`; fall back to `FetchContent` when no system package exists. - FetchContent fails offline: pre-seed `FETCHCONTENT_SOURCE_DIR_<NAME>` with a local copy. - Generator expression error: check the expression name against `cmake --help-manual cmake-generator-expressions`. - Preset rejected: confirm `cmake --version` meets the schema version's floor. - Stale cache after option changes: delete the build directory or pass `-U` to clear a cache entry; do not edit `CMakeCache.txt` by hand. ## Output A working `CMakeLists.txt` or build configuration, verified by a successful `cmake -S . -B build` and `cmake --build build`. For debugging tasks, the diagnosed error with its cause and applied fix.
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.