CleanupTmp = "tmp"
)
-var (
- DryRun = flag.Bool("dry-run", false, "do no delete files during cleanup, just show them")
-)
+var DryRun *bool
+
+func init() {
+ if CmdName() != CmdNameRedoCleanup {
+ return
+ }
+ DryRun = flag.Bool("n", false, "do no delete files during cleanup, just show them")
+}
func redoDirClean(root, what string) error {
root, err := filepath.Abs(root)
infinite number of workers (@code{=0}). Also you can set
@env{$REDO_JOBS} to automatically apply that setting globally.
-With @option{-logs} (@env{$REDO_LOGS=1}) option you can capture job's
+With @option{-k} (@env{$REDO_LOGS=1}) option you can capture job's
@code{stderr} on the disk and read it later with @command{redo-log}
command. Log's lines have @url{http://cr.yp.to/libtai/tai64.html,
TAI64N} timestamp. You can decode it with @command{tai64nlocal} utility
from @url{http://cr.yp.to/daemontools.html, daemontools}, or similar
one: @code{go get go.cypherpunks.ru/tai64n/cmd/tai64nlocal}.
-@option{-silent} (@env{$REDO_SILENT=1}) omits @code{stderr} printing at
-all, but you can still capture it with @option{-logs}.
+@option{-s} (@env{$REDO_SILENT=1}) omits @code{stderr} printing at
+all, but you can still capture it with @option{-k}.
@option{-log-pid} (@env{$REDO_LOG_PID=1}) can be used to prefix job's
@code{stderr} with the PID, that could be useful during parallel builds.
@node News
@unnumbered News
+@anchor{Release 1.9.0}
+@section Release 1.9.0
+@itemize
+@item
+ Do not enable command lines options not applicable to the command.
+ For example @option{-j} option is only applicable to @command{redo}
+ and @command{redo-ifchange} commands.
+@item
+ @itemize
+ @item @option{-dry-run} option is renamed to @option{-n}
+ @item @option{-logs} option is renamed to @option{-k}
+ @item @option{-silent} option is renamed to @option{-s}
+ @end itemize
+@end itemize
+
@anchor{Release 1.8.0}
@section Release 1.8.0
@itemize
jsTokens map[byte]int
jsTokensM sync.Mutex
- flagJobs = flag.Int("j", -1, fmt.Sprintf("number of parallel jobs (0=inf, <0=1) (%s)", EnvJobs))
+ flagJobs *int
)
+func init() {
+ cmdName := CmdName()
+ if !(cmdName == CmdNameRedo || cmdName == CmdNameRedoIfchange) {
+ return
+ }
+ flagJobs = flag.Int("j", -1,
+ fmt.Sprintf("number of parallel jobs (0=inf, <0=1) (%s)", EnvJobs))
+}
+
func jsStart(jobsEnv string) {
jobs := uint64(1)
var err error
CReset string
CNone string = "NONE"
- flagNoProgress = flag.Bool("no-progress", false, fmt.Sprintf("no progress printing (%s=1), also implies -no-status", EnvNoProgress))
flagDebug = flag.Bool("d", false, fmt.Sprintf("enable debug logging (%s=1)", EnvDebug))
- flagLogWait = flag.Bool("log-wait", false, fmt.Sprintf("enable wait messages logging (%s=1)", EnvLogWait))
- flagLogLock = flag.Bool("log-lock", false, fmt.Sprintf("enable lock messages logging (%s=1)", EnvLogLock))
- flagLogPid = flag.Bool("log-pid", false, fmt.Sprintf("append PIDs (%s=1)", EnvLogPid))
- flagLogJS = flag.Bool("log-js", false, fmt.Sprintf("enable jobserver messages logging (%s=1)", EnvLogJS))
+ flagNoProgress *bool
+ flagLogWait *bool
+ flagLogLock *bool
+ flagLogPid *bool
+ flagLogJS *bool
LogMutex sync.Mutex
KeyEraseLine string
CJS = string(t.Escape.White)
CReset = string(t.Escape.Reset)
KeyEraseLine = fmt.Sprintf("%s[K", CReset[0:1])
+
+ cmdName := CmdName()
+ if !(cmdName == CmdNameRedo || cmdName == CmdNameRedoIfchange) {
+ return
+ }
+ flagNoProgress = flag.Bool("no-progress", false,
+ fmt.Sprintf("no progress printing (%s=1), also implies -no-status", EnvNoProgress))
+ flagLogWait = flag.Bool("log-wait", false,
+ fmt.Sprintf("enable wait messages logging (%s=1)", EnvLogWait))
+ flagLogLock = flag.Bool("log-lock", false,
+ fmt.Sprintf("enable lock messages logging (%s=1)", EnvLogLock))
+ flagLogPid = flag.Bool("log-pid", false,
+ fmt.Sprintf("append PIDs (%s=1)", EnvLogPid))
+ flagLogJS = flag.Bool("log-js", false,
+ fmt.Sprintf("enable jobserver messages logging (%s=1)", EnvLogJS))
}
func erasedStatus(s, end string) string {
"golang.org/x/sys/unix"
)
+const (
+ CmdNameGoredo = "goredo"
+ CmdNameRedo = "redo"
+ CmdNameRedoAffects = "redo-affects"
+ CmdNameRedoAlways = "redo-always"
+ CmdNameRedoCleanup = "redo-cleanup"
+ CmdNameRedoDot = "redo-dot"
+ CmdNameRedoIfchange = "redo-ifchange"
+ CmdNameRedoIfcreate = "redo-ifcreate"
+ CmdNameRedoLog = "redo-log"
+ CmdNameRedoOOD = "redo-ood"
+ CmdNameRedoSources = "redo-sources"
+ CmdNameRedoStamp = "redo-stamp"
+ CmdNameRedoTargets = "redo-targets"
+ CmdNameRedoWhichdo = "redo-whichdo"
+)
+
var (
Cwd string
BuildUUID string
return fd
}
+func CmdName() string {
+ return path.Base(os.Args[0])
+}
+
func main() {
version := flag.Bool("version", false, "print version")
warranty := flag.Bool("warranty", false, "print warranty information")
- symlinks := flag.Bool("symlinks", false, "create necessary symlinks in current directory")
+
+ var symlinks *bool
+ cmdName := CmdName()
+ if cmdName == "goredo" {
+ symlinks = flag.Bool("symlinks", false, "create necessary symlinks in current directory")
+ }
flag.Usage = func() { usage(os.Args[0]) }
flag.Parse()
fmt.Println("goredo", Version, "built with", runtime.Version())
return
}
- if *symlinks {
+ if cmdName == CmdNameGoredo && *symlinks {
rc := 0
for _, cmdName := range []string{
- "redo",
- "redo-affects",
- "redo-always",
- "redo-cleanup",
- "redo-dot",
- "redo-ifchange",
- "redo-ifcreate",
- "redo-log",
- "redo-ood",
- "redo-sources",
- "redo-stamp",
- "redo-targets",
- "redo-whichdo",
+ CmdNameRedo,
+ CmdNameRedoAffects,
+ CmdNameRedoAlways,
+ CmdNameRedoCleanup,
+ CmdNameRedoDot,
+ CmdNameRedoIfchange,
+ CmdNameRedoIfcreate,
+ CmdNameRedoLog,
+ CmdNameRedoOOD,
+ CmdNameRedoSources,
+ CmdNameRedoStamp,
+ CmdNameRedoTargets,
+ CmdNameRedoWhichdo,
} {
fmt.Println(os.Args[0], "<-", cmdName)
if err := os.Symlink(os.Args[0], cmdName); err != nil {
DirPrefix = os.Getenv(EnvDirPrefix)
DepCwd = os.Getenv(EnvDepCwd)
- if *flagStderrKeep {
+ if flagStderrKeep != nil && *flagStderrKeep {
mustSetenv(EnvStderrKeep, "1")
}
- if *flagStderrSilent {
+ if flagStderrSilent != nil && *flagStderrSilent {
mustSetenv(EnvStderrSilent, "1")
}
- if *flagNoProgress {
+ if flagNoProgress != nil && *flagNoProgress {
mustSetenv(EnvNoProgress, "1")
}
- if *flagDebug {
+ if flagDebug != nil && *flagDebug {
mustSetenv(EnvDebug, "1")
}
- if *flagLogWait {
+ if flagLogWait != nil && *flagLogWait {
mustSetenv(EnvLogWait, "1")
}
- if *flagLogLock {
+ if flagLogLock != nil && *flagLogLock {
mustSetenv(EnvLogLock, "1")
}
- if *flagLogPid {
+ if flagLogPid != nil && *flagLogPid {
mustSetenv(EnvLogPid, "1")
}
- if *flagLogJS {
+ if flagLogJS != nil && *flagLogJS {
mustSetenv(EnvLogJS, "1")
}
StderrKeep = os.Getenv(EnvStderrKeep) == "1"
MyPid = os.Getpid()
}
var traced bool
- if *flagTraceAll {
+ if flagTraceAll != nil && *flagTraceAll {
mustSetenv(EnvTrace, "1")
}
if os.Getenv(EnvTrace) == "1" {
TracedAll = true
traced = true
- } else {
+ } else if flagTrace != nil {
traced = *flagTrace
}
}
}
- statusInit()
+ if cmdName == CmdNameRedo || cmdName == CmdNameRedoIfchange {
+ statusInit()
+ }
for i, tgt := range tgts {
if path.IsAbs(tgt) {
ok := true
err = nil
- cmdName := path.Base(os.Args[0])
trace(
CDebug, "[%s] run: %s %s cwd:%s dirprefix:%s",
BuildUUID, cmdName, tgts, Cwd, DirPrefix,
CmdSwitch:
switch cmdName {
- case "redo":
+ case CmdNameRedo:
for _, tgt := range tgts {
ok, err = ifchange([]string{tgt}, true, traced)
if err != nil || !ok {
break
}
}
- case "redo-ifchange":
+ case CmdNameRedoIfchange:
ok, err = ifchange(tgts, false, traced)
if err == nil {
err = writeDeps(fdDep, tgts)
}
- case "redo-ifcreate":
+ case CmdNameRedoIfcreate:
if fdDep == nil {
log.Fatalln("no", EnvDepFd)
}
break
}
}
- case "redo-always":
+ case CmdNameRedoAlways:
if fdDep == nil {
log.Fatalln("no", EnvDepFd)
}
err = always(fdDep)
- case "redo-cleanup":
+ case CmdNameRedoCleanup:
for _, what := range tgts {
err = cleanupWalker(Cwd, what)
if err != nil {
break
}
}
- case "redo-dot":
+ case CmdNameRedoDot:
err = dotPrint(tgts)
- case "redo-stamp":
+ case CmdNameRedoStamp:
if fdDep == nil {
log.Fatalln("no", EnvDepFd)
}
err = stamp(fdDep, os.Stdin)
- case "redo-log":
+ case CmdNameRedoLog:
if len(tgts) != 1 {
log.Fatalln("single target expected")
}
break
}
_, err = io.Copy(os.Stdout, fd)
- case "redo-whichdo":
+ case CmdNameRedoWhichdo:
if len(tgts) != 1 {
log.Fatalln("single target expected")
}
}
fmt.Println(rel)
}
- case "redo-targets":
+ case CmdNameRedoTargets:
if tgtsWasEmpty {
tgts = []string{Cwd}
}
for _, tgt := range tgts {
fmt.Println(tgt)
}
- case "redo-affects":
+ case CmdNameRedoAffects:
if tgtsWasEmpty {
log.Fatalln("no targets specified")
}
for _, dep := range tgts {
fmt.Println(dep)
}
- case "redo-ood":
+ case CmdNameRedoOOD:
if tgtsWasEmpty {
tgts, err = targetsWalker([]string{Cwd})
if err != nil {
fmt.Println(tgt)
}
}
- case "redo-sources":
+ case CmdNameRedoSources:
if tgtsWasEmpty {
tgts, err = targetsWalker([]string{Cwd})
if err != nil {
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
var (
FdStatus *os.File
- flagNoStatus = flag.Bool("no-status", false, "disable statusline (REDO_NO_STATUS=1)")
+ flagNoStatus *bool
)
+func init() {
+ cmdName := CmdName()
+ if !(cmdName == CmdNameRedo || cmdName == CmdNameRedoIfchange) {
+ return
+ }
+ flagNoStatus = flag.Bool("no-status", false, "disable statusline (REDO_NO_STATUS=1)")
+}
+
func statusInit() {
if NoProgress || *flagNoStatus {
return
)
const (
- Version = "1.8.0"
+ Version = "1.9.0"
Warranty = `Copyright (C) 2020-2021 Sergey Matveev
This program is free software: you can redistribute it and/or modify
func usage(cmd string) {
var d string
switch cmd {
- case "redo":
- d = `Usage: redo [options] [target ...]
+ case CmdNameRedo:
+ d = `Usage: redo [-j X] [-k] [-s] [-x|-xx] [options] [target ...]
Forcefully and *sequentially* build specified targets.
If no targets specified, then use "all" one.`
- case "redo-ifchange":
- d = `Usage: redo-ifchange target [...]
+ case CmdNameRedoIfchange:
+ d = `Usage: redo-ifchange [-j X] [-k] [-s] [-x|-xx] target [...]
Build specified targets in parallel, if they are changed.
Record them as dependencies for current target.`
- case "redo-ifcreate":
+ case CmdNameRedoIfcreate:
d = `Usage: redo-ifcreate target [...]
Record ifcreate dependency for current target. Unusable outside .do.`
- case "redo-always":
+ case CmdNameRedoAlways:
d = `Usage: redo-always
Always build current target. Unusable outside .do.`
- case "redo-cleanup":
- d = `Usage: redo-cleanup [-dry-run] {full,log,tmp} [...]
+ case CmdNameRedoCleanup:
+ d = `Usage: redo-cleanup [-n] {full,log,tmp} [...]
Remove either all goredo's related temporary files, or kept stderr
logs, or everything (including .redo directories) related.`
- case "redo-log":
+ case CmdNameRedoLog:
d = `Usage: redo-log target [ | tai64nlocal ]
Display kept target's stderr with TAI64N timestamped lines. Only the
last build is kept. You must enable stderr keeping with either -logs,
or REDO_LOGS=1.`
- case "redo-dot":
+ case CmdNameRedoDot:
d = `Usage: redo-dot target [...]
Write dependency DOT graph to stdout.`
- case "redo-stamp":
+ case CmdNameRedoStamp:
d = `Usage: redo-stamp < [$3]
Record stamp dependency for current target. Unusable outside .do.
Stamp dependency does not play any role, as all targets are hashed
anyway.`
- case "redo-whichdo":
+ case CmdNameRedoWhichdo:
d = `Usage: redo-whichdo target
Display .do search paths for specified target. Exits successfully
if the last .do in output if the found existing one.`
- case "redo-targets":
+ case CmdNameRedoTargets:
d = `Usage: redo-targets [target ...]
List all currently known targets.`
- case "redo-sources":
+ case CmdNameRedoSources:
d = `Usage: redo-sources [target ...]
List all currently known source files.`
- case "redo-ood":
+ case CmdNameRedoOOD:
d = `Usage: redo-ood [target ...]
List all currently known out-of-date targets.`
- case "redo-affects":
+ case CmdNameRedoAffects:
d = `Usage: redo-affects target [...]
List all targets that will be affected by changing the specified ones.`
fmt.Fprintln(os.Stderr, `
Additional environment variables:
NO_COLOR -- disable messages colouring
- REDO_NO_SYNC -- disable files/directories explicit filesystem syncing
REDO_TOP_DIR -- do not search for .do above that directory
- (it can contain .redo/top as an alternative)
+ (it can contain .redo/top as an alternative)`)
+ if cmd == CmdNameRedo || cmd == CmdNameRedoIfchange {
+ fmt.Fprintln(os.Stderr, `
+ REDO_NO_SYNC -- disable files/directories explicit filesystem syncing
REDO_INODE_NO_TRUST -- do not trust inode information (except for size)
and always check file's hash
REDO_MAKE -- bmake/gmake/none(default) jobserver protocol compatibility`)
+ }
}