X-Git-Url: http://www.git.cypherpunks.ru/?a=blobdiff_plain;f=run.go;h=ca8eebbcd9ca542dd7c9f31e77dfaca18eb4b7de;hb=3d2efdb8e781a148ab3633405534534dfd8c7545;hp=4ca90d649af939e151326ea2636f65cd59e6f3bc;hpb=ce60c3b66ff2d3f14392cac1eb3ec6dfc33fc893;p=goredo.git diff --git a/run.go b/run.go index 4ca90d6..ca8eebb 100644 --- a/run.go +++ b/run.go @@ -25,10 +25,12 @@ import ( "flag" "fmt" "io" + "log" "os" "os/exec" "path" "path/filepath" + "strconv" "strings" "sync" "syscall" @@ -42,6 +44,7 @@ import ( const ( EnvDepFd = "REDO_DEP_FD" EnvDirPrefix = "REDO_DIRPREFIX" + EnvDepCwd = "REDO_DEP_CWD" EnvBuildUUID = "REDO_BUILD_UUID" EnvStderrPrefix = "REDO_STDERR_PREFIX" EnvTrace = "REDO_TRACE" @@ -49,11 +52,12 @@ const ( EnvStderrSilent = "REDO_SILENT" EnvNoSync = "REDO_NO_SYNC" - RedoDir = ".redo" - LockSuffix = ".lock" - DepSuffix = ".rec" - TmpPrefix = ".redo." - LogSuffix = ".log" + RedoDir = ".redo" + LockSuffix = ".lock" + DepSuffix = ".rec" + TmpPrefix = ".redo." + LogSuffix = ".log" + LogRecSuffix = ".log-rec" ) var ( @@ -63,14 +67,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 @@ -362,31 +380,18 @@ 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 var fdStderr *os.File if StderrKeep { fdStderr, err = os.OpenFile( path.Join(redoDir, tgt+LogSuffix), - os.O_WRONLY|os.O_CREATE, + os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.FileMode(0666), ) if err != nil { cleanup() return TgtErr{tgtOrig, err} } - fdStderr.Truncate(0) } shCtx := fmt.Sprintf( "sh: %s: %s %s cwd:%s dirprefix:%s", @@ -396,11 +401,27 @@ 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)) @@ -419,15 +440,85 @@ func runScript(tgtOrig string, errs chan error, traced bool) error { 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+LogRecSuffix) + if fdStderr, err = os.OpenFile( + logRecPath, + os.O_WRONLY|os.O_CREATE|os.O_TRUNC, + os.FileMode(0666), + ); err == nil { + 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 _, env := range cmd.Env { + fields = append(fields, recfile.Field{Name: "Env", Value: env}) + } + if exitErr != nil { + fields = append(fields, recfile.Field{ + Name: "ExitCode", + Value: strconv.Itoa(exitErr.ProcessState.ExitCode()), + }) + } + w := bufio.NewWriter(fdStderr) + + var depInfo *DepInfo + fdDep, err := os.Open(fdDepPath) + if err != nil { + goto Err + } + depInfo, err = depRead(fdDep) + fdDep.Close() + if err != nil { + goto Err + } + for _, dep := range depInfo.ifchanges { + fields = append(fields, recfile.Field{ + Name: "Ifchange", + Value: dep["Target"], + }) + } + _, err = recfile.NewWriter(w).WriteFields(fields...) + if err != nil { + goto Err + } + err = w.Flush() + Err: + if err != nil { + log.Println(err) + os.Remove(logRecPath) + } + fdStderr.Close() + } else { + log.Println("can not open", logRecPath, ":", err) + } } + lockRelease() os.Remove(fdDep.Name()) os.Remove(fdStdout.Name()) os.Remove(tmpPath) @@ -486,9 +577,10 @@ 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 { + exitErr = err.(*exec.ExitError) runErr.Err = err errs <- runErr return @@ -578,7 +670,8 @@ func runScript(tgtOrig string, errs chan error, traced bool) error { goto Finish } } - err = os.Rename(fdDep.Name(), path.Join(redoDir, tgt+DepSuffix)) + fdDepPath = path.Join(redoDir, tgt+DepSuffix) + err = os.Rename(fdDep.Name(), fdDepPath) if err != nil { goto Finish }