/*
goredo -- djb's redo implementation on pure Go
-Copyright (C) 2020-2021 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2020-2023 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
package main
import (
- "os"
- "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,
-) []string {
- cwd, tgt := cwdAndTgt(path.Join(cwd, tgtOrig))
- depPath := path.Join(cwd, RedoDir, tgt+DepSuffix)
- fdDep, err := os.Open(depPath)
- if err != nil {
+) []*Tgt {
+ if _, ok := DepCache[tgt.rel]; ok {
return nil
}
- depInfo, err := depRead(fdDep)
- fdDep.Close()
+ dep, err := depRead(tgt)
if err != nil {
return nil
}
- var alwayses []string
+ DepCache[tgt.rel] = dep
+ 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,
- )
+ if dep.always {
+ if dep.build == BuildUUID {
+ tracef(CDebug, "ood: %s%s always, but already build",
+ 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 _, m := range depInfo.ifchanges {
- dep := m["Target"]
- if dep == "" {
- return alwayses
- }
- if dep == tgt {
+ for _, ifchange := range dep.ifchanges {
+ if ifchange.tgt.rel == tgt.rel {
continue
}
- if !includeSrc && isSrc(cwd, dep) {
+ if !includeSrc && isSrc(ifchange.tgt) {
continue
}
if !returnReady {
- depRel := cwdMustRel(cwd, dep)
- if m, ok := deps[depRel]; ok {
- m[tgtRel] = struct{}{}
+ if m, ok := deps[ifchange.tgt.rel]; ok {
+ m[tgt.rel] = tgt
} else {
- m = map[string]struct{}{}
- m[tgtRel] = struct{}{}
- deps[depRel] = m
+ deps[ifchange.tgt.rel] = map[string]*Tgt{tgt.rel: tgt}
}
- alwayses = append(
- alwayses,
- collectDeps(cwd, dep, level+1, deps, includeSrc)...,
- )
+ alwayses = append(alwayses,
+ collectDeps(ifchange.tgt, level+1, deps, includeSrc)...)
}
}
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{}{}
+ seen := make(map[string]*Tgt)
+ deps := make(map[string]map[string]*Tgt)
for _, tgtInitial := range tgts {
- for _, tgt := range collectDeps(Cwd, tgtInitial, 0, deps, false) {
- if tgt != tgtInitial {
- seen[tgt] = struct{}{}
+ for _, tgt := range collectDeps(tgtInitial, 0, deps, false) {
+ if tgt.rel != tgtInitial.rel {
+ seen[tgt.rel] = tgt
}
}
}
+ TgtCache = make(map[string]*Tgt)
+ IfchangeCache = nil
if len(seen) == 0 {
- return nil
+ return seen
}
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))
- for tgt := range seen {
- if err := runScript(tgt, errs, false); err != nil {
+ ok := true
+ okChecker := make(chan struct{})
+ go func() {
+ for err := range errs {
+ ok = isOkRun(err) && ok
+ }
+ close(okChecker)
+ }()
+ for _, tgt := range seen {
+ if err := runScript(tgt, errs, false, false); err != nil {
tracef(CErr, "always run error: %s, skipping dependants", err)
+ Jobs.Wait()
+ close(errs)
return nil
}
}
- ok := true
- for i := 0; i < len(seen); i++ {
- ok = isOkRun(<-errs) && ok
- }
Jobs.Wait()
close(errs)
+ <-okChecker
if !ok {
tracef(CDebug, "alwayses failed, skipping dependants")
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)
}
- if len(queueSrc) == 0 {
- return seen
- }
RebuildDeps:
tracef(CDebug, "checking %d dependant targets: %v", len(queueSrc), queueSrc)
- queue := []string{}
+ queue := make(map[string]*Tgt)
for _, tgt := range queueSrc {
- for dep := range deps[tgt] {
- queue = append(queue, dep)
+ for _, dep := range deps[tgt.rel] {
+ queue[dep.rel] = dep
}
}
+
tracef(CDebug, "building %d dependant targets: %v", len(queue), queue)
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)
+ ood, err := isOODWithTrace(tgt, 0, seen)
if err != nil {
tracef(CErr, "dependant error: %s, skipping dependants", err)
return nil
if !ood {
continue
}
- if err := runScript(tgt, errs, false); err != nil {
+ if err := runScript(tgt, errs, false, false); err != nil {
tracef(CErr, "dependant error: %s, skipping dependants", err)
return nil
}
queueSrc = append(queueSrc, tgt)
- seen[tgt] = struct{}{}
+ seen[tgt.rel] = tgt
jobs++
}
- for i := 0; i < jobs; i++ {
- ok = isOkRun(<-errs) && ok
- }
+ Jobs.Wait()
+ close(errs)
+ <-okChecker
if !ok {
tracef(CDebug, "dependants failed, skipping them")
return nil
}
- Jobs.Wait()
- close(errs)
if jobs == 0 {
return seen
}
goto RebuildDeps
}
-func ifchange(tgts []string, forced, traced bool) (bool, error) {
+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)
+ }
+ }
+
jsInit()
if !IsTopRedo {
defer jsAcquire("ifchange exiting")
}
- defer Jobs.Wait()
seen := buildDependants(tgts)
+ if seen == nil {
+ Jobs.Wait()
+ return false, nil
+ }
oodTgtsClear()
tracef(CDebug, "building %d targets: %v", len(tgts), tgts)
- jobs := 0
- errs := make(chan error, len(tgts))
var ood bool
var err error
+ ok := true
+ okChecker := make(chan struct{})
+ errs := make(chan error, len(tgts))
+ go func() {
+ for err := range errs {
+ ok = isOkRun(err) && ok
+ }
+ close(okChecker)
+ }()
for _, tgt := range tgts {
- if _, ok := seen[tgt]; ok {
+ if _, ok := seen[tgt.rel]; 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 {
- return false, err
+ Jobs.Wait()
+ close(errs)
+ return false, ErrLine(err)
}
}
if !ood {
continue
}
- if isSrc(Cwd, tgt) {
+ if isSrc(tgt) {
tracef(CDebug, "%s is source, not redoing", tgt)
continue
}
- if err = runScript(tgt, errs, traced); err != nil {
- return false, err
+ if err = runScript(tgt, errs, forced, traced); err != nil {
+ Jobs.Wait()
+ close(errs)
+ return false, ErrLine(err)
}
- jobs++
- }
- ok := true
- for ; jobs > 0; jobs-- {
- ok = isOkRun(<-errs) && ok
}
+ Jobs.Wait()
+ close(errs)
+ <-okChecker
return ok, nil
}