15 KiB
CMake Configuration Validation
Comprehensive guide to validating CMake configuration and catching dependency issues early.
Overview
The CMake validation toolkit provides four powerful tools to catch configuration issues before they cause build failures:
- validate-cmake-config.cmake - Validates CMake cache and configuration
- check-include-paths.sh - Verifies include paths in compile commands
- visualize-deps.py - Generates dependency graphs
- test-cmake-presets.sh - Tests all CMake presets
Quick Start
# 1. Validate configuration after running cmake
cmake --preset mac-dbg
cmake -P scripts/validate-cmake-config.cmake build
# 2. Check include paths
./scripts/check-include-paths.sh build
# 3. Visualize dependencies
python3 scripts/visualize-deps.py build --format graphviz --stats
# 4. Test all presets for your platform
./scripts/test-cmake-presets.sh --platform mac
Tool 1: validate-cmake-config.cmake
Purpose
Validates CMake configuration by checking:
- Required targets exist
- Feature flags are consistent
- Compiler settings are correct
- Platform-specific configuration (especially Windows/Abseil)
- Output directories are created
- Common configuration issues
Usage
# Validate default build directory
cmake -P scripts/validate-cmake-config.cmake
# Validate specific build directory
cmake -P scripts/validate-cmake-config.cmake build_ai
# Validate after configuration
cmake --preset win-ai
cmake -P scripts/validate-cmake-config.cmake build
Exit Codes
- 0 - All checks passed
- 1 - Validation failed (errors detected)
What It Checks
1. Required Targets
Ensures core targets exist:
yaze_common- Common interface library
2. Feature Flag Consistency
- When
YAZE_ENABLE_AIis ON,YAZE_ENABLE_GRPCmust also be ON - When
YAZE_ENABLE_GRPCis ON, validates gRPC version is set
3. Compiler Configuration
- C++ standard is set to 23
- MSVC runtime library is configured correctly on Windows
- Compiler flags are propagated correctly
4. Abseil Configuration (Windows)
CRITICAL for Windows builds with gRPC:
- Checks
CMAKE_MSVC_RUNTIME_LIBRARYis set toMultiThreaded - Validates
ABSL_PROPAGATE_CXX_STDis enabled - Verifies Abseil include directories exist
This prevents the "Abseil missing include paths" issue.
5. Output Directories
build/binexistsbuild/libexists
6. Common Issues
- LTO enabled in Debug builds (warning)
- Missing compile_commands.json
- Generator expressions not expanded
Example Output
=== CMake Configuration Validator ===
✓ Build directory: build
✓ Loaded 342 cache variables
=== Validating required targets ===
✓ Required target exists: yaze_common
=== Validating feature flags ===
✓ gRPC enabled: ON
✓ gRPC version: 1.67.1
✓ Tests enabled
✓ AI features enabled
=== Validating compiler flags ===
✓ C++ standard: 23
✓ CXX flags set: /EHsc /W4 /bigobj
=== Validating Windows/Abseil configuration ===
✓ MSVC runtime: MultiThreaded$<$<CONFIG:Debug>:Debug>
✓ Abseil CXX standard propagation enabled
=== Validation Summary ===
✓ All validation checks passed!
Configuration is ready for build
Tool 2: check-include-paths.sh
Purpose
Validates include paths in compile_commands.json to catch missing includes before compilation.
Key Problem Solved: On Windows, Abseil includes from gRPC were sometimes not propagated, causing build failures. This tool catches that early.
Usage
# Check default build directory
./scripts/check-include-paths.sh
# Check specific build directory
./scripts/check-include-paths.sh build_ai
# Verbose mode (shows all include directories)
VERBOSE=1 ./scripts/check-include-paths.sh build
Prerequisites
- jq (optional but recommended):
brew install jq/apt install jq - Without jq, uses basic grep parsing
What It Checks
1. Common Dependencies
- SDL2 includes
- ImGui includes
- yaml-cpp includes
2. Platform-Specific Includes
Validates platform-specific headers based on detected OS
3. Abseil Includes (Windows Critical)
When gRPC is enabled:
- Checks
build/_deps/grpc-build/third_party/abseil-cppexists - Validates Abseil paths are in compile commands
- Warns about unexpanded generator expressions
4. Suspicious Configurations
- No
-Iflags at all (error) - Relative paths with
../(warning) - Duplicate include paths (warning)
Exit Codes
- 0 - All checks passed or warnings only
- 1 - Critical errors detected
Example Output
=== Include Path Validation ===
Build directory: build
✓ Using jq for JSON parsing
=== Common Dependencies ===
✓ SDL2 includes found
✓ ImGui includes found
⚠ yaml-cpp includes not found (may be optional)
=== Platform-Specific Includes ===
Platform: macOS
✓ SDL2 framework/library
=== Checking Abseil Includes (Windows Issue) ===
gRPC build detected - checking Abseil paths...
✓ Abseil from gRPC build: build/_deps/grpc-build/third_party/abseil-cpp
=== Suspicious Configurations ===
✓ Include flags present (234/245 commands)
✓ No duplicate include paths
=== Summary ===
Checks performed: 5
Warnings: 1
✓ All include path checks passed!
Tool 3: visualize-deps.py
Purpose
Generates visual dependency graphs and detects circular dependencies.
Usage
# Generate GraphViz diagram (default)
python3 scripts/visualize-deps.py build
# Generate Mermaid diagram
python3 scripts/visualize-deps.py build --format mermaid -o deps.mmd
# Generate text tree
python3 scripts/visualize-deps.py build --format text
# Show statistics
python3 scripts/visualize-deps.py build --stats
Output Formats
1. GraphViz (DOT)
python3 scripts/visualize-deps.py build --format graphviz -o dependencies.dot
# Render to PNG
dot -Tpng dependencies.dot -o dependencies.png
# Render to SVG (better for large graphs)
dot -Tsvg dependencies.dot -o dependencies.svg
Color Coding:
- Blue boxes: Executables
- Green boxes: Libraries
- Gray boxes: Unknown type
- Red arrows: Circular dependencies
2. Mermaid
python3 scripts/visualize-deps.py build --format mermaid -o dependencies.mmd
View at https://mermaid.live/edit or include in Markdown:
```mermaid
graph LR
yaze_app-->yaze_lib
yaze_lib-->SDL2
```
3. Text Tree
python3 scripts/visualize-deps.py build --format text
Simple text representation for quick overview.
Circular Dependency Detection
The tool automatically detects and highlights circular dependencies:
✗ Found 1 circular dependencies
libA -> libB -> libC -> libA
Circular dependencies in graphs are shown with red arrows.
Statistics Output
With --stats flag:
=== Dependency Statistics ===
Total targets: 47
Total dependencies: 156
Average dependencies per target: 3.32
Most connected targets:
yaze_lib: 23 dependencies
yaze_app: 18 dependencies
yaze_cli: 15 dependencies
...
Tool 4: test-cmake-presets.sh
Purpose
Tests that all CMake presets can configure successfully, ensuring no configuration regressions.
Usage
# Test all presets for current platform
./scripts/test-cmake-presets.sh
# Test specific preset
./scripts/test-cmake-presets.sh --preset mac-ai
# Test only Mac presets
./scripts/test-cmake-presets.sh --platform mac
# Test in parallel (4 jobs)
./scripts/test-cmake-presets.sh --parallel 4
# Quick mode (don't clean between tests)
./scripts/test-cmake-presets.sh --quick
# Verbose output
./scripts/test-cmake-presets.sh --verbose
Options
| Option | Description |
|---|---|
--parallel N |
Test N presets in parallel (default: 4) |
--preset PRESET |
Test only specific preset |
--platform PLATFORM |
Test only presets for platform (mac/win/lin) |
--quick |
Skip cleaning between tests (faster) |
--verbose |
Show full CMake output |
Platform Detection
Automatically skips presets for other platforms:
- On macOS: Only tests
mac-*and generic presets - On Linux: Only tests
lin-*and generic presets - On Windows: Only tests
win-*and generic presets
Example Output
=== CMake Preset Configuration Tester ===
Platform: mac
Parallel jobs: 4
Presets to test:
- mac-dbg
- mac-rel
- mac-ai
- dev
- ci
Running tests in parallel (jobs: 4)...
✓ mac-dbg configured successfully (12s)
✓ dev configured successfully (15s)
✓ mac-rel configured successfully (11s)
✓ mac-ai configured successfully (45s)
✓ ci configured successfully (18s)
=== Test Summary ===
Total presets tested: 5
Passed: 5
Failed: 0
✓ All presets configured successfully!
Failure Handling
When a preset fails:
✗ win-ai failed (34s)
Log saved to: preset_test_win-ai.log
=== Test Summary ===
Total presets tested: 3
Passed: 2
Failed: 1
Failed presets:
- win-ai
Check log files for details: preset_test_*.log
Integration with CI
Add to GitHub Actions Workflow
name: CMake Validation
on: [push, pull_request]
jobs:
validate-cmake:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Configure CMake
run: cmake --preset ci-linux
- name: Validate Configuration
run: cmake -P scripts/validate-cmake-config.cmake build
- name: Check Include Paths
run: ./scripts/check-include-paths.sh build
- name: Detect Circular Dependencies
run: python3 scripts/visualize-deps.py build --stats
Pre-Configuration Check
Run validation as first CI step to fail fast:
- name: Fast Configuration Check
run: |
cmake --preset minimal
cmake -P scripts/validate-cmake-config.cmake build
Common Issues and Solutions
Issue 1: Missing Abseil Includes on Windows
Symptom:
✗ Missing required include: Abseil from gRPC build
Solution:
- Ensure
ABSL_PROPAGATE_CXX_STDis ON in cmake/dependencies/grpc.cmake - Reconfigure with
--fresh:cmake --preset win-ai --fresh - Check that gRPC was built successfully
Prevention:
Run cmake -P scripts/validate-cmake-config.cmake after every configuration.
Issue 2: Circular Dependencies
Symptom:
✗ Found 2 circular dependencies
libA -> libB -> libA
Solution:
- Visualize full graph:
python3 scripts/visualize-deps.py build --format graphviz -o deps.dot - Render:
dot -Tpng deps.dot -o deps.png - Identify and break cycles by:
- Moving shared code to a new library
- Using forward declarations instead of includes
- Restructuring dependencies
Issue 3: Preset Configuration Fails
Symptom:
✗ mac-ai failed (34s)
Log saved to: preset_test_mac-ai.log
Solution:
- Check log file:
cat preset_test_mac-ai.log - Common causes:
- Missing dependencies (gRPC build failure)
- Incompatible compiler flags
- Platform condition mismatch
- Test preset manually:
cmake --preset mac-ai -B test_build -v
Issue 4: Generator Expressions Not Expanded
Symptom:
⚠ Generator expressions found in compile commands (may not be expanded)
Solution:
This is usually harmless. Generator expressions like $<BUILD_INTERFACE:...> are CMake-internal and won't appear in final compile commands. If build fails, the issue is elsewhere.
Best Practices
1. Run Validation After Every Configuration
# Configure
cmake --preset mac-ai
# Validate immediately
cmake -P scripts/validate-cmake-config.cmake build
./scripts/check-include-paths.sh build
2. Test All Presets Before Committing
# Quick test of all platform presets
./scripts/test-cmake-presets.sh --platform mac --parallel 4
3. Check Dependencies When Adding New Targets
# After adding new target to CMakeLists.txt
cmake --preset dev
python3 scripts/visualize-deps.py build --stats
Look for:
- Unexpected high dependency counts
- New circular dependencies
4. Use in Git Hooks
Create .git/hooks/pre-commit:
#!/bin/bash
# Validate CMake configuration before commit
if [ -f "build/CMakeCache.txt" ]; then
echo "Validating CMake configuration..."
cmake -P scripts/validate-cmake-config.cmake build || exit 1
fi
5. Periodic Full Validation
Weekly or before releases:
# Full validation suite
./scripts/test-cmake-presets.sh --parallel 4
cmake --preset dev
cmake -P scripts/validate-cmake-config.cmake build
./scripts/check-include-paths.sh build
python3 scripts/visualize-deps.py build --format graphviz --stats -o deps.dot
Troubleshooting
Tool doesn't run on Windows
Bash scripts:
Use Git Bash, WSL, or MSYS2 to run .sh scripts.
CMake scripts: Should work natively on Windows:
cmake -P scripts\validate-cmake-config.cmake build
jq not found
Install jq for better JSON parsing:
# macOS
brew install jq
# Ubuntu/Debian
sudo apt install jq
# Windows (via Chocolatey)
choco install jq
Scripts will work without jq but with reduced functionality.
Python script fails
Ensure Python 3.7+ is installed:
python3 --version
No external dependencies required - uses only standard library.
GraphViz rendering fails
Install GraphViz:
# macOS
brew install graphviz
# Ubuntu/Debian
sudo apt install graphviz
# Windows (via Chocolatey)
choco install graphviz
Advanced Usage
Custom Validation Rules
Edit scripts/validate-cmake-config.cmake to add project-specific checks:
# Add after existing checks
log_header "Custom Project Checks"
if(DEFINED CACHE_MY_CUSTOM_FLAG)
if(CACHE_MY_CUSTOM_FLAG)
log_success "Custom flag enabled"
else()
log_error "Custom flag must be enabled for this build"
endif()
endif()
Automated Dependency Reports
Generate weekly dependency reports:
#!/bin/bash
# weekly-deps-report.sh
DATE=$(date +%Y-%m-%d)
REPORT_DIR="reports/$DATE"
mkdir -p "$REPORT_DIR"
# Configure
cmake --preset ci
# Generate all formats
python3 scripts/visualize-deps.py build \
--format graphviz --stats -o "$REPORT_DIR/deps.dot"
python3 scripts/visualize-deps.py build \
--format mermaid -o "$REPORT_DIR/deps.mmd"
python3 scripts/visualize-deps.py build \
--format text -o "$REPORT_DIR/deps.txt"
# Render GraphViz
dot -Tsvg "$REPORT_DIR/deps.dot" -o "$REPORT_DIR/deps.svg"
echo "Report generated in $REPORT_DIR"
CI Matrix Testing
Test all presets across platforms:
jobs:
test-presets:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- name: Test Presets
run: ./scripts/test-cmake-presets.sh --parallel 2
Quick Reference
| Task | Command |
|---|---|
| Validate config | cmake -P scripts/validate-cmake-config.cmake build |
| Check includes | ./scripts/check-include-paths.sh build |
| Visualize deps | python3 scripts/visualize-deps.py build |
| Test all presets | ./scripts/test-cmake-presets.sh |
| Test one preset | ./scripts/test-cmake-presets.sh --preset mac-ai |
| Generate PNG graph | python3 scripts/visualize-deps.py build -o d.dot && dot -Tpng d.dot -o d.png |
| Check for cycles | python3 scripts/visualize-deps.py build --stats |
| Verbose include check | VERBOSE=1 ./scripts/check-include-paths.sh build |
See Also
- Build Quick Reference - Build commands
- Build Troubleshooting - Common build issues
- CMakePresets.json - All available presets
- GitHub Actions Workflows - CI configuration