blob: 45c19371506f82a08b3c74f6bc02a388c9836ae7 [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 Cross96c44122020-11-25 14:29:50 -080019)
20
Colin Crossc85750b2022-04-21 12:50:51 -070021// DepSet is designed to be conceptually compatible with Bazel's depsets:
Colin Cross96c44122020-11-25 14:29:50 -080022// https://docs.bazel.build/versions/master/skylark/depsets.html
23
24type DepSetOrder int
25
26const (
27 PREORDER DepSetOrder = iota
28 POSTORDER
29 TOPOLOGICAL
30)
31
32func (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 Crossc85750b2022-04-21 12:50:51 -070045type 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 Cross96c44122020-11-25 14:29:50 -080050//
Colin Crossc85750b2022-04-21 12:50:51 -070051// 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 -080052// 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 Crossc85750b2022-04-21 12:50:51 -070055// elements in the DepSet or any of its transitive dependencies, in which case the ordering of the
Colin Cross96c44122020-11-25 14:29:50 -080056// duplicated element is not guaranteed).
57//
Colin Crossc85750b2022-04-21 12:50:51 -070058// 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.
60type DepSet[T depSettableType] struct {
Colin Cross96c44122020-11-25 14:29:50 -080061 preorder bool
62 reverse bool
63 order DepSetOrder
Colin Crossc85750b2022-04-21 12:50:51 -070064 direct []T
65 transitive []*DepSet[T]
Colin Cross96c44122020-11-25 14:29:50 -080066}
67
Colin Crossc85750b2022-04-21 12:50:51 -070068// NewDepSet returns an immutable DepSet with the given order, direct and transitive contents.
69func 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 Cross96c44122020-11-25 14:29:50 -080077 }
78
Colin Crossc85750b2022-04-21 12:50:51 -070079 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 Crossb5e3f7d2023-07-06 15:37:53 -070082 directCopy = ReverseSlice(direct)
83 transitiveCopy = ReverseSlice(transitive)
Colin Crossc85750b2022-04-21 12:50:51 -070084 } else {
85 directCopy = append([]T(nil), direct...)
86 transitiveCopy = append([]*DepSet[T](nil), transitive...)
87 }
88
89 return &DepSet[T]{
Colin Cross96c44122020-11-25 14:29:50 -080090 preorder: order == PREORDER,
91 reverse: order == TOPOLOGICAL,
92 order: order,
93 direct: directCopy,
Colin Crossc85750b2022-04-21 12:50:51 -070094 transitive: transitiveCopy,
Colin Cross96c44122020-11-25 14:29:50 -080095 }
96}
97
Colin Crossc85750b2022-04-21 12:50:51 -070098// DepSetBuilder is used to create an immutable DepSet.
99type DepSetBuilder[T depSettableType] struct {
Colin Cross96c44122020-11-25 14:29:50 -0800100 order DepSetOrder
Colin Crossc85750b2022-04-21 12:50:51 -0700101 direct []T
102 transitive []*DepSet[T]
Colin Cross96c44122020-11-25 14:29:50 -0800103}
104
Colin Crossc85750b2022-04-21 12:50:51 -0700105// 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.
107func NewDepSetBuilder[T depSettableType](order DepSetOrder) *DepSetBuilder[T] {
108 return &DepSetBuilder[T]{
109 order: order,
Colin Cross96c44122020-11-25 14:29:50 -0800110 }
111}
112
Colin Crossc85750b2022-04-21 12:50:51 -0700113// 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.
115func (b *DepSetBuilder[T]) DirectSlice(direct []T) *DepSetBuilder[T] {
116 b.direct = append(b.direct, direct...)
Colin Cross96c44122020-11-25 14:29:50 -0800117 return b
118}
119
Colin Crossc85750b2022-04-21 12:50:51 -0700120// 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.
122func (b *DepSetBuilder[T]) Direct(direct ...T) *DepSetBuilder[T] {
123 b.direct = append(b.direct, direct...)
Colin Cross96c44122020-11-25 14:29:50 -0800124 return b
125}
126
127// Transitive adds transitive contents to the DepSet being built by a DepSetBuilder. Newly added
Colin Crossc85750b2022-04-21 12:50:51 -0700128// transitive contents are to the right of any existing transitive contents.
129func (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 Cross96c44122020-11-25 14:29:50 -0800137 return b
138}
139
Colin Crossc85750b2022-04-21 12:50:51 -0700140// Returns the DepSet being built by this DepSetBuilder. The DepSetBuilder retains its contents
Colin Cross96c44122020-11-25 14:29:50 -0800141// for creating more depSets.
Colin Crossc85750b2022-04-21 12:50:51 -0700142func (b *DepSetBuilder[T]) Build() *DepSet[T] {
143 return NewDepSet(b.order, b.direct, b.transitive)
Colin Cross96c44122020-11-25 14:29:50 -0800144}
145
146// walk calls the visit method in depth-first order on a DepSet, preordered if d.preorder is set,
147// otherwise postordered.
Colin Crossc85750b2022-04-21 12:50:51 -0700148func (d *DepSet[T]) walk(visit func([]T)) {
149 visited := make(map[*DepSet[T]]bool)
Colin Cross96c44122020-11-25 14:29:50 -0800150
Colin Crossc85750b2022-04-21 12:50:51 -0700151 var dfs func(d *DepSet[T])
152 dfs = func(d *DepSet[T]) {
Colin Cross96c44122020-11-25 14:29:50 -0800153 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 Crossc85750b2022-04-21 12:50:51 -0700171// 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 Cross96c44122020-11-25 14:29:50 -0800173// 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 Crossc85750b2022-04-21 12:50:51 -0700177func (d *DepSet[T]) ToList() []T {
Colin Cross96c44122020-11-25 14:29:50 -0800178 if d == nil {
179 return nil
180 }
Colin Crossc85750b2022-04-21 12:50:51 -0700181 var list []T
182 d.walk(func(paths []T) {
183 list = append(list, paths...)
Colin Cross96c44122020-11-25 14:29:50 -0800184 })
Colin Cross48016d52023-06-27 09:45:26 -0700185 list = firstUniqueInPlace(list)
Colin Cross96c44122020-11-25 14:29:50 -0800186 if d.reverse {
Colin Crossb5e3f7d2023-07-06 15:37:53 -0700187 ReverseSliceInPlace(list)
Colin Cross96c44122020-11-25 14:29:50 -0800188 }
189 return list
190}