github.com/SAP/cloud-mta-build-tool@v1.2.27/internal/buildops/modules_deps.go (about) 1 package buildops 2 3 import ( 4 "fmt" 5 6 "github.com/deckarep/golang-set" 7 "github.com/pkg/errors" 8 9 "github.com/SAP/cloud-mta-build-tool/internal/archive" 10 "github.com/SAP/cloud-mta/mta" 11 ) 12 13 type graphNode struct { 14 module string 15 deps mapset.Set 16 index int 17 } 18 19 // ProvideModules - provides modules in order of their dependencies 20 func ProvideModules(source, desc string, extensions []string, wdGetter func() (string, error)) error { 21 loc, err := dir.Location(source, "", desc, extensions, wdGetter) 22 if err != nil { 23 return errors.Wrap(err, locFailedMsg) 24 } 25 m, err := loc.ParseFile() 26 if err != nil { 27 return err 28 } 29 modules, err := GetModulesNames(m) 30 if err != nil { 31 return err 32 } 33 // Get list of modules names 34 fmt.Println(modules) 35 return nil 36 } 37 38 // ProcessDependencies - processes module dependencies 39 // function prepares all artifacts required for module 40 // copying them from required modules 41 func ProcessDependencies(mtaParser dir.IMtaParser, moduleSource dir.ISourceModule, moduleName string) error { 42 m, err := mtaParser.ParseFile() 43 if err != nil { 44 return err 45 } 46 module, err := m.GetModuleByName(moduleName) 47 if err != nil { 48 return err 49 } 50 requires := GetBuildRequires(module) 51 if len(requires) > 0 { 52 for _, req := range requires { 53 e := ProcessRequirements(moduleSource, m, &req, module.Name) 54 if e != nil { 55 return e 56 } 57 } 58 } 59 return nil 60 } 61 62 // New graphs node 63 func newGn(module *string, deps mapset.Set, index int) *graphNode { 64 return &graphNode{module: *module, deps: deps, index: index} 65 } 66 67 // graphs - graph map 68 type graphs map[string]*graphNode 69 70 // GetModulesNames returns a list of module names. 71 func GetModulesNames(m *mta.MTA) ([]string, error) { 72 return getModulesOrder(m) 73 } 74 75 // getModulesOrder - Provides Modules ordered according to build-parameters' dependencies 76 func getModulesOrder(m *mta.MTA) ([]string, error) { 77 var graph = make(graphs) 78 for index, module := range m.Modules { 79 deps := mapset.NewSet() 80 requires := GetBuildRequires(module) 81 if len(requires) > 0 { 82 for _, req := range requires { 83 _, err := m.GetModuleByName(req.Name) 84 if err != nil { 85 return nil, err 86 } 87 deps.Add(req.Name) 88 } 89 } 90 graph[module.Name] = newGn(&module.Name, deps, index) 91 } 92 return resolveGraph(&graph, m) 93 } 94 95 // Resolves the dependency graphs 96 // For resolving cyclic dependencies Kahn’s algorithm of topological sorting is used. 97 // https://en.wikipedia.org/wiki/Topological_sorting 98 func resolveGraph(graph *graphs, m *mta.MTA) ([]string, error) { 99 overleft := *graph 100 101 // Iteratively find and remove nodes from the graphs which have no dependencies. 102 // If at some point there are still nodes in the graphs and we cannot find 103 // nodes without dependencies, that means we have a circular dependency 104 var resolved []string 105 for len(overleft) != 0 { 106 // Get all nodes from the graphs which have no dependencies 107 readyNodesSet := mapset.NewSet() 108 readyModulesSet := mapset.NewSet() 109 for _, node := range overleft { 110 if node.deps.Cardinality() == 0 { 111 readyNodesSet.Add(node) 112 readyModulesSet.Add(node.module) 113 } 114 } 115 // If there aren't any ready nodes, then we have a circular dependency 116 if readyNodesSet.Cardinality() == 0 { 117 module1, module2 := provideCyclicModules(&overleft) 118 return nil, errors.Errorf(circularDepsMsg, module1, module2) 119 } 120 // Remove the ready nodes and add them to the resolved graphs 121 readyModulesIndexes := mapset.NewSet() 122 for node := range readyNodesSet.Iter() { 123 delete(overleft, node.(*graphNode).module) 124 readyModulesIndexes.Add(node.(*graphNode).index) 125 } 126 127 for index, module := range m.Modules { 128 if readyModulesIndexes.Contains(index) { 129 resolved = append(resolved, module.Name) 130 } 131 } 132 133 // remove the ready nodes from the remaining node dependencies as well 134 for _, node := range overleft { 135 node.deps = node.deps.Difference(readyModulesSet) 136 } 137 } 138 139 return resolved, nil 140 } 141 142 // provideCyclicModules - provide some of modules having cyclic dependencies 143 func provideCyclicModules(overleft *graphs) (string, string) { 144 module1 := "" 145 module2 := "" 146 index := 0 147 for _, node := range *overleft { 148 if index == 0 { 149 module1 = node.module 150 index = 1 151 } else { 152 module2 = node.module 153 break 154 } 155 } 156 return module1, module2 157 }