X-Git-Url: http://www.git.cypherpunks.ru/?a=blobdiff_plain;f=ifchange.go;h=5c294013f7bf270e7dc89a5c50db0586e3213492;hb=HEAD;hp=0f430fdebd76741065215382cb76ee406690e3fe;hpb=ca5290c19761bc1806b98d54d966a9cbbfeabd15;p=goredo.git diff --git a/ifchange.go b/ifchange.go index 0f430fd..5c29401 100644 --- a/ifchange.go +++ b/ifchange.go @@ -1,176 +1,167 @@ -/* -goredo -- djb's redo implementation on pure Go -Copyright (C) 2020-2021 Sergey Matveev - -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 . -*/ +// goredo -- djb's redo implementation on pure Go +// Copyright (C) 2020-2024 Sergey Matveev +// +// 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 . 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 { - trace( - 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 { - trace(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() - trace(CDebug, "collecting deps") - seen := map[string]struct{}{} - deps := map[string]map[string]struct{}{} + tracef(CDebug, "collecting deps") + 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 - trace(CDebug, "building %d alwayses: %v", len(seen), seen) + 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 { - trace(CErr, "always run error: %s, skipping dependants", err) + 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 = ok && isOkRun(<-errs) - } Jobs.Wait() close(errs) + <-okChecker if !ok { - trace(CDebug, "alwayses failed, skipping dependants") + 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: - trace(CDebug, "checking %d dependant targets: %v", len(queueSrc), queueSrc) - queue := []string{} + tracef(CDebug, "checking %d dependant targets: %v", len(queueSrc), queueSrc) + 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 } } - trace(CDebug, "building %d dependant targets: %v", len(queue), queue) + + 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 { - trace(CErr, "dependant error: %s, skipping dependants", err) + tracef(CErr, "dependant error: %s, skipping dependants", err) return nil } if !ood { continue } - if err := runScript(tgt, errs, false); err != nil { - trace(CErr, "dependant error: %s, skipping dependants", err) + 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 = ok && isOkRun(<-errs) - } + Jobs.Wait() + close(errs) + <-okChecker if !ok { - trace(CDebug, "dependants failed, skipping them") + tracef(CDebug, "dependants failed, skipping them") return nil } - Jobs.Wait() - close(errs) if jobs == 0 { return seen } @@ -178,44 +169,70 @@ RebuildDeps: 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() - defer jsAcquire("ifchange exiting") - defer Jobs.Wait() + if !IsTopRedo { + defer jsAcquire("ifchange exiting") + } seen := buildDependants(tgts) + if seen == nil { + Jobs.Wait() + return false, nil + } oodTgtsClear() - trace(CDebug, "building %d targets: %v", len(tgts), tgts) - jobs := 0 - errs := make(chan error, len(tgts)) + tracef(CDebug, "building %d targets: %v", len(tgts), 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 { - trace(CDebug, "%s was already build as a dependant", tgt) + 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) { - trace(CDebug, "%s is source, not redoing", 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 = ok && isOkRun(<-errs) } + Jobs.Wait() + close(errs) + <-okChecker return ok, nil }