]> Cypherpunks.ru repositories - goredo.git/blobdiff - run.go
Refactor target paths, less CPU, less memory, more clarity
[goredo.git] / run.go
diff --git a/run.go b/run.go
index 1d03e06612c4f7843b9d1363ca81f9012fcb8fcd..bea836661746b36060927e9c604d5c61c02b1e94 100644 (file)
--- a/run.go
+++ b/run.go
@@ -102,7 +102,7 @@ func init() {
 }
 
 type RunError struct {
-       Tgt      string
+       Tgt      *Tgt
        DoFile   string
        Started  *time.Time
        Finished *time.Time
@@ -112,7 +112,7 @@ type RunError struct {
 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)
        }
@@ -133,17 +133,17 @@ func mkdirs(pth string) error {
        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
@@ -169,14 +169,13 @@ func syncDir(dir string) error {
        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() {
@@ -187,12 +186,12 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
 
        // 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,
@@ -212,13 +211,13 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
        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)
@@ -236,13 +235,13 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
                                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")
@@ -253,7 +252,7 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
                                }
                        }
                        if err != nil {
-                               err = TgtError{tgtOrig, err}
+                               err = TgtError{tgt, err}
                        }
                        errs <- err
                }()
@@ -262,13 +261,13 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
 
        // 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)}
                }
        }
 
@@ -280,17 +279,17 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
        }
 
        // 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
                }()
@@ -299,10 +298,10 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
        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()
@@ -317,42 +316,46 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
                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
@@ -361,8 +364,8 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
        // 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"
@@ -371,14 +374,14 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
                } 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()
@@ -386,13 +389,13 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
        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...)
@@ -431,13 +434,13 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
        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)
@@ -505,7 +508,7 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
                        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,
@@ -546,18 +549,21 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
                                                        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 {
@@ -603,7 +609,7 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
                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() {
@@ -651,7 +657,7 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
                }
 
                // 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
@@ -706,8 +712,8 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
 
                // 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
@@ -726,22 +732,22 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
                                        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
                                        }
@@ -754,17 +760,17 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
                                        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
                        }
@@ -778,8 +784,7 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
                                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
                }
@@ -793,13 +798,13 @@ func runScript(tgtOrig string, errs chan error, forced, traced bool) error {
                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)
                                }
                        }