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
+ }
+ depInfo := DepInfo{}
+ if b := m["Build"]; b == "" {
+ return nil, errors.New(".dep missing Build:")
+ } else {
+ depInfo.build = b
}
- if m["Build"] == "" {
- return false, r, errors.New(".dep missing Build:")
+ 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 m["Build"] == BuildUUID, r, nil
+ return &depInfo, nil
}
-func rebuildStamped(cwd, tgt, depPath string) (string, error) {
+func rebuildStamped(cwd, tgt, depPath, stampPrev string) (bool, error) {
relTgt := cwdMustRel(cwd, tgt)
errs := make(chan error, 1)
- if err := runScript(relTgt, errs); err != nil {
- return "", err
+ if err := runScript(relTgt, errs, stampPrev); err != nil {
+ return false, err
}
if err := <-errs; !isOkRun(err) {
- return "", errors.New("build failed")
+ return false, errors.New("build failed")
}
fdDep, err := os.Open(depPath)
if err != nil {
- return "", err
+ return false, err
}
defer fdDep.Close()
- builtNow, r, err := isBuiltNow(fdDep)
+ depInfo, err := depRead(fdDep)
if err != nil {
- return "", err
- }
- if !builtNow {
- return "", errors.New("is not built")
+ return false, err
}
- var stampTheir string
- for {
- m, err := r.NextMap()
- if err != nil {
- if err == io.EOF {
- break
- }
- return "", err
- }
- if m["Type"] == DepTypeStamp {
- stampTheir = m["Hash"]
- break
- }
+ if depInfo.build != BuildUUID {
+ return false, errors.New("is not built")
}
- return stampTheir, nil
+ return depInfo.stampSame, nil
+}
+
+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) (bool, error) {
+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 := path.Join(cwd, RedoDir, tgt+DepSuffix)
- fdDep, err := os.Open(depPath)
- if err != nil {
- trace(CDebug, "ood: %s%s -> no dep: %s", indent, tgtOrig, depPath)
- return true, nil
+ depPath := formDepPath(cwd, tgt)
+ if depInfo == nil {
+ fdDep, err := os.Open(depPath)
+ if err != nil {
+ trace(CDebug, "ood: %s%s -> no dep: %s", indent, tgtOrig, depPath)
+ return true, nil
+ }
+ defer fdDep.Close()
+ depInfo, err = depRead(fdDep)
+ if err != nil {
+ return true, TgtErr{tgtOrig, err}
+ }
}
- defer fdDep.Close()
- ood := false
- builtNow, r, err := isBuiltNow(fdDep)
- if err != nil {
- return true, TgtErr{tgtOrig, err}
- }
- if builtNow {
+ if depInfo.build == BuildUUID {
trace(CDebug, "ood: %s%s -> already built", indent, tgtOrig)
return false, nil
}
-
- var stampOur string
- ifcreates := []map[string]string{}
- ifchanges := []map[string]string{}
- for {
- m, err := r.NextMap()
- if err != nil {
- if err == io.EOF {
- break
- }
- return true, TgtErr{tgtOrig, err}
- }
- switch m["Type"] {
- case DepTypeAlways:
- trace(CDebug, "ood: %s%s -> always", indent, tgtOrig)
- ood = true
- case DepTypeIfcreate:
- ifcreates = append(ifcreates, m)
- case DepTypeIfchange:
- ifchanges = append(ifchanges, m)
- case DepTypeStamp:
- stampOur = m["Hash"]
- trace(CDebug, "ood: %s%s -> stamped: %s", indent, tgtOrig, stampOur)
- default:
- return ood, TgtErr{tgtOrig, errors.New("invalid format of .dep")}
- }
- }
+ ood := depInfo.always
if ood {
goto StampCheck
}
- for _, m := range ifcreates {
- theirTgt := m["Target"]
- if theirTgt == "" {
- return ood, TgtErr{tgtOrig, errors.New("invalid format of .dep")}
- }
- if _, err = os.Stat(path.Join(cwd, theirTgt)); err == nil {
- trace(CDebug, "ood: %s%s -> created", indent, tgtOrig)
+ 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
goto StampCheck
}
}
- for _, m := range ifchanges {
+ for _, m := range depInfo.ifchanges {
dep := m["Target"]
theirTs := m["Ctime"]
theirHsh := m["Hash"]
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) {
return ood, TgtErr{tgtOrig, err}
}
defer fd.Close()
- ts, err := fileCtime(fd)
- if err != nil {
- return ood, TgtErr{tgtOrig, err}
+
+ 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}
+ }
+ defer depFdDep.Close()
+ depDepInfo, err = depRead(depFdDep)
+ if err != nil {
+ return ood, TgtErr{path.Join(tgtOrig, dep), 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 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, TgtErr{tgtOrig, err}
}
- if theirHsh != hsh {
- trace(CDebug, "ood: %s%s -> %s: hash differs", indent, tgtOrig, dep)
+ 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, TgtErr{tgtOrig, err}
+ }
+ 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)
}
- trace(CDebug, "ood: %s%s -> %s: same hash", indent, tgtOrig, dep)
}
- fd.Close()
+
if dep == tgt {
trace(CDebug, "ood: %s%s -> %s: same target", indent, tgtOrig, dep)
continue
trace(CDebug, "ood: %s%s -> %s: is source", indent, tgtOrig, dep)
continue
}
- depOod, err := isOOD(cwd, dep, level+1)
+
+ depOod, err := isOOD(cwd, dep, level+1, depDepInfo)
if err != nil {
return ood, TgtErr{tgtOrig, err}
}
}
StampCheck:
- if ood && stampOur != "" {
+ if ood && depInfo.stamp != "" {
trace(CDebug, "ood: %s%s run, because stamped", indent, tgtOrig)
- stampTheir, err := rebuildStamped(cwd, tgt, depPath)
+ stampSame, err := rebuildStamped(cwd, tgt, depPath, depInfo.stamp)
if err != nil {
return true, TgtErr{tgtOrig, err}
}
- trace(CDebug, "ood: %s%s -> stamp: %s %s", indent, tgtOrig, stampOur, stampTheir)
- if stampOur == stampTheir {
- ood = false
- }
+ 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