Skip to content

Commit 9d8d967

Browse files
authored
fix: topological dependency sorting (#5647)
* fix: topological dependency sorting * chore: fix typo
1 parent 1b9cde3 commit 9d8d967

File tree

1 file changed

+45
-29
lines changed

1 file changed

+45
-29
lines changed

lib/services/android-project-service.ts

Lines changed: 45 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -59,58 +59,74 @@ interface NativeDependency {
5959
// native dependenciess need to be sorted so the deepst dependencies are built before it's parents
6060
//
6161
// for example, given this dep structure (assuming these are all native dependencies that need to be built)
62+
// (note: we list all dependencies at the root level, so the leaf nodes are essentially references to the root nodes)
63+
//
6264
// |- dep1
6365
// |- dep2
64-
// |- dep3
65-
// |- dep4
66-
// |-dep5
67-
// |- dep6
66+
// |- |- dep3
67+
// |- |- dep4
68+
// |- |- |- dep5
69+
// |- dep3
70+
// |- dep4
71+
// |- |- dep5
72+
// |- dep5
6873
//
6974
// It is sorted:
70-
// |- dep1 - doesn't depend on anything, so the order stays the same as in the input list
71-
// |- dep3 - doesn't depend on anything, so the order stays the same as in the input list
72-
// |- dep5 - doesn't depend on anything, so the order stays the same as in the input list
73-
// |- dep6 - doesn't depend on anything, so the order stays the same as in the input list
74-
// |- dep4 - depends on dep6, so dep6 must be built first, ie above ^
75-
// |- dep2 - depends on dep3, dep4, dep5 and dep6, so all of them must be built first
75+
//
76+
// |- dep1
77+
// |- dep3
78+
// |- dep5
79+
// |- dep4 # depends on dep5, so dep5 must be built first, ie above ^
80+
// |- |- dep5
81+
// |- dep2 # depends on dep3, dep4 (and dep5 through dep4) so all of them must be built first before dep2 is built
82+
// |- |- dep3
83+
// |- |- dep4
84+
// |- |- |- dep5
7685
//
7786
// for more details see: https://wikiless.org/wiki/Topological_sorting?lang=en
7887
//
7988
function topologicalSortNativeDependencies(
80-
nativeDeps: NativeDependency[],
89+
dependencies: NativeDependency[],
8190
start: NativeDependency[] = [],
82-
depth = 0
91+
depth = 0,
92+
total = 0 // do not pass in, we calculate it in the initial run!
8393
): NativeDependency[] {
84-
const processedDeps = nativeDeps.reduce(
85-
(accumulator, nativeDep: NativeDependency) => {
86-
if (
87-
nativeDep.dependencies.every(
88-
Array.prototype.includes,
89-
accumulator.map((n) => n.name)
90-
)
91-
) {
92-
accumulator.push(nativeDep);
94+
// we set the total on the initial call - and never increment it, as it's used for esacaping the recursion
95+
if (total === 0) {
96+
total = dependencies.length;
97+
}
98+
99+
const sortedDeps = dependencies.reduce(
100+
(sortedDeps, currentDependency: NativeDependency) => {
101+
const allSubDependenciesProcessed = currentDependency.dependencies.every(
102+
(subDependency) => {
103+
return sortedDeps.some((dep) => dep.name === subDependency);
104+
}
105+
);
106+
if (allSubDependenciesProcessed) {
107+
sortedDeps.push(currentDependency);
93108
}
94-
return accumulator;
109+
return sortedDeps;
95110
},
96111
start
97112
);
98113

99-
const remainingDeps = nativeDeps.filter(
100-
(nativeDep) => !processedDeps.includes(nativeDep)
114+
const remainingDeps = dependencies.filter(
115+
(nativeDep) => !sortedDeps.includes(nativeDep)
101116
);
102117

103-
// recurse if we still have unprocessed deps
118+
// recurse if we still have remaining deps
104119
// the second condition here prevents infinite recursion
105-
if (remainingDeps.length && depth <= nativeDeps.length) {
120+
if (remainingDeps.length && sortedDeps.length < total) {
106121
return topologicalSortNativeDependencies(
107122
remainingDeps,
108-
processedDeps,
109-
depth + 1
123+
sortedDeps,
124+
depth + 1,
125+
total
110126
);
111127
}
112128

113-
return processedDeps;
129+
return sortedDeps;
114130
}
115131

116132
export class AndroidProjectService extends projectServiceBaseLib.PlatformProjectServiceBase {

0 commit comments

Comments
 (0)