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())
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)
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
}
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
}()
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()
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
}
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
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
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:]
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))
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))
} 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
)
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()
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)
// 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
}
if tmpExists {
fd, err = os.Open(tmpPath)
if err != nil {
- errs <- errf(err)
- return
+ goto Finish
}
defer fd.Close()
} else if fiStdout.Size() > 0 {
// 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
}