2 goredo -- redo implementation on pure Go
3 Copyright (C) 2020 Sergey Matveev <stargrave@stargrave.org>
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.
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.
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/>.
18 // Out-of-date determination
31 "go.cypherpunks.ru/recfile"
39 func (e TgtErr) Unwrap() error { return e.Err }
41 func (e TgtErr) Error() string {
42 return fmt.Sprintf("%s: %s", e.Tgt, e.Err)
45 func cwdAndTgt(tgt string) (string, string) {
46 cwd, tgt := path.Split(tgt)
47 cwd, err := filepath.Abs(cwd)
54 func isSrc(cwd, tgt string) bool {
55 d, f := path.Split(path.Join(cwd, tgt))
56 if _, err := os.Stat(path.Join(d, f)); err != nil {
59 if _, err := os.Stat(path.Join(d, RedoDir, f+DepSuffix)); err == nil {
65 func isBuiltNow(fdDep *os.File) (bool, *recfile.Reader, error) {
66 r := recfile.NewReader(fdDep)
69 return false, nil, err
72 return false, r, errors.New(".dep missing Build:")
74 return m["Build"] == BuildUUID, r, nil
77 func rebuildStamped(cwd, tgt, depPath string) (string, error) {
78 relTgt, err := filepath.Rel(Cwd, path.Join(cwd, tgt))
82 errs := make(chan error, 1)
83 if err = runScript(relTgt, errs); err != nil {
86 if err = <-errs; !isOkRun(err) {
87 return "", errors.New("build failed")
89 fdDep, err := os.Open(depPath)
94 builtNow, r, err := isBuiltNow(fdDep)
99 return "", errors.New("is not built")
101 var stampTheir string
103 m, err := r.NextMap()
110 if m["Type"] == "stamp" {
111 stampTheir = m["Hash"]
115 return stampTheir, nil
118 func isOOD(cwd, tgt string, level int) (bool, error) {
120 indent := strings.Repeat(". ", level)
121 trace(CDebug, "ood: %s%s checking", indent, tgtOrig)
122 cwd, tgt = cwdAndTgt(path.Join(cwd, tgt))
123 depPath := path.Join(cwd, RedoDir, tgt+DepSuffix)
124 fdDep, err := os.Open(depPath)
126 trace(CDebug, "ood: %s%s -> no dep: %s", indent, tgtOrig, depPath)
132 builtNow, r, err := isBuiltNow(fdDep)
134 return true, TgtErr{tgtOrig, err}
137 trace(CDebug, "ood: %s%s -> already built", indent, tgtOrig)
142 ifcreates := []map[string]string{}
143 ifchanges := []map[string]string{}
145 m, err := r.NextMap()
150 return true, TgtErr{tgtOrig, err}
154 trace(CDebug, "ood: %s%s -> always", indent, tgtOrig)
157 ifcreates = append(ifcreates, m)
159 ifchanges = append(ifchanges, m)
162 trace(CDebug, "ood: %s%s -> stamped: %s", indent, tgtOrig, stampOur)
164 return ood, TgtErr{tgtOrig, errors.New("invalid format of .dep")}
171 for _, m := range ifcreates {
172 theirTgt := m["Target"]
174 return ood, TgtErr{tgtOrig, errors.New("invalid format of .dep")}
176 if _, err = os.Stat(path.Join(cwd, theirTgt)); err == nil {
177 trace(CDebug, "ood: %s%s -> created", indent, tgtOrig)
183 for _, m := range ifchanges {
185 theirTs := m["Ctime"]
186 theirHsh := m["Hash"]
187 if dep == "" || theirTs == "" {
188 return ood, TgtErr{tgtOrig, errors.New("invalid format of .dep")}
190 trace(CDebug, "ood: %s%s -> %s: checking", indent, tgtOrig, dep)
191 fd, err := os.Open(path.Join(cwd, dep))
193 if os.IsNotExist(err) {
194 trace(CDebug, "ood: %s%s -> %s: not exists", indent, tgtOrig, dep)
198 return ood, TgtErr{tgtOrig, err}
201 ts, err := fileCtime(fd)
203 return ood, TgtErr{tgtOrig, err}
206 trace(CDebug, "ood: %s%s -> %s: same ctime", indent, tgtOrig, dep)
207 } else if NoHash || theirHsh == "" {
208 trace(CDebug, "ood: %s%s -> %s: ctime differs", indent, tgtOrig, dep)
212 hsh, err := fileHash(fd)
214 return ood, TgtErr{tgtOrig, err}
217 trace(CDebug, "ood: %s%s -> %s: hash differs", indent, tgtOrig, dep)
221 trace(CDebug, "ood: %s%s -> %s: same hash", indent, tgtOrig, dep)
225 trace(CDebug, "ood: %s%s -> %s: same target", indent, tgtOrig, dep)
229 trace(CDebug, "ood: %s%s -> %s: is source", indent, tgtOrig, dep)
232 depOod, err := isOOD(cwd, dep, level+1)
234 return ood, TgtErr{tgtOrig, err}
237 trace(CDebug, "ood: %s%s -> %s: ood", indent, tgtOrig, dep)
241 trace(CDebug, "ood: %s%s -> %s: !ood", indent, tgtOrig, dep)
245 if ood && stampOur != "" {
246 trace(CDebug, "ood: %s%s run, because stamped", indent, tgtOrig)
247 stampTheir, err := rebuildStamped(cwd, tgt, depPath)
249 return true, TgtErr{tgtOrig, err}
251 trace(CDebug, "ood: %s%s -> stamp: %s %s", indent, tgtOrig, stampOur, stampTheir)
252 if stampOur == stampTheir {
256 trace(CDebug, "ood: %s%s: %v", indent, tgtOrig, ood)