|
| 1 | +--- |
| 2 | +paths: ["**/CMakeLists.txt", "**/*.cmake"] |
| 3 | +--- |
| 4 | + |
| 5 | +# CMake Guidelines |
| 6 | + |
| 7 | +> **Purpose**: Prevent CMake target name conflicts and build system issues |
| 8 | +> **Impact**: Eliminates build failures caused by duplicate or reserved target names |
| 9 | +
|
| 10 | +## Core Principle |
| 11 | + |
| 12 | +Always use unique, project-prefixed target names to avoid conflicts with CMake reserved names, dependency targets, and targets in other subdirectories. |
| 13 | + |
| 14 | +## Reserved Target Names |
| 15 | + |
| 16 | +Never use these names as CMake targets - they are reserved by CMake or common conventions: |
| 17 | + |
| 18 | +| Reserved Name | Reason | Alternative | |
| 19 | +|---------------|--------|-------------| |
| 20 | +| `test` | Reserved by CTest | `${PROJECT_NAME}_test` | |
| 21 | +| `install` | Reserved by CMake | `${PROJECT_NAME}_install` | |
| 22 | +| `clean` | Reserved by CMake | N/A (don't create) | |
| 23 | +| `all` | Reserved by CMake | N/A (don't create) | |
| 24 | +| `package` | Reserved by CPack | `${PROJECT_NAME}_package` | |
| 25 | +| `rebuild_cache` | Reserved by CMake | N/A | |
| 26 | +| `edit_cache` | Reserved by CMake | N/A | |
| 27 | +| `uninstall` | Common convention | `${PROJECT_NAME}_uninstall` | |
| 28 | + |
| 29 | +## Naming Conventions |
| 30 | + |
| 31 | +### Library Targets |
| 32 | + |
| 33 | +```cmake |
| 34 | +# Use project name as prefix |
| 35 | +add_library(${PROJECT_NAME}_core STATIC |
| 36 | + src/core.cpp |
| 37 | +) |
| 38 | +
|
| 39 | +add_library(${PROJECT_NAME}_utils STATIC |
| 40 | + src/utils.cpp |
| 41 | +) |
| 42 | +
|
| 43 | +# For header-only libraries |
| 44 | +add_library(${PROJECT_NAME}_headers INTERFACE) |
| 45 | +``` |
| 46 | + |
| 47 | +### Executable Targets |
| 48 | + |
| 49 | +```cmake |
| 50 | +# Main executable - can use project name directly |
| 51 | +add_executable(${PROJECT_NAME} |
| 52 | + src/main.cpp |
| 53 | +) |
| 54 | +
|
| 55 | +# Additional executables with descriptive suffixes |
| 56 | +add_executable(${PROJECT_NAME}_cli |
| 57 | + src/cli_main.cpp |
| 58 | +) |
| 59 | +
|
| 60 | +add_executable(${PROJECT_NAME}_server |
| 61 | + src/server_main.cpp |
| 62 | +) |
| 63 | +``` |
| 64 | + |
| 65 | +### Test Targets |
| 66 | + |
| 67 | +```cmake |
| 68 | +# Individual test executables |
| 69 | +add_executable(${PROJECT_NAME}_test_unit |
| 70 | + tests/unit_tests.cpp |
| 71 | +) |
| 72 | +
|
| 73 | +add_executable(${PROJECT_NAME}_test_integration |
| 74 | + tests/integration_tests.cpp |
| 75 | +) |
| 76 | +
|
| 77 | +# Register with CTest |
| 78 | +add_test(NAME ${PROJECT_NAME}_unit_tests |
| 79 | + COMMAND ${PROJECT_NAME}_test_unit |
| 80 | +) |
| 81 | +``` |
| 82 | + |
| 83 | +### Alias Targets |
| 84 | + |
| 85 | +```cmake |
| 86 | +# Create namespaced aliases for external consumption |
| 87 | +add_library(MyProject::core ALIAS ${PROJECT_NAME}_core) |
| 88 | +add_library(MyProject::utils ALIAS ${PROJECT_NAME}_utils) |
| 89 | +``` |
| 90 | + |
| 91 | +## Pre-Creation Verification |
| 92 | + |
| 93 | +Before adding a new target, verify it doesn't conflict: |
| 94 | + |
| 95 | +### 1. Check Existing Targets |
| 96 | + |
| 97 | +```bash |
| 98 | +# List all targets in the build directory |
| 99 | +cmake --build build --target help 2>/dev/null | grep -v "^\.\.\." | sort |
| 100 | + |
| 101 | +# Or using CMake directly |
| 102 | +cd build && cmake --graphviz=targets.dot .. && cat targets.dot |
| 103 | +``` |
| 104 | + |
| 105 | +### 2. Search for Potential Conflicts |
| 106 | + |
| 107 | +```bash |
| 108 | +# Search for target definitions in all CMakeLists.txt |
| 109 | +grep -rn "add_library\|add_executable" --include="CMakeLists.txt" . |
| 110 | + |
| 111 | +# Check imported targets from dependencies |
| 112 | +grep -rn "IMPORTED" --include="*.cmake" . |
| 113 | +``` |
| 114 | + |
| 115 | +### 3. Verify Against Dependencies |
| 116 | + |
| 117 | +```cmake |
| 118 | +# In CMakeLists.txt, check if target exists before creating |
| 119 | +if(NOT TARGET ${PROJECT_NAME}_mylib) |
| 120 | + add_library(${PROJECT_NAME}_mylib STATIC src/mylib.cpp) |
| 121 | +endif() |
| 122 | +``` |
| 123 | + |
| 124 | +## Common Conflict Scenarios |
| 125 | + |
| 126 | +### Scenario 1: Duplicate Target in Subdirectory |
| 127 | + |
| 128 | +``` |
| 129 | +CMake Error: add_library cannot create target "utils" because another |
| 130 | +target with the same name already exists. |
| 131 | +``` |
| 132 | + |
| 133 | +**Solution**: Use project-prefixed names |
| 134 | + |
| 135 | +```cmake |
| 136 | +# Bad |
| 137 | +add_library(utils ...) # Conflicts with parent/sibling directory |
| 138 | +
|
| 139 | +# Good |
| 140 | +add_library(${PROJECT_NAME}_utils ...) |
| 141 | +``` |
| 142 | + |
| 143 | +### Scenario 2: Conflict with Dependency Target |
| 144 | + |
| 145 | +``` |
| 146 | +CMake Error: add_library cannot create target "core" because an imported |
| 147 | +target with the same name already exists. |
| 148 | +``` |
| 149 | + |
| 150 | +**Solution**: Check imported targets and use unique names |
| 151 | + |
| 152 | +```cmake |
| 153 | +# Check if target already exists (from find_package) |
| 154 | +if(TARGET core) |
| 155 | + message(WARNING "Target 'core' already exists, using prefixed name") |
| 156 | +endif() |
| 157 | +
|
| 158 | +add_library(${PROJECT_NAME}_core ...) |
| 159 | +``` |
| 160 | + |
| 161 | +### Scenario 3: Reserved Name Collision |
| 162 | + |
| 163 | +``` |
| 164 | +CMake Error: The target name "test" is reserved or not valid for certain |
| 165 | +CMake features. |
| 166 | +``` |
| 167 | + |
| 168 | +**Solution**: Never use reserved names |
| 169 | + |
| 170 | +```cmake |
| 171 | +# Bad |
| 172 | +add_executable(test tests/main.cpp) |
| 173 | +
|
| 174 | +# Good |
| 175 | +add_executable(${PROJECT_NAME}_tests tests/main.cpp) |
| 176 | +``` |
| 177 | + |
| 178 | +## Debugging Commands |
| 179 | + |
| 180 | +### List All Targets |
| 181 | + |
| 182 | +```bash |
| 183 | +# Using cmake --build |
| 184 | +cmake --build build --target help |
| 185 | + |
| 186 | +# Using make (if using Makefiles) |
| 187 | +make -C build help |
| 188 | + |
| 189 | +# Using ninja (if using Ninja) |
| 190 | +ninja -C build -t targets all |
| 191 | +``` |
| 192 | + |
| 193 | +### Visualize Target Dependencies |
| 194 | + |
| 195 | +```bash |
| 196 | +# Generate Graphviz dot file |
| 197 | +cmake --graphviz=build/targets.dot -B build |
| 198 | + |
| 199 | +# Convert to PNG (requires graphviz) |
| 200 | +dot -Tpng build/targets.dot -o build/targets.png |
| 201 | +``` |
| 202 | + |
| 203 | +### Find Target Definition |
| 204 | + |
| 205 | +```bash |
| 206 | +# Search for where a target is defined |
| 207 | +grep -rn "add_library(mylib\|add_executable(mylib" --include="CMakeLists.txt" . |
| 208 | + |
| 209 | +# Search in cmake modules |
| 210 | +grep -rn "add_library(mylib\|IMPORTED" --include="*.cmake" /usr/share/cmake |
| 211 | +``` |
| 212 | + |
| 213 | +## Best Practices |
| 214 | + |
| 215 | +### Project Structure |
| 216 | + |
| 217 | +```cmake |
| 218 | +# Top-level CMakeLists.txt |
| 219 | +cmake_minimum_required(VERSION 3.16) |
| 220 | +project(MyProject VERSION 1.0.0) |
| 221 | +
|
| 222 | +# Set project-wide naming prefix |
| 223 | +set(TARGET_PREFIX ${PROJECT_NAME}) |
| 224 | +
|
| 225 | +# Add subdirectories |
| 226 | +add_subdirectory(src) |
| 227 | +add_subdirectory(tests) |
| 228 | +``` |
| 229 | + |
| 230 | +### Subdirectory Pattern |
| 231 | + |
| 232 | +```cmake |
| 233 | +# src/CMakeLists.txt |
| 234 | +add_library(${TARGET_PREFIX}_core |
| 235 | + core/core.cpp |
| 236 | + core/utils.cpp |
| 237 | +) |
| 238 | +
|
| 239 | +target_include_directories(${TARGET_PREFIX}_core |
| 240 | + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include |
| 241 | +) |
| 242 | +``` |
| 243 | + |
| 244 | +### Export Configuration |
| 245 | + |
| 246 | +```cmake |
| 247 | +# For install and package export |
| 248 | +install(TARGETS ${PROJECT_NAME}_core ${PROJECT_NAME}_utils |
| 249 | + EXPORT ${PROJECT_NAME}Targets |
| 250 | + LIBRARY DESTINATION lib |
| 251 | + ARCHIVE DESTINATION lib |
| 252 | + RUNTIME DESTINATION bin |
| 253 | +) |
| 254 | +
|
| 255 | +install(EXPORT ${PROJECT_NAME}Targets |
| 256 | + FILE ${PROJECT_NAME}Targets.cmake |
| 257 | + NAMESPACE ${PROJECT_NAME}:: |
| 258 | + DESTINATION lib/cmake/${PROJECT_NAME} |
| 259 | +) |
| 260 | +``` |
| 261 | + |
| 262 | +## Checklist Before Commit |
| 263 | + |
| 264 | +- [ ] All targets use `${PROJECT_NAME}_` prefix |
| 265 | +- [ ] No reserved names used (test, install, clean, all, package) |
| 266 | +- [ ] Alias targets use namespace pattern (`MyProject::target`) |
| 267 | +- [ ] Test targets registered with CTest using unique names |
| 268 | +- [ ] No conflicts with imported dependency targets |
| 269 | +- [ ] `cmake --build build --target help` shows no duplicates |
0 commit comments