| Colin Cross | 96c4412 | 2020-11-25 14:29:50 -0800 | [diff] [blame] | 1 | // Copyright 2020 Google Inc. All rights reserved. | 
|  | 2 | // | 
|  | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | 4 | // you may not use this file except in compliance with the License. | 
|  | 5 | // You may obtain a copy of the License at | 
|  | 6 | // | 
|  | 7 | //     http://www.apache.org/licenses/LICENSE-2.0 | 
|  | 8 | // | 
|  | 9 | // Unless required by applicable law or agreed to in writing, software | 
|  | 10 | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | 12 | // See the License for the specific language governing permissions and | 
|  | 13 | // limitations under the License. | 
|  | 14 |  | 
|  | 15 | package android | 
|  | 16 |  | 
|  | 17 | import ( | 
|  | 18 | "fmt" | 
| Colin Cross | 96c4412 | 2020-11-25 14:29:50 -0800 | [diff] [blame] | 19 | ) | 
|  | 20 |  | 
| Colin Cross | c85750b | 2022-04-21 12:50:51 -0700 | [diff] [blame] | 21 | // DepSet is designed to be conceptually compatible with Bazel's depsets: | 
| Colin Cross | 96c4412 | 2020-11-25 14:29:50 -0800 | [diff] [blame] | 22 | // https://docs.bazel.build/versions/master/skylark/depsets.html | 
|  | 23 |  | 
|  | 24 | type DepSetOrder int | 
|  | 25 |  | 
|  | 26 | const ( | 
|  | 27 | PREORDER DepSetOrder = iota | 
|  | 28 | POSTORDER | 
|  | 29 | TOPOLOGICAL | 
|  | 30 | ) | 
|  | 31 |  | 
|  | 32 | func (o DepSetOrder) String() string { | 
|  | 33 | switch o { | 
|  | 34 | case PREORDER: | 
|  | 35 | return "PREORDER" | 
|  | 36 | case POSTORDER: | 
|  | 37 | return "POSTORDER" | 
|  | 38 | case TOPOLOGICAL: | 
|  | 39 | return "TOPOLOGICAL" | 
|  | 40 | default: | 
|  | 41 | panic(fmt.Errorf("Invalid DepSetOrder %d", o)) | 
|  | 42 | } | 
|  | 43 | } | 
|  | 44 |  | 
| Colin Cross | c85750b | 2022-04-21 12:50:51 -0700 | [diff] [blame] | 45 | type depSettableType comparable | 
|  | 46 |  | 
|  | 47 | // A DepSet efficiently stores a slice of an arbitrary type from transitive dependencies without | 
|  | 48 | // copying. It is stored as a DAG of DepSet nodes, each of which has some direct contents and a list | 
|  | 49 | // of dependency DepSet nodes. | 
| Colin Cross | 96c4412 | 2020-11-25 14:29:50 -0800 | [diff] [blame] | 50 | // | 
| Colin Cross | c85750b | 2022-04-21 12:50:51 -0700 | [diff] [blame] | 51 | // A DepSet has an order that will be used to walk the DAG when ToList() is called.  The order | 
| Colin Cross | 96c4412 | 2020-11-25 14:29:50 -0800 | [diff] [blame] | 52 | // can be POSTORDER, PREORDER, or TOPOLOGICAL.  POSTORDER and PREORDER orders return a postordered | 
|  | 53 | // or preordered left to right flattened list.  TOPOLOGICAL returns a list that guarantees that | 
|  | 54 | // elements of children are listed after all of their parents (unless there are duplicate direct | 
| Colin Cross | c85750b | 2022-04-21 12:50:51 -0700 | [diff] [blame] | 55 | // elements in the DepSet or any of its transitive dependencies, in which case the ordering of the | 
| Colin Cross | 96c4412 | 2020-11-25 14:29:50 -0800 | [diff] [blame] | 56 | // duplicated element is not guaranteed). | 
|  | 57 | // | 
| Colin Cross | c85750b | 2022-04-21 12:50:51 -0700 | [diff] [blame] | 58 | // A DepSet is created by NewDepSet or NewDepSetBuilder.Build from the slice for direct contents | 
|  | 59 | // and the *DepSets of dependencies. A DepSet is immutable once created. | 
|  | 60 | type DepSet[T depSettableType] struct { | 
| Colin Cross | 96c4412 | 2020-11-25 14:29:50 -0800 | [diff] [blame] | 61 | preorder   bool | 
|  | 62 | reverse    bool | 
|  | 63 | order      DepSetOrder | 
| Colin Cross | c85750b | 2022-04-21 12:50:51 -0700 | [diff] [blame] | 64 | direct     []T | 
|  | 65 | transitive []*DepSet[T] | 
| Colin Cross | 96c4412 | 2020-11-25 14:29:50 -0800 | [diff] [blame] | 66 | } | 
|  | 67 |  | 
| Colin Cross | c85750b | 2022-04-21 12:50:51 -0700 | [diff] [blame] | 68 | // NewDepSet returns an immutable DepSet with the given order, direct and transitive contents. | 
|  | 69 | func NewDepSet[T depSettableType](order DepSetOrder, direct []T, transitive []*DepSet[T]) *DepSet[T] { | 
|  | 70 | var directCopy []T | 
|  | 71 | var transitiveCopy []*DepSet[T] | 
|  | 72 | for _, t := range transitive { | 
|  | 73 | if t.order != order { | 
|  | 74 | panic(fmt.Errorf("incompatible order, new DepSet is %s but transitive DepSet is %s", | 
|  | 75 | order, t.order)) | 
|  | 76 | } | 
| Colin Cross | 96c4412 | 2020-11-25 14:29:50 -0800 | [diff] [blame] | 77 | } | 
|  | 78 |  | 
| Colin Cross | c85750b | 2022-04-21 12:50:51 -0700 | [diff] [blame] | 79 | if order == TOPOLOGICAL { | 
|  | 80 | // TOPOLOGICAL is implemented as a postorder traversal followed by reversing the output. | 
|  | 81 | // Pre-reverse the inputs here so their order is maintained in the output. | 
| Colin Cross | b5e3f7d | 2023-07-06 15:37:53 -0700 | [diff] [blame] | 82 | directCopy = ReverseSlice(direct) | 
|  | 83 | transitiveCopy = ReverseSlice(transitive) | 
| Colin Cross | c85750b | 2022-04-21 12:50:51 -0700 | [diff] [blame] | 84 | } else { | 
|  | 85 | directCopy = append([]T(nil), direct...) | 
|  | 86 | transitiveCopy = append([]*DepSet[T](nil), transitive...) | 
|  | 87 | } | 
|  | 88 |  | 
|  | 89 | return &DepSet[T]{ | 
| Colin Cross | 96c4412 | 2020-11-25 14:29:50 -0800 | [diff] [blame] | 90 | preorder:   order == PREORDER, | 
|  | 91 | reverse:    order == TOPOLOGICAL, | 
|  | 92 | order:      order, | 
|  | 93 | direct:     directCopy, | 
| Colin Cross | c85750b | 2022-04-21 12:50:51 -0700 | [diff] [blame] | 94 | transitive: transitiveCopy, | 
| Colin Cross | 96c4412 | 2020-11-25 14:29:50 -0800 | [diff] [blame] | 95 | } | 
|  | 96 | } | 
|  | 97 |  | 
| Colin Cross | c85750b | 2022-04-21 12:50:51 -0700 | [diff] [blame] | 98 | // DepSetBuilder is used to create an immutable DepSet. | 
|  | 99 | type DepSetBuilder[T depSettableType] struct { | 
| Colin Cross | 96c4412 | 2020-11-25 14:29:50 -0800 | [diff] [blame] | 100 | order      DepSetOrder | 
| Colin Cross | c85750b | 2022-04-21 12:50:51 -0700 | [diff] [blame] | 101 | direct     []T | 
|  | 102 | transitive []*DepSet[T] | 
| Colin Cross | 96c4412 | 2020-11-25 14:29:50 -0800 | [diff] [blame] | 103 | } | 
|  | 104 |  | 
| Colin Cross | c85750b | 2022-04-21 12:50:51 -0700 | [diff] [blame] | 105 | // NewDepSetBuilder returns a DepSetBuilder to create an immutable DepSet with the given order and | 
|  | 106 | // type, represented by a slice of type that will be in the DepSet. | 
|  | 107 | func NewDepSetBuilder[T depSettableType](order DepSetOrder) *DepSetBuilder[T] { | 
|  | 108 | return &DepSetBuilder[T]{ | 
|  | 109 | order: order, | 
| Colin Cross | 96c4412 | 2020-11-25 14:29:50 -0800 | [diff] [blame] | 110 | } | 
|  | 111 | } | 
|  | 112 |  | 
| Colin Cross | c85750b | 2022-04-21 12:50:51 -0700 | [diff] [blame] | 113 | // DirectSlice adds direct contents to the DepSet being built by a DepSetBuilder. Newly added direct | 
|  | 114 | // contents are to the right of any existing direct contents. | 
|  | 115 | func (b *DepSetBuilder[T]) DirectSlice(direct []T) *DepSetBuilder[T] { | 
|  | 116 | b.direct = append(b.direct, direct...) | 
| Colin Cross | 96c4412 | 2020-11-25 14:29:50 -0800 | [diff] [blame] | 117 | return b | 
|  | 118 | } | 
|  | 119 |  | 
| Colin Cross | c85750b | 2022-04-21 12:50:51 -0700 | [diff] [blame] | 120 | // Direct adds direct contents to the DepSet being built by a DepSetBuilder. Newly added direct | 
|  | 121 | // contents are to the right of any existing direct contents. | 
|  | 122 | func (b *DepSetBuilder[T]) Direct(direct ...T) *DepSetBuilder[T] { | 
|  | 123 | b.direct = append(b.direct, direct...) | 
| Colin Cross | 96c4412 | 2020-11-25 14:29:50 -0800 | [diff] [blame] | 124 | return b | 
|  | 125 | } | 
|  | 126 |  | 
|  | 127 | // Transitive adds transitive contents to the DepSet being built by a DepSetBuilder. Newly added | 
| Colin Cross | c85750b | 2022-04-21 12:50:51 -0700 | [diff] [blame] | 128 | // transitive contents are to the right of any existing transitive contents. | 
|  | 129 | func (b *DepSetBuilder[T]) Transitive(transitive ...*DepSet[T]) *DepSetBuilder[T] { | 
|  | 130 | for _, t := range transitive { | 
|  | 131 | if t.order != b.order { | 
|  | 132 | panic(fmt.Errorf("incompatible order, new DepSet is %s but transitive DepSet is %s", | 
|  | 133 | b.order, t.order)) | 
|  | 134 | } | 
|  | 135 | } | 
|  | 136 | b.transitive = append(b.transitive, transitive...) | 
| Colin Cross | 96c4412 | 2020-11-25 14:29:50 -0800 | [diff] [blame] | 137 | return b | 
|  | 138 | } | 
|  | 139 |  | 
| Colin Cross | c85750b | 2022-04-21 12:50:51 -0700 | [diff] [blame] | 140 | // Returns the DepSet being built by this DepSetBuilder.  The DepSetBuilder retains its contents | 
| Colin Cross | 96c4412 | 2020-11-25 14:29:50 -0800 | [diff] [blame] | 141 | // for creating more depSets. | 
| Colin Cross | c85750b | 2022-04-21 12:50:51 -0700 | [diff] [blame] | 142 | func (b *DepSetBuilder[T]) Build() *DepSet[T] { | 
|  | 143 | return NewDepSet(b.order, b.direct, b.transitive) | 
| Colin Cross | 96c4412 | 2020-11-25 14:29:50 -0800 | [diff] [blame] | 144 | } | 
|  | 145 |  | 
|  | 146 | // walk calls the visit method in depth-first order on a DepSet, preordered if d.preorder is set, | 
|  | 147 | // otherwise postordered. | 
| Colin Cross | c85750b | 2022-04-21 12:50:51 -0700 | [diff] [blame] | 148 | func (d *DepSet[T]) walk(visit func([]T)) { | 
|  | 149 | visited := make(map[*DepSet[T]]bool) | 
| Colin Cross | 96c4412 | 2020-11-25 14:29:50 -0800 | [diff] [blame] | 150 |  | 
| Colin Cross | c85750b | 2022-04-21 12:50:51 -0700 | [diff] [blame] | 151 | var dfs func(d *DepSet[T]) | 
|  | 152 | dfs = func(d *DepSet[T]) { | 
| Colin Cross | 96c4412 | 2020-11-25 14:29:50 -0800 | [diff] [blame] | 153 | visited[d] = true | 
|  | 154 | if d.preorder { | 
|  | 155 | visit(d.direct) | 
|  | 156 | } | 
|  | 157 | for _, dep := range d.transitive { | 
|  | 158 | if !visited[dep] { | 
|  | 159 | dfs(dep) | 
|  | 160 | } | 
|  | 161 | } | 
|  | 162 |  | 
|  | 163 | if !d.preorder { | 
|  | 164 | visit(d.direct) | 
|  | 165 | } | 
|  | 166 | } | 
|  | 167 |  | 
|  | 168 | dfs(d) | 
|  | 169 | } | 
|  | 170 |  | 
| Colin Cross | c85750b | 2022-04-21 12:50:51 -0700 | [diff] [blame] | 171 | // ToList returns the DepSet flattened to a list.  The order in the list is based on the order | 
|  | 172 | // of the DepSet.  POSTORDER and PREORDER orders return a postordered or preordered left to right | 
| Colin Cross | 96c4412 | 2020-11-25 14:29:50 -0800 | [diff] [blame] | 173 | // flattened list.  TOPOLOGICAL returns a list that guarantees that elements of children are listed | 
|  | 174 | // after all of their parents (unless there are duplicate direct elements in the DepSet or any of | 
|  | 175 | // its transitive dependencies, in which case the ordering of the duplicated element is not | 
|  | 176 | // guaranteed). | 
| Colin Cross | c85750b | 2022-04-21 12:50:51 -0700 | [diff] [blame] | 177 | func (d *DepSet[T]) ToList() []T { | 
| Colin Cross | 96c4412 | 2020-11-25 14:29:50 -0800 | [diff] [blame] | 178 | if d == nil { | 
|  | 179 | return nil | 
|  | 180 | } | 
| Colin Cross | c85750b | 2022-04-21 12:50:51 -0700 | [diff] [blame] | 181 | var list []T | 
|  | 182 | d.walk(func(paths []T) { | 
|  | 183 | list = append(list, paths...) | 
| Colin Cross | 96c4412 | 2020-11-25 14:29:50 -0800 | [diff] [blame] | 184 | }) | 
| Colin Cross | 48016d5 | 2023-06-27 09:45:26 -0700 | [diff] [blame] | 185 | list = firstUniqueInPlace(list) | 
| Colin Cross | 96c4412 | 2020-11-25 14:29:50 -0800 | [diff] [blame] | 186 | if d.reverse { | 
| Colin Cross | b5e3f7d | 2023-07-06 15:37:53 -0700 | [diff] [blame] | 187 | ReverseSliceInPlace(list) | 
| Colin Cross | 96c4412 | 2020-11-25 14:29:50 -0800 | [diff] [blame] | 188 | } | 
|  | 189 | return list | 
|  | 190 | } |