@@ -48,6 +48,71 @@ import { IInjector } from "../common/definitions/yok";
4848import { injector } from "../common/yok" ;
4949import { INotConfiguredEnvOptions } from "../common/definitions/commands" ;
5050
51+ interface NativeDependency {
52+ name : string ;
53+ directory : string ;
54+ dependencies : string [ ] ;
55+ }
56+
57+ //
58+ // we sort the native dependencies topologically to make sure they are processed in the right order
59+ // native dependenciess need to be sorted so the deepst dependencies are built before it's parents
60+ //
61+ // for example, given this dep structure (assuming these are all native dependencies that need to be built)
62+ // |- dep1
63+ // |- dep2
64+ // |- dep3
65+ // |- dep4
66+ // |-dep5
67+ // |- dep6
68+ //
69+ // 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
76+ //
77+ // for more details see: https://wikiless.org/wiki/Topological_sorting?lang=en
78+ //
79+ function topologicalSortNativeDependencies (
80+ nativeDeps : NativeDependency [ ] ,
81+ start : NativeDependency [ ] = [ ] ,
82+ depth = 0
83+ ) : 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 ) ;
93+ }
94+ return accumulator ;
95+ } ,
96+ start
97+ ) ;
98+
99+ const remainingDeps = nativeDeps . filter (
100+ ( nativeDep ) => ! processedDeps . includes ( nativeDep )
101+ ) ;
102+
103+ // recurse if we still have unprocessed deps
104+ // the second condition here prevents infinite recursion
105+ if ( remainingDeps . length && depth <= nativeDeps . length ) {
106+ return topologicalSortNativeDependencies (
107+ remainingDeps ,
108+ processedDeps ,
109+ depth + 1
110+ ) ;
111+ }
112+
113+ return processedDeps ;
114+ }
115+
51116export class AndroidProjectService extends projectServiceBaseLib . PlatformProjectServiceBase {
52117 private static VALUES_DIRNAME = "values" ;
53118 private static VALUES_VERSION_DIRNAME_PREFIX =
@@ -635,10 +700,10 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject
635700 public async beforePrepareAllPlugins (
636701 projectData : IProjectData ,
637702 dependencies ?: IDependencyData [ ]
638- ) : Promise < void > {
703+ ) : Promise < IDependencyData [ ] > {
639704 if ( dependencies ) {
640705 dependencies = this . filterUniqueDependencies ( dependencies ) ;
641- this . provideDependenciesJson ( projectData , dependencies ) ;
706+ return this . provideDependenciesJson ( projectData , dependencies ) ;
642707 }
643708 }
644709
@@ -666,7 +731,7 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject
666731 private provideDependenciesJson (
667732 projectData : IProjectData ,
668733 dependencies : IDependencyData [ ]
669- ) : void {
734+ ) : IDependencyData [ ] {
670735 const platformDir = path . join (
671736 projectData . platformsDir ,
672737 AndroidProjectService . ANDROID_PLATFORM_NAME
@@ -675,15 +740,37 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject
675740 platformDir ,
676741 constants . DEPENDENCIES_JSON_NAME
677742 ) ;
678- const nativeDependencies = dependencies
679- . filter ( AndroidProjectService . isNativeAndroidDependency )
680- . map ( ( { name, directory } ) => ( {
681- name,
682- directory : path . relative ( platformDir , directory ) ,
683- } ) ) ;
684- const jsonContent = JSON . stringify ( nativeDependencies , null , 4 ) ;
743+ let nativeDependencyData = dependencies . filter (
744+ AndroidProjectService . isNativeAndroidDependency
745+ ) ;
685746
747+ let nativeDependencies = nativeDependencyData . map (
748+ ( { name, directory, dependencies } ) => {
749+ return {
750+ name,
751+ directory : path . relative ( platformDir , directory ) ,
752+ dependencies : dependencies . filter ( ( dep ) => {
753+ // filter out transient dependencies that don't have native dependencies
754+ return (
755+ nativeDependencyData . findIndex (
756+ ( nativeDep ) => nativeDep . name === dep
757+ ) !== - 1
758+ ) ;
759+ } ) ,
760+ } as NativeDependency ;
761+ }
762+ ) ;
763+ nativeDependencies = topologicalSortNativeDependencies ( nativeDependencies ) ;
764+ const jsonContent = JSON . stringify ( nativeDependencies , null , 4 ) ;
686765 this . $fs . writeFile ( dependenciesJsonPath , jsonContent ) ;
766+
767+ // we sort all the dependencies to respect the topological sorting of the native dependencies
768+ return dependencies . sort ( function ( a , b ) {
769+ return (
770+ nativeDependencies . findIndex ( ( n ) => n . name === a . name ) -
771+ nativeDependencies . findIndex ( ( n ) => n . name === b . name )
772+ ) ;
773+ } ) ;
687774 }
688775
689776 private static isNativeAndroidDependency ( {
0 commit comments