Skip to content

Commit

Permalink
cmake: extensions: Add zephyr_topological_sort()
Browse files Browse the repository at this point in the history
This function performs topological sorting of CMake targets. Its initial
use case in Zephyr will be for implementing sysbuild image dependencies,
i.e., specifying an image order for CMake configuration and flashing.

Sourced from a comment on PR #57884 (anchor: #discussion_r1206807021)

Authored-by: Torsten Rasmussen <Torsten.Rasmussen@nordicsemi.no>
Signed-off-by: Grzegorz Swiderski <grzegorz.swiderski@nordicsemi.no>
  • Loading branch information
57300 authored and carlescufi committed Sep 5, 2023
1 parent 35479cc commit 6c2ad89
Showing 1 changed file with 70 additions and 0 deletions.
70 changes: 70 additions & 0 deletions cmake/modules/extensions.cmake
Expand Up @@ -3005,6 +3005,76 @@ function(target_byproducts)
)
endfunction()

# Usage:
# topological_sort(TARGETS <target> [<target> ...]
# PROPERTY_NAME <property>
# RESULT <out-variable>)
#
# This function performs topological sorting of CMake targets using a specific
# <property>, which dictates target dependencies. A fatal error occurs if the
# provided dependencies cannot be met, e.g., if they contain cycles.
#
# TARGETS: List of target names.
# PROPERTY_NAME: Name of the target property to be used when sorting. For every
# target listed in TARGETS, this property must contain a list
# (possibly empty) of other targets, which this target depends on
# for a particular purpose. The property must not contain any
# target which is not also found in TARGETS.
# RESULT: Output variable, where the topologically sorted list of target
# names will be returned.
#
function(topological_sort)
cmake_parse_arguments(TS "" "RESULT;PROPERTY_NAME" "TARGETS" ${ARGN})

set(dep_targets)
set(start_targets)
set(sorted_targets)

foreach(target ${TS_TARGETS})
get_target_property(${target}_dependencies ${target} ${TS_PROPERTY_NAME})

if(${target}_dependencies)
list(APPEND dep_targets ${target})
else()
list(APPEND start_targets ${target})
endif()
endforeach()

while(TRUE)
list(POP_FRONT start_targets node)
list(APPEND sorted_targets ${node})
set(to_remove)
foreach(target ${dep_targets})
if("${node}" IN_LIST ${target}_dependencies)
list(REMOVE_ITEM ${target}_dependencies ${node})
if(NOT ${target}_dependencies)
list(APPEND start_targets ${target})
list(APPEND to_remove ${target})
endif()
endif()
endforeach()

foreach(target ${to_remove})
list(REMOVE_ITEM dep_targets ${target})
endforeach()
if(NOT start_targets)
break()
endif()
endwhile()

if(dep_targets)
foreach(target ${dep_targets})
get_target_property(deps ${target} ${TS_PROPERTY_NAME})
list(JOIN deps " " deps)
list(APPEND dep_string "${target} depends on: ${deps}")
endforeach()
list(JOIN dep_string "\n" dep_string)
message(FATAL_ERROR "Unmet or cyclic dependencies:\n${dep_string}")
endif()

set(${TS_RESULT} "${sorted_targets}" PARENT_SCOPE)
endfunction()

########################################################
# 4. Devicetree extensions
########################################################
Expand Down

0 comments on commit 6c2ad89

Please sign in to comment.