blob: 6dab3b327d310bde51b8a0d05b06be76dcfab551 [file] [log] [blame]
Colin Cross96c44122020-11-25 14:29:50 -08001// 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
15package android
16
17import (
18 "fmt"
Colin Crosse98d3452024-10-23 13:04:01 -070019 "iter"
20 "slices"
Yu Liu467d7c52024-09-18 21:54:44 +000021
22 "github.com/google/blueprint"
Colin Cross96c44122020-11-25 14:29:50 -080023)
24
Colin Crossc85750b2022-04-21 12:50:51 -070025// DepSet is designed to be conceptually compatible with Bazel's depsets:
Colin Cross96c44122020-11-25 14:29:50 -080026// https://docs.bazel.build/versions/master/skylark/depsets.html
27
28type DepSetOrder int
29
30const (
31 PREORDER DepSetOrder = iota
32 POSTORDER
33 TOPOLOGICAL
34)
35
36func (o DepSetOrder) String() string {
37 switch o {
38 case PREORDER:
39 return "PREORDER"
40 case POSTORDER:
41 return "POSTORDER"
42 case TOPOLOGICAL:
43 return "TOPOLOGICAL"
44 default:
45 panic(fmt.Errorf("Invalid DepSetOrder %d", o))
46 }
47}
48
Colin Crossc85750b2022-04-21 12:50:51 -070049type depSettableType comparable
50
51// A DepSet efficiently stores a slice of an arbitrary type from transitive dependencies without
52// copying. It is stored as a DAG of DepSet nodes, each of which has some direct contents and a list
53// of dependency DepSet nodes.
Colin Cross96c44122020-11-25 14:29:50 -080054//
Colin Crossc85750b2022-04-21 12:50:51 -070055// A DepSet has an order that will be used to walk the DAG when ToList() is called. The order
Colin Cross96c44122020-11-25 14:29:50 -080056// can be POSTORDER, PREORDER, or TOPOLOGICAL. POSTORDER and PREORDER orders return a postordered
57// or preordered left to right flattened list. TOPOLOGICAL returns a list that guarantees that
58// elements of children are listed after all of their parents (unless there are duplicate direct
Colin Crossc85750b2022-04-21 12:50:51 -070059// elements in the DepSet or any of its transitive dependencies, in which case the ordering of the
Colin Cross96c44122020-11-25 14:29:50 -080060// duplicated element is not guaranteed).
61//
Colin Crossc85750b2022-04-21 12:50:51 -070062// A DepSet is created by NewDepSet or NewDepSetBuilder.Build from the slice for direct contents
63// and the *DepSets of dependencies. A DepSet is immutable once created.
64type DepSet[T depSettableType] struct {
Colin Crosse98d3452024-10-23 13:04:01 -070065 handle *depSet[T]
66}
67
68type depSet[T depSettableType] struct {
Colin Cross96c44122020-11-25 14:29:50 -080069 preorder bool
70 reverse bool
71 order DepSetOrder
Colin Crossc85750b2022-04-21 12:50:51 -070072 direct []T
Colin Crosse98d3452024-10-23 13:04:01 -070073 transitive []DepSet[T]
74}
75
76func (d DepSet[T]) impl() *depSet[T] {
77 return d.handle
78}
79
80func (d DepSet[T]) order() DepSetOrder {
81 impl := d.impl()
82 return impl.order
Colin Cross96c44122020-11-25 14:29:50 -080083}
84
Yu Liu467d7c52024-09-18 21:54:44 +000085type depSetGob[T depSettableType] struct {
86 Preorder bool
87 Reverse bool
88 Order DepSetOrder
89 Direct []T
Colin Crosse98d3452024-10-23 13:04:01 -070090 Transitive []DepSet[T]
Yu Liu467d7c52024-09-18 21:54:44 +000091}
Yu Liu26a716d2024-08-30 23:40:32 +000092
Yu Liu467d7c52024-09-18 21:54:44 +000093func (d *DepSet[T]) ToGob() *depSetGob[T] {
Colin Crosse98d3452024-10-23 13:04:01 -070094 impl := d.impl()
Yu Liu467d7c52024-09-18 21:54:44 +000095 return &depSetGob[T]{
Colin Crosse98d3452024-10-23 13:04:01 -070096 Preorder: impl.preorder,
97 Reverse: impl.reverse,
98 Order: impl.order,
99 Direct: impl.direct,
100 Transitive: impl.transitive,
Yu Liu467d7c52024-09-18 21:54:44 +0000101 }
102}
103
104func (d *DepSet[T]) FromGob(data *depSetGob[T]) {
Colin Crosse98d3452024-10-23 13:04:01 -0700105 d.handle = &depSet[T]{
106 preorder: data.Preorder,
107 reverse: data.Reverse,
108 order: data.Order,
109 direct: data.Direct,
110 transitive: data.Transitive,
111 }
Yu Liu467d7c52024-09-18 21:54:44 +0000112}
113
114func (d *DepSet[T]) GobEncode() ([]byte, error) {
115 return blueprint.CustomGobEncode[depSetGob[T]](d)
Yu Liu26a716d2024-08-30 23:40:32 +0000116}
117
118func (d *DepSet[T]) GobDecode(data []byte) error {
Yu Liu467d7c52024-09-18 21:54:44 +0000119 return blueprint.CustomGobDecode[depSetGob[T]](data, d)
Yu Liu26a716d2024-08-30 23:40:32 +0000120}
121
Colin Crossc85750b2022-04-21 12:50:51 -0700122// NewDepSet returns an immutable DepSet with the given order, direct and transitive contents.
Colin Crosse98d3452024-10-23 13:04:01 -0700123func NewDepSet[T depSettableType](order DepSetOrder, direct []T, transitive []DepSet[T]) DepSet[T] {
Colin Crossc85750b2022-04-21 12:50:51 -0700124 var directCopy []T
Colin Crosse98d3452024-10-23 13:04:01 -0700125 var transitiveCopy []DepSet[T]
126 nonEmptyTransitiveCount := 0
Colin Crossc85750b2022-04-21 12:50:51 -0700127 for _, t := range transitive {
Colin Crosse98d3452024-10-23 13:04:01 -0700128 if t.handle != nil {
129 if t.order() != order {
130 panic(fmt.Errorf("incompatible order, new DepSet is %s but transitive DepSet is %s",
131 order, t.order()))
132 }
133 nonEmptyTransitiveCount++
Colin Crossc85750b2022-04-21 12:50:51 -0700134 }
Colin Cross96c44122020-11-25 14:29:50 -0800135 }
136
Colin Crosse98d3452024-10-23 13:04:01 -0700137 if nonEmptyTransitiveCount > 0 {
138 transitiveCopy = make([]DepSet[T], 0, nonEmptyTransitiveCount)
139 }
140 var transitiveIter iter.Seq2[int, DepSet[T]]
Colin Crossc85750b2022-04-21 12:50:51 -0700141 if order == TOPOLOGICAL {
142 // TOPOLOGICAL is implemented as a postorder traversal followed by reversing the output.
143 // Pre-reverse the inputs here so their order is maintained in the output.
Colin Crossb5e3f7d2023-07-06 15:37:53 -0700144 directCopy = ReverseSlice(direct)
Colin Crosse98d3452024-10-23 13:04:01 -0700145 transitiveIter = slices.Backward(transitive)
Colin Crossc85750b2022-04-21 12:50:51 -0700146 } else {
Colin Crosse98d3452024-10-23 13:04:01 -0700147 directCopy = slices.Clone(direct)
148 transitiveIter = slices.All(transitive)
149 }
150 for _, t := range transitiveIter {
151 if t.handle != nil {
152 transitiveCopy = append(transitiveCopy, t)
153 }
Colin Crossc85750b2022-04-21 12:50:51 -0700154 }
155
Colin Crosse98d3452024-10-23 13:04:01 -0700156 if len(directCopy) == 0 && len(transitive) == 0 {
157 return DepSet[T]{nil}
158 }
159
160 depSet := &depSet[T]{
Colin Cross96c44122020-11-25 14:29:50 -0800161 preorder: order == PREORDER,
162 reverse: order == TOPOLOGICAL,
163 order: order,
164 direct: directCopy,
Colin Crossc85750b2022-04-21 12:50:51 -0700165 transitive: transitiveCopy,
Colin Cross96c44122020-11-25 14:29:50 -0800166 }
Colin Crosse98d3452024-10-23 13:04:01 -0700167
168 return DepSet[T]{depSet}
Colin Cross96c44122020-11-25 14:29:50 -0800169}
170
Colin Crossc85750b2022-04-21 12:50:51 -0700171// DepSetBuilder is used to create an immutable DepSet.
172type DepSetBuilder[T depSettableType] struct {
Colin Cross96c44122020-11-25 14:29:50 -0800173 order DepSetOrder
Colin Crossc85750b2022-04-21 12:50:51 -0700174 direct []T
Colin Crosse98d3452024-10-23 13:04:01 -0700175 transitive []DepSet[T]
Colin Cross96c44122020-11-25 14:29:50 -0800176}
177
Colin Crossc85750b2022-04-21 12:50:51 -0700178// NewDepSetBuilder returns a DepSetBuilder to create an immutable DepSet with the given order and
179// type, represented by a slice of type that will be in the DepSet.
180func NewDepSetBuilder[T depSettableType](order DepSetOrder) *DepSetBuilder[T] {
181 return &DepSetBuilder[T]{
182 order: order,
Colin Cross96c44122020-11-25 14:29:50 -0800183 }
184}
185
Colin Crossc85750b2022-04-21 12:50:51 -0700186// DirectSlice adds direct contents to the DepSet being built by a DepSetBuilder. Newly added direct
187// contents are to the right of any existing direct contents.
188func (b *DepSetBuilder[T]) DirectSlice(direct []T) *DepSetBuilder[T] {
189 b.direct = append(b.direct, direct...)
Colin Cross96c44122020-11-25 14:29:50 -0800190 return b
191}
192
Colin Crossc85750b2022-04-21 12:50:51 -0700193// Direct adds direct contents to the DepSet being built by a DepSetBuilder. Newly added direct
194// contents are to the right of any existing direct contents.
195func (b *DepSetBuilder[T]) Direct(direct ...T) *DepSetBuilder[T] {
196 b.direct = append(b.direct, direct...)
Colin Cross96c44122020-11-25 14:29:50 -0800197 return b
198}
199
200// Transitive adds transitive contents to the DepSet being built by a DepSetBuilder. Newly added
Colin Crossc85750b2022-04-21 12:50:51 -0700201// transitive contents are to the right of any existing transitive contents.
Colin Crosse98d3452024-10-23 13:04:01 -0700202func (b *DepSetBuilder[T]) Transitive(transitive ...DepSet[T]) *DepSetBuilder[T] {
Colin Crossc85750b2022-04-21 12:50:51 -0700203 for _, t := range transitive {
Colin Crosse98d3452024-10-23 13:04:01 -0700204 if t.handle != nil && t.order() != b.order {
Colin Crossc85750b2022-04-21 12:50:51 -0700205 panic(fmt.Errorf("incompatible order, new DepSet is %s but transitive DepSet is %s",
Colin Crosse98d3452024-10-23 13:04:01 -0700206 b.order, t.order()))
Colin Crossc85750b2022-04-21 12:50:51 -0700207 }
208 }
209 b.transitive = append(b.transitive, transitive...)
Colin Cross96c44122020-11-25 14:29:50 -0800210 return b
211}
212
Colin Crossc85750b2022-04-21 12:50:51 -0700213// Returns the DepSet being built by this DepSetBuilder. The DepSetBuilder retains its contents
Colin Cross96c44122020-11-25 14:29:50 -0800214// for creating more depSets.
Colin Crosse98d3452024-10-23 13:04:01 -0700215func (b *DepSetBuilder[T]) Build() DepSet[T] {
Colin Crossc85750b2022-04-21 12:50:51 -0700216 return NewDepSet(b.order, b.direct, b.transitive)
Colin Cross96c44122020-11-25 14:29:50 -0800217}
218
219// walk calls the visit method in depth-first order on a DepSet, preordered if d.preorder is set,
220// otherwise postordered.
Colin Crosse98d3452024-10-23 13:04:01 -0700221func (d DepSet[T]) walk(visit func([]T)) {
222 visited := make(map[DepSet[T]]bool)
Colin Cross96c44122020-11-25 14:29:50 -0800223
Colin Crosse98d3452024-10-23 13:04:01 -0700224 var dfs func(d DepSet[T])
225 dfs = func(d DepSet[T]) {
226 impl := d.impl()
Colin Cross96c44122020-11-25 14:29:50 -0800227 visited[d] = true
Colin Crosse98d3452024-10-23 13:04:01 -0700228 if impl.preorder {
229 visit(impl.direct)
Colin Cross96c44122020-11-25 14:29:50 -0800230 }
Colin Crosse98d3452024-10-23 13:04:01 -0700231 for _, dep := range impl.transitive {
Colin Cross96c44122020-11-25 14:29:50 -0800232 if !visited[dep] {
233 dfs(dep)
234 }
235 }
236
Colin Crosse98d3452024-10-23 13:04:01 -0700237 if !impl.preorder {
238 visit(impl.direct)
Colin Cross96c44122020-11-25 14:29:50 -0800239 }
240 }
241
242 dfs(d)
243}
244
Colin Crossc85750b2022-04-21 12:50:51 -0700245// ToList returns the DepSet flattened to a list. The order in the list is based on the order
246// of the DepSet. POSTORDER and PREORDER orders return a postordered or preordered left to right
Colin Cross96c44122020-11-25 14:29:50 -0800247// flattened list. TOPOLOGICAL returns a list that guarantees that elements of children are listed
248// after all of their parents (unless there are duplicate direct elements in the DepSet or any of
249// its transitive dependencies, in which case the ordering of the duplicated element is not
250// guaranteed).
Colin Crosse98d3452024-10-23 13:04:01 -0700251func (d DepSet[T]) ToList() []T {
252 if d.handle == nil {
Colin Cross96c44122020-11-25 14:29:50 -0800253 return nil
254 }
Colin Crosse98d3452024-10-23 13:04:01 -0700255 impl := d.impl()
Colin Crossc85750b2022-04-21 12:50:51 -0700256 var list []T
257 d.walk(func(paths []T) {
258 list = append(list, paths...)
Colin Cross96c44122020-11-25 14:29:50 -0800259 })
Colin Cross48016d52023-06-27 09:45:26 -0700260 list = firstUniqueInPlace(list)
Colin Crosse98d3452024-10-23 13:04:01 -0700261 if impl.reverse {
Colin Crossb5e3f7d2023-07-06 15:37:53 -0700262 ReverseSliceInPlace(list)
Colin Cross96c44122020-11-25 14:29:50 -0800263 }
264 return list
265}