]> Cypherpunks.ru repositories - goredo.git/blobdiff - run.go
Symlinks -cleanup friendliness
[goredo.git] / run.go
diff --git a/run.go b/run.go
index 0f7277bfd34313daf56c01e3b60ab4dee272f922..c85543a0f8d7b4897bfc1ee65f69ef3b87200c82 100644 (file)
--- a/run.go
+++ b/run.go
@@ -21,7 +21,6 @@ package main
 
 import (
        "bufio"
-       "encoding/hex"
        "errors"
        "flag"
        "fmt"
@@ -29,13 +28,13 @@ import (
        "os"
        "os/exec"
        "path"
-       "strconv"
        "strings"
        "sync"
        "syscall"
        "time"
 
        "go.cypherpunks.ru/recfile"
+       "go.cypherpunks.ru/tai64n"
        "golang.org/x/sys/unix"
 )
 
@@ -57,16 +56,16 @@ const (
 )
 
 var (
-       Trace        bool = false
        NoSync       bool = false
        StderrKeep   bool = false
        StderrSilent bool = false
        StderrPrefix string
        Jobs         sync.WaitGroup
 
-       flagTrace        = flag.Bool("x", false, "trace current target (sh -x) (set REDO_TRACE=1 for others too)")
-       flagStderrKeep   = flag.Bool("logs", false, "keep job's stderr (REDO_LOGS=1)")
-       flagStderrSilent = flag.Bool("silent", false, "do not print job's stderr (REDO_SILENT=1)")
+       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))
 )
 
 type RunErr struct {
@@ -103,16 +102,6 @@ func mkdirs(pth string) error {
        return os.MkdirAll(pth, os.FileMode(0777))
 }
 
-func tempsuffix() string {
-       return strconv.FormatInt((time.Now().UnixNano()+int64(os.Getpid()))&0xFFFFFFFF, 16)
-}
-
-func tempfile(dir, prefix string) (*os.File, error) {
-       // It respects umask, unlike ioutil.TempFile
-       name := path.Join(dir, TmpPrefix+prefix+"."+tempsuffix())
-       return os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, os.FileMode(0666))
-}
-
 func isModified(cwd, redoDir, tgt string) (bool, string, error) {
        fdDep, err := os.Open(path.Join(redoDir, tgt+DepSuffix))
        if err != nil {
@@ -165,7 +154,7 @@ func syncDir(dir string) error {
        return err
 }
 
-func runScript(tgtOrig string, errs chan error) error {
+func runScript(tgtOrig string, errs chan error, traced bool) error {
        cwd, tgt := cwdAndTgt(tgtOrig)
        redoDir := path.Join(cwd, RedoDir)
        if err := mkdirs(redoDir); err != nil {
@@ -194,14 +183,20 @@ func runScript(tgtOrig string, errs chan error) error {
                        fdLock.Close()
                        return TgtErr{tgtOrig, err}
                }
-               trace(CDebug, "waiting: %s", tgtOrig)
                Jobs.Add(1)
+               trace(CDebug, "waiting: %s", tgtOrig)
+               if FdStatus != nil {
+                       FdStatus.Write([]byte{StatusWait})
+               }
                go func() {
                        defer Jobs.Done()
                        trace(CLock, "LOCK_EX: %s", fdLock.Name())
                        unix.Flock(int(fdLock.Fd()), unix.LOCK_EX)
                        lockRelease()
                        trace(CDebug, "waiting done: %s", tgtOrig)
+                       if FdStatus != nil {
+                               FdStatus.Write([]byte{StatusWaited})
+                       }
                        var depInfo *DepInfo
                        fdDep, err := os.Open(path.Join(redoDir, tgt+DepSuffix))
                        if err != nil {
@@ -270,10 +265,6 @@ func runScript(tgtOrig string, errs chan error) error {
                cleanup()
                return TgtErr{tgtOrig, errors.New("no .do found")}
        }
-       if err = writeDep(fdDep, cwd, doFile); err != nil {
-               cleanup()
-               return TgtErr{tgtOrig, err}
-       }
 
        // Determine basename and DIRPREFIX
        ents := strings.Split(cwd, "/")
@@ -284,52 +275,32 @@ func runScript(tgtOrig string, errs chan error) error {
                cwd = path.Join(cwd, "..")
        }
        cwd = path.Clean(cwd)
+       doFilePath := path.Join(cwd, doFile)
        basename := tgt
        runErr := RunErr{Tgt: tgtOrig}
        if strings.HasPrefix(doFile, "default.") {
                basename = tgt[:len(tgt)-(len(doFile)-len("default.")-len(".do"))-1]
                runErr.DoFile = doFile
        }
+
+       if err = writeDep(fdDep, cwd, doFile); err != nil {
+               cleanup()
+               return TgtErr{tgtOrig, err}
+       }
        trace(CWait, "%s", runErr.Name())
-       doFile = path.Base(doFile)
 
        // Prepare command line
        var cmdName string
        var args []string
-       if err = unix.Access(path.Join(cwd, doFile), unix.X_OK); err == nil {
-               // Ordinary executable file
-               cmdName = doFile
+       if err = unix.Access(doFilePath, unix.X_OK); err == nil {
+               cmdName = doFilePath
                args = make([]string, 0, 3)
        } else {
-               fd, err := os.Open(path.Join(cwd, doFile))
-               if err != nil {
-                       cleanup()
-                       return TgtErr{tgtOrig, err}
-               }
-               buf := make([]byte, 512)
-               n, err := fd.Read(buf)
-               if err != nil {
-                       cleanup()
-                       return TgtErr{tgtOrig, err}
-               }
-               if n > 3 && string(buf[:3]) == "#!/" {
-                       // Shebanged
-                       t := string(buf[2:n])
-                       nlIdx := strings.Index(t, "\n")
-                       if nlIdx == -1 {
-                               cleanup()
-                               return TgtErr{tgtOrig, errors.New("not fully read shebang")}
-                       }
-                       args = strings.Split(t[:nlIdx], " ")
-                       cmdName, args = args[0], args[1:]
+               cmdName = "/bin/sh"
+               if traced {
+                       args = append(args, "-ex")
                } else {
-                       // Shell
-                       cmdName = "/bin/sh"
-                       if Trace {
-                               args = append(args, "-ex")
-                       } else {
-                               args = append(args, "-e")
-                       }
+                       args = append(args, "-e")
                }
                args = append(args, doFile)
        }
@@ -355,10 +326,19 @@ func runScript(tgtOrig string, errs chan error) error {
                "%s=%s", EnvStderrPrefix, childStderrPrefix,
        ))
 
-       cmd.ExtraFiles = append(cmd.ExtraFiles, fdDep)
        fdNum := 0
+       cmd.ExtraFiles = append(cmd.ExtraFiles, fdDep)
        cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%d", EnvDepFd, 3+fdNum))
        fdNum++
+
+       if FdStatus == nil {
+               cmd.Env = append(cmd.Env, fmt.Sprintf("%s=NO", EnvStatusFd))
+       } else {
+               cmd.ExtraFiles = append(cmd.ExtraFiles, FdStatus)
+               cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%d", EnvStatusFd, 3+fdNum))
+               fdNum++
+       }
+
        if JSR == nil {
                // infinite jobs
                cmd.Env = append(cmd.Env, fmt.Sprintf("%s=NO", EnvJSFd))
@@ -395,6 +375,9 @@ func runScript(tgtOrig string, errs chan error) error {
        Jobs.Add(1)
        go func() {
                jsAcquire(shCtx)
+               if FdStatus != nil {
+                       FdStatus.Write([]byte{StatusRun})
+               }
                defer func() {
                        jsRelease(shCtx)
                        lockRelease()
@@ -407,6 +390,9 @@ func runScript(tgtOrig string, errs chan error) error {
                        os.Remove(fdStdout.Name())
                        os.Remove(tmpPath)
                        os.Remove(fdLock.Name())
+                       if FdStatus != nil {
+                               FdStatus.Write([]byte{StatusDone})
+                       }
                        Jobs.Done()
                }()
                started := time.Now()
@@ -424,7 +410,7 @@ func runScript(tgtOrig string, errs chan error) error {
                go func() {
                        scanner := bufio.NewScanner(stderr)
                        var line string
-                       ts := new(TAI64N)
+                       ts := new(tai64n.TAI64N)
                        for scanner.Scan() {
                                line = scanner.Text()
                                if strings.HasPrefix(line, childStderrPrefix) {
@@ -433,8 +419,10 @@ func runScript(tgtOrig string, errs chan error) error {
                                        continue
                                }
                                if fdStderr != nil {
-                                       tai64nNow(ts)
-                                       fmt.Fprintf(fdStderr, "@%s %s\n", hex.EncodeToString(ts[:]), line)
+                                       tai64n.FromTime(time.Now(), ts)
+                                       LogMutex.Lock()
+                                       fmt.Fprintf(fdStderr, "%s %s\n", ts.Encode(), line)
+                                       LogMutex.Unlock()
                                }
                                if StderrSilent {
                                        continue