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
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)
}
}
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])
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,
)
}
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
}
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)
}
}
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
}
}
subs = append(subs, &BuildLogJob{
- dir: dirNormalized,
tgt: dep,
started: started,
exitCode: exitCode,
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)
}
}
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},
}
type DepInfoIfchange struct {
- tgt string
+ tgt *Tgt
inode *Inode
hash []byte
}
build string
always bool
stamp []byte
- ifcreates []string
+ ifcreates []*Tgt
ifchanges []DepInfoIfchange
}
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
}
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 == "" {
}
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"]
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 {
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()
}
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
}
return data, nil
}
-func dotPrint(tgts []string) error {
+func dotPrint(tgts []*Tgt) error {
data := map[DotNodes]bool{}
var err error
for _, tgt := range tgts {
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))
}
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()
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
}
}
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
return nil
}
queueSrc = append(queueSrc, tgt)
- seen[tgt] = struct{}{}
+ seen[tgt.a] = tgt
jobs++
}
Jobs.Wait()
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 {
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)
if !ood {
continue
}
- if isSrc(Cwd, tgt) {
+ if isSrc(tgt) {
tracef(CDebug, "%s is source, not redoing", tgt)
continue
}
"os"
"os/signal"
"path"
- "path/filepath"
"runtime"
"sort"
"strconv"
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 (
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 == "" {
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 == "" {
raw[0:4], raw[4:6], raw[6:8], raw[8:10], raw[10:],
)
if tgtsWasEmpty {
- tgts = []string{"all"}
+ tgts = append(tgts, NewTgt("all"))
}
}
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() {
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
}
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
}
err = always(fdDep)
case CmdNameRedoCleanup:
for _, what := range tgts {
- err = cleanupWalker(Cwd, what)
+ err = cleanupWalker(Cwd, what.t)
if err != nil {
break
}
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")
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
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, "..")
}
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
}
}
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)
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)
"io/fs"
"log"
"os"
- "path"
"strings"
"golang.org/x/sys/unix"
}
type TgtError struct {
- Tgt string
+ Tgt *Tgt
Err error
}
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
}
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
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)
+}
}
type RunError struct {
- Tgt string
+ Tgt *Tgt
DoFile string
Started *time.Time
Finished *time.Time
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)
}
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
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() {
// 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,
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)
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")
}
}
if err != nil {
- err = TgtError{tgtOrig, err}
+ err = TgtError{tgt, err}
}
errs <- err
}()
// 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)}
}
}
}
// 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
}()
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()
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
// 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"
} 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()
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...)
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)
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,
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 {
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() {
}
// 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
// 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
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
}
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
}
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
}
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)
}
}
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
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
)
func targetsCollect(root string, tgts map[string]struct{}) error {
- root = mustAbs(root)
dir, err := os.Open(root)
if err != nil {
return ErrLine(err)
}
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
}
}
}
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)
}
}