Skip to content

Creating a cppdep.yaml file from CMake

jsinge edited this page Jan 16, 2022 · 1 revision

In a large scale project you might not want to maintain yet another config file with all the packages and source locations. This script may be helpful to create a cppdep.yaml config file automatically for all targets in a CMake project.

First, add a cppdep-header.yaml with the static parts of the config, i.e. the external packages and name of your package group:

external:
  - name: Platform
    path: /
    packages:
      - name: Standard Libraries
        pattern: 
          - "[a-z_]+$"
          - ^std[a-z_]+.h$
          - string.h
          - math.h 
          - limits.h
  - name: Thirdparties
    path: /
    packages:
      - name: boost
        pattern:
          - boost\\      
    
internal:  # A list of package groups for analysis.
  - name: your-project-name  # The name of a package-group.
    path: C:/projects/your-project-name  # The root path for the packages in the group.
    packages:  # A list of member packages, generated by CMake.

Then, add a file cppdep.cmake to your project with these contents:

function(get_all_targets result targets_category)
    set(targets)
    get_all_targets_recursive(targets ${CMAKE_CURRENT_SOURCE_DIR} ${targets_category})
    set(${result} ${targets} PARENT_SCOPE)
endfunction()

macro(get_all_targets_recursive result dir targets_category)
    get_property(subdirectories DIRECTORY ${dir} PROPERTY SUBDIRECTORIES)
    foreach(subdir ${subdirectories})
        get_all_targets_recursive(${result} ${subdir} ${targets_category})
    endforeach()

    get_property(current_targets DIRECTORY ${dir} PROPERTY ${targets_category})
    list(APPEND ${result} ${current_targets})
endmacro()


function(cppdep_get_package_yaml_section target target_type_exclusion_regex target_exclusion_regex result )
    set(package_description)
    get_target_property(target_source_dir ${target} SOURCE_DIR)
    get_target_property(sourcefiles ${target} SOURCES)
    get_target_property(target_type ${target} TYPE)
    if( NOT sourcefiles
            OR (target_type_exclusion_regex AND target_type MATCHES "${target_type_exclusion_regex}")
            OR (target_exclusion_regex AND target MATCHES "${target_exclusion_regex}"))
        set(${result} "" PARENT_SCOPE)
        return()
    endif()
    set(package_description "      - name: ${target}\n        include:\n            - .\n        src:\n")
    foreach(src ${sourcefiles})
        string(APPEND package_description "              - ${target_source_dir}/${src}\n")
    endforeach()
    set(${result} ${package_description} PARENT_SCOPE)
endfunction()


function(cppdep_create_yaml)
    cmake_parse_arguments(arg "" "TARGET_TYPE_EXCLUSION_REGEX;TARGET_EXCLUSION_REGEX" "" "${ARGN}")

    if(arg_UNPARSED_ARGUMENTS)
        message( SEND_ERROR "Unknown arguments: ${arg_UNPARSED_ARGUMENTS}")
    endif()

    file(READ "${CMAKE_SOURCE_DIR}/cppdep-header.yaml" cppdep_header)
    SET(cppdep_yaml_contents "${cppdep_header}\n")

    get_all_targets(build_targets BUILDSYSTEM_TARGETS)
    foreach(target ${build_targets})
        cppdep_get_package_yaml_section(${target} "${arg_TARGET_TYPE_EXCLUSION_REGEX}" "${arg_TARGET_EXCLUSION_REGEX}" section)
        string(APPEND cppdep_yaml_contents ${section})
    endforeach()

    file(GENERATE OUTPUT "${PROJECT_BINARY_DIR}/cppdep.yaml" CONTENT ${cppdep_yaml_contents})
endfunction()

Invoke the cppdep_create_yaml method like this at the end of the root CMakeLists.txt:

Include(cppdep.cmake)
cppdep_create_yaml()

Or if you want to exclude some of the targets by their name:

cppdep_create_yaml(TARGET_EXCLUSION_REGEX ".*Test")

Now, CMake will create a cppdep.yaml file in the builder folder during project configuration that may be used for the cppdep analysis.