summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--flags/flags.go1
-rw-r--r--main.go90
-rw-r--r--modes.go31
3 files changed, 90 insertions, 32 deletions
diff --git a/flags/flags.go b/flags/flags.go
index 7138522..f349ce7 100644
--- a/flags/flags.go
+++ b/flags/flags.go
@@ -3,6 +3,7 @@ package flags
import "github.com/spf13/pflag"
var (
+ Profile = pflag.Bool("profile", false, "enable profiling")
// Here makes pik run the target at the current location instead of in its source directory
Here = pflag.BoolP("here", "h", false, "run target in current directory instead of source location")
// At makes pik run in an arbitrary location
diff --git a/main.go b/main.go
index 7de29e8..843be56 100644
--- a/main.go
+++ b/main.go
@@ -2,6 +2,7 @@ package main
import (
_ "embed"
+ "errors"
"fmt"
"github.com/ewy1/pik/cache"
"github.com/ewy1/pik/crawl"
@@ -22,6 +23,7 @@ import (
"github.com/ewy1/pik/spool"
"github.com/spf13/pflag"
"os"
+ "runtime/pprof"
)
// syncInitializers are ran before the initializers.
@@ -75,11 +77,43 @@ var SourcesWithoutResults *cache.Cache
var version string
func main() {
+ result := pik()
+ if profileFd != nil {
+ pprof.StopCPUProfile()
+ err := profileFd.Close()
+ if err != nil {
+ _, _ = spool.Warn("%v\n", err)
+ }
+ }
+ if result != 0 {
+ os.Exit(result)
+ }
+}
+
+func mode[T any](list ModeMap[T], fire func(mode T) error) *int {
+ err := list.Traverse(func(in T) error {
+ return fire(in)
+ })
+ if errors.Is(err, Success) {
+ zero := 0
+ return &zero
+ } else if err != nil {
+ _, _ = spool.Warn("%v\n", err)
+ one := 1
+ return &one
+ }
+ return nil
+}
+
+func pik() int {
pflag.Parse()
- statelessModes.Traverse(func(in func() error) error {
- return in()
+ code := mode(statelessModes, func(mode func() error) error {
+ return mode()
})
+ if code != nil {
+ return *code
+ }
syncInitializers.RunSync(func(initializer model.Initializer) error {
return initializer.Init()
@@ -92,19 +126,19 @@ func main() {
here, err := os.Getwd()
if err != nil {
_, _ = spool.Warn("%v\n", err)
- os.Exit(1)
+ return 1
}
locs := crawl.RichLocations(here)
last := locs[len(locs)-1]
root, err := os.OpenRoot(last)
if root == nil {
_, _ = spool.Warn("%v\n", err)
- os.Exit(1)
+ return 1
}
fs := root.FS()
if err != nil {
_, _ = spool.Warn("%v\n", err)
- os.Exit(1)
+ return 1
}
var st *model.State
var stateErrors []error
@@ -112,15 +146,18 @@ func main() {
var c *cache.Cache
if !*flags.All {
st, stateErrors = model.NewState(fs, locs, indexers, runners)
- err = cache.Insert(st)
- if err != nil {
- spool.Warn("%v\n", err)
- }
+ go func() {
+ err := cache.Insert(st)
+ if err != nil {
+ spool.Warn("%v\n", err)
+ }
+
+ }()
} else {
c, err = cache.LoadFile(fs, cache.Path[1:])
if err != nil {
_, _ = spool.Warn("%v\n", err)
- os.Exit(1)
+ return 1
}
st, stateErrors = cache.LoadState(fs, c, indexers, runners)
}
@@ -128,9 +165,12 @@ func main() {
_, _ = spool.Warn("%v\n", stateErrors)
}
- statefulModes.Traverse(func(in func(st *model.State) error) error {
- return in(st)
+ code = mode(statefulModes, func(mode func(st *model.State) error) error {
+ return mode(st)
})
+ if code != nil {
+ return *code
+ }
args := pflag.Args()
@@ -141,7 +181,7 @@ func main() {
md, err := menu.Show(st, hydrators)
if err != nil {
_, _ = spool.Warn("%v\n", err)
- os.Exit(1)
+ return 1
}
cancelled = md.Cancel
source, target := md.Result()
@@ -166,43 +206,45 @@ func main() {
ForceConfirm = true
if err != nil {
_, _ = spool.Warn("%v\n", err)
- os.Exit(1)
+ return 1
}
SourcesWithoutResults = c
- main()
- return
+ return pik()
}
if cancelled {
_, _ = spool.Warn("no target selected\n")
- os.Exit(0)
- return
+ return 0
}
if result.Target == nil {
_, _ = spool.Warn("target not found\n")
- os.Exit(1)
- return
+ return 1
}
if result.NeedsConfirmation || ForceConfirm {
_, _ = fmt.Fprintf(os.Stderr, "this target is out of tree.\n")
if !menu.Confirm(os.Stdin, result.Source, result.Target, args...) {
- os.Exit(0)
+ return 0
}
}
if result.Overridden {
_, _ = fmt.Fprintln(os.Stderr, menu.OverrideWarning(result.Target))
}
- selectionModes.Traverse(func(in func(st *model.State, src *model.Source, t model.Target) error) error {
- return in(st, result.Source, result.Target)
+ code = mode(selectionModes, func(mode func(st *model.State, src *model.Source, t model.Target) error) error {
+ return mode(st, result.Source, result.Target)
})
+ if code != nil {
+ return *code
+ }
err = run.Run(result.Source, result.Target, result.Args...)
if err != nil {
_, _ = spool.Warn("%v\n", err)
- os.Exit(1)
+ return 1
}
+
+ return 0
}
diff --git a/modes.go b/modes.go
index 43652b4..5650f5c 100644
--- a/modes.go
+++ b/modes.go
@@ -9,6 +9,8 @@ import (
"github.com/ewy1/pik/run"
"github.com/ewy1/pik/spool"
"os"
+ "runtime"
+ "runtime/pprof"
)
// ModeMap maps flags to specific operation modes
@@ -16,14 +18,14 @@ type ModeMap[T any] map[*bool]T
// Continue can be returned as an error to continue program flow
var Continue = errors.New("not an error; continue flow")
+var Success = errors.New("not an error; finished operations")
// Traverse checks the entries of the map. If any flags are set on,
-// run that mode. If Continue is returned, it's non-exclusive. Otherwise,
+// pik that mode. If Continue is returned, it's non-exclusive. Otherwise,
// we quit after one mode.
//
// `then` should simply be the method call (necessary due to generics)
-// no additional error handling is required
-func (m ModeMap[T]) Traverse(then func(in T) error) {
+func (m ModeMap[T]) Traverse(then func(in T) error) error {
for enabled, mode := range m {
if !*enabled {
continue
@@ -32,14 +34,15 @@ func (m ModeMap[T]) Traverse(then func(in T) error) {
if errors.Is(err, Continue) {
continue
} else if err != nil {
- _, _ = spool.Warn("%v\n", err)
- os.Exit(1)
- } else {
- os.Exit(0)
+ return err
}
+ return Success
}
+ return nil
}
+var profileFd *os.File
+
// statelessModes are program modes which do not require state to operate.
// like --version and --completion
var statelessModes = ModeMap[func() error]{
@@ -49,7 +52,19 @@ var statelessModes = ModeMap[func() error]{
},
flags.Completion: func() error {
return completion.Echo()
-
+ },
+ flags.Profile: func() error {
+ fd, err := os.Create("pik-profile.out")
+ if err != nil {
+ return err
+ }
+ runtime.SetCPUProfileRate(1000)
+ err = pprof.StartCPUProfile(profileFd)
+ if err != nil {
+ return err
+ }
+ profileFd = fd
+ return Continue
},
}