X-Git-Url: http://www.git.cypherpunks.ru/?p=goredo.git;a=blobdiff_plain;f=run.go;h=58b9f6daff2fa933a17c6ff906766676169c86dd;hp=36e18d2c21c5771dfd90bac244104e07006eeeed;hb=bc7701e7a4f95cee680e0736ec3e68a8b0b5c09f;hpb=f15fe27971bcf37a06bb9542ea1907f11444515a diff --git a/run.go b/run.go index 36e18d2..58b9f6d 100644 --- a/run.go +++ b/run.go @@ -103,25 +103,59 @@ func isModified(cwd, redoDir, tgt string) (bool, error) { return false, nil } +type RunErr struct { + Tgt string + DoFile string + Started *time.Time + Finished *time.Time + Err error +} + +func (e RunErr) Unwrap() error { return e.Err } + +func (e *RunErr) Name() string { + var name string + if e.DoFile == "" { + name = e.Tgt + } else { + name = fmt.Sprintf("%s (%s)", e.Tgt, e.DoFile) + } + if e.Finished == nil { + return name + } + return fmt.Sprintf("%s (%fsec)", name, e.Finished.Sub(*e.Started).Seconds()) +} + +func (e RunErr) Error() string { + return fmt.Sprintf("%s: %s", e.Name(), e.Err) +} + +func syncDir(dir string) error { + fd, err := os.Open(dir) + if err != nil { + return err + } + err = fd.Sync() + fd.Close() + return err +} + func runScript(tgt string, errs chan error) error { tgtOrig := tgt cwd, tgt := cwdAndTgt(tgt) redoDir := path.Join(cwd, RedoDir) - errf := func(err error) error { - return fmt.Errorf("%s: %s", tgtOrig, err) - } if err := mkdirs(redoDir); err != nil { - return errf(err) + return TgtErr{tgtOrig, err} } // Acquire lock fdLock, err := os.OpenFile( - path.Join(redoDir, "lock."+tgt), + path.Join(redoDir, tgt+LockSuffix), os.O_WRONLY|os.O_TRUNC|os.O_CREATE, os.FileMode(0666), ) if err != nil { - return errf(err) + return TgtErr{tgtOrig, err} } lockRelease := func() { trace(CLock, "LOCK_UN: %s", fdLock.Name()) @@ -129,10 +163,12 @@ func runScript(tgt string, errs chan error) error { fdLock.Close() } trace(CLock, "LOCK_NB: %s", fdLock.Name()) + + // Waiting for job completion, already taken by someone else if err = unix.Flock(int(fdLock.Fd()), unix.LOCK_EX|unix.LOCK_NB); err != nil { if uintptr(err.(syscall.Errno)) != uintptr(unix.EWOULDBLOCK) { fdLock.Close() - return errf(err) + return TgtErr{tgtOrig, err} } trace(CDebug, "waiting: %s", tgtOrig) Jobs.Add(1) @@ -146,7 +182,7 @@ func runScript(tgt string, errs chan error) error { fdDep, err := os.Open(path.Join(redoDir, tgt+DepSuffix)) if err != nil { if os.IsNotExist(err) { - err = fmt.Errorf("%s is not built", tgtOrig) + err = errors.New("was not built") } goto Finish } @@ -155,11 +191,11 @@ func runScript(tgt string, errs chan error) error { goto Finish } if !builtNow { - err = fmt.Errorf("%s is not built", tgtOrig) + err = errors.New("was not built") } Finish: if err != nil { - err = errf(err) + err = TgtErr{tgtOrig, err} } errs <- err }() @@ -170,24 +206,20 @@ func runScript(tgt string, errs chan error) error { modified, err := isModified(cwd, redoDir, tgt) if err != nil { lockRelease() - return errf(err) + return TgtErr{tgtOrig, err} } if modified { trace(CWarn, "%s externally modified: not redoing", tgtOrig) lockRelease() - Jobs.Add(1) - go func() { - errs <- nil - Jobs.Done() - }() - return nil + errs <- nil + return TgtErr{tgtOrig, err} } - // Start preparing dep. + // Start preparing .dep fdDep, err := tempfile(redoDir, tgt+DepSuffix) if err != nil { lockRelease() - return errf(err) + return TgtErr{tgtOrig, err} } cleanup := func() { lockRelease() @@ -198,22 +230,22 @@ func runScript(tgt string, errs chan error) error { recfile.Field{Name: "Build", Value: BuildUUID}, ); err != nil { cleanup() - return errf(err) + return TgtErr{tgtOrig, err} } // Find .do doFile, upLevels, err := findDo(fdDep, cwd, tgt) if err != nil { cleanup() - return errf(err) + return TgtErr{tgtOrig, err} } if doFile == "" { cleanup() - return errf(errors.New("no .do found")) + return TgtErr{tgtOrig, errors.New("no .do found")} } if err = writeDep(fdDep, cwd, doFile); err != nil { cleanup() - return errf(err) + return TgtErr{tgtOrig, err} } // Determine basename and DIRPREFIX @@ -226,12 +258,12 @@ func runScript(tgt string, errs chan error) error { } cwd = path.Clean(cwd) basename := tgt + runErr := RunErr{Tgt: tgtOrig} if strings.HasPrefix(doFile, "default.") { basename = tgt[:len(tgt)-(len(doFile)-len("default.")-len(".do"))-1] - trace(CRedo, "%s (%s)", tgtOrig, doFile) - } else { - trace(CRedo, "%s", tgtOrig) + runErr.DoFile = doFile } + trace(CWait, "%s", runErr.Name()) doFile = path.Base(doFile) // Prepare command line @@ -245,13 +277,13 @@ func runScript(tgt string, errs chan error) error { fd, err := os.Open(path.Join(cwd, doFile)) if err != nil { cleanup() - return errf(err) + return TgtErr{tgtOrig, err} } buf := make([]byte, 512) n, err := fd.Read(buf) if err != nil { cleanup() - return errf(err) + return TgtErr{tgtOrig, err} } if n > 3 && string(buf[:3]) == "#!/" { // Shebanged @@ -259,7 +291,7 @@ func runScript(tgt string, errs chan error) error { nlIdx := strings.Index(t, "\n") if nlIdx == -1 { cleanup() - return errf(errors.New("not fully read shebang")) + return TgtErr{tgtOrig, errors.New("not fully read shebang")} } args = strings.Split(t[:nlIdx], " ") cmdName, args = args[0], args[1:] @@ -279,7 +311,7 @@ func runScript(tgt string, errs chan error) error { fdStdout, err := tempfile(cwd, tgt) if err != nil { cleanup() - return errf(err) + return TgtErr{tgtOrig, err} } tmpPath := fdStdout.Name() + ".3" // and for $3 args = append(args, tgt, basename, path.Base(tmpPath)) @@ -291,9 +323,14 @@ func runScript(tgt string, errs chan error) error { cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", RedoDirPrefixEnv, dirPrefix)) cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", RedoBuildUUIDEnv, BuildUUID)) childStderrPrefix := tempsuffix() - cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", RedoStderrPrefixEnv, childStderrPrefix)) + cmd.Env = append(cmd.Env, fmt.Sprintf( + "%s=%s", RedoStderrPrefixEnv, childStderrPrefix, + )) + cmd.ExtraFiles = append(cmd.ExtraFiles, fdDep) - cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%d", RedoDepFdEnv, 3+0)) + fdNum := 0 + cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%d", RedoDepFdEnv, 3+fdNum)) + fdNum++ if JSR == nil { // infinite jobs cmd.Env = append(cmd.Env, fmt.Sprintf("%s=NO", RedoJSRFdEnv)) @@ -301,8 +338,10 @@ func runScript(tgt string, errs chan error) error { } else { cmd.ExtraFiles = append(cmd.ExtraFiles, JSR) cmd.ExtraFiles = append(cmd.ExtraFiles, JSW) - cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%d", RedoJSRFdEnv, 3+1)) - cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%d", RedoJSWFdEnv, 3+2)) + cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%d", RedoJSRFdEnv, 3+fdNum)) + fdNum++ + cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%d", RedoJSWFdEnv, 3+fdNum)) + fdNum++ } // Preparing stderr @@ -319,18 +358,18 @@ func runScript(tgt string, errs chan error) error { ) if err != nil { cleanup() - return errf(err) + return TgtErr{tgtOrig, err} } fdStderr.Truncate(0) } - trace(CDebug, "sh: %s: %s %s [%s]", tgtOrig, cmdName, args, cwd) + shCtx := fmt.Sprintf("sh: %s: %s %s [%s]", tgtOrig, cmdName, args, cwd) + trace(CDebug, "%s", shCtx) Jobs.Add(1) go func() { - jsAcquire() - started := time.Now() + jsAcquire(shCtx) defer func() { - jsRelease() + jsRelease(shCtx) lockRelease() fdDep.Close() fdStdout.Close() @@ -341,13 +380,14 @@ func runScript(tgt string, errs chan error) error { os.Remove(fdStdout.Name()) os.Remove(tmpPath) os.Remove(fdLock.Name()) - finished := time.Now() - trace(CDone, "%s (%fsec)", tgtOrig, finished.Sub(started).Seconds()) Jobs.Done() }() + started := time.Now() + runErr.Started = &started err := cmd.Start() if err != nil { - errs <- errf(err) + runErr.Err = err + errs <- runErr return } pid := fmt.Sprintf("[%d]", cmd.Process.Pid) @@ -384,27 +424,33 @@ func runScript(tgt string, errs chan error) error { // Wait for job completion <-stderrTerm err = cmd.Wait() + finished := time.Now() + runErr.Finished = &finished if err != nil { - errs <- errf(err) + runErr.Err = err + errs <- runErr return } // Does it produce both stdout and tmp? fiStdout, err := os.Stat(fdStdout.Name()) if err != nil { - errs <- errf(err) + runErr.Err = err + errs <- runErr return } tmpExists := false _, err = os.Stat(tmpPath) if err == nil { if fiStdout.Size() > 0 { - errs <- errf(fmt.Errorf("%s created both tmp and stdout", tgtOrig)) + runErr.Err = errors.New("created both tmp and stdout") + errs <- runErr return } tmpExists = true } else if !os.IsNotExist(err) { - errs <- errf(err) + runErr.Err = err + errs <- runErr return } @@ -413,8 +459,7 @@ func runScript(tgt string, errs chan error) error { if tmpExists { fd, err = os.Open(tmpPath) if err != nil { - errs <- errf(err) - return + goto Finish } defer fd.Close() } else if fiStdout.Size() > 0 { @@ -423,63 +468,53 @@ func runScript(tgt string, errs chan error) error { // Do we need to ifcreate it, of ifchange with renaming? if fd == nil { - if err = ifcreate(fdDep, tgt); err != nil { - errs <- errf(err) - return + err = ifcreate(fdDep, tgt) + if err != nil { + goto Finish } } else { if !NoSync { - if err = fd.Sync(); err != nil { - errs <- errf(err) - return + err = fd.Sync() + if err != nil { + goto Finish } } - if err = os.Rename(fd.Name(), path.Join(cwdOrig, tgt)); err != nil { - errs <- errf(err) - return + err = os.Rename(fd.Name(), path.Join(cwdOrig, tgt)) + if err != nil { + goto Finish } if !NoSync { - fdDir, err := os.Open(cwdOrig) + err = syncDir(cwdOrig) if err != nil { - errs <- errf(err) - return - } - defer fdDir.Close() - if err = fdDir.Sync(); err != nil { - errs <- errf(err) - return + goto Finish } } - if err = writeDep(fdDep, cwdOrig, tgt); err != nil { - errs <- errf(err) - return + err = writeDep(fdDep, cwdOrig, tgt) + if err != nil { + goto Finish } } - // Commit dep. + // Commit .dep if !NoSync { - if err = fdDep.Sync(); err != nil { - errs <- errf(err) - return + err = fdDep.Sync() + if err != nil { + goto Finish } } - if err = os.Rename(fdDep.Name(), path.Join(redoDir, tgt+DepSuffix)); err != nil { - errs <- errf(err) - return + err = os.Rename(fdDep.Name(), path.Join(redoDir, tgt+DepSuffix)) + if err != nil { + goto Finish } if !NoSync { - fdDir, err := os.Open(redoDir) + err = syncDir(redoDir) if err != nil { - errs <- errf(err) - return - } - defer fdDir.Close() - if err = fdDir.Sync(); err != nil { - errs <- errf(err) - return + goto Finish } } - errs <- nil + Finish: + runErr.Err = err + errs <- runErr }() return nil }