|
@@ -12,7 +12,6 @@ import (
|
|
|
"fmt"
|
|
|
"io"
|
|
|
"os"
|
|
|
- "runtime/pprof"
|
|
|
|
|
|
"src.elv.sh/pkg/logutil"
|
|
|
)
|
|
@@ -22,52 +21,11 @@ import (
|
|
|
// 0.X.
|
|
|
var DeprecationLevel = 17
|
|
|
|
|
|
-// Flags keeps command-line flags.
|
|
|
-type Flags struct {
|
|
|
- Log, CPUProfile string
|
|
|
-
|
|
|
- Help, Version, BuildInfo, JSON bool
|
|
|
-
|
|
|
- CodeInArg, CompileOnly, NoRc bool
|
|
|
- RC string
|
|
|
-
|
|
|
- Daemon bool
|
|
|
- DB, Sock string
|
|
|
-
|
|
|
- LSP bool
|
|
|
-}
|
|
|
-
|
|
|
-func newFlagSet(f *Flags) *flag.FlagSet {
|
|
|
- fs := flag.NewFlagSet("elvish", flag.ContinueOnError)
|
|
|
- // Error and usage will be printed explicitly.
|
|
|
- fs.SetOutput(io.Discard)
|
|
|
-
|
|
|
- fs.StringVar(&f.Log, "log", "", "a file to write debug log to except for the daemon")
|
|
|
- fs.StringVar(&f.CPUProfile, "cpuprofile", "", "write cpu profile to file")
|
|
|
-
|
|
|
- fs.BoolVar(&f.Help, "help", false, "show usage help and quit")
|
|
|
- fs.BoolVar(&f.Version, "version", false, "show version and quit")
|
|
|
- fs.BoolVar(&f.BuildInfo, "buildinfo", false, "show build info and quit")
|
|
|
- fs.BoolVar(&f.JSON, "json", false, "show output in JSON. Useful with -buildinfo and -compileonly")
|
|
|
-
|
|
|
- // The `-i` option is for compatibility with POSIX shells so that programs, such as the `script`
|
|
|
- // command, will work when asked to launch an interactive Elvish shell.
|
|
|
- fs.Bool("i", false, "force interactive mode; currently ignored")
|
|
|
- fs.BoolVar(&f.CodeInArg, "c", false, "take first argument as code to execute")
|
|
|
- fs.BoolVar(&f.CompileOnly, "compileonly", false, "Parse/Compile but do not execute")
|
|
|
- fs.BoolVar(&f.NoRc, "norc", false, "run elvish without invoking rc.elv")
|
|
|
- fs.StringVar(&f.RC, "rc", "", "path to rc.elv")
|
|
|
-
|
|
|
- fs.BoolVar(&f.Daemon, "daemon", false, "[internal flag] run the storage daemon instead of shell")
|
|
|
-
|
|
|
- fs.StringVar(&f.DB, "db", "", "[internal flag] path to the database")
|
|
|
- fs.StringVar(&f.Sock, "sock", "", "[internal flag] path to the daemon socket")
|
|
|
-
|
|
|
- fs.BoolVar(&f.LSP, "lsp", false, "run LSP server instead of shell")
|
|
|
-
|
|
|
- fs.IntVar(&DeprecationLevel, "deprecation-level", DeprecationLevel, "show warnings for all features deprecated as of version 0.X")
|
|
|
-
|
|
|
- return fs
|
|
|
+// Program represents a subprogram.
|
|
|
+type Program interface {
|
|
|
+ RegisterFlags(fs *FlagSet)
|
|
|
+ // Run runs the subprogram.
|
|
|
+ Run(fds [3]*os.File, args []string) error
|
|
|
}
|
|
|
|
|
|
func usage(out io.Writer, fs *flag.FlagSet) {
|
|
@@ -80,8 +38,18 @@ func usage(out io.Writer, fs *flag.FlagSet) {
|
|
|
// Run parses command-line flags and runs the first applicable subprogram. It
|
|
|
// returns the exit status of the program.
|
|
|
func Run(fds [3]*os.File, args []string, p Program) int {
|
|
|
- f := &Flags{}
|
|
|
- fs := newFlagSet(f)
|
|
|
+ fs := flag.NewFlagSet("elvish", flag.ContinueOnError)
|
|
|
+ // Error and usage will be printed explicitly.
|
|
|
+ fs.SetOutput(io.Discard)
|
|
|
+
|
|
|
+ var log string
|
|
|
+ var help bool
|
|
|
+ fs.StringVar(&log, "log", "", "a file to write debug log to except for the daemon")
|
|
|
+ fs.BoolVar(&help, "help", false, "show usage help and quit")
|
|
|
+ fs.IntVar(&DeprecationLevel, "deprecation-level", DeprecationLevel, "show warnings for all features deprecated as of version 0.X")
|
|
|
+
|
|
|
+ p.RegisterFlags(&FlagSet{FlagSet: fs})
|
|
|
+
|
|
|
err := fs.Parse(args[1:])
|
|
|
if err != nil {
|
|
|
if err == flag.ErrHelp {
|
|
@@ -97,38 +65,25 @@ func Run(fds [3]*os.File, args []string, p Program) int {
|
|
|
return 2
|
|
|
}
|
|
|
|
|
|
- // Handle flags common to all subprograms.
|
|
|
- if f.CPUProfile != "" {
|
|
|
- f, err := os.Create(f.CPUProfile)
|
|
|
- if err != nil {
|
|
|
- fmt.Fprintln(fds[2], "Warning: cannot create CPU profile:", err)
|
|
|
- fmt.Fprintln(fds[2], "Continuing without CPU profiling.")
|
|
|
- } else {
|
|
|
- pprof.StartCPUProfile(f)
|
|
|
- defer pprof.StopCPUProfile()
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if f.Daemon {
|
|
|
- // We expect our stdout file handle is open on a unique log file for the daemon to write its
|
|
|
- // log messages. See daemon.Spawn() in pkg/daemon.
|
|
|
- logutil.SetOutput(fds[1])
|
|
|
- } else if f.Log != "" {
|
|
|
- err = logutil.SetOutputFile(f.Log)
|
|
|
+ if log != "" {
|
|
|
+ err = logutil.SetOutputFile(log)
|
|
|
if err != nil {
|
|
|
fmt.Fprintln(fds[2], err)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if f.Help {
|
|
|
+ if help {
|
|
|
usage(fds[1], fs)
|
|
|
return 0
|
|
|
}
|
|
|
|
|
|
- err = p.Run(fds, f, fs.Args())
|
|
|
+ err = p.Run(fds, fs.Args())
|
|
|
if err == nil {
|
|
|
return 0
|
|
|
}
|
|
|
+ if err == ErrNextProgram {
|
|
|
+ err = errNoSuitableSubprogram
|
|
|
+ }
|
|
|
if msg := err.Error(); msg != "" {
|
|
|
fmt.Fprintln(fds[2], msg)
|
|
|
}
|
|
@@ -144,26 +99,34 @@ func Run(fds [3]*os.File, args []string, p Program) int {
|
|
|
// Composite returns a Program that tries each of the given programs,
|
|
|
// terminating at the first one that doesn't return NotSuitable().
|
|
|
func Composite(programs ...Program) Program {
|
|
|
- return compositeProgram(programs)
|
|
|
+ return composite(programs)
|
|
|
}
|
|
|
|
|
|
-type compositeProgram []Program
|
|
|
+type composite []Program
|
|
|
+
|
|
|
+func (cp composite) RegisterFlags(f *FlagSet) {
|
|
|
+ for _, p := range cp {
|
|
|
+ p.RegisterFlags(f)
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
-func (cp compositeProgram) Run(fds [3]*os.File, f *Flags, args []string) error {
|
|
|
+func (cp composite) Run(fds [3]*os.File, args []string) error {
|
|
|
for _, p := range cp {
|
|
|
- err := p.Run(fds, f, args)
|
|
|
- if err != ErrNotSuitable {
|
|
|
+ err := p.Run(fds, args)
|
|
|
+ if err != ErrNextProgram {
|
|
|
return err
|
|
|
}
|
|
|
}
|
|
|
- // If we have reached here, all subprograms have returned errNotSuitable
|
|
|
- return ErrNotSuitable
|
|
|
+ // If we have reached here, all subprograms have returned ErrNextProgram
|
|
|
+ return ErrNextProgram
|
|
|
}
|
|
|
|
|
|
-// ErrNotSuitable is a special error that may be returned by Program.Run, to
|
|
|
-// signify that this Program should not be run. It is useful when a Program is
|
|
|
-// used in Composite.
|
|
|
-var ErrNotSuitable = errors.New("internal error: no suitable subprogram")
|
|
|
+var errNoSuitableSubprogram = errors.New("internal error: no suitable subprogram")
|
|
|
+
|
|
|
+// ErrNextProgram is a special error that may be returned by Program.Run that
|
|
|
+// is part of a Composite program, indicating that the next program should be
|
|
|
+// tried.
|
|
|
+var ErrNextProgram = errors.New("next program")
|
|
|
|
|
|
// BadUsage returns a special error that may be returned by Program.Run. It
|
|
|
// causes the main function to print out a message, the usage information and
|
|
@@ -187,9 +150,3 @@ func Exit(exit int) error {
|
|
|
type exitError struct{ exit int }
|
|
|
|
|
|
func (e exitError) Error() string { return "" }
|
|
|
-
|
|
|
-// Program represents a subprogram.
|
|
|
-type Program interface {
|
|
|
- // Run runs the subprogram.
|
|
|
- Run(fds [3]*os.File, f *Flags, args []string) error
|
|
|
-}
|