]> Cypherpunks.ru repositories - goredo.git/blobdiff - ood.go
Repetitive OOD optimization
[goredo.git] / ood.go
diff --git a/ood.go b/ood.go
index 95c3ed29958bd65c415b326c5e2ed1b62a3f4499..cad1ce38f5f23f6beb1124749774a7d4111303ec 100644 (file)
--- a/ood.go
+++ b/ood.go
@@ -1,6 +1,6 @@
 /*
-goredo -- redo implementation on pure Go
-Copyright (C) 2020 Sergey Matveev <stargrave@stargrave.org>
+goredo -- djb's redo implementation on pure Go
+Copyright (C) 2020-2021 Sergey Matveev <stargrave@stargrave.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -26,6 +26,8 @@ import (
        "path"
        "path/filepath"
        "strings"
+
+       "golang.org/x/sys/unix"
 )
 
 const (
@@ -33,6 +35,15 @@ const (
        DepTypeIfchange = "ifchange"
        DepTypeAlways   = "always"
        DepTypeStamp    = "stamp"
+
+       EnvOODTgtsFd     = "REDO_OOD_TGTS_FD"
+       EnvOODTgtsLockFd = "REDO_OOD_TGTS_LOCK_FD"
+)
+
+var (
+       OODTgts       map[string]struct{}
+       FdOODTgts     *os.File
+       FdOODTgtsLock *os.File
 )
 
 type TgtErr struct {
@@ -68,6 +79,9 @@ func isSrc(cwd, tgt string) bool {
        if _, err := os.Stat(path.Join(d, f)); err != nil {
                return false
        }
+       if _, err := os.Stat(path.Join(d, f+".do")); err == nil {
+               return false
+       }
        if _, err := os.Stat(path.Join(d, RedoDir, f+DepSuffix)); err == nil {
                return false
        }
@@ -106,11 +120,14 @@ func isOOD(cwd, tgtOrig string, level int, seen map[string]struct{}) (bool, erro
 
        for _, m := range depInfo.ifchanges {
                dep := m["Target"]
-               theirTs := m["Ctime"]
-               theirHsh := m["Hash"]
-               if dep == "" || theirTs == "" {
-                       return ood, TgtErr{tgtOrig, errors.New("invalid format of .dep")}
+               if dep == "" {
+                       return ood, TgtErr{tgtOrig, errors.New("invalid format of .rec: missing Target")}
+               }
+               theirInode, err := inodeFromRec(m)
+               if err != nil {
+                       return ood, TgtErr{tgtOrig, fmt.Errorf("invalid format of .rec: %v", err)}
                }
+               theirHsh := m["Hash"]
                trace(CDebug, "ood: %s%s -> %s: checking", indent, tgtOrig, dep)
 
                fd, err := os.Open(path.Join(cwd, dep))
@@ -124,14 +141,19 @@ func isOOD(cwd, tgtOrig string, level int, seen map[string]struct{}) (bool, erro
                }
                defer fd.Close()
 
-               ts, err := fileCtime(fd)
+               inode, err := inodeFromFile(fd)
                if err != nil {
                        return ood, TgtErr{tgtOrig, err}
                }
-               if theirTs == ts {
-                       trace(CDebug, "ood: %s%s -> %s: same ctime", indent, tgtOrig, dep)
+               if inode.Size != theirInode.Size {
+                       trace(CDebug, "ood: %s%s -> %s: size differs", indent, tgtOrig, dep)
+                       ood = true
+                       goto Done
+               }
+               if InodeTrust && inode.Equals(theirInode) {
+                       trace(CDebug, "ood: %s%s -> %s: same inode", indent, tgtOrig, dep)
                } else {
-                       trace(CDebug, "ood: %s%s -> %s: ctime differs", indent, tgtOrig, dep)
+                       trace(CDebug, "ood: %s%s -> %s: inode differs", indent, tgtOrig, dep)
                        hsh, err := fileHash(fd)
                        if err != nil {
                                return ood, TgtErr{tgtOrig, err}
@@ -159,7 +181,7 @@ func isOOD(cwd, tgtOrig string, level int, seen map[string]struct{}) (bool, erro
                        continue
                }
 
-               depOod, err := isOOD(cwd, dep, level+1, seen)
+               depOod, err := isOODWithTrace(cwd, dep, level+1, seen)
                if err != nil {
                        return ood, TgtErr{tgtOrig, err}
                }
@@ -175,3 +197,49 @@ Done:
        trace(CDebug, "ood: %s%s: %v", indent, tgtOrig, ood)
        return ood, nil
 }
+
+func isOODWithTrace(
+       cwd, tgtOrig string,
+       level int,
+       seen map[string]struct{},
+) (bool, error) {
+       p, err := filepath.Abs(path.Join(cwd, tgtOrig))
+       if err != nil {
+               panic(err)
+       }
+       _, ood := OODTgts[p]
+       if ood {
+               trace(
+                       CDebug,
+                       "ood: %s%s true, external decision",
+                       strings.Repeat(". ", level), tgtOrig,
+               )
+               goto RecordOODTgt
+       }
+       ood, err = isOOD(cwd, tgtOrig, level, seen)
+       if !ood {
+               return ood, err
+       }
+RecordOODTgt:
+       if err = unix.Flock(int(FdOODTgtsLock.Fd()), unix.LOCK_EX); err != nil {
+               panic(err)
+       }
+       if _, err = FdOODTgts.Seek(0, os.SEEK_END); err != nil {
+               panic(err)
+       }
+       if _, err := FdOODTgts.WriteString(p + "\x00"); err != nil {
+               panic(err)
+       }
+       unix.Flock(int(FdOODTgtsLock.Fd()), unix.LOCK_UN)
+       return true, nil
+}
+
+func oodTgtsClear() {
+       if err := unix.Flock(int(FdOODTgtsLock.Fd()), unix.LOCK_EX); err != nil {
+               panic(err)
+       }
+       if err := FdOODTgts.Truncate(0); err != nil {
+               panic(err)
+       }
+       unix.Flock(int(FdOODTgtsLock.Fd()), unix.LOCK_UN)
+}