]> Cypherpunks.ru repositories - goredo.git/blob - ifchange.go
Remove useless check
[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         "path"
22         "strings"
23 )
24
25 func collectDeps(
26         cwd, tgtOrig string,
27         level int,
28         deps map[string]map[string]struct{},
29         includeSrc bool,
30         seen map[string]struct{},
31 ) []string {
32         cwd, tgt := cwdAndTgt(path.Join(cwd, tgtOrig))
33         tgtFull := path.Join(cwd, tgt)
34         if _, ok := seen[tgtFull]; ok {
35                 return nil
36         }
37         depPath := path.Join(cwd, RedoDir, tgt+DepSuffix)
38         depInfo, err := depRead(depPath)
39         if err != nil {
40                 return nil
41         }
42         // DepInfoCache[depPath] = depInfo
43         seen[tgtFull] = struct{}{}
44         var alwayses []string
45         returnReady := false
46         tgtRel := cwdMustRel(cwd, tgt)
47         if depInfo.always {
48                 if depInfo.build == BuildUUID {
49                         tracef(
50                                 CDebug, "ood: %s%s always, but already build",
51                                 strings.Repeat(". ", level), tgtOrig,
52                         )
53                         returnReady = true
54                 } else {
55                         tracef(CDebug, "ood: %s%s always", strings.Repeat(". ", level), tgtOrig)
56                         alwayses = append(alwayses, tgtRel)
57                         returnReady = true
58                 }
59         }
60         for _, dep := range depInfo.ifchanges {
61                 if dep.tgt == tgt {
62                         continue
63                 }
64                 if !includeSrc && isSrc(cwd, dep.tgt) {
65                         continue
66                 }
67                 if !returnReady {
68                         depRel := cwdMustRel(cwd, dep.tgt)
69                         if m, ok := deps[depRel]; ok {
70                                 m[tgtRel] = struct{}{}
71                         } else {
72                                 m = map[string]struct{}{}
73                                 m[tgtRel] = struct{}{}
74                                 deps[depRel] = m
75                         }
76                         alwayses = append(alwayses,
77                                 collectDeps(cwd, dep.tgt, level+1, deps, includeSrc, seen)...)
78                 }
79         }
80         return alwayses
81 }
82
83 func buildDependants(tgts []string) map[string]struct{} {
84         defer Jobs.Wait()
85         tracef(CDebug, "collecting deps")
86         seen := map[string]struct{}{}
87         deps := map[string]map[string]struct{}{}
88         collectDepsSeen := make(map[string]struct{})
89         for _, tgtInitial := range tgts {
90                 for _, tgt := range collectDeps(Cwd, tgtInitial, 0, deps, false, collectDepsSeen) {
91                         if tgt != tgtInitial {
92                                 seen[tgt] = struct{}{}
93                         }
94                 }
95         }
96         if len(seen) == 0 {
97                 return seen
98         }
99         collectDepsSeen = nil
100
101         levelOrig := Level
102         defer func() {
103                 Level = levelOrig
104         }()
105         Level = 1
106         tracef(CDebug, "building %d alwayses: %v", len(seen), seen)
107         errs := make(chan error, len(seen))
108         ok := true
109         okChecker := make(chan struct{})
110         go func() {
111                 for err := range errs {
112                         ok = isOkRun(err) && ok
113                 }
114                 close(okChecker)
115         }()
116         for tgt := range seen {
117                 if err := runScript(tgt, errs, false, false); err != nil {
118                         tracef(CErr, "always run error: %s, skipping dependants", err)
119                         Jobs.Wait()
120                         close(errs)
121                         return nil
122                 }
123         }
124         Jobs.Wait()
125         close(errs)
126         <-okChecker
127         if !ok {
128                 tracef(CDebug, "alwayses failed, skipping dependants")
129                 return nil
130         }
131
132         queueSrc := make([]string, 0, len(seen))
133         for tgt := range seen {
134                 queueSrc = append(queueSrc, tgt)
135         }
136
137 RebuildDeps:
138         tracef(CDebug, "checking %d dependant targets: %v", len(queueSrc), queueSrc)
139         queue := map[string]struct{}{}
140         for _, tgt := range queueSrc {
141                 for dep := range deps[tgt] {
142                         queue[dep] = struct{}{}
143                 }
144         }
145
146         tracef(CDebug, "building %d dependant targets: %v", len(queue), queue)
147         errs = make(chan error, len(queue))
148         okChecker = make(chan struct{})
149         jobs := 0
150         queueSrc = []string{}
151         go func() {
152                 for err := range errs {
153                         ok = isOkRun(err) && ok
154                 }
155                 close(okChecker)
156         }()
157         for tgt := range queue {
158                 ood, err := isOODWithTrace(Cwd, tgt, 0, seen)
159                 if err != nil {
160                         tracef(CErr, "dependant error: %s, skipping dependants", err)
161                         return nil
162                 }
163                 if !ood {
164                         continue
165                 }
166                 if err := runScript(tgt, errs, false, false); err != nil {
167                         tracef(CErr, "dependant error: %s, skipping dependants", err)
168                         return nil
169                 }
170                 queueSrc = append(queueSrc, tgt)
171                 seen[tgt] = struct{}{}
172                 jobs++
173         }
174         Jobs.Wait()
175         close(errs)
176         <-okChecker
177         if !ok {
178                 tracef(CDebug, "dependants failed, skipping them")
179                 return nil
180         }
181         if jobs == 0 {
182                 return seen
183         }
184         Level++
185         goto RebuildDeps
186 }
187
188 func ifchange(tgts []string, forced, traced bool) (bool, error) {
189         // only unique elements
190         m := make(map[string]struct{})
191         for _, t := range tgts {
192                 m[t] = struct{}{}
193         }
194         tgts = tgts[:0]
195         for t := range m {
196                 tgts = append(tgts, t)
197         }
198         m = nil
199
200         jsInit()
201         if !IsTopRedo {
202                 defer jsAcquire("ifchange exiting")
203         }
204         seen := buildDependants(tgts)
205         if seen == nil {
206                 Jobs.Wait()
207                 return false, nil
208         }
209         oodTgtsClear()
210         tracef(CDebug, "building %d targets: %v", len(tgts), tgts)
211         var ood bool
212         var err error
213         ok := true
214         okChecker := make(chan struct{})
215         errs := make(chan error, len(tgts))
216         go func() {
217                 for err := range errs {
218                         ok = isOkRun(err) && ok
219                 }
220                 close(okChecker)
221         }()
222         for _, tgt := range tgts {
223                 if _, ok := seen[tgt]; ok {
224                         tracef(CDebug, "%s was already build as a dependant", tgt)
225                         continue
226                 }
227                 ood = true
228                 if !forced {
229                         ood, err = isOODWithTrace(Cwd, tgt, 0, seen)
230                         if err != nil {
231                                 Jobs.Wait()
232                                 close(errs)
233                                 return false, ErrLine(err)
234                         }
235                 }
236                 if !ood {
237                         continue
238                 }
239                 if isSrc(Cwd, tgt) {
240                         tracef(CDebug, "%s is source, not redoing", tgt)
241                         continue
242                 }
243                 if err = runScript(tgt, errs, forced, traced); err != nil {
244                         Jobs.Wait()
245                         close(errs)
246                         return false, ErrLine(err)
247                 }
248         }
249         Jobs.Wait()
250         close(errs)
251         <-okChecker
252         return ok, nil
253 }