]> Cypherpunks.ru repositories - goredo.git/blobdiff - ood.go
default.do-s must not depend on themselves
[goredo.git] / ood.go
diff --git a/ood.go b/ood.go
index 912b6c61b4ce62c5e93ba9de78a3d17fd8283eff..c455a3b01e22c3221f0b9a5eccc72db6b83996d0 100644 (file)
--- a/ood.go
+++ b/ood.go
@@ -22,10 +22,14 @@ package main
 import (
        "errors"
        "fmt"
+       "io"
+       "log"
        "os"
        "path"
        "path/filepath"
        "strings"
+
+       "golang.org/x/sys/unix"
 )
 
 const (
@@ -33,6 +37,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 {
@@ -77,6 +90,21 @@ func isSrc(cwd, tgt string) bool {
        return true
 }
 
+func isOODByBuildUUID(cwd, tgtOrig string) bool {
+       cwd, tgt := cwdAndTgt(path.Join(cwd, tgtOrig))
+       depPath := path.Join(cwd, RedoDir, tgt+DepSuffix)
+       fdDep, err := os.Open(depPath)
+       if err != nil {
+               return true
+       }
+       depInfo, err := depRead(fdDep)
+       fdDep.Close()
+       if err != nil || depInfo.build != BuildUUID {
+               return true
+       }
+       return false
+}
+
 func isOOD(cwd, tgtOrig string, level int, seen map[string]struct{}) (bool, error) {
        indent := strings.Repeat(". ", level)
        trace(CDebug, "ood: %s%s checking", indent, tgtOrig)
@@ -97,6 +125,10 @@ func isOOD(cwd, tgtOrig string, level int, seen map[string]struct{}) (bool, erro
                trace(CDebug, "ood: %s%s -> already built", indent, tgtOrig)
                return false, nil
        }
+       if _, err := os.Stat(path.Join(cwd, tgt)); err != nil && os.IsNotExist(err) {
+               trace(CDebug, "ood: %s%s -> non-existent", indent, tgtOrig)
+               return true, nil
+       }
        ood := false
 
        for _, dep := range depInfo.ifcreates {
@@ -110,11 +142,11 @@ func isOOD(cwd, tgtOrig string, level int, seen map[string]struct{}) (bool, erro
        for _, m := range depInfo.ifchanges {
                dep := m["Target"]
                if dep == "" {
-                       return ood, TgtErr{tgtOrig, errors.New("invalid format of .dep: missing Target")}
+                       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 .dep: %v", err)}
+                       return ood, TgtErr{tgtOrig, fmt.Errorf("invalid format of .rec: %w", err)}
                }
                theirHsh := m["Hash"]
                trace(CDebug, "ood: %s%s -> %s: checking", indent, tgtOrig, dep)
@@ -170,7 +202,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}
                }
@@ -186,3 +218,57 @@ 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 {
+               if !isOODByBuildUUID(cwd, tgtOrig) {
+                       trace(
+                               CDebug,
+                               "ood: %s%s -> already built",
+                               strings.Repeat(". ", level), tgtOrig,
+                       )
+                       return false, nil
+               }
+               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 {
+               log.Fatalln(err)
+       }
+       if _, err = FdOODTgts.Seek(0, io.SeekEnd); err != nil {
+               log.Fatalln(err)
+       }
+       if _, err := FdOODTgts.WriteString(p + "\x00"); err != nil {
+               log.Fatalln(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 {
+               log.Fatalln(err)
+       }
+       if err := FdOODTgts.Truncate(0); err != nil {
+               log.Fatalln(err)
+       }
+       unix.Flock(int(FdOODTgtsLock.Fd()), unix.LOCK_UN)
+}