-/*
-goredo -- djb's redo implementation on pure Go
-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
-the Free Software Foundation, version 3 of the License.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
+// goredo -- djb's redo implementation on pure Go
+// Copyright (C) 2020-2024 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
+// the Free Software Foundation, version 3 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
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 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 := 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.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{}
- for tgt := range queue {
- ood, err := isOODWithTrace(Cwd, tgt, 0, seen)
+ queueSrc = make([]*Tgt, 0)
+ go func() {
+ for err := range errs {
+ ok = isOkRun(err) && ok
+ }
+ close(okChecker)
+ }()
+ for _, tgt := range queue {
+ 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) {
- // 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 {
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
}