]> Cypherpunks.ru repositories - goredo.git/blob - depfix.go
Download link for 2.6.2 release
[goredo.git] / depfix.go
1 // goredo -- djb's redo implementation on pure Go
2 // Copyright (C) 2020-2024 Sergey Matveev <stargrave@stargrave.org>
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, version 3 of the License.
7 //
8 // This program is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 // GNU General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
15
16 package main
17
18 import (
19         "bufio"
20         "bytes"
21         "errors"
22         "io"
23         "io/fs"
24         "os"
25         "path"
26         "strings"
27
28         "github.com/google/uuid"
29         "go.cypherpunks.ru/recfile"
30 )
31
32 var DepFixHashCache map[string]Hash
33
34 func recfileWrite(fdDep io.StringWriter, fields ...recfile.Field) error {
35         w := recfile.NewWriter(fdDep)
36         if _, err := w.RecordStart(); err != nil {
37                 return err
38         }
39         if _, err := w.WriteFields(fields...); err != nil {
40                 return err
41         }
42         return nil
43 }
44
45 func depFix(root string) error {
46         tracef(CDebug, "depfix: entering %s", root)
47         dir, err := os.Open(root)
48         if err != nil {
49                 return ErrLine(err)
50         }
51         defer dir.Close()
52         for {
53                 entries, err := dir.ReadDir(1 << 10)
54                 if err != nil {
55                         if err == io.EOF {
56                                 break
57                         }
58                         return ErrLine(err)
59                 }
60                 for _, entry := range entries {
61                         if entry.IsDir() {
62                                 if err = depFix(path.Join(root, entry.Name())); err != nil {
63                                         return err
64                                 }
65                         }
66                 }
67         }
68         dir.Close()
69
70         redoDir := path.Join(root, RedoDir)
71         dir, err = os.Open(redoDir)
72         if err != nil {
73                 if errors.Is(err, fs.ErrNotExist) {
74                         return nil
75                 }
76                 return ErrLine(err)
77         }
78         defer dir.Close()
79         for {
80                 entries, err := dir.ReadDir(1 << 10)
81                 if err != nil {
82                         if err == io.EOF {
83                                 break
84                         }
85                         return ErrLine(err)
86                 }
87                 for _, entry := range entries {
88                         switch path.Ext(entry.Name()) {
89                         case DepSuffix:
90                         case ".rec":
91                         default:
92                                 continue
93                         }
94                         tracef(CDebug, "depfix: checking %s/%s", root, entry.Name())
95                         fdDepPath := path.Join(redoDir, entry.Name())
96                         data, err := os.ReadFile(fdDepPath)
97                         if err != nil {
98                                 return ErrLine(err)
99                         }
100                         fdDep, err := tempfile(redoDir, entry.Name())
101                         if err != nil {
102                                 return ErrLine(err)
103                         }
104                         defer os.Remove(fdDep.Name())
105                         tracef(
106                                 CDebug, "depfix: %s/%s: tmp %s",
107                                 root, entry.Name(), fdDep.Name(),
108                         )
109                         fdDepW := bufio.NewWriter(fdDep)
110                         switch path.Ext(entry.Name()) {
111                         case DepSuffix:
112                                 if _, err = depParse(NewTgt(""), data); err != nil {
113                                         return ErrLine(err)
114                                 }
115                                 build, data, err := depHeadParse(data)
116                                 if err != nil {
117                                         return ErrLine(err)
118                                 }
119                                 if err = depBuildWrite(fdDepW, build); err != nil {
120                                         return ErrLine(err)
121                                 }
122                                 var typ byte
123                                 var chunk []byte
124                                 for len(data) > 0 {
125                                         typ, chunk, data, _ = chunkRead(data)
126                                         switch typ {
127                                         case DepTypeAlways:
128                                                 err = always(fdDepW, fdDep.Name())
129                                         case DepTypeStamp:
130                                                 p := mustAbs(path.Join(root,
131                                                         strings.TrimSuffix(entry.Name(), DepSuffix)))
132                                                 hsh, ok := DepFixHashCache[p]
133                                                 if !ok {
134                                                         var fd *os.File
135                                                         fd, err = os.Open(p)
136                                                         if err != nil {
137                                                                 break
138                                                         }
139                                                         hsh, err = fileHash(fd)
140                                                         fd.Close()
141                                                         if err != nil {
142                                                                 break
143                                                         }
144                                                         DepFixHashCache[p] = hsh
145                                                 }
146                                                 err = stamp(fdDepW, fdDep.Name(), hsh)
147                                         case DepTypeIfcreate:
148                                                 err = ifcreate(fdDepW, fdDep.Name(), string(chunk))
149                                         case DepTypeIfchange:
150                                                 name := string(chunk[InodeLen+HashLen:])
151                                                 p := mustAbs(path.Join(root, name))
152                                                 var fd *os.File
153                                                 fd, err = os.Open(p)
154                                                 if err != nil {
155                                                         break
156                                                 }
157                                                 var inode *Inode
158                                                 inode, _, err = inodeFromFileByFd(fd)
159                                                 if err != nil {
160                                                         fd.Close()
161                                                         break
162                                                 }
163                                                 hsh, ok := DepFixHashCache[p]
164                                                 if !ok {
165                                                         hsh, err = fileHash(fd)
166                                                         if err != nil {
167                                                                 break
168                                                         }
169                                                         DepFixHashCache[p] = hsh
170                                                 }
171                                                 fd.Close()
172                                                 _, err = io.Copy(fdDepW, bytes.NewBuffer(
173                                                         chunkWrite(bytes.Join([][]byte{
174                                                                 {DepTypeIfchange},
175                                                                 inode[:],
176                                                                 []byte(hsh),
177                                                                 []byte(name),
178                                                         }, nil))))
179                                         case DepTypeIfchangeNonex:
180                                                 err = depWriteNonex(fdDepW, fdDep.Name(), string(chunk))
181                                         }
182                                         if err != nil {
183                                                 return ErrLine(err)
184                                         }
185                                 }
186                         case ".rec":
187                                 defer os.Remove(fdDepPath)
188                                 fdDepPath = fdDepPath[:len(fdDepPath)-4] + DepSuffix
189                                 r := recfile.NewReader(bytes.NewReader(data))
190                                 m, err := r.NextMap()
191                                 if err != nil {
192                                         return err
193                                 }
194                                 var build uuid.UUID
195                                 build, err = uuid.Parse(m["Build"])
196                                 if err != nil {
197                                         break
198                                 }
199                                 if err = depBuildWrite(fdDepW, build); err != nil {
200                                         return ErrLine(err)
201                                 }
202                                 for {
203                                         m, err := r.NextMap()
204                                         if err != nil {
205                                                 if errors.Is(err, io.EOF) {
206                                                         break
207                                                 }
208                                                 return ErrLine(err)
209                                         }
210                                         switch m["Type"] {
211                                         case "always":
212                                                 err = always(fdDepW, m["Target"])
213                                         case "stamp":
214                                                 p := mustAbs(path.Join(root,
215                                                         strings.TrimSuffix(entry.Name(), DepSuffix)))
216                                                 hsh, ok := DepFixHashCache[p]
217                                                 if !ok {
218                                                         var fd *os.File
219                                                         fd, err = os.Open(p)
220                                                         if err != nil {
221                                                                 break
222                                                         }
223                                                         hsh, err = fileHash(fd)
224                                                         fd.Close()
225                                                         if err != nil {
226                                                                 break
227                                                         }
228                                                         DepFixHashCache[p] = hsh
229                                                 }
230                                                 err = stamp(fdDepW, fdDep.Name(), hsh)
231                                         case "ifcreate":
232                                                 err = ifcreate(fdDepW, fdDep.Name(), m["Target"])
233                                         case "ifchange":
234                                                 if m["Size"] == "" {
235                                                         err = depWriteNonex(fdDepW, fdDep.Name(), m["Target"])
236                                                         break
237                                                 }
238                                                 name := string(m["Target"])
239                                                 p := mustAbs(path.Join(root, name))
240                                                 var fd *os.File
241                                                 fd, err = os.Open(p)
242                                                 if err != nil {
243                                                         break
244                                                 }
245                                                 var inode *Inode
246                                                 inode, _, err = inodeFromFileByFd(fd)
247                                                 if err != nil {
248                                                         fd.Close()
249                                                         break
250                                                 }
251                                                 hsh, ok := DepFixHashCache[p]
252                                                 if !ok {
253                                                         hsh, err = fileHash(fd)
254                                                         if err != nil {
255                                                                 break
256                                                         }
257                                                         DepFixHashCache[p] = hsh
258                                                 }
259                                                 fd.Close()
260                                                 _, err = io.Copy(fdDepW, bytes.NewBuffer(
261                                                         chunkWrite(bytes.Join([][]byte{
262                                                                 {DepTypeIfchange},
263                                                                 inode[:],
264                                                                 []byte(hsh),
265                                                                 []byte(name),
266                                                         }, nil))))
267                                         }
268                                         if err != nil {
269                                                 return ErrLine(err)
270                                         }
271                                 }
272                         }
273                         err = fdDepW.Flush()
274                         if err != nil {
275                                 return ErrLine(err)
276                         }
277                         if !NoSync {
278                                 if err = fdDep.Sync(); err != nil {
279                                         return ErrLine(err)
280                                 }
281                         }
282                         fdDep.Close()
283                         if err = os.Rename(fdDep.Name(), fdDepPath); err != nil {
284                                 return ErrLine(err)
285                         }
286                         tracef(CRedo, "%s", fdDepPath)
287                 }
288                 if !NoSync {
289                         if err = syncDir(redoDir); err != nil {
290                                 return err
291                         }
292                 }
293         }
294         return nil
295 }