X-Git-Url: http://www.git.cypherpunks.ru/?a=blobdiff_plain;f=run.go;h=fe78a87e68198abb8f63d20383394a3f50c37121;hb=7492a39768035abafa55be738649a8dd035a89bd;hp=25e8bacebf3e20e88174412a51b5e50b0a0bc688;hpb=5ca39bfc0b769607db3f84213fc7f16f0c4019fa;p=goredo.git diff --git a/run.go b/run.go index 25e8bac..fe78a87 100644 --- a/run.go +++ b/run.go @@ -29,19 +29,21 @@ import ( "os/exec" "path" "path/filepath" + "strconv" "strings" "sync" "syscall" "time" "go.cypherpunks.ru/recfile" - "go.cypherpunks.ru/tai64n" + "go.cypherpunks.ru/tai64n/v2" "golang.org/x/sys/unix" ) const ( EnvDepFd = "REDO_DEP_FD" EnvDirPrefix = "REDO_DIRPREFIX" + EnvDepCwd = "REDO_DEP_CWD" EnvBuildUUID = "REDO_BUILD_UUID" EnvStderrPrefix = "REDO_STDERR_PREFIX" EnvTrace = "REDO_TRACE" @@ -63,12 +65,28 @@ var ( StderrPrefix string Jobs sync.WaitGroup - flagTrace = flag.Bool("x", false, "trace (sh -x) current targets") - flagTraceAll = flag.Bool("xx", false, fmt.Sprintf("trace (sh -x) all targets (%s=1)", EnvTrace)) - flagStderrKeep = flag.Bool("logs", false, fmt.Sprintf("keep job's stderr (%s=1)", EnvStderrKeep)) - flagStderrSilent = flag.Bool("silent", false, fmt.Sprintf("do not print job's stderr (%s=1)", EnvStderrSilent)) + flagTrace *bool + flagTraceAll *bool + flagStderrKeep *bool + flagStderrSilent *bool + + TracedAll bool ) +func init() { + cmdName := CmdName() + if !(cmdName == CmdNameRedo || cmdName == CmdNameRedoIfchange) { + return + } + flagTrace = flag.Bool("x", false, "trace (sh -x) current targets") + flagTraceAll = flag.Bool("xx", false, + fmt.Sprintf("trace (sh -x) all targets (%s=1)", EnvTrace)) + flagStderrKeep = flag.Bool("k", false, + fmt.Sprintf("keep job's stderr (%s=1)", EnvStderrKeep)) + flagStderrSilent = flag.Bool("s", false, + fmt.Sprintf("silent, do not print job's stderr (%s=1)", EnvStderrSilent)) +} + type RunErr struct { Tgt string DoFile string @@ -87,7 +105,7 @@ func (e *RunErr) Name() string { if e.Finished == nil { return name } - return fmt.Sprintf("%s (%fsec)", name, e.Finished.Sub(*e.Started).Seconds()) + return fmt.Sprintf("%s (%.3fs)", name, e.Finished.Sub(*e.Started).Seconds()) } func (e RunErr) Error() string { @@ -246,6 +264,7 @@ func runScript(tgtOrig string, errs chan error, traced bool) error { lockRelease() return TgtErr{tgtOrig, err} } + fdDepPath := fdDep.Name() cleanup := func() { lockRelease() fdDep.Close() @@ -292,6 +311,7 @@ func runScript(tgtOrig string, errs chan error, traced bool) error { cleanup() return TgtErr{tgtOrig, err} } + fdDep.Close() trace(CWait, "%s", runErr.Name()) // Prepare command line @@ -302,7 +322,7 @@ func runScript(tgtOrig string, errs chan error, traced bool) error { args = make([]string, 0, 3) } else { cmdName = "/bin/sh" - if traced { + if traced || TracedAll { args = append(args, "-ex") } else { args = append(args, "-e") @@ -316,7 +336,9 @@ func runScript(tgtOrig string, errs chan error, traced bool) error { cleanup() return TgtErr{tgtOrig, err} } - tmpPath := fdStdout.Name() + ".3" // and for $3 + stdoutPath := fdStdout.Name() + fdStdout.Close() + tmpPath := stdoutPath + ".3" // and for $3 tmpPathRel, err := filepath.Rel(cwd, tmpPath) if err != nil { panic(err) @@ -330,7 +352,6 @@ func runScript(tgtOrig string, errs chan error, traced bool) error { cmd := exec.Command(cmdName, args...) cmd.Dir = cwd - cmd.Stdout = fdStdout // cmd.Stdin reads from /dev/null by default cmd.Env = append(os.Environ(), fmt.Sprintf("%s=%d", EnvLevel, Level+1)) cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", EnvDirPrefix, dirPrefix)) @@ -342,8 +363,11 @@ func runScript(tgtOrig string, errs chan error, traced bool) error { )) fdNum := 0 - cmd.ExtraFiles = append(cmd.ExtraFiles, fdDep) - cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%d", EnvDepFd, 3+fdNum)) + cmd.ExtraFiles = append(cmd.ExtraFiles, FdOODTgts) + cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%d", EnvOODTgtsFd, 3+fdNum)) + fdNum++ + cmd.ExtraFiles = append(cmd.ExtraFiles, FdOODTgtsLock) + cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%d", EnvOODTgtsLockFd, 3+fdNum)) fdNum++ if FdStatus == nil { @@ -354,23 +378,7 @@ func runScript(tgtOrig string, errs chan error, traced bool) error { fdNum++ } - if JSR == nil { - // infinite jobs - cmd.Env = append(cmd.Env, fmt.Sprintf("%s=NO", EnvJSFd)) - } else { - cmd.ExtraFiles = append(cmd.ExtraFiles, JSR) - cmd.ExtraFiles = append(cmd.ExtraFiles, JSW) - cmd.Env = append(cmd.Env, fmt.Sprintf( - "%s=%d,%d", EnvJSFd, 3+fdNum+0, 3+fdNum+1, - )) - fdNum += 2 - } - // Preparing stderr - stderr, err := cmd.StderrPipe() - if err != nil { - panic(err) - } var fdStderr *os.File if StderrKeep { fdStderr, err = os.OpenFile( @@ -392,18 +400,101 @@ func runScript(tgtOrig string, errs chan error, traced bool) error { Jobs.Add(1) go func() { - jsAcquire(shCtx) + jsToken := jsAcquire(shCtx) + if JSR == nil { + // infinite jobs + cmd.Env = append(cmd.Env, fmt.Sprintf("%s=NO", EnvJobs)) + } else { + cmd.ExtraFiles = append(cmd.ExtraFiles, JSR) + cmd.ExtraFiles = append(cmd.ExtraFiles, JSW) + cmd.Env = append(cmd.Env, fmt.Sprintf( + "%s=%s %s%d,%d", + MakeFlagsName, MakeFlags, MakeJSArg, 3+fdNum+0, 3+fdNum+1, + )) + fdNum += 2 + cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%d", EnvJSToken, jsToken)) + } + if FdStatus != nil { FdStatus.Write([]byte{StatusRun}) } + + var finished time.Time + var exitErr *exec.ExitError + started := time.Now() + runErr.Started = &started + fdStdout, err = os.OpenFile(stdoutPath, os.O_RDWR, os.FileMode(0666)) + if err != nil { + runErr.Err = err + errs <- runErr + return + } + cmd.Stdout = fdStdout + fdDep, err = os.OpenFile(fdDepPath, os.O_WRONLY|os.O_APPEND, os.FileMode(0666)) + if err != nil { + runErr.Err = err + errs <- runErr + return + } + cmd.ExtraFiles = append(cmd.ExtraFiles, fdDep) + cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%d", EnvDepFd, 3+fdNum)) + fdNum++ + cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", EnvDepCwd, cwd)) + defer func() { - jsRelease(shCtx) - lockRelease() + jsRelease(shCtx, jsToken) fdDep.Close() fdStdout.Close() if fdStderr != nil { fdStderr.Close() + logRecPath := path.Join(redoDir, tgt+LogSuffix+DepSuffix) + if fdStderr, err = os.OpenFile( + logRecPath, + os.O_WRONLY|os.O_CREATE, + os.FileMode(0666), + ); err == nil { + fdStderr.Truncate(0) + fields := []recfile.Field{ + {Name: "Build", Value: BuildUUID}, + {Name: "PID", Value: strconv.Itoa(cmd.Process.Pid)}, + {Name: "PPID", Value: strconv.Itoa(os.Getpid())}, + {Name: "Cwd", Value: cwd}, + } + ts := new(tai64n.TAI64N) + ts.FromTime(started) + fields = append(fields, + recfile.Field{Name: "Started", Value: tai64n.Encode(ts[:])}, + ) + ts.FromTime(finished) + fields = append(fields, + recfile.Field{Name: "Finished", Value: tai64n.Encode(ts[:])}) + fields = append(fields, recfile.Field{ + Name: "Duration", + Value: strconv.FormatInt(finished.Sub(started).Nanoseconds(), 10), + }) + fields = append(fields, recfile.Field{Name: "Cmd", Value: cmdName}) + for _, arg := range args { + fields = append(fields, recfile.Field{Name: "Cmd", Value: arg}) + } + for _, e := range cmd.Env { + fields = append(fields, recfile.Field{Name: "Env", Value: e}) + } + if exitErr != nil { + fields = append(fields, recfile.Field{ + Name: "ExitCode", + Value: strconv.Itoa(exitErr.ProcessState.ExitCode()), + }) + } + w := bufio.NewWriter(fdStderr) + _, err = recfile.NewWriter(w).WriteFields(fields...) + w.Flush() + fdStderr.Close() + if err != nil { + os.Remove(logRecPath) + } + } } + lockRelease() os.Remove(fdDep.Name()) os.Remove(fdStdout.Name()) os.Remove(tmpPath) @@ -413,9 +504,14 @@ func runScript(tgtOrig string, errs chan error, traced bool) error { } Jobs.Done() }() - started := time.Now() - runErr.Started = &started - err := cmd.Start() + stderr, err := cmd.StderrPipe() + if err != nil { + runErr.Err = err + errs <- runErr + return + } + started = time.Now() + err = cmd.Start() if err != nil { runErr.Err = err errs <- runErr @@ -437,9 +533,9 @@ func runScript(tgtOrig string, errs chan error, traced bool) error { continue } if fdStderr != nil { - tai64n.FromTime(time.Now(), ts) + ts.FromTime(time.Now()) LogMutex.Lock() - fmt.Fprintf(fdStderr, "%s %s\n", ts.Encode(), line) + fmt.Fprintln(fdStderr, tai64n.Encode(ts[:]), line) LogMutex.Unlock() } if StderrSilent { @@ -457,9 +553,19 @@ func runScript(tgtOrig string, errs chan error, traced bool) error { // Wait for job completion <-stderrTerm err = cmd.Wait() - finished := time.Now() + finished = time.Now() runErr.Finished = &finished if err != nil { + // Try to commit .rec anyway + if !NoSync { + fdDep.Sync() + } + os.Rename(fdDep.Name(), path.Join(redoDir, tgt+DepSuffix)) + if !NoSync { + err = syncDir(redoDir) + } + + exitErr = err.(*exec.ExitError) runErr.Err = err errs <- runErr return