]> 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 5edac297178e314ccceec81380fbbeddeb8350fd..c455a3b01e22c3221f0b9a5eccc72db6b83996d0 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
@@ -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 {
@@ -109,11 +141,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: %w", err)}
                }
+               theirHsh := m["Hash"]
                trace(CDebug, "ood: %s%s -> %s: checking", indent, tgtOrig, dep)
 
                fd, err := os.Open(path.Join(cwd, dep))
@@ -127,14 +162,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}
@@ -162,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}
                }
@@ -178,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)
+}