]> Cypherpunks.ru repositories - goredo.git/blob - ifchange.go
Redundant @documentencoding
[goredo.git] / ifchange.go
1 /*
2 goredo -- djb's redo implementation on pure Go
3 Copyright (C) 2020-2023 Sergey Matveev <stargrave@stargrave.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, version 3 of the License.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 package main
19
20 import (
21         "os"
22         "path"
23         "strings"
24 )
25
26 func collectDeps(
27         cwd, tgtOrig string,
28         level int,
29         deps map[string]map[string]struct{},
30         includeSrc bool,
31 ) []string {
32         cwd, tgt := cwdAndTgt(path.Join(cwd, tgtOrig))
33         depPath := path.Join(cwd, RedoDir, tgt+DepSuffix)
34         fdDep, err := os.Open(depPath)
35         if err != nil {
36                 return nil
37         }
38         depInfo, err := depRead(fdDep)
39         fdDep.Close()
40         if err != nil {
41                 return nil
42         }
43         var alwayses []string
44         returnReady := false
45         tgtRel := cwdMustRel(cwd, tgt)
46         if depInfo.always {
47                 if depInfo.build == BuildUUID {
48                         tracef(
49                                 CDebug, "ood: %s%s always, but already build",
50                                 strings.Repeat(". ", level), tgtOrig,
51                         )
52                         returnReady = true
53                 } else {
54                         tracef(CDebug, "ood: %s%s always", strings.Repeat(". ", level), tgtOrig)
55                         alwayses = append(alwayses, tgtRel)
56                         returnReady = true
57                 }
58         }
59         for _, m := range depInfo.ifchanges {
60                 dep := m["Target"]
61                 if dep == "" {
62                         return alwayses
63                 }
64                 if dep == tgt {
65                         continue
66                 }
67                 if !includeSrc && isSrc(cwd, dep) {
68                         continue
69                 }
70                 if !returnReady {
71                         depRel := cwdMustRel(cwd, dep)
72                         if m, ok := deps[depRel]; ok {
73                                 m[tgtRel] = struct{}{}
74                         } else {
75                                 m = map[string]struct{}{}
76                                 m[tgtRel] = struct{}{}
77                                 deps[depRel] = m
78                         }
79                         alwayses = append(
80                                 alwayses,
81                                 collectDeps(cwd, dep, level+1, deps, includeSrc)...,
82                         )
83                 }
84         }
85         return alwayses
86 }
87
88 func buildDependants(tgts []string) map[string]struct{} {
89         defer Jobs.Wait()
90         tracef(CDebug, "collecting deps")
91         seen := map[string]struct{}{}
92         deps := map[string]map[string]struct{}{}
93         for _, tgtInitial := range tgts {
94                 for _, tgt := range collectDeps(Cwd, tgtInitial, 0, deps, false) {
95                         if tgt != tgtInitial {
96                                 seen[tgt] = struct{}{}
97                         }
98                 }
99         }
100         if len(seen) == 0 {
101                 return seen
102         }
103
104         levelOrig := Level
105         defer func() {
106                 Level = levelOrig
107         }()
108         Level = 1
109         tracef(CDebug, "building %d alwayses: %v", len(seen), seen)
110         errs := make(chan error, len(seen))
111         for tgt := range seen {
112                 if err := runScript(tgt, errs, false); err != nil {
113                         tracef(CErr, "always run error: %s, skipping dependants", err)
114                         return nil
115                 }
116         }
117         ok := true
118         for i := 0; i < len(seen); i++ {
119                 ok = isOkRun(<-errs) && ok
120         }
121         Jobs.Wait()
122         close(errs)
123         if !ok {
124                 tracef(CDebug, "alwayses failed, skipping dependants")
125                 return nil
126         }
127
128         queueSrc := make([]string, 0, len(seen))
129         for tgt := range seen {
130                 queueSrc = append(queueSrc, tgt)
131         }
132         if len(queueSrc) == 0 {
133                 return seen
134         }
135
136 RebuildDeps:
137         tracef(CDebug, "checking %d dependant targets: %v", len(queueSrc), queueSrc)
138         queue := map[string]struct{}{}
139         for _, tgt := range queueSrc {
140                 for dep := range deps[tgt] {
141                         queue[dep] = struct{}{}
142                 }
143         }
144
145         tracef(CDebug, "building %d dependant targets: %v", len(queue), queue)
146         errs = make(chan error, len(queue))
147         jobs := 0
148         queueSrc = []string{}
149         for tgt := range queue {
150                 ood, err := isOODWithTrace(Cwd, tgt, 0, seen)
151                 if err != nil {
152                         tracef(CErr, "dependant error: %s, skipping dependants", err)
153                         return nil
154                 }
155                 if !ood {
156                         continue
157                 }
158                 if err := runScript(tgt, errs, false); err != nil {
159                         tracef(CErr, "dependant error: %s, skipping dependants", err)
160                         return nil
161                 }
162                 queueSrc = append(queueSrc, tgt)
163                 seen[tgt] = struct{}{}
164                 jobs++
165         }
166         for i := 0; i < jobs; i++ {
167                 ok = isOkRun(<-errs) && ok
168         }
169         if !ok {
170                 tracef(CDebug, "dependants failed, skipping them")
171                 return nil
172         }
173         Jobs.Wait()
174         close(errs)
175         if jobs == 0 {
176                 return seen
177         }
178         Level++
179         goto RebuildDeps
180 }
181
182 func ifchange(tgts []string, forced, traced bool) (bool, error) {
183         // only unique elements
184         m := make(map[string]struct{})
185         for _, t := range tgts {
186                 m[t] = struct{}{}
187         }
188         tgts = tgts[:0]
189         for t := range m {
190                 tgts = append(tgts, t)
191         }
192         m = nil
193
194         jsInit()
195         if !IsTopRedo {
196                 defer jsAcquire("ifchange exiting")
197         }
198         defer Jobs.Wait()
199         seen := buildDependants(tgts)
200         if seen == nil {
201                 return false, nil
202         }
203         oodTgtsClear()
204         tracef(CDebug, "building %d targets: %v", len(tgts), tgts)
205         jobs := 0
206         errs := make(chan error, len(tgts))
207         var ood bool
208         var err error
209         for _, tgt := range tgts {
210                 if _, ok := seen[tgt]; ok {
211                         tracef(CDebug, "%s was already build as a dependant", tgt)
212                         continue
213                 }
214                 ood = true
215                 if !forced {
216                         ood, err = isOODWithTrace(Cwd, tgt, 0, seen)
217                         if err != nil {
218                                 return false, err
219                         }
220                 }
221                 if !ood {
222                         continue
223                 }
224                 if isSrc(Cwd, tgt) {
225                         tracef(CDebug, "%s is source, not redoing", tgt)
226                         continue
227                 }
228                 if err = runScript(tgt, errs, traced); err != nil {
229                         return false, err
230                 }
231                 jobs++
232         }
233         ok := true
234         for ; jobs > 0; jobs-- {
235                 ok = isOkRun(<-errs) && ok
236         }
237         return ok, nil
238 }