]> Cypherpunks.ru repositories - goredo.git/blobdiff - ood.go
redo-stamp with REDO_NO_HASH workability fix
[goredo.git] / ood.go
diff --git a/ood.go b/ood.go
index a7c2d22422443c75b3d4c84d5f876ed0e5fc483a..9b6ebbab03b294b2d82f648e01098aa4747b3a29 100644 (file)
--- a/ood.go
+++ b/ood.go
@@ -31,6 +31,32 @@ import (
        "go.cypherpunks.ru/recfile"
 )
 
+const (
+       DepTypeIfcreate = "ifcreate"
+       DepTypeIfchange = "ifchange"
+       DepTypeAlways   = "always"
+       DepTypeStamp    = "stamp"
+)
+
+type TgtErr struct {
+       Tgt string
+       Err error
+}
+
+func (e TgtErr) Unwrap() error { return e.Err }
+
+func (e TgtErr) Error() string {
+       return fmt.Sprintf("%s: %s", e.Tgt, e.Err)
+}
+
+func cwdMustRel(paths ...string) string {
+       rel, err := filepath.Rel(Cwd, path.Join(paths...))
+       if err != nil {
+               panic(err)
+       }
+       return rel
+}
+
 func cwdAndTgt(tgt string) (string, string) {
        cwd, tgt := path.Split(tgt)
        cwd, err := filepath.Abs(cwd)
@@ -51,175 +77,234 @@ func isSrc(cwd, tgt string) bool {
        return true
 }
 
-func isBuiltNow(fdDep *os.File) (bool, *recfile.Reader, error) {
+type DepInfo struct {
+       build     string
+       always    bool
+       stamp     string
+       stampSame bool
+       ifcreates []string
+       ifchanges []map[string]string
+}
+
+func depRead(fdDep *os.File) (*DepInfo, error) {
        r := recfile.NewReader(fdDep)
        m, err := r.NextMap()
        if err != nil {
-               return false, nil, err
+               return nil, err
        }
-       if m["Build"] == "" {
-               return false, r, errors.New("dep. missing Build:")
+       depInfo := DepInfo{}
+       if b := m["Build"]; b == "" {
+               return nil, errors.New(".dep missing Build:")
+       } else {
+               depInfo.build = b
        }
-       return m["Build"] == BuildUUID, r, nil
+       for {
+               m, err := r.NextMap()
+               if err != nil {
+                       if err == io.EOF {
+                               break
+                       }
+                       return nil, err
+               }
+               switch m["Type"] {
+               case DepTypeAlways:
+                       depInfo.always = true
+               case DepTypeIfcreate:
+                       dep := m["Target"]
+                       if dep == "" {
+                               return nil, errors.New("invalid format of .dep")
+                       }
+                       depInfo.ifcreates = append(depInfo.ifcreates, dep)
+               case DepTypeIfchange:
+                       delete(m, "Type")
+                       depInfo.ifchanges = append(depInfo.ifchanges, m)
+               case DepTypeStamp:
+                       hsh := m["Hash"]
+                       if hsh == "" {
+                               return nil, errors.New("invalid format of .dep")
+                       }
+                       depInfo.stamp = hsh
+                       depInfo.stampSame = m["Same"] == "true"
+               default:
+                       return nil, errors.New("invalid format of .dep")
+               }
+       }
+       return &depInfo, nil
 }
 
-func isOOD(cwd, tgt string, level int) (bool, error) {
-       tgtOrig := tgt
-       indent := strings.Repeat(". ", level)
-       trace(CDebug, "ood: %s%s checking", indent, tgtOrig)
-       cwd, tgt = cwdAndTgt(path.Join(cwd, tgt))
-       depPath := path.Join(cwd, RedoDir, tgt+DepSuffix)
+func rebuildStamped(cwd, tgt, depPath, stampPrev string) (bool, error) {
+       relTgt := cwdMustRel(cwd, tgt)
+       errs := make(chan error, 1)
+       if err := runScript(relTgt, errs, stampPrev); err != nil {
+               return false, err
+       }
+       if err := <-errs; !isOkRun(err) {
+               return false, errors.New("build failed")
+       }
        fdDep, err := os.Open(depPath)
        if err != nil {
-               trace(CDebug, "ood: %s%s -> no dep: %s", indent, tgtOrig, depPath)
-               return true, nil
+               return false, err
        }
        defer fdDep.Close()
-       ood := false
-
-       builtNow, r, err := isBuiltNow(fdDep)
+       depInfo, err := depRead(fdDep)
        if err != nil {
-               return true, err
+               return false, err
        }
-       if builtNow {
-               trace(CDebug, "ood: %s%s -> already built", indent, tgtOrig)
-               return false, nil
+       if depInfo.build != BuildUUID {
+               return false, errors.New("is not built")
        }
+       return depInfo.stampSame, nil
+}
 
-       var stampOur string
-       for {
-               m, err := r.NextMap()
+func formDepPath(cwd, tgt string) string {
+       cwd, tgt = cwdAndTgt(path.Join(cwd, tgt))
+       return path.Join(cwd, RedoDir, tgt+DepSuffix)
+}
+
+func isOOD(cwd, tgtOrig string, level int, depInfo *DepInfo) (bool, error) {
+       indent := strings.Repeat(". ", level)
+       trace(CDebug, "ood: %s%s checking", indent, tgtOrig)
+       cwd, tgt := cwdAndTgt(path.Join(cwd, tgtOrig))
+       depPath := formDepPath(cwd, tgt)
+       if depInfo == nil {
+               fdDep, err := os.Open(depPath)
                if err != nil {
-                       if err == io.EOF {
-                               break
-                       }
-                       return true, err
+                       trace(CDebug, "ood: %s%s -> no dep: %s", indent, tgtOrig, depPath)
+                       return true, nil
                }
-               switch m["Type"] {
-               case "always":
-                       trace(CDebug, "ood: %s%s -> always", indent, tgtOrig)
+               defer fdDep.Close()
+               depInfo, err = depRead(fdDep)
+               if err != nil {
+                       return true, TgtErr{tgtOrig, err}
+               }
+       }
+
+       if depInfo.build == BuildUUID {
+               trace(CDebug, "ood: %s%s -> already built", indent, tgtOrig)
+               return false, nil
+       }
+       ood := depInfo.always
+       if ood {
+               goto StampCheck
+       }
+
+       for _, dep := range depInfo.ifcreates {
+               if _, err := os.Stat(path.Join(cwd, dep)); err == nil {
+                       trace(CDebug, "ood: %s%s -> %s created", indent, tgtOrig, dep)
                        ood = true
-               case "ifcreate":
-                       theirTgt := m["Target"]
-                       if theirTgt == "" {
-                               return ood, errors.New("invalid format of dep." + tgtOrig)
-                       }
-                       if _, err = os.Stat(path.Join(cwd, theirTgt)); err == nil {
-                               trace(CDebug, "ood: %s%s -> created", indent, tgtOrig)
+                       goto StampCheck
+               }
+       }
+
+       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")}
+               }
+               trace(CDebug, "ood: %s%s -> %s: checking", indent, tgtOrig, dep)
+
+               fd, err := os.Open(path.Join(cwd, dep))
+               if err != nil {
+                       if os.IsNotExist(err) {
+                               trace(CDebug, "ood: %s%s -> %s: not exists", indent, tgtOrig, dep)
                                ood = true
+                               goto StampCheck
                        }
-               case "ifchange":
-                       dep := m["Target"]
-                       theirTs := m["Ctime"]
-                       theirHsh := m["Hash"]
-                       if dep == "" || theirTs == "" {
-                               return ood, errors.New("invalid format of dep." + tgtOrig)
+                       return ood, TgtErr{tgtOrig, err}
+               }
+               defer fd.Close()
+
+               var depDepInfo *DepInfo
+               if !(dep == tgt || isSrc(cwd, dep)) {
+                       trace(CDebug, "ood: %s%s -> %s: prereading .dep", indent, tgtOrig, dep)
+                       depFdDep, err := os.Open(formDepPath(cwd, dep))
+                       if err != nil {
+                               return ood, TgtErr{path.Join(tgtOrig, dep), err}
                        }
-                       trace(CDebug, "ood: %s%s -> %s: checking", indent, tgtOrig, dep)
-                       fd, err := os.Open(path.Join(cwd, dep))
+                       defer depFdDep.Close()
+                       depDepInfo, err = depRead(depFdDep)
                        if err != nil {
-                               if os.IsNotExist(err) {
-                                       trace(CDebug, "ood: %s%s -> %s: not exists", indent, tgtOrig, dep)
-                                       ood = true
-                                       continue
-                               }
-                               return ood, err
+                               return ood, TgtErr{path.Join(tgtOrig, dep), err}
                        }
-                       defer fd.Close()
+               }
+
+               if depDepInfo != nil && depDepInfo.build == BuildUUID {
+                       trace(
+                               CDebug, "ood: %s%s -> %s: .dep says build is same",
+                               indent, tgtOrig, dep,
+                       )
+                       if !depDepInfo.stampSame {
+                               trace(
+                                       CDebug, "ood: %s%s -> %s: .dep says stamp is not same",
+                                       indent, tgtOrig, dep,
+                               )
+                               ood = true
+                               return ood, nil
+                       }
+                       trace(
+                               CDebug, "ood: %s%s -> %s: .dep says stamp is same",
+                               indent, tgtOrig, dep,
+                       )
+                       continue
+               }
+
+               if depDepInfo == nil || !depDepInfo.always && depDepInfo.stamp == "" {
                        ts, err := fileCtime(fd)
                        if err != nil {
-                               return ood, err
+                               return ood, TgtErr{tgtOrig, err}
                        }
                        if theirTs == ts {
                                trace(CDebug, "ood: %s%s -> %s: same ctime", indent, tgtOrig, dep)
                        } else if NoHash || theirHsh == "" {
                                trace(CDebug, "ood: %s%s -> %s: ctime differs", indent, tgtOrig, dep)
                                ood = true
+                               goto StampCheck
                        } else {
                                hsh, err := fileHash(fd)
                                if err != nil {
-                                       return ood, err
+                                       return ood, TgtErr{tgtOrig, err}
                                }
-                               if theirHsh == hsh {
-                                       trace(CDebug, "ood: %s%s -> %s: same hash", indent, tgtOrig, dep)
-                               } else {
+                               if theirHsh != hsh {
                                        trace(CDebug, "ood: %s%s -> %s: hash differs", indent, tgtOrig, dep)
                                        ood = true
+                                       goto StampCheck
                                }
+                               trace(CDebug, "ood: %s%s -> %s: same hash", indent, tgtOrig, dep)
                        }
-                       fd.Close()
-                       if ood {
-                               continue
-                       }
-                       if dep == tgt {
-                               trace(CDebug, "ood: %s%s -> %s: same target", indent, tgtOrig, dep)
-                               continue
-                       }
-                       if isSrc(cwd, dep) {
-                               trace(CDebug, "ood: %s%s -> %s: is source", indent, tgtOrig, dep)
-                               continue
-                       } else {
-                               depOod, err := isOOD(cwd, dep, level+1)
-                               if depOod {
-                                       ood = true
-                                       trace(CDebug, "ood: %s%s -> %s: ood", indent, tgtOrig, dep)
-                               } else {
-                                       trace(CDebug, "ood: %s%s -> %s: !ood", indent, tgtOrig, dep)
-                               }
-                               if err != nil {
-                                       return ood, err
-                               }
-                       }
-               case "stamp":
-                       stampOur = m["Hash"]
-                       trace(CDebug, "ood: %s%s -> stamped: %s", indent, tgtOrig, stampOur)
-               default:
-                       return ood, errors.New("invalid format of dep." + tgtOrig)
                }
-       }
-       if ood && stampOur != "" {
-               errs := make(chan error, 0)
-               trace(CDebug, "ood: %s%s running, because stamped", indent, tgtOrig)
-               relTgt, err := filepath.Rel(Cwd, path.Join(cwd, tgt))
-               if err != nil {
-                       return true, err
-               }
-               if err = runScript(relTgt, errs); err != nil {
-                       return true, err
-               }
-               if err = <-errs; err != nil {
-                       return true, err
+
+               if dep == tgt {
+                       trace(CDebug, "ood: %s%s -> %s: same target", indent, tgtOrig, dep)
+                       continue
                }
-               fdDep, err := os.Open(depPath)
-               if err != nil {
-                       return true, err
+               if isSrc(cwd, dep) {
+                       trace(CDebug, "ood: %s%s -> %s: is source", indent, tgtOrig, dep)
+                       continue
                }
-               defer fdDep.Close()
-               builtNow, r, err := isBuiltNow(fdDep)
+
+               depOod, err := isOOD(cwd, dep, level+1, depDepInfo)
                if err != nil {
-                       return true, err
-               }
-               if !builtNow {
-                       return true, fmt.Errorf("%s is not built", tgtOrig)
+                       return ood, TgtErr{tgtOrig, err}
                }
-               var stampTheir string
-               for {
-                       m, err := r.NextMap()
-                       if err != nil {
-                               if err == io.EOF {
-                                       break
-                               }
-                               return true, err
-                       }
-                       if m["Type"] == "stamp" {
-                               stampTheir = m["Hash"]
-                               break
-                       }
+               if depOod {
+                       trace(CDebug, "ood: %s%s -> %s: ood", indent, tgtOrig, dep)
+                       ood = true
+                       goto StampCheck
                }
-               trace(CDebug, "ood: %s%s -> stamp: %s %s", indent, tgtOrig, stampOur, stampTheir)
-               if stampOur == stampTheir {
-                       ood = false
+               trace(CDebug, "ood: %s%s -> %s: !ood", indent, tgtOrig, dep)
+       }
+
+StampCheck:
+       if ood && depInfo.stamp != "" {
+               trace(CDebug, "ood: %s%s run, because stamped", indent, tgtOrig)
+               stampSame, err := rebuildStamped(cwd, tgt, depPath, depInfo.stamp)
+               if err != nil {
+                       return true, TgtErr{tgtOrig, err}
                }
+               trace(CDebug, "ood: %s%s -> stamp: same: %v", indent, tgtOrig, stampSame)
+               ood = !stampSame
        }
        trace(CDebug, "ood: %s%s: %v", indent, tgtOrig, ood)
        return ood, nil