]> Cypherpunks.ru repositories - goredo.git/commitdiff
Refactor target paths, less CPU, less memory, more clarity
authorSergey Matveev <stargrave@stargrave.org>
Tue, 3 Oct 2023 18:06:01 +0000 (21:06 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Tue, 3 Oct 2023 18:16:10 +0000 (21:16 +0300)
buildlog.go
dep.go
dot.go
ifchange.go
main.go
ood.go
path.go
run.go
sources.go
targets.go

index b0d2f2d620b1961a306576594c501517f88763f7..956cf4fdc9aa353b5abfca806e19cc8ae19c56ac 100644 (file)
@@ -38,8 +38,7 @@ import (
 const HumanTimeFmt = "2006-01-02 15:04:05.000000000 Z07:00"
 
 type BuildLogJob struct {
-       dir      string
-       tgt      string
+       tgt      *Tgt
        started  time.Time
        exitCode int
        rec      map[string][]string
@@ -75,8 +74,8 @@ func init() {
        buildLogSeen = make(map[string]struct{})
 }
 
-func parseBuildLogRec(dir, tgt string) (map[string][]string, error) {
-       fd, err := os.Open(path.Join(dir, RedoDir, tgt+LogRecSuffix))
+func parseBuildLogRec(tgt *Tgt) (map[string][]string, error) {
+       fd, err := os.Open(path.Join(tgt.h, RedoDir, tgt.t+LogRecSuffix))
        if err != nil {
                return nil, ErrLine(err)
        }
@@ -94,18 +93,17 @@ func depthPrefix(depth int) string {
 }
 
 func showBuildLogSub(sub *BuildLogJob, depth int) error {
-       abs := mustAbs(path.Join(sub.dir, sub.tgt))
-       if _, ok := buildLogSeen[abs]; ok {
+       if _, ok := buildLogSeen[sub.tgt.a]; ok {
                return nil
        }
-       buildLogSeen[abs] = struct{}{}
+       buildLogSeen[sub.tgt.a] = struct{}{}
        dp := depthPrefix(depth)
        fmt.Printf(
                "%s%s%s\n",
                sub.rec["Started"][0], dp,
-               colourize(CRedo, "redo "+sub.tgt),
+               colourize(CRedo, "redo "+sub.tgt.String()),
        )
-       if err := showBuildLog(sub.dir, sub.tgt, sub.rec, depth+1); err != nil {
+       if err := showBuildLog(sub.tgt, sub.rec, depth+1); err != nil {
                return err
        }
        durationSec, durationNsec, err := durationToInts(sub.rec["Duration"][0])
@@ -116,14 +114,14 @@ func showBuildLogSub(sub *BuildLogJob, depth int) error {
                fmt.Printf(
                        "%s%s%s (code: %d) (%d.%ds)\n\n",
                        sub.rec["Finished"][0], dp,
-                       colourize(CErr, "err "+sub.tgt),
+                       colourize(CErr, "err "+sub.tgt.String()),
                        sub.exitCode, durationSec, durationNsec,
                )
        } else {
                fmt.Printf(
                        "%s%s%s (%d.%ds)\n\n",
                        sub.rec["Finished"][0], dp,
-                       colourize(CRedo, "done "+sub.tgt),
+                       colourize(CRedo, "done "+sub.tgt.String()),
                        durationSec, durationNsec,
                )
        }
@@ -168,11 +166,10 @@ func showBuildLogCmd(m map[string][]string, depth int) error {
        return nil
 }
 
-func showBuildLog(dir, tgt string, buildLogRec map[string][]string, depth int) error {
+func showBuildLog(tgt *Tgt, buildLogRec map[string][]string, depth int) error {
        var err error
-       dirNormalized, tgtNormalized := cwdAndTgt(path.Join(dir, tgt))
        if *flagBuildLogCommands || *flagBuildLogRecursive {
-               buildLogRec, err = parseBuildLogRec(dirNormalized, tgtNormalized)
+               buildLogRec, err = parseBuildLogRec(tgt)
                if err != nil {
                        return err
                }
@@ -182,7 +179,7 @@ func showBuildLog(dir, tgt string, buildLogRec map[string][]string, depth int) e
                        return err
                }
        }
-       fd, err := os.Open(path.Join(dirNormalized, RedoDir, tgtNormalized+LogSuffix))
+       fd, err := os.Open(path.Join(tgt.h, RedoDir, tgt.t+LogSuffix))
        if err != nil {
                return ErrLine(err)
        }
@@ -198,12 +195,12 @@ func showBuildLog(dir, tgt string, buildLogRec map[string][]string, depth int) e
        }
        defer fd.Close()
        subs := make([]*BuildLogJob, 0, len(buildLogRec["Ifchange"]))
-       for _, dep := range buildLogRec["Ifchange"] {
-               subDir, subTgt := cwdAndTgt(path.Join(dirNormalized, dep))
-               if subDir == dirNormalized && subTgt == tgtNormalized {
+       for _, depPath := range buildLogRec["Ifchange"] {
+               dep := NewTgt(path.Join(tgt.h, depPath))
+               if dep.a == tgt.a {
                        continue
                }
-               rec, err := parseBuildLogRec(subDir, subTgt)
+               rec, err := parseBuildLogRec(dep)
                if err != nil {
                        if errors.Is(err, fs.ErrNotExist) {
                                continue
@@ -225,7 +222,6 @@ func showBuildLog(dir, tgt string, buildLogRec map[string][]string, depth int) e
                        }
                }
                subs = append(subs, &BuildLogJob{
-                       dir:      dirNormalized,
                        tgt:      dep,
                        started:  started,
                        exitCode: exitCode,
diff --git a/dep.go b/dep.go
index 6644ac54695674cc8c8ad539a1e17ecadc37f7b5..e587e6aa88ced9193b503b94ed2302fb459fba46 100644 (file)
--- a/dep.go
+++ b/dep.go
@@ -86,9 +86,9 @@ func fileHash(fd *os.File) ([]byte, error) {
        return h.Sum(nil), nil
 }
 
-func depWrite(fdDep *os.File, cwd, tgt string, hsh []byte) error {
+func depWrite(fdDep *os.File, cwd string, tgt *Tgt, hsh []byte) error {
        tracef(CDebug, "ifchange: %s <- %s", fdDep.Name(), tgt)
-       fd, err := os.Open(path.Join(cwd, tgt))
+       fd, err := os.Open(tgt.a)
        if err != nil {
                return ErrLine(err)
        }
@@ -108,30 +108,30 @@ func depWrite(fdDep *os.File, cwd, tgt string, hsh []byte) error {
        }
        fields := []recfile.Field{
                {Name: "Type", Value: DepTypeIfchange},
-               {Name: "Target", Value: tgt},
+               {Name: "Target", Value: tgt.RelTo(cwd)},
                {Name: "Hash", Value: hex.EncodeToString(hsh)},
        }
        fields = append(fields, inode.RecfileFields()...)
        return recfileWrite(fdDep, fields...)
 }
 
-func depsWrite(fdDep *os.File, tgts []string) error {
+func depsWrite(fdDep *os.File, tgts []*Tgt) error {
        if fdDep == nil {
                tracef(CDebug, "no opened fdDep: %s", tgts)
                return nil
        }
        var err error
+       var cwd string
        for _, tgt := range tgts {
-               tgtAbs := mustAbs(tgt)
-               cwd := Cwd
+               cwd = Cwd
                if DepCwd != "" && Cwd != DepCwd {
                        cwd = DepCwd
                }
                tgtDir := path.Join(cwd, DirPrefix)
-               tgtRel := mustRel(tgtDir, tgtAbs)
-               if _, errStat := os.Stat(tgt); errStat == nil {
-                       err = ErrLine(depWrite(fdDep, tgtDir, tgtRel, nil))
+               if _, errStat := os.Stat(tgt.a); errStat == nil {
+                       err = ErrLine(depWrite(fdDep, tgtDir, tgt, nil))
                } else {
+                       tgtRel := tgt.RelTo(tgtDir)
                        tracef(CDebug, "ifchange: %s <- %s (non-existing)", fdDep.Name(), tgtRel)
                        fields := []recfile.Field{
                                {Name: "Type", Value: DepTypeIfchange},
@@ -149,7 +149,7 @@ func depsWrite(fdDep *os.File, tgts []string) error {
 }
 
 type DepInfoIfchange struct {
-       tgt   string
+       tgt   *Tgt
        inode *Inode
        hash  []byte
 }
@@ -158,7 +158,7 @@ type DepInfo struct {
        build     string
        always    bool
        stamp     []byte
-       ifcreates []string
+       ifcreates []*Tgt
        ifchanges []DepInfoIfchange
 }
 
@@ -172,8 +172,8 @@ func mustHexDecode(s string) []byte {
 
 var missingBuild = errors.New(".rec missing Build:")
 
-func depRead(pth string) (*DepInfo, error) {
-       data, err := os.ReadFile(pth)
+func depRead(tgt *Tgt) (*DepInfo, error) {
+       data, err := os.ReadFile(tgt.Dep())
        if err != nil {
                return nil, err
        }
@@ -204,7 +204,8 @@ func depRead(pth string) (*DepInfo, error) {
                        if dep == "" {
                                return nil, ErrBadRecFormat
                        }
-                       depInfo.ifcreates = append(depInfo.ifcreates, dep)
+                       depInfo.ifcreates = append(depInfo.ifcreates,
+                               NewTgt(path.Join(tgt.h, dep)))
                case DepTypeIfchange:
                        dep := m["Target"]
                        if dep == "" {
@@ -217,7 +218,7 @@ func depRead(pth string) (*DepInfo, error) {
                        }
                        hsh := mustHexDecode(m["Hash"])
                        depInfo.ifchanges = append(depInfo.ifchanges, DepInfoIfchange{
-                               tgt: dep, inode: inode, hash: hsh,
+                               tgt: NewTgt(path.Join(tgt.h, dep)), inode: inode, hash: hsh,
                        })
                case DepTypeStamp:
                        hsh := m["Hash"]
@@ -232,6 +233,29 @@ func depRead(pth string) (*DepInfo, error) {
        return &depInfo, nil
 }
 
+func depReadOnlyIfchanges(pth string) (ifchanges []string, err error) {
+       data, err := os.ReadFile(pth)
+       if err != nil {
+               return
+       }
+       r := recfile.NewReader(bytes.NewReader(data))
+       var m map[string]string
+       for {
+               m, err = r.NextMap()
+               if err != nil {
+                       if errors.Is(err, io.EOF) {
+                               err = nil
+                               break
+                       }
+                       return
+               }
+               if m["Type"] == DepTypeIfchange {
+                       ifchanges = append(ifchanges, m["Target"])
+               }
+       }
+       return
+}
+
 func depReadBuild(pth string) (string, error) {
        fd, err := os.Open(pth)
        if err != nil {
diff --git a/dot.go b/dot.go
index b8c28b75346e8d451be77cceb1a8dbb521006adf..9199f7dd0e43099c010e972911831fdf8fd715bf 100644 (file)
--- a/dot.go
+++ b/dot.go
@@ -34,15 +34,13 @@ type DotNodes struct {
        to   string
 }
 
-func dotWalker(data map[DotNodes]bool, tgtOrig string) (map[DotNodes]bool, error) {
-       cwd, tgt := cwdAndTgt(tgtOrig)
-       depPath := path.Join(cwd, RedoDir, tgt+DepSuffix)
-       fdDep, err := os.Open(depPath)
+func dotWalker(data map[DotNodes]bool, tgt *Tgt) (map[DotNodes]bool, error) {
+       fdDep, err := os.Open(tgt.Dep())
        if err != nil {
                return nil, ErrLine(err)
        }
        defer fdDep.Close()
-       var dep string
+       var dep *Tgt
        r := recfile.NewReader(fdDep)
        for {
                m, err := r.NextMap()
@@ -54,17 +52,17 @@ func dotWalker(data map[DotNodes]bool, tgtOrig string) (map[DotNodes]bool, error
                }
                switch m["Type"] {
                case DepTypeIfcreate:
-                       data[DotNodes{tgtOrig, cwdMustRel(cwd, m["Target"])}] = true
+                       data[DotNodes{tgt.String(), NewTgt(m["Target"]).String()}] = true
                case DepTypeIfchange:
-                       dep = m["Target"]
-                       if dep == tgt {
+                       dep = NewTgt(path.Join(tgt.h, m["Target"]))
+                       if dep.a == tgt.a {
                                continue
                        }
-                       data[DotNodes{tgtOrig, cwdMustRel(cwd, dep)}] = false
-                       if isSrc(cwd, dep) {
+                       data[DotNodes{tgt.String(), dep.String()}] = false
+                       if isSrc(dep) {
                                continue
                        }
-                       data, err = dotWalker(data, cwdMustRel(cwd, dep))
+                       data, err = dotWalker(data, dep)
                        if err != nil {
                                return nil, err
                        }
@@ -73,7 +71,7 @@ func dotWalker(data map[DotNodes]bool, tgtOrig string) (map[DotNodes]bool, error
        return data, nil
 }
 
-func dotPrint(tgts []string) error {
+func dotPrint(tgts []*Tgt) error {
        data := map[DotNodes]bool{}
        var err error
        for _, tgt := range tgts {
index 77d6d93dccc01459b3950d18c7819d22f5729519..9f7f4b572d583c1a5fbdf216f7788e280a7e8431 100644 (file)
@@ -18,90 +18,80 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 package main
 
 import (
-       "path"
        "strings"
 )
 
 func collectDeps(
-       cwd, tgtOrig string,
+       tgt *Tgt,
        level int,
-       deps map[string]map[string]struct{},
+       deps map[string]map[string]*Tgt,
        includeSrc bool,
        seen map[string]struct{},
-) []string {
-       cwd, tgt := cwdAndTgt(path.Join(cwd, tgtOrig))
-       tgtFull := path.Join(cwd, tgt)
-       if _, ok := seen[tgtFull]; ok {
+) []*Tgt {
+       if _, ok := seen[tgt.a]; ok {
                return nil
        }
-       depPath := path.Join(cwd, RedoDir, tgt+DepSuffix)
-       depInfo, err := depRead(depPath)
+       depInfo, err := depRead(tgt)
        if err != nil {
                return nil
        }
-       // DepInfoCache[depPath] = depInfo
-       seen[tgtFull] = struct{}{}
-       var alwayses []string
+       seen[tgt.a] = struct{}{}
+       var alwayses []*Tgt
        returnReady := false
-       tgtRel := cwdMustRel(cwd, tgt)
        if depInfo.always {
                if depInfo.build == BuildUUID {
                        tracef(
                                CDebug, "ood: %s%s always, but already build",
-                               strings.Repeat(". ", level), tgtOrig,
+                               strings.Repeat(". ", level), tgt,
                        )
                        returnReady = true
                } else {
-                       tracef(CDebug, "ood: %s%s always", strings.Repeat(". ", level), tgtOrig)
-                       alwayses = append(alwayses, tgtRel)
+                       tracef(CDebug, "ood: %s%s always", strings.Repeat(". ", level), tgt)
+                       alwayses = append(alwayses, tgt)
                        returnReady = true
                }
        }
        for _, dep := range depInfo.ifchanges {
-               if dep.tgt == tgt {
+               if dep.tgt.a == tgt.a {
                        continue
                }
-               if !includeSrc && isSrc(cwd, dep.tgt) {
+               if !includeSrc && isSrc(dep.tgt) {
                        continue
                }
                if !returnReady {
-                       depRel := cwdMustRel(cwd, dep.tgt)
-                       if m, ok := deps[depRel]; ok {
-                               m[tgtRel] = struct{}{}
+                       if m, ok := deps[dep.tgt.a]; ok {
+                               m[tgt.a] = tgt
                        } else {
-                               m = map[string]struct{}{}
-                               m[tgtRel] = struct{}{}
-                               deps[depRel] = m
+                               deps[dep.tgt.a] = map[string]*Tgt{tgt.a: tgt}
                        }
                        alwayses = append(alwayses,
-                               collectDeps(cwd, dep.tgt, level+1, deps, includeSrc, seen)...)
+                               collectDeps(dep.tgt, level+1, deps, includeSrc, seen)...)
                }
        }
        return alwayses
 }
 
-func buildDependants(tgts []string) map[string]struct{} {
+func buildDependants(tgts []*Tgt) map[string]*Tgt {
        defer Jobs.Wait()
        tracef(CDebug, "collecting deps")
-       seen := map[string]struct{}{}
-       deps := map[string]map[string]struct{}{}
-       collectDepsSeen := make(map[string]struct{})
-       for _, tgtInitial := range tgts {
-               for _, tgt := range collectDeps(Cwd, tgtInitial, 0, deps, false, collectDepsSeen) {
-                       if tgt != tgtInitial {
-                               seen[tgt] = struct{}{}
+       seen := make(map[string]*Tgt)
+       deps := make(map[string]map[string]*Tgt)
+       {
+               collectDepsSeen := make(map[string]struct{})
+               for _, tgtInitial := range tgts {
+                       for _, tgt := range collectDeps(tgtInitial, 0, deps, false, collectDepsSeen) {
+                               if tgt.a != tgtInitial.a {
+                                       seen[tgt.a] = tgt
+                               }
                        }
                }
        }
        if len(seen) == 0 {
                return seen
        }
-       collectDepsSeen = nil
 
        levelOrig := Level
-       defer func() {
-               Level = levelOrig
-       }()
+       defer func() { Level = levelOrig }()
        Level = 1
        tracef(CDebug, "building %d alwayses: %v", len(seen), seen)
        errs := make(chan error, len(seen))
@@ -113,7 +103,7 @@ func buildDependants(tgts []string) map[string]struct{} {
                }
                close(okChecker)
        }()
-       for tgt := range seen {
+       for _, tgt := range seen {
                if err := runScript(tgt, errs, false, false); err != nil {
                        tracef(CErr, "always run error: %s, skipping dependants", err)
                        Jobs.Wait()
@@ -129,17 +119,17 @@ func buildDependants(tgts []string) map[string]struct{} {
                return nil
        }
 
-       queueSrc := make([]string, 0, len(seen))
-       for tgt := range seen {
+       queueSrc := make([]*Tgt, 0, len(seen))
+       for _, tgt := range seen {
                queueSrc = append(queueSrc, tgt)
        }
 
 RebuildDeps:
        tracef(CDebug, "checking %d dependant targets: %v", len(queueSrc), queueSrc)
-       queue := map[string]struct{}{}
+       queue := make(map[string]*Tgt)
        for _, tgt := range queueSrc {
-               for dep := range deps[tgt] {
-                       queue[dep] = struct{}{}
+               for _, dep := range deps[tgt.a] {
+                       queue[dep.a] = dep
                }
        }
 
@@ -147,15 +137,15 @@ RebuildDeps:
        errs = make(chan error, len(queue))
        okChecker = make(chan struct{})
        jobs := 0
-       queueSrc = []string{}
+       queueSrc = make([]*Tgt, 0)
        go func() {
                for err := range errs {
                        ok = isOkRun(err) && ok
                }
                close(okChecker)
        }()
-       for tgt := range queue {
-               ood, err := isOODWithTrace(Cwd, tgt, 0, seen)
+       for _, tgt := range queue {
+               ood, err := isOODWithTrace(tgt, 0, seen)
                if err != nil {
                        tracef(CErr, "dependant error: %s, skipping dependants", err)
                        return nil
@@ -168,7 +158,7 @@ RebuildDeps:
                        return nil
                }
                queueSrc = append(queueSrc, tgt)
-               seen[tgt] = struct{}{}
+               seen[tgt.a] = tgt
                jobs++
        }
        Jobs.Wait()
@@ -185,17 +175,18 @@ RebuildDeps:
        goto RebuildDeps
 }
 
-func ifchange(tgts []string, forced, traced bool) (bool, error) {
-       // only unique elements
-       m := make(map[string]struct{})
-       for _, t := range tgts {
-               m[t] = struct{}{}
-       }
-       tgts = tgts[:0]
-       for t := range m {
-               tgts = append(tgts, t)
+func ifchange(tgts []*Tgt, forced, traced bool) (bool, error) {
+       {
+               // only unique elements
+               m := make(map[string]*Tgt)
+               for _, t := range tgts {
+                       m[t.a] = t
+               }
+               tgts = tgts[:0]
+               for _, t := range m {
+                       tgts = append(tgts, t)
+               }
        }
-       m = nil
 
        jsInit()
        if !IsTopRedo {
@@ -220,13 +211,13 @@ func ifchange(tgts []string, forced, traced bool) (bool, error) {
                close(okChecker)
        }()
        for _, tgt := range tgts {
-               if _, ok := seen[tgt]; ok {
+               if _, ok := seen[tgt.a]; ok {
                        tracef(CDebug, "%s was already build as a dependant", tgt)
                        continue
                }
                ood = true
                if !forced {
-                       ood, err = isOODWithTrace(Cwd, tgt, 0, seen)
+                       ood, err = isOODWithTrace(tgt, 0, seen)
                        if err != nil {
                                Jobs.Wait()
                                close(errs)
@@ -236,7 +227,7 @@ func ifchange(tgts []string, forced, traced bool) (bool, error) {
                if !ood {
                        continue
                }
-               if isSrc(Cwd, tgt) {
+               if isSrc(tgt) {
                        tracef(CDebug, "%s is source, not redoing", tgt)
                        continue
                }
diff --git a/main.go b/main.go
index 39c6d118a0d941ce0a6f113ca378ef55298e39e8..bba64d02112bdd42c1904fe580344f153996db48 100644 (file)
--- a/main.go
+++ b/main.go
@@ -29,7 +29,6 @@ import (
        "os"
        "os/signal"
        "path"
-       "path/filepath"
        "runtime"
        "sort"
        "strconv"
@@ -45,17 +44,17 @@ const (
        CmdNameRedoAffects  = "redo-affects"
        CmdNameRedoAlways   = "redo-always"
        CmdNameRedoCleanup  = "redo-cleanup"
+       CmdNameRedoDepFix   = "redo-depfix"
        CmdNameRedoDot      = "redo-dot"
        CmdNameRedoIfchange = "redo-ifchange"
        CmdNameRedoIfcreate = "redo-ifcreate"
+       CmdNameRedoInode    = "redo-inode"
        CmdNameRedoLog      = "redo-log"
        CmdNameRedoOOD      = "redo-ood"
        CmdNameRedoSources  = "redo-sources"
        CmdNameRedoStamp    = "redo-stamp"
        CmdNameRedoTargets  = "redo-targets"
        CmdNameRedoWhichdo  = "redo-whichdo"
-       CmdNameRedoDepFix   = "redo-depfix"
-       CmdNameRedoInode    = "redo-inode"
 )
 
 var (
@@ -252,7 +251,7 @@ func main() {
                if err = unix.FcntlFlock(fdLock.Fd(), unix.F_SETLK, &flock); err != nil {
                        log.Fatal(err)
                }
-               OODTgts = map[string]struct{}{}
+               OODTgts = make(map[string]struct{})
                for _, tgtRaw := range bytes.Split(tgtsRaw, []byte{0}) {
                        t := string(tgtRaw)
                        if t == "" {
@@ -279,7 +278,10 @@ func main() {
                fdDep = mustParseFd(v, EnvDepFd)
        }
 
-       tgts := flag.Args()
+       tgts := make([]*Tgt, 0, len(flag.Args()))
+       for _, arg := range flag.Args() {
+               tgts = append(tgts, NewTgt(arg))
+       }
        BuildUUID = os.Getenv(EnvBuildUUID)
        tgtsWasEmpty := len(tgts) == 0
        if BuildUUID == "" {
@@ -294,7 +296,7 @@ func main() {
                        raw[0:4], raw[4:6], raw[6:8], raw[8:10], raw[10:],
                )
                if tgtsWasEmpty {
-                       tgts = []string{"all"}
+                       tgts = append(tgts, NewTgt("all"))
                }
        }
 
@@ -302,12 +304,6 @@ func main() {
                statusInit()
        }
 
-       for i, tgt := range tgts {
-               if path.IsAbs(tgt) {
-                       tgts[i] = cwdMustRel(tgt)
-               }
-       }
-
        killed := make(chan os.Signal, 1)
        signal.Notify(killed, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
        go func() {
@@ -333,7 +329,7 @@ CmdSwitch:
        switch cmdName {
        case CmdNameRedo:
                for _, tgt := range tgts {
-                       ok, err = ifchange([]string{tgt}, true, traced)
+                       ok, err = ifchange([]*Tgt{tgt}, true, traced)
                        if err != nil || !ok {
                                break
                        }
@@ -348,15 +344,7 @@ CmdSwitch:
                        log.Fatalln("no", EnvDepFd)
                }
                for _, tgt := range tgts {
-                       tgtRel, err := filepath.Rel(
-                               path.Join(Cwd, DirPrefix),
-                               path.Join(Cwd, tgt),
-                       )
-                       if err != nil {
-                               err = ErrLine(err)
-                               break
-                       }
-                       err = ifcreate(fdDep, tgtRel)
+                       err = ifcreate(fdDep, tgt.RelTo(path.Join(Cwd, DirPrefix)))
                        if err != nil {
                                break
                        }
@@ -368,7 +356,7 @@ CmdSwitch:
                err = always(fdDep)
        case CmdNameRedoCleanup:
                for _, what := range tgts {
-                       err = cleanupWalker(Cwd, what)
+                       err = cleanupWalker(Cwd, what.t)
                        if err != nil {
                                break
                        }
@@ -384,8 +372,7 @@ CmdSwitch:
                if len(tgts) != 1 {
                        log.Fatal("single target expected")
                }
-               d, t := cwdAndTgt(tgts[0])
-               err = showBuildLog(d, t, nil, 0)
+               err = showBuildLog(tgts[0], nil, 0)
        case CmdNameRedoWhichdo:
                if len(tgts) != 1 {
                        log.Fatal("single target expected")
@@ -400,8 +387,8 @@ CmdSwitch:
                        err = ErrLine(err)
                        break
                }
-               cwd, tgt := cwdAndTgt(tgts[0])
-               doFile, upLevels, err := findDo(fdTmp, cwd, tgt)
+               tgt := tgts[0]
+               doFile, upLevels, err := findDo(fdTmp, tgt.h, tgt.t)
                if err != nil {
                        err = ErrLine(err)
                        break
@@ -421,13 +408,13 @@ CmdSwitch:
                                err = ErrLine(err)
                                break CmdSwitch
                        }
-                       fmt.Println(cwdMustRel(cwd, m["Target"]))
+                       fmt.Println(cwdMustRel(tgt.h, m["Target"]))
                }
                if doFile == "" {
                        ok = false
                } else {
                        p := make([]string, 0, upLevels+2)
-                       p = append(p, cwd)
+                       p = append(p, tgt.h)
                        for i := 0; i < upLevels; i++ {
                                p = append(p, "..")
                        }
@@ -436,55 +423,66 @@ CmdSwitch:
                        fmt.Println(rel)
                }
        case CmdNameRedoTargets:
+               raws := make([]string, 0, len(tgts))
+               for _, tgt := range tgts {
+                       raws = append(raws, tgt.String())
+               }
                if tgtsWasEmpty {
-                       tgts = []string{Cwd}
+                       raws = []string{Cwd}
                }
-               tgts, err = targetsWalker(tgts)
+               raws, err = targetsWalker(raws)
                if err != nil {
                        err = ErrLine(err)
                        break
                }
-               sort.Strings(tgts)
-               for _, tgt := range tgts {
+               sort.Strings(raws)
+               for _, tgt := range raws {
                        fmt.Println(tgt)
                }
        case CmdNameRedoAffects:
                if tgtsWasEmpty {
                        log.Fatal("no targets specified")
                }
-               var tgtsKnown []string
-               tgtsKnown, err = targetsWalker([]string{Cwd})
-               if err != nil {
-                       err = ErrLine(err)
-                       break
-               }
-               deps := map[string]map[string]struct{}{}
-               for _, tgt := range tgtsKnown {
-                       collectDeps(Cwd, tgt, 0, deps, true, map[string]struct{}{})
-               }
-               seen := map[string]struct{}{}
-               for _, tgt := range tgts {
-                       collectWholeDeps(deps[tgt], deps, seen)
-               }
-               tgts := make([]string, 0, len(seen))
-               for dep := range seen {
-                       tgts = append(tgts, dep)
+               var res []string
+               {
+                       var tgtsKnown []string
+                       tgtsKnown, err = targetsWalker([]string{Cwd})
+                       if err != nil {
+                               err = ErrLine(err)
+                               break
+                       }
+                       deps := make(map[string]map[string]*Tgt)
+                       for _, tgt := range tgtsKnown {
+                               collectDeps(NewTgt(tgt), 0, deps, true, make(map[string]struct{}))
+                       }
+                       seen := make(map[string]*Tgt)
+                       for _, tgt := range tgts {
+                               collectWholeDeps(deps[tgt.a], deps, seen)
+                       }
+                       res = make([]string, 0, len(seen))
+                       for _, dep := range seen {
+                               res = append(res, dep.String())
+                       }
                }
-               sort.Strings(tgts)
-               for _, dep := range tgts {
+               sort.Strings(res)
+               for _, dep := range res {
                        fmt.Println(dep)
                }
        case CmdNameRedoOOD:
+               raws := make([]string, 0, len(tgts))
+               for _, tgt := range tgts {
+                       raws = append(raws, tgt.String())
+               }
                if tgtsWasEmpty {
-                       tgts, err = targetsWalker([]string{Cwd})
+                       raws, err = targetsWalker([]string{Cwd})
                        if err != nil {
                                break
                        }
                }
-               sort.Strings(tgts)
+               sort.Strings(raws)
                var ood bool
-               for _, tgt := range tgts {
-                       ood, err = isOOD(Cwd, tgt, 0, nil)
+               for _, tgt := range raws {
+                       ood, err = isOOD(NewTgt(tgt), 0, nil)
                        if err != nil {
                                err = ErrLine(err)
                                break
@@ -494,27 +492,34 @@ CmdSwitch:
                        }
                }
        case CmdNameRedoSources:
-               if tgtsWasEmpty {
-                       tgts, err = targetsWalker([]string{Cwd})
-                       if err != nil {
-                               err = ErrLine(err)
-                               break
+               srcs := make(map[string]*Tgt)
+               {
+                       raws := make([]string, 0, len(tgts))
+                       for _, tgt := range tgts {
+                               raws = append(raws, tgt.String())
+                       }
+                       if tgtsWasEmpty {
+                               raws, err = targetsWalker([]string{Cwd})
+                               if err != nil {
+                                       err = ErrLine(err)
+                                       break
+                               }
+                       }
+                       sort.Strings(raws)
+                       tgts = tgts[:0]
+                       for _, raw := range raws {
+                               tgts = append(tgts, NewTgt(raw))
                        }
+                       seen := make(map[string]struct{})
+                       seenDeps := make(map[string]struct{})
+                       err = ErrLine(sourcesWalker(tgts, seen, seenDeps, srcs))
                }
-               sort.Strings(tgts)
-               seen := make(map[string]struct{})
-               seenDeps := make(map[string]struct{})
-               srcs := make(map[string]struct{})
-               err = ErrLine(sourcesWalker(tgts, seen, seenDeps, srcs))
-               seen = nil
-               seenDeps = nil
                if err != nil {
                        break
                }
-               seenDeps = nil
                res := make([]string, 0, len(srcs))
-               for p := range srcs {
-                       res = append(res, p)
+               for _, tgt := range srcs {
+                       res = append(res, tgt.String())
                }
                srcs = nil
                sort.Strings(res)
@@ -526,13 +531,13 @@ CmdSwitch:
        case CmdNameRedoInode:
                var inode *Inode
                for _, tgt := range tgts {
-                       inode, err = inodeFromFileByPath(tgt)
+                       inode, err = inodeFromFileByPath(tgt.a)
                        if err != nil {
                                err = ErrLine(err)
                                break
                        }
                        err = recfileWrite(os.Stdout, append(
-                               []recfile.Field{{Name: "Target", Value: tgt}},
+                               []recfile.Field{{Name: "Target", Value: tgt.String()}},
                                inode.RecfileFields()...)...)
                        if err != nil {
                                err = ErrLine(err)
diff --git a/ood.go b/ood.go
index 5dc543f56aa263d821b33dc03a4d8fa30d66ccf7..dffaa4ef06b8ea1335e51636751a880d0191cbe7 100644 (file)
--- a/ood.go
+++ b/ood.go
@@ -27,7 +27,6 @@ import (
        "io/fs"
        "log"
        "os"
-       "path"
        "strings"
 
        "golang.org/x/sys/unix"
@@ -70,7 +69,7 @@ func FileExists(p string) bool {
 }
 
 type TgtError struct {
-       Tgt string
+       Tgt *Tgt
        Err error
 }
 
@@ -80,183 +79,165 @@ func (e TgtError) Error() string {
        return fmt.Sprintf("%s: %s", e.Tgt, e.Err)
 }
 
-func isSrc(cwd, tgt string) bool {
-       d, f := path.Split(path.Join(cwd, tgt))
-       if !FileExists(path.Join(d, f)) {
+func isSrc(tgt *Tgt) bool {
+       if !FileExists(tgt.a) {
                return false
        }
-       if FileExists(path.Join(d, f+".do")) {
+       if FileExists(tgt.a + ".do") {
                return false
        }
-       if FileExists(path.Join(d, RedoDir, f+DepSuffix)) {
+       if FileExists(tgt.Dep()) {
                return false
        }
        return true
 }
 
-func isOODByBuildUUID(cwd, tgtOrig string) bool {
-       cwd, tgt := cwdAndTgt(path.Join(cwd, tgtOrig))
-       depPath := path.Join(cwd, RedoDir, tgt+DepSuffix)
-       build, err := depReadBuild(depPath)
+func isOODByBuildUUID(tgt *Tgt) bool {
+       build, err := depReadBuild(tgt.Dep())
        return err != nil || build != BuildUUID
 }
 
-func isOOD(cwd, tgtOrig string, level int, seen map[string]struct{}) (bool, error) {
+func isOOD(tgt *Tgt, level int, seen map[string]*Tgt) (bool, error) {
        indent := strings.Repeat(". ", level)
-       tracef(CDebug, "ood: %s%s checking", indent, tgtOrig)
-       cwd, tgt := cwdAndTgt(path.Join(cwd, tgtOrig))
-       ood, cached := OODCache[path.Join(cwd, tgt)]
+       tracef(CDebug, "ood: %s%s checking", indent, tgt)
+       ood, cached := OODCache[tgt.a]
        if cached {
-               tracef(CDebug, "ood: %s%s -> cached: %v", indent, tgtOrig, ood)
+               tracef(CDebug, "ood: %s%s -> cached: %v", indent, tgt, ood)
                return ood, nil
        }
-       depPath := path.Join(cwd, RedoDir, tgt+DepSuffix)
-       depInfo, err := depRead(depPath)
+       depInfo, err := depRead(tgt)
        if err != nil {
                if errors.Is(err, fs.ErrNotExist) {
-                       if isSrc(cwd, tgt) {
+                       if isSrc(tgt) {
                                ood = false
-                               tracef(CDebug, "ood: %s%s -> is source", indent, tgtOrig)
+                               tracef(CDebug, "ood: %s%s -> is source", indent, tgt)
                        } else {
                                ood = true
-                               tracef(CDebug, "ood: %s%s -> no dep: %s", indent, tgtOrig, depPath)
+                               tracef(CDebug, "ood: %s%s -> no dep: %s", indent, tgt, tgt.Dep())
                        }
-                       OODCache[path.Join(cwd, tgt)] = ood
+                       OODCache[tgt.a] = ood
                        return ood, nil
                }
                if err != nil {
-                       return true, TgtError{tgtOrig, ErrLine(err)}
+                       return true, TgtError{tgt, ErrLine(err)}
                }
        }
 
        if depInfo.build == BuildUUID {
-               tracef(CDebug, "ood: %s%s -> already built", indent, tgtOrig)
-               OODCache[path.Join(cwd, tgt)] = false
+               tracef(CDebug, "ood: %s%s -> already built", indent, tgt)
+               OODCache[tgt.a] = false
                return false, nil
        }
-       if !FileExists(path.Join(cwd, tgt)) {
-               tracef(CDebug, "ood: %s%s -> non-existent", indent, tgtOrig)
-               OODCache[path.Join(cwd, tgt)] = true
+       if !FileExists(tgt.a) {
+               tracef(CDebug, "ood: %s%s -> non-existent", indent, tgt)
+               OODCache[tgt.a] = true
                return true, nil
        }
 
        for _, dep := range depInfo.ifcreates {
-               if FileExists(path.Join(cwd, dep)) {
-                       tracef(CDebug, "ood: %s%s -> %s created", indent, tgtOrig, dep)
+               if FileExists(dep.a) {
+                       tracef(CDebug, "ood: %s%s -> %s created", indent, tgt, dep)
                        ood = true
                        goto Done
                }
        }
 
        for _, dep := range depInfo.ifchanges {
-               tracef(CDebug, "ood: %s%s -> %s: checking", indent, tgtOrig, dep.tgt)
-               ood, cached = OODCache[path.Join(cwd, dep.tgt)]
+               tracef(CDebug, "ood: %s%s -> %s: checking", indent, tgt, dep.tgt)
+               ood, cached = OODCache[dep.tgt.a]
                if cached {
-                       tracef(CDebug, "ood: %s%s -> %s: cached: %v", indent, tgtOrig, dep.tgt, ood)
+                       tracef(CDebug, "ood: %s%s -> %s: cached: %v", indent, tgt, dep.tgt, ood)
                        if ood {
                                goto Done
                        }
                        continue
                }
 
-               inode, err := inodeFromFileByPath(path.Join(cwd, dep.tgt))
+               inode, err := inodeFromFileByPath(dep.tgt.a)
                if err != nil {
                        if errors.Is(err, fs.ErrNotExist) {
-                               tracef(CDebug, "ood: %s%s -> %s: not exists", indent, tgtOrig, dep.tgt)
+                               tracef(CDebug, "ood: %s%s -> %s: not exists", indent, tgt, dep.tgt)
                                ood = true
-                               OODCache[path.Join(cwd, dep.tgt)] = ood
+                               OODCache[dep.tgt.a] = ood
                                goto Done
                        }
-                       return ood, TgtError{tgtOrig, ErrLine(err)}
+                       return ood, TgtError{tgt, ErrLine(err)}
                }
 
                if inode.Size != dep.inode.Size {
-                       tracef(CDebug, "ood: %s%s -> %s: size differs", indent, tgtOrig, dep.tgt)
+                       tracef(CDebug, "ood: %s%s -> %s: size differs", indent, tgt, dep.tgt)
                        ood = true
-                       OODCache[path.Join(cwd, dep.tgt)] = ood
+                       OODCache[dep.tgt.a] = ood
                        goto Done
                }
                if InodeTrust != InodeTrustNone && inode.Equals(dep.inode) {
-                       tracef(CDebug, "ood: %s%s -> %s: same inode", indent, tgtOrig, dep.tgt)
+                       tracef(CDebug, "ood: %s%s -> %s: same inode", indent, tgt, dep.tgt)
                } else {
-                       tracef(CDebug, "ood: %s%s -> %s: inode differs", indent, tgtOrig, dep.tgt)
-                       fd, err := os.Open(path.Join(cwd, dep.tgt))
+                       tracef(CDebug, "ood: %s%s -> %s: inode differs", indent, tgt, dep.tgt)
+                       fd, err := os.Open(dep.tgt.a)
                        if err != nil {
-                               return ood, TgtError{tgtOrig, ErrLine(err)}
+                               return ood, TgtError{tgt, ErrLine(err)}
                        }
                        hsh, err := fileHash(fd)
                        fd.Close()
                        if err != nil {
-                               return ood, TgtError{tgtOrig, ErrLine(err)}
+                               return ood, TgtError{tgt, ErrLine(err)}
                        }
                        if !bytes.Equal(dep.hash, hsh) {
-                               tracef(CDebug, "ood: %s%s -> %s: hash differs", indent, tgtOrig, dep.tgt)
+                               tracef(CDebug, "ood: %s%s -> %s: hash differs", indent, tgt, dep.tgt)
                                ood = true
-                               OODCache[path.Join(cwd, dep.tgt)] = ood
+                               OODCache[dep.tgt.a] = ood
                                goto Done
                        }
-                       tracef(CDebug, "ood: %s%s -> %s: same hash", indent, tgtOrig, dep.tgt)
+                       tracef(CDebug, "ood: %s%s -> %s: same hash", indent, tgt, dep.tgt)
                }
 
-               if dep.tgt == tgt {
-                       tracef(CDebug, "ood: %s%s -> %s: same target", indent, tgtOrig, dep.tgt)
+               if dep.tgt.a == tgt.a {
+                       tracef(CDebug, "ood: %s%s -> %s: same target", indent, tgt, dep.tgt)
                        continue
                }
-               if isSrc(cwd, dep.tgt) {
-                       tracef(CDebug, "ood: %s%s -> %s: is source", indent, tgtOrig, dep.tgt)
-                       OODCache[path.Join(cwd, dep.tgt)] = false
+               if isSrc(dep.tgt) {
+                       tracef(CDebug, "ood: %s%s -> %s: is source", indent, tgt, dep.tgt)
+                       OODCache[dep.tgt.a] = false
                        continue
                }
 
-               if _, ok := seen[cwdMustRel(cwd, dep.tgt)]; ok {
-                       tracef(CDebug, "ood: %s%s -> %s: was always built", indent, tgtOrig, dep.tgt)
-                       OODCache[path.Join(cwd, dep.tgt)] = false
+               if _, ok := seen[dep.tgt.a]; ok {
+                       tracef(CDebug, "ood: %s%s -> %s: was always built", indent, tgt, dep.tgt)
+                       OODCache[dep.tgt.a] = false
                        continue
                }
 
-               depOOD, err := isOODWithTrace(cwd, dep.tgt, level+1, seen)
+               depOOD, err := isOODWithTrace(dep.tgt, level+1, seen)
                if err != nil {
-                       return ood, TgtError{tgtOrig, err}
+                       return ood, TgtError{tgt, err}
                }
                if depOOD {
-                       tracef(CDebug, "ood: %s%s -> %s: ood", indent, tgtOrig, dep.tgt)
+                       tracef(CDebug, "ood: %s%s -> %s: ood", indent, tgt, dep.tgt)
                        ood = true
                        goto Done
                }
-               tracef(CDebug, "ood: %s%s -> %s: !ood", indent, tgtOrig, dep.tgt)
+               tracef(CDebug, "ood: %s%s -> %s: !ood", indent, tgt, dep.tgt)
        }
 
 Done:
-       tracef(CDebug, "ood: %s%s: %v", indent, tgtOrig, ood)
-       OODCache[path.Join(cwd, tgt)] = ood
+       tracef(CDebug, "ood: %s%s: %v", indent, tgt, ood)
+       OODCache[tgt.a] = ood
        return ood, nil
 }
 
-func isOODWithTrace(
-       cwd, tgtOrig string,
-       level int,
-       seen map[string]struct{},
-) (bool, error) {
-       p := mustAbs(path.Join(cwd, tgtOrig))
-       _, ood := OODTgts[p]
+func isOODWithTrace(tgt *Tgt, level int, seen map[string]*Tgt) (bool, error) {
+       _, ood := OODTgts[tgt.a]
        var err error
        if ood {
-               if !isOODByBuildUUID(cwd, tgtOrig) {
-                       tracef(
-                               CDebug,
-                               "ood: %s%s -> already built",
-                               strings.Repeat(". ", level), tgtOrig,
-                       )
+               if !isOODByBuildUUID(tgt) {
+                       tracef(CDebug, "ood: %s%s -> already built", strings.Repeat(". ", level), tgt)
                        return false, nil
                }
-               tracef(
-                       CDebug,
-                       "ood: %s%s true, external decision",
-                       strings.Repeat(". ", level), tgtOrig,
-               )
+               tracef(CDebug, "ood: %s%s true, external decision", strings.Repeat(". ", level), tgt)
                goto RecordOODTgt
        }
-       ood, err = isOOD(cwd, tgtOrig, level, seen)
+       ood, err = isOOD(tgt, level, seen)
        if !ood {
                return ood, err
        }
@@ -271,7 +252,7 @@ RecordOODTgt:
        if _, err = FdOODTgts.Seek(0, io.SeekEnd); err != nil {
                log.Fatal(err)
        }
-       if _, err := FdOODTgts.WriteString(p + "\x00"); err != nil {
+       if _, err := FdOODTgts.WriteString(tgt.a + "\x00"); err != nil {
                log.Fatal(err)
        }
        flock.Type = unix.F_UNLCK
diff --git a/path.go b/path.go
index efb3e9834b536def8eb82e0e59cb2a818fa6fa30..0c961960e8c5186e9fba22091fe8c84f432bcad1 100644 (file)
--- a/path.go
+++ b/path.go
@@ -29,3 +29,37 @@ func cwdAndTgt(tgt string) (string, string) {
        cwd, tgt := path.Split(tgt)
        return mustAbs(cwd), tgt
 }
+
+type Tgt struct {
+       // a/h/t resemble zsh'es :a, :h, :t modifiers
+       a   string // absolute path
+       h   string // head of the path, directory
+       t   string // tail of the path, only name
+       rel string // relative to Cwd
+       dep string // path to dependency file
+}
+
+func NewTgt(tgt string) *Tgt {
+       t := Tgt{a: mustAbs(tgt)}
+       t.h, t.t = path.Split(t.a)
+       if len(t.h) > 1 {
+               t.h = t.h[:len(t.h)-1]
+       }
+       t.rel = mustRel(Cwd, t.a)
+       return &t
+}
+
+func (tgt *Tgt) String() string {
+       return tgt.rel
+}
+
+func (tgt *Tgt) Dep() string {
+       if tgt.dep == "" {
+               tgt.dep = path.Join(tgt.h, RedoDir, tgt.t+DepSuffix)
+       }
+       return tgt.dep
+}
+
+func (tgt *Tgt) RelTo(cwd string) string {
+       return mustRel(cwd, tgt.a)
+}
diff --git a/run.go b/run.go
index 1d03e06612c4f7843b9d1363ca81f9012fcb8fcd..bea836661746b36060927e9c604d5c61c02b1e94 100644 (file)
--- a/run.go
+++ b/run.go
@@ -102,7 +102,7 @@ func init() {
 }
 
 type RunError struct {
-       Tgt      string
+       Tgt      *Tgt
        DoFile   string
        Started  *time.Time
        Finished *time.Time
@@ -112,7 +112,7 @@ type RunError struct {
 func (e *RunError) Name() string {
        var name string
        if e.DoFile == "" {
-               name = e.Tgt
+               name = e.Tgt.String()
        } else {
                name = fmt.Sprintf("%s (%s)", e.Tgt, e.DoFile)
        }
@@ -133,17 +133,17 @@ func mkdirs(pth string) error {
        return os.MkdirAll(pth, os.FileMode(0777))
 }
 
-func isModified(depInfo *DepInfo, cwd, tgt string) (
+func isModified(depInfo *DepInfo, tgt *Tgt) (
        modified bool, ourInode *Inode, hshPrev []byte, err error,
 ) {
        if depInfo == nil {
                return
        }
        for _, dep := range depInfo.ifchanges {
-               if dep.tgt != tgt {
+               if dep.tgt.a != tgt.a {
                        continue
                }
-               ourInode, err = inodeFromFileByPath(path.Join(cwd, tgt))
+               ourInode, err = inodeFromFileByPath(tgt.a)
                if err != nil {
                        if os.IsNotExist(err) {
                                err = nil
@@ -169,14 +169,13 @@ func syncDir(dir string) error {
        return err
 }
 
-func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
-       cwd, tgt := cwdAndTgt(tgtOrig)
-       redoDir := path.Join(cwd, RedoDir)
+func runScript(tgt *Tgt, errs chan error, forced, traced bool) error {
+       redoDir := path.Join(tgt.h, RedoDir)
        if err := mkdirs(redoDir); err != nil {
-               return TgtError{tgtOrig, ErrLine(err)}
+               return TgtError{tgt, ErrLine(err)}
        }
 
-       shCtx := fmt.Sprintf("sh: %s: cwd:%s", tgtOrig, cwd)
+       shCtx := fmt.Sprintf("sh: %s: cwd:%s", tgt, tgt.h)
        jsToken := jsAcquire(shCtx)
        jsNeedsRelease := true
        defer func() {
@@ -187,12 +186,12 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
 
        // Acquire lock
        fdLock, err := os.OpenFile(
-               path.Join(redoDir, tgt+LockSuffix),
+               path.Join(redoDir, tgt.t+LockSuffix),
                os.O_WRONLY|os.O_TRUNC|os.O_CREATE,
                os.FileMode(0666),
        )
        if err != nil {
-               return TgtError{tgtOrig, ErrLine(err)}
+               return TgtError{tgt, ErrLine(err)}
        }
        flock := unix.Flock_t{
                Type:   unix.F_WRLCK,
@@ -212,13 +211,13 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
        if err = unix.FcntlFlock(fdLock.Fd(), unix.F_SETLK, &flock); err != nil {
                if uintptr(err.(syscall.Errno)) != uintptr(unix.EAGAIN) {
                        fdLock.Close()
-                       return TgtError{tgtOrig, ErrLine(err)}
+                       return TgtError{tgt, ErrLine(err)}
                }
                Jobs.Add(1)
                if err = unix.FcntlFlock(fdLock.Fd(), unix.F_GETLK, &flock); err != nil {
                        log.Fatalln(err, fdLock.Name())
                }
-               tracef(CDebug, "waiting: %s (pid=%d)", tgtOrig, flock.Pid)
+               tracef(CDebug, "waiting: %s (pid=%d)", tgt, flock.Pid)
                if FdStatus != nil {
                        if _, err = FdStatus.Write([]byte{StatusWait}); err != nil {
                                log.Fatal(err)
@@ -236,13 +235,13 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
                                log.Fatalln(err, fdLock.Name())
                        }
                        lockRelease()
-                       tracef(CDebug, "waiting done: %s", tgtOrig)
+                       tracef(CDebug, "waiting done: %s", tgt)
                        if FdStatus != nil {
                                if _, err = FdStatus.Write([]byte{StatusWaited}); err != nil {
                                        log.Fatal(err)
                                }
                        }
-                       build, err := depReadBuild(path.Join(redoDir, tgt+DepSuffix))
+                       build, err := depReadBuild(tgt.Dep())
                        if err == nil {
                                if build != BuildUUID {
                                        err = errors.New("was not built: build differs")
@@ -253,7 +252,7 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
                                }
                        }
                        if err != nil {
-                               err = TgtError{tgtOrig, err}
+                               err = TgtError{tgt, err}
                        }
                        errs <- err
                }()
@@ -262,13 +261,13 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
 
        // It scans the whole .rec file while searching for the single target,
        // but that one is always located at the very end
-       depInfo, err := depRead(path.Join(redoDir, tgt+DepSuffix))
+       depInfo, err := depRead(tgt)
        if err != nil {
                if errors.Is(err, fs.ErrNotExist) {
                        err = nil
                } else {
                        lockRelease()
-                       return TgtError{tgtOrig, err}
+                       return TgtError{tgt, ErrLine(err)}
                }
        }
 
@@ -280,17 +279,17 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
        }
 
        // Check if target is not modified externally
-       modified, inodePrev, hshPrev, err := isModified(depInfo, cwd, tgt)
+       modified, inodePrev, hshPrev, err := isModified(depInfo, tgt)
        if err != nil {
                lockRelease()
-               return TgtError{tgtOrig, err}
+               return TgtError{tgt, ErrLine(err)}
        }
        if modified {
                lockRelease()
                if StopIfMod {
-                       return fmt.Errorf("%s externally modified", tgtOrig)
+                       return fmt.Errorf("%s externally modified", tgt)
                }
-               tracef(CWarn, "%s externally modified: not redoing", tgtOrig)
+               tracef(CWarn, "%s externally modified: not redoing", tgt)
                go func() {
                        errs <- nil
                }()
@@ -299,10 +298,10 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
        depInfo = nil
 
        // Start preparing .rec
-       fdDep, err := tempfile(redoDir, tgt+DepSuffix)
+       fdDep, err := tempfile(redoDir, tgt.t+DepSuffix)
        if err != nil {
                lockRelease()
-               return TgtError{tgtOrig, ErrLine(err)}
+               return TgtError{tgt, ErrLine(err)}
        }
        fdDepOpened := true
        fdDepPath := fdDep.Name()
@@ -317,42 +316,46 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
                recfile.Field{Name: "Build", Value: BuildUUID},
        ); err != nil {
                cleanup()
-               return TgtError{tgtOrig, ErrLine(err)}
-       }
-
-       // Find .do
-       doFile, upLevels, err := findDo(fdDep, cwd, tgt)
-       if err != nil {
-               cleanup()
-               return TgtError{tgtOrig, ErrLine(err)}
-       }
-       if doFile == "" {
-               cleanup()
-               return TgtError{tgtOrig, errors.New("no .do found")}
+               return TgtError{tgt, ErrLine(err)}
        }
 
+       var cwd string
+       var dirPrefix string
+       var doFile *Tgt
+       basename := tgt.t
+       runErr := RunError{Tgt: tgt}
        // Determine basename and DIRPREFIX
-       doFileRelPath := doFile
-       ents := strings.Split(cwd, "/")
-       ents = ents[len(ents)-upLevels:]
-       dirPrefix := path.Join(ents...)
-       cwdOrig := cwd
-       for i := 0; i < upLevels; i++ {
-               cwd = path.Join(cwd, "..")
-               doFileRelPath = path.Join("..", doFileRelPath)
-       }
-       cwd = path.Clean(cwd)
-       doFilePath := path.Join(cwd, doFile)
-       basename := tgt
-       runErr := RunError{Tgt: tgtOrig}
-       if strings.HasPrefix(doFile, "default.") {
-               basename = tgt[:len(tgt)-(len(doFile)-len("default.")-len(".do"))-1]
-               runErr.DoFile = doFileRelPath
+       {
+               doFileRelPath, upLevels, err := findDo(fdDep, tgt.h, tgt.t)
+               if err != nil {
+                       cleanup()
+                       return TgtError{tgt, ErrLine(err)}
+               }
+               if doFileRelPath == "" {
+                       cleanup()
+                       return TgtError{tgt, errors.New("no .do found")}
+               }
+               // ents := strings.Split(strings.TrimSuffix(tgt.h, "/"), "/")
+               ents := strings.Split(tgt.h, "/")
+               ents = ents[len(ents)-upLevels:]
+               dirPrefix = path.Join(ents...)
+               ups := make([]string, 0, upLevels+2)
+               ups = append(ups, tgt.h)
+               for i := 0; i < upLevels; i++ {
+                       ups = append(ups, "..")
+               }
+               ups = append(ups, doFileRelPath)
+               cwd = path.Clean(path.Join(ups[:len(ups)-1]...))
+               doFile = NewTgt(path.Join(ups...))
+               if strings.HasPrefix(doFile.t, "default.") {
+                       basename = basename[:len(basename)-(len(doFile.t)-len("default.")-len(".do"))-1]
+                       runErr.DoFile = doFile.String()
+               }
        }
 
-       if err = depWrite(fdDep, cwdOrig, doFileRelPath, nil); err != nil {
+       if err = depWrite(fdDep, tgt.h, doFile, nil); err != nil {
                cleanup()
-               return TgtError{tgtOrig, ErrLine(err)}
+               return TgtError{tgt, ErrLine(err)}
        }
        fdDep.Close()
        fdDepOpened = false
@@ -361,8 +364,8 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
        // Prepare command line
        var cmdName string
        var args []string
-       if err = unix.Access(doFilePath, unix.X_OK); err == nil {
-               cmdName = doFilePath
+       if err = unix.Access(doFile.String(), unix.X_OK); err == nil {
+               cmdName = doFile.t
                args = make([]string, 0, 3)
        } else {
                cmdName = "/bin/sh"
@@ -371,14 +374,14 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
                } else {
                        args = append(args, "-e")
                }
-               args = append(args, doFile)
+               args = append(args, doFile.t)
        }
 
        // Temporary file for stdout
-       fdStdout, err := tempfile(cwdOrig, tgt)
+       fdStdout, err := tempfile(tgt.h, tgt.t)
        if err != nil {
                cleanup()
-               return TgtError{tgtOrig, ErrLine(err)}
+               return TgtError{tgt, ErrLine(err)}
        }
        stdoutPath := fdStdout.Name()
        fdStdout.Close()
@@ -386,13 +389,13 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
        tmpPathRel := mustRel(cwd, tmpPath)
        args = append(
                args,
-               path.Join(dirPrefix, tgt),
+               path.Join(dirPrefix, tgt.t),
                path.Join(dirPrefix, basename),
                tmpPathRel,
        )
        shCtx = fmt.Sprintf(
                "sh: %s: %s %s cwd:%s dirprefix:%s",
-               tgtOrig, cmdName, args, cwd, dirPrefix,
+               tgt, cmdName, args, cwd, dirPrefix,
        )
 
        cmd := exec.Command(cmdName, args...)
@@ -431,13 +434,13 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
        var fdStderr *os.File
        if StderrKeep {
                fdStderr, err = os.OpenFile(
-                       path.Join(redoDir, tgt+LogSuffix),
+                       path.Join(redoDir, tgt.t+LogSuffix),
                        os.O_WRONLY|os.O_CREATE|os.O_TRUNC,
                        os.FileMode(0666),
                )
                if err != nil {
                        cleanup()
-                       return TgtError{tgtOrig, ErrLine(err)}
+                       return TgtError{tgt, ErrLine(err)}
                }
        }
        tracef(CDebug, "%s", shCtx)
@@ -505,7 +508,7 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
                        fdStdout.Close()
                        if fdStderr != nil {
                                fdStderr.Close()
-                               logRecPath := path.Join(redoDir, tgt+LogRecSuffix)
+                               logRecPath := path.Join(redoDir, tgt.t+LogRecSuffix)
                                if fdStderr, err = os.OpenFile(
                                        logRecPath,
                                        os.O_WRONLY|os.O_CREATE|os.O_TRUNC,
@@ -546,18 +549,21 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
                                                        Value: strconv.Itoa(exitErr.ProcessState.ExitCode()),
                                                })
                                        }
-                                       w := bufio.NewWriter(fdStderr)
 
-                                       depInfo, err := depRead(fdDepPath)
-                                       if err != nil {
-                                               err = ErrLine(err)
-                                               goto Err
-                                       }
-                                       for _, dep := range depInfo.ifchanges {
-                                               fields = append(fields, recfile.Field{
-                                                       Name:  "Ifchange",
-                                                       Value: dep.tgt,
-                                               })
+                                       w := bufio.NewWriter(fdStderr)
+                                       {
+                                               var ifchanges []string
+                                               ifchanges, err = depReadOnlyIfchanges(tgt.Dep())
+                                               if err != nil {
+                                                       err = ErrLine(err)
+                                                       goto Err
+                                               }
+                                               for _, dep := range ifchanges {
+                                                       fields = append(fields, recfile.Field{
+                                                               Name:  "Ifchange",
+                                                               Value: dep,
+                                                       })
+                                               }
                                        }
                                        _, err = recfile.NewWriter(w).WriteFields(fields...)
                                        if err != nil {
@@ -603,7 +609,7 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
                RunningProcs[cmd.Process.Pid] = cmd.Process
                RunningProcsM.Unlock()
                pid := fmt.Sprintf("[%d]", cmd.Process.Pid)
-               tracef(CDebug, "%s runs %s", tgtOrig, pid)
+               tracef(CDebug, "%s runs %s", tgt, pid)
 
                stderrTerm := make(chan struct{})
                go func() {
@@ -651,7 +657,7 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
                }
 
                // Was $1 touched?
-               if inode, err := inodeFromFileByPath(path.Join(cwdOrig, tgt)); err == nil {
+               if inode, err := inodeFromFileByPath(tgt.a); err == nil {
                        if inodePrev == nil {
                                runErr.Err = Err1WasTouched
                                errs <- runErr
@@ -706,8 +712,8 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
 
                // Do we need to ifcreate it, or ifchange with renaming?
                if fd == nil {
-                       os.Remove(path.Join(cwdOrig, tgt))
-                       err = ifcreate(fdDep, tgt)
+                       os.Remove(tgt.a)
+                       err = ifcreate(fdDep, tgt.t)
                        if err != nil {
                                err = ErrLine(err)
                                goto Finish
@@ -726,22 +732,22 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
                                        goto Finish
                                }
                                if bytes.Equal(hsh, hshPrev) {
-                                       tracef(CDebug, "%s has same hash, not renaming", tgtOrig)
+                                       tracef(CDebug, "%s has same hash, not renaming", tgt)
                                        err = ErrLine(os.Remove(fd.Name()))
                                        if err != nil {
                                                goto Finish
                                        }
-                                       err = ErrLine(os.Chtimes(path.Join(cwdOrig, tgt), finished, finished))
+                                       err = ErrLine(os.Chtimes(tgt.a, finished, finished))
                                        if err != nil {
                                                goto Finish
                                        }
                                        if !NoSync {
-                                               err = ErrLine(syncDir(cwdOrig))
+                                               err = ErrLine(syncDir(tgt.h))
                                                if err != nil {
                                                        goto Finish
                                                }
                                        }
-                                       err = ErrLine(depWrite(fdDep, cwdOrig, tgt, hsh))
+                                       err = ErrLine(depWrite(fdDep, tgt.h, tgt, hsh))
                                        if err != nil {
                                                goto Finish
                                        }
@@ -754,17 +760,17 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
                                        goto Finish
                                }
                        }
-                       err = ErrLine(os.Rename(fd.Name(), path.Join(cwdOrig, tgt)))
+                       err = ErrLine(os.Rename(fd.Name(), tgt.a))
                        if err != nil {
                                goto Finish
                        }
                        if !NoSync {
-                               err = ErrLine(syncDir(cwdOrig))
+                               err = ErrLine(syncDir(tgt.h))
                                if err != nil {
                                        goto Finish
                                }
                        }
-                       err = ErrLine(depWrite(fdDep, cwdOrig, tgt, hsh))
+                       err = ErrLine(depWrite(fdDep, tgt.h, tgt, hsh))
                        if err != nil {
                                goto Finish
                        }
@@ -778,8 +784,7 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
                                goto Finish
                        }
                }
-               fdDepPath = path.Join(redoDir, tgt+DepSuffix)
-               err = ErrLine(os.Rename(fdDep.Name(), fdDepPath))
+               err = ErrLine(os.Rename(fdDep.Name(), tgt.Dep()))
                if err != nil {
                        goto Finish
                }
@@ -793,13 +798,13 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
                fdDepOpened = false
 
                // Post-commit .rec sanitizing
-               if depInfo, err := depRead(fdDepPath); err == nil {
+               if depInfo, err := depRead(tgt); err == nil {
                        ifchangeSeen := make(map[string]struct{}, len(depInfo.ifchanges))
                        for _, dep := range depInfo.ifchanges {
-                               ifchangeSeen[dep.tgt] = struct{}{}
+                               ifchangeSeen[dep.tgt.a] = struct{}{}
                        }
                        for _, dep := range depInfo.ifcreates {
-                               if _, exists := ifchangeSeen[dep]; exists {
+                               if _, exists := ifchangeSeen[dep.a]; exists {
                                        tracef(CWarn, "simultaneous ifcreate and ifchange records: %s", tgt)
                                }
                        }
index 95b8e4e5bcfb54d7611f4c5e0a29ea1b0dae40a1..acf842b4547904e6ed5812c462e707541acaaf6a 100644 (file)
@@ -20,24 +20,20 @@ package main
 import (
        "errors"
        "io/fs"
-       "path"
 )
 
 func sourcesWalker(
-       tgts []string,
+       tgts []*Tgt,
        seen map[string]struct{},
        seenDeps map[string]struct{},
-       srcs map[string]struct{},
+       srcs map[string]*Tgt,
 ) error {
        for _, tgt := range tgts {
-               tgtAbsPath := mustAbs(path.Join(Cwd, tgt))
-               cwd, f := path.Split(path.Join(Cwd, tgt))
-               depPath := path.Join(cwd, RedoDir, f+DepSuffix)
-               if _, ok := seenDeps[depPath]; ok {
+               if _, ok := seenDeps[tgt.Dep()]; ok {
                        continue
                }
-               seenDeps[depPath] = struct{}{}
-               depInfo, err := depRead(depPath)
+               seenDeps[tgt.Dep()] = struct{}{}
+               depInfo, err := depRead(tgt)
                if err != nil {
                        if errors.Is(err, fs.ErrNotExist) {
                                continue
@@ -45,16 +41,15 @@ func sourcesWalker(
                        return ErrLine(err)
                }
                for _, dep := range depInfo.ifchanges {
-                       depTgtAbsPath := mustAbs(path.Join(cwd, dep.tgt))
-                       if _, ok := seen[depTgtAbsPath]; ok {
+                       if _, ok := seen[dep.tgt.a]; ok {
                                continue
                        }
-                       seen[depTgtAbsPath] = struct{}{}
-                       if isSrc(cwd, dep.tgt) {
-                               srcs[cwdMustRel(depTgtAbsPath)] = struct{}{}
-                       } else if depTgtAbsPath != tgtAbsPath {
+                       seen[dep.tgt.a] = struct{}{}
+                       if isSrc(dep.tgt) {
+                               srcs[dep.tgt.a] = dep.tgt
+                       } else if dep.tgt.a != tgt.a {
                                if err := sourcesWalker(
-                                       []string{cwdMustRel(depTgtAbsPath)},
+                                       []*Tgt{dep.tgt},
                                        seen, seenDeps, srcs,
                                ); err != nil {
                                        return err
index 6459a2db5529d9061ce393b044073f62e9c2ba71..662612ea976b77f8418abc1d3c1fb4cb51ae541b 100644 (file)
@@ -25,7 +25,6 @@ import (
 )
 
 func targetsCollect(root string, tgts map[string]struct{}) error {
-       root = mustAbs(root)
        dir, err := os.Open(root)
        if err != nil {
                return ErrLine(err)
@@ -72,9 +71,9 @@ func targetsCollect(root string, tgts map[string]struct{}) error {
 }
 
 func targetsWalker(tgts []string) ([]string, error) {
-       tgtsMap := map[string]struct{}{}
+       tgtsMap := make(map[string]struct{})
        for _, tgt := range tgts {
-               if err := targetsCollect(tgt, tgtsMap); err != nil {
+               if err := targetsCollect(mustAbs(tgt), tgtsMap); err != nil {
                        return nil, err
                }
        }
@@ -86,12 +85,15 @@ func targetsWalker(tgts []string) ([]string, error) {
 }
 
 func collectWholeDeps(
-       tgts map[string]struct{},
-       deps map[string]map[string]struct{},
-       seen map[string]struct{},
+       tgts map[string]*Tgt,
+       deps map[string]map[string]*Tgt,
+       seen map[string]*Tgt,
 ) {
-       for tgt := range tgts {
-               seen[tgt] = struct{}{}
-               collectWholeDeps(deps[tgt], deps, seen)
+       for _, tgt := range tgts {
+               if _, exists := seen[tgt.a]; exists {
+                       continue
+               }
+               seen[tgt.a] = tgt
+               collectWholeDeps(deps[tgt.a], deps, seen)
        }
 }