]> Cypherpunks.ru repositories - goredo.git/blob - ifchange.go
isModified check must only look at ifchanges
[goredo.git] / ifchange.go
1 /*
2 goredo -- djb's redo implementation on pure Go
3 Copyright (C) 2020-2021 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                         trace(
49                                 CDebug, "ood: %s%s always, but already build",
50                                 strings.Repeat(". ", level), tgtOrig,
51                         )
52                         returnReady = true
53                 } else {
54                         trace(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         trace(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 nil
102         }
103
104         levelOrig := Level
105         defer func() {
106                 Level = levelOrig
107         }()
108         Level = 1
109         trace(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                         trace(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                 trace(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         trace(CDebug, "checking %d dependant targets: %v", len(queueSrc), queueSrc)
138         queue := []string{}
139         for _, tgt := range queueSrc {
140                 for dep := range deps[tgt] {
141                         queue = append(queue, dep)
142                 }
143         }
144         trace(CDebug, "building %d dependant targets: %v", len(queue), queue)
145         errs = make(chan error, len(queue))
146         jobs := 0
147         queueSrc = []string{}
148         for _, tgt := range queue {
149                 ood, err := isOODWithTrace(Cwd, tgt, 0, seen)
150                 if err != nil {
151                         trace(CErr, "dependant error: %s, skipping dependants", err)
152                         return nil
153                 }
154                 if !ood {
155                         continue
156                 }
157                 if err := runScript(tgt, errs, false); err != nil {
158                         trace(CErr, "dependant error: %s, skipping dependants", err)
159                         return nil
160                 }
161                 queueSrc = append(queueSrc, tgt)
162                 seen[tgt] = struct{}{}
163                 jobs++
164         }
165         for i := 0; i < jobs; i++ {
166                 ok = isOkRun(<-errs) && ok
167         }
168         if !ok {
169                 trace(CDebug, "dependants failed, skipping them")
170                 return nil
171         }
172         Jobs.Wait()
173         close(errs)
174         if jobs == 0 {
175                 return seen
176         }
177         Level++
178         goto RebuildDeps
179 }
180
181 func ifchange(tgts []string, forced, traced bool) (bool, error) {
182         jsInit()
183         if !IsTopRedo {
184                 defer jsAcquire("ifchange exiting")
185         }
186         defer Jobs.Wait()
187         seen := buildDependants(tgts)
188         oodTgtsClear()
189         trace(CDebug, "building %d targets: %v", len(tgts), tgts)
190         jobs := 0
191         errs := make(chan error, len(tgts))
192         var ood bool
193         var err error
194         for _, tgt := range tgts {
195                 if _, ok := seen[tgt]; ok {
196                         trace(CDebug, "%s was already build as a dependant", tgt)
197                         continue
198                 }
199                 ood = true
200                 if !forced {
201                         ood, err = isOODWithTrace(Cwd, tgt, 0, seen)
202                         if err != nil {
203                                 return false, err
204                         }
205                 }
206                 if !ood {
207                         continue
208                 }
209                 if isSrc(Cwd, tgt) {
210                         trace(CDebug, "%s is source, not redoing", tgt)
211                         continue
212                 }
213                 if err = runScript(tgt, errs, traced); err != nil {
214                         return false, err
215                 }
216                 jobs++
217         }
218         ok := true
219         for ; jobs > 0; jobs-- {
220                 ok = isOkRun(<-errs) && ok
221         }
222         return ok, nil
223 }