1# Copyright (c) 2007 Douglas Gregor <doug.gregor@gmail.com> Copyright 2007 Troy
2# Copyright (c) 2010 Kitware, Inc.
3# Copyright (c) 2019 - 2024 David Guibert
6# SPDX-License-Identifier: Apache-2.0
8# https://gitlab.kitware.com/paraview/common-superbuild/-/raw/master/cmake/TopologicalSort.cmake
9# Perform a reverse topological sort on the given LIST.
11# topological_sort(my_list "MY_" "_EDGES")
13# LIST is the name of a variable containing a list of elements to be sorted in
14# reverse topological order. Each element in the list has a set of outgoing
15# edges (for example, those other list elements that it depends on). In the
16# resulting reverse topological ordering (written back into the variable named
17# LIST), an element will come later in the list than any of the elements that
18# can be reached by following its outgoing edges and the outgoing edges of any
19# vertices they target, recursively. Thus, if the edges represent dependencies
20# on build targets, for example, the reverse topological ordering is the order
21# in which one would build those targets.
23# For each element E in this list, the edges for E are contained in the variable
24# named ${PREFIX}${E}${SUFFIX}. If no such variable exists, then it is assumed
25# that there are no edges. For example, if my_list contains a, b, and c, one
26# could provide a dependency graph using the following variables:
28# MY_A_EDGES b MY_B_EDGES MY_C_EDGES a b
30# With the involcation of topological_sort shown above and these variables, the
31# resulting reverse topological ordering will be b, a, c.
33# ##############################################################################
34# Modified from Boost Utilities
36# Copyright 2010 Kitware, Inc.
37# ##############################################################################
38# Copyright 2007 Douglas Gregor <doug.gregor@gmail.com> Copyright 2007 Troy
41# Distributed under the Boost Software License, Version 1.0.
42# ##############################################################################
43# Boost Software License - Version 1.0 - August 17th, 2003
45# Permission is hereby granted, free of charge, to any person or organization
46# obtaining a copy of the software and accompanying documentation covered by
47# this license (the "Software") to use, reproduce, display, distribute, execute,
48# and transmit the Software, and to prepare derivative works of the Software,
49# and to permit third-parties to whom the Software is furnished to do so, all
50# subject to the following:
52# The copyright notices in the Software and this entire statement, including the
53# above license grant, this restriction and the following disclaimer, must be
54# included in all copies of the Software, in whole or in part, and all
55# derivative works of the Software, unless such copies or derivative works are
56# solely in the form of machine-executable object code generated by a source
59# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
60# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
61# FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
62# SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR
63# ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
64# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
65# DEALINGS IN THE SOFTWARE.
66# ##############################################################################
68function(topological_sort LIST PREFIX SUFFIX)
69 # Clear the stack and output variable
70 set(VERTICES "${${LIST}}")
74 # Loop over all of the vertices, starting the topological sort from each one.
75 foreach(VERTEX ${VERTICES})
77 # If we haven't already processed this vertex, start a depth-first search
79 if(NOT FOUND_${VERTEX})
80 # Push this vertex onto the stack with all of its outgoing edges
81 string(REPLACE ";" " " NEW_ELEMENT
82 "${VERTEX};${${PREFIX}${VERTEX}${SUFFIX}}")
83 list(APPEND STACK ${NEW_ELEMENT})
85 # We've now seen this vertex
86 set(FOUND_${VERTEX} TRUE)
88 # While the depth-first search stack is not empty
89 list(LENGTH STACK STACK_LENGTH)
90 while(STACK_LENGTH GREATER 0)
91 # Remove the vertex and its remaining out-edges from the top of the
93 list(GET STACK -1 OUT_EDGES)
94 list(REMOVE_AT STACK -1)
96 # Get the source vertex and the list of out-edges
97 separate_arguments(OUT_EDGES)
98 list(GET OUT_EDGES 0 SOURCE)
99 list(REMOVE_AT OUT_EDGES 0)
101 # While there are still out-edges remaining
102 list(LENGTH OUT_EDGES OUT_DEGREE)
103 while(OUT_DEGREE GREATER 0)
104 # Pull off the first outgoing edge
105 list(GET OUT_EDGES 0 TARGET)
106 list(REMOVE_AT OUT_EDGES 0)
108 if(NOT FOUND_${TARGET})
109 # We have not seen the target before, so we will traverse its
110 # outgoing edges before coming back to our source. This is the key
111 # to the depth-first traversal.
113 # We've now seen this vertex
114 set(FOUND_${TARGET} TRUE)
116 # Push the remaining edges for the current vertex onto the stack
117 string(REPLACE ";" " " NEW_ELEMENT "${SOURCE};${OUT_EDGES}")
118 list(APPEND STACK ${NEW_ELEMENT})
120 # Setup the new source and outgoing edges
121 set(SOURCE ${TARGET})
122 set(OUT_EDGES ${${PREFIX}${SOURCE}${SUFFIX}})
123 endif(NOT FOUND_${TARGET})
125 list(LENGTH OUT_EDGES OUT_DEGREE)
126 endwhile(OUT_DEGREE GREATER 0)
128 # We have finished all of the outgoing edges for SOURCE; add it to the
130 list(APPEND ${LIST} ${SOURCE})
132 # Check the length of the stack
133 list(LENGTH STACK STACK_LENGTH)
134 endwhile(STACK_LENGTH GREATER 0)
135 endif(NOT FOUND_${VERTEX})
141endfunction(topological_sort)