projectrules.ai

CMake Best Practices

CMakeBuild SystemCross-PlatformTestingC Programming

Description

CMake Build System Guidelines

Globs

**/*
---
description: CMake Build System Guidelines
globs: **/*
---

# CMake Best Practices

> **Related Guidelines:**
> - See [c.md] for general C programming guidelines
> - See [directory.md] for directory structure conventions

## General Guidelines
- Use modern CMake (3.10+) with target-based approach
- Specify minimum required CMake version
- Set project name and version information
- Use consistent indentation (2 spaces recommended)
- Organize CMake files hierarchically with subdirectories
- Use lowercase for commands and uppercase for variables
- Add meaningful comments for complex logic

## Project Structure
- Create a root CMakeLists.txt file
- Use add_subdirectory() for component directories
- Separate build configuration from source code
- Place CMake modules in cmake/ directory
- Use find_package() for external dependencies
- Create config files for package installation
- Use consistent naming conventions for targets

## Targets and Dependencies
- Use target_include_directories() with PUBLIC/PRIVATE/INTERFACE
- Use target_link_libraries() with PUBLIC/PRIVATE/INTERFACE
- Prefer PRIVATE visibility when possible
- Use target_compile_definitions() for preprocessor definitions
- Set target properties with set_target_properties()
- Use generator expressions for platform-specific settings
- Create INTERFACE libraries for header-only libraries

## Cross-Platform Considerations
- Use platform-independent paths with file(TO_CMAKE_PATH)
- Check for platform-specific features with if(CMAKE_SYSTEM_NAME)
- Use find_library() and find_path() for platform-specific libraries
- Set compiler flags conditionally based on compiler ID
- Use CMAKE_<LANG>_COMPILER_ID to detect compiler
- Test builds on all target platforms (Ubuntu, Debian, FreeBSD)
- Use CMAKE_INSTALL_PREFIX for installation paths

## Testing
- Use CTest for test automation
- Add tests with add_test() command
- Group tests with labels
- Set test properties with set_tests_properties()
- Create test fixtures for setup/teardown
- Enable testing with enable_testing()
- Configure test environment variables

## Build Types
- Set default build type if not specified
- Configure Debug and Release builds
- Set appropriate compiler flags for each build type
- Use CMAKE_BUILD_TYPE to control build configuration
- Add sanitizer builds for development
- Configure optimization levels appropriately
- Set debug symbol generation options

## Project-Specific Guidelines
- Support cross-compilation for FreeBSD
- Configure TweetNaCl as a local dependency
- Set up OpenSSL dependency correctly
- Configure thread support with Pthreads
- Set up module compilation as shared libraries (.so)
- Configure installation paths for binaries and modules
- Set up version information for modules

## Core Principles

### Modern CMake Philosophy
- Use **target-based** approach rather than variable-based approach
- Think in terms of dependencies between targets
- Prefer to set properties on targets rather than using global variables

## Version and Project Setup

### Minimum Requirements
Always start CMakeLists.txt with version requirement:
```cmake
cmake_minimum_required(VERSION 3.16)
```

### Project Declaration
Use detailed project declaration:
```cmake
project(RemoteAgentSystem
        VERSION 1.0.0
        DESCRIPTION "Secure cross-platform remote management system"
        LANGUAGES C)
```

## Target-Based Programming

### Creating Targets
Define clear targets for each component:
```cmake
# Library targets
add_library(agent_lib STATIC
    src/agent/network.c
    src/agent/protocol.c
    src/agent/security.c
)

# Executable targets
add_executable(agent
    src/agent/main.c
)
```

### Configuring Targets
Set properties on specific targets:
```cmake
# Include directories - ONLY for this specific target
target_include_directories(agent_lib
    PUBLIC
        ${CMAKE_CURRENT_SOURCE_DIR}/include
    PRIVATE
        ${CMAKE_CURRENT_SOURCE_DIR}/src/agent
)

# Link libraries
target_link_libraries(agent
    PRIVATE
        agent_lib
        ${CMAKE_DL_LIBS}  # For dlopen
        pthread
)

# Compile definitions
target_compile_definitions(agent_lib
    PRIVATE
        $<$:ENABLE_LOGGING>
)
```

## Modular Organization

### Avoid Global Settings
Prefer target-specific settings over global settings:

Avoid:
```cmake
include_directories(include)
add_definitions(-DSOME_DEFINE)
```

Prefer:
```cmake
target_include_directories(target_name PRIVATE include)
target_compile_definitions(target_name PRIVATE SOME_DEFINE)
```

### Organize Complex Projects
Use subdirectories to split complex builds:
```cmake
add_subdirectory(src/agent)
add_subdirectory(src/client)
add_subdirectory(src/modules)
```

Use separate files for complex find logic:
```cmake
include(cmake/FindTweetNaCl.cmake)
```

## Cross-Platform Considerations

### Platform-Specific Logic
Use generator expressions for platform-specific settings:
```cmake
target_compile_options(agent PRIVATE
    $<$:-Wall -Wextra>
    $<$:-Wall -Wextra -Werror>
)
```

### Conditional Logic
Use clear conditional logic when necessary:
```cmake
if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
    # FreeBSD-specific settings
    target_link_libraries(proclist_module PRIVATE kvm util)
endif()
```

## Options and Configuration

### User Options
Define user options with clear descriptions:
```cmake
option(BUILD_TESTING "Build the testing tree" ON)
option(ENABLE_SECURITY "Enable security features" ON)
```

### Conditional Features
Build features conditionally:
```cmake
if(BUILD_TESTING)
    enable_testing()
    add_subdirectory(tests)
endif()
```

## Testing

### Enable Testing
Set up testing properly:
```cmake
if(BUILD_TESTING)
    enable_testing()

    # Add test executable
    add_executable(test_network tests/test_network.c)
    target_link_libraries(test_network PRIVATE agent_lib)

    # Register with CTest
    add_test(NAME NetworkTest COMMAND test_network)
endif()
```

### Test with Different Configurations
Test under various conditions:
```cmake
add_test(NAME HighLatencyTest
         COMMAND test_transfer --latency=200ms)
```

## Installation

### Define Installation Rules
Set up proper installation rules:
```cmake
install(TARGETS agent client
        RUNTIME DESTINATION bin)

install(FILES ${CMAKE_CURRENT_BINARY_DIR}/config.h
        DESTINATION include)
```

### Configure Package
For distributable packages:
```cmake
include(CMakePackageConfigHelpers)
configure_package_config_file(
    ${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in
    ${CMAKE_CURRENT_BINARY_DIR}/RemoteAgentSystemConfig.cmake
    INSTALL_DESTINATION lib/cmake/RemoteAgentSystem
)
```

## Performance Best Practices

### Avoid File Globbing
Explicitly list source files instead of using globbing:

Avoid:
```cmake
file(GLOB SOURCES src/*.c)
add_executable(app ${SOURCES})
```

Prefer:
```cmake
add_executable(app
    src/main.c
    src/network.c
    src/protocol.c
)
```

### Precompiled Headers
For large projects, use precompiled headers:
```cmake
target_precompile_headers(agent_lib
    PRIVATE
        
        
        
)
```

## References
- Modern CMake guidelines
- Professional CMake: A Practical Guide
- CMake Official Documentation