+import (
+ "os"
+ "path"
+ "strings"
+)
+
+func collectDeps(
+ cwd, tgtOrig string,
+ level int,
+ deps map[string]map[string]struct{},
+ 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 {
+ return nil
+ }
+ depInfo, err := depRead(fdDep)
+ fdDep.Close()
+ if err != nil {
+ return nil
+ }
+ var alwayses []string
+ 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,
+ )
+ returnReady = true
+ } else {
+ tracef(CDebug, "ood: %s%s always", strings.Repeat(". ", level), tgtOrig)
+ alwayses = append(alwayses, tgtRel)
+ returnReady = true
+ }
+ }
+ for _, m := range depInfo.ifchanges {
+ dep := m["Target"]
+ if dep == "" {
+ return alwayses
+ }
+ if dep == tgt {
+ continue
+ }
+ if !includeSrc && isSrc(cwd, dep) {
+ continue
+ }
+ if !returnReady {
+ depRel := cwdMustRel(cwd, dep)
+ if m, ok := deps[depRel]; ok {
+ m[tgtRel] = struct{}{}
+ } else {
+ m = map[string]struct{}{}
+ m[tgtRel] = struct{}{}
+ deps[depRel] = m
+ }
+ alwayses = append(
+ alwayses,
+ collectDeps(cwd, dep, level+1, deps, includeSrc)...,
+ )
+ }
+ }
+ return alwayses
+}
+
+func buildDependants(tgts []string) map[string]struct{} {
+ defer Jobs.Wait()
+ tracef(CDebug, "collecting deps")
+ seen := map[string]struct{}{}
+ deps := map[string]map[string]struct{}{}
+ for _, tgtInitial := range tgts {
+ for _, tgt := range collectDeps(Cwd, tgtInitial, 0, deps, false) {
+ if tgt != tgtInitial {
+ seen[tgt] = struct{}{}
+ }
+ }
+ }
+ if len(seen) == 0 {
+ return nil
+ }
+
+ levelOrig := Level
+ 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 {
+ tracef(CErr, "always run error: %s, skipping dependants", err)
+ return nil
+ }
+ }
+ ok := true
+ for i := 0; i < len(seen); i++ {
+ ok = isOkRun(<-errs) && ok
+ }
+ Jobs.Wait()
+ close(errs)
+ if !ok {
+ tracef(CDebug, "alwayses failed, skipping dependants")
+ return nil
+ }
+
+ queueSrc := make([]string, 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{}
+ for _, tgt := range queueSrc {
+ for dep := range deps[tgt] {
+ queue = append(queue, dep)
+ }
+ }
+ tracef(CDebug, "building %d dependant targets: %v", len(queue), queue)
+ errs = make(chan error, len(queue))
+ jobs := 0
+ queueSrc = []string{}
+ for _, tgt := range queue {
+ ood, err := isOODWithTrace(Cwd, 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 {
+ tracef(CErr, "dependant error: %s, skipping dependants", err)
+ return nil
+ }
+ queueSrc = append(queueSrc, tgt)
+ seen[tgt] = struct{}{}
+ jobs++