Skip to content

Commit 4ad5b3c

Browse files
authored
Feat/optimize rebuilds (#1)
* prevent rebuild if nothing has changed * clean up flags and remove extra matrfile parsing * allow forced rebuilds and ignore cache
1 parent 7233137 commit 4ad5b3c

File tree

4 files changed

+106
-102
lines changed

4 files changed

+106
-102
lines changed

Matrfile.go

+6
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,9 @@ func Test(ctx context.Context, args []string) error {
1919

2020
return nil
2121
}
22+
23+
// Run is the primary entrypoint to matrs cli tool.
24+
func Run(ctx context.Context, args []string) error {
25+
fmt.Println("Running matr")
26+
return nil
27+
}

matr/discovery.go

+88-31
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package matr
22

33
import (
44
"bytes"
5+
"crypto/sha256"
56
"errors"
67
"flag"
78
"fmt"
@@ -22,51 +23,66 @@ var (
2223
matrFilePath string
2324
helpFlag bool
2425
versionFlag bool
26+
cleanFlag bool
27+
noCacheFlag bool
2528
)
2629

2730
// Run is the primary entrypoint to matrs cli tool.
2831
// This is where the matrfile path is resolved, compiled and executed
2932
func Run() {
3033
// TODO: clean up this shit show
31-
flag.StringVar(&matrFilePath, "matrfile", "./", "path to Matrfile")
32-
flag.BoolVar(&helpFlag, "h", false, "Display usage info")
33-
flag.BoolVar(&versionFlag, "v", false, "Display version")
34-
flag.Parse()
35-
if versionFlag {
36-
fmt.Println(Version)
37-
return
34+
// create a new flagset
35+
fs := flag.NewFlagSet("matr", flag.ExitOnError)
36+
fs.StringVar(&matrFilePath, "matrfile", "./Matrfile.go", "path to Matrfile")
37+
fs.BoolVar(&cleanFlag, "clean", false, "clean the matr cache")
38+
fs.BoolVar(&helpFlag, "h", false, "Display usage info")
39+
fs.BoolVar(&versionFlag, "v", false, "Display version")
40+
fs.BoolVar(&noCacheFlag, "no-cache", false, "Don't use the matr cache")
41+
if err := fs.Parse(os.Args[1:]); err != nil {
42+
fmt.Println(err)
43+
os.Exit(1)
44+
}
45+
46+
if cleanFlag {
47+
if err := clean(matrFilePath); err != nil {
48+
fmt.Println(err)
49+
os.Exit(1)
50+
}
51+
os.Exit(0)
3852
}
3953

40-
args := flag.Args()
41-
4254
if helpFlag {
43-
args = append([]string{"-h"}, args...)
55+
fs.Usage()
4456
}
4557

46-
cmds, err := parseMatrfile(matrFilePath)
47-
if err != nil {
48-
flag.Usage()
49-
if helpFlag && flag.Arg(0) == "" {
50-
fmt.Print("\nTargets:\n No Matrfile.go or Matrfile found\n")
51-
return
52-
}
53-
54-
fmt.Print("\n " + err.Error() + "\n")
58+
if versionFlag {
59+
fmt.Printf("\nmatr version: %s\n\n", Version)
5560
return
5661
}
5762

58-
matrCachePath, err := build(matrFilePath, cmds)
63+
matrCachePath, err := build(matrFilePath, noCacheFlag)
5964
if err != nil {
65+
fs.Usage()
6066
os.Stderr.WriteString(err.Error() + "\n")
6167
return
6268
}
6369

64-
if err := run(matrCachePath, args...); err != nil {
70+
if err := run(matrCachePath, fs.Args()...); err != nil {
6571
os.Stderr.WriteString(err.Error() + "\n")
6672
return
6773
}
6874
}
6975

76+
func clean(matrfilePath string) error {
77+
matrfilePath, err := getMatrfilePath(matrfilePath)
78+
if err != nil {
79+
return err
80+
}
81+
82+
cachePath := filepath.Join(filepath.Dir(matrfilePath), defaultCacheFolder)
83+
return os.RemoveAll(cachePath)
84+
}
85+
7086
func parseMatrfile(path string) ([]parser.Command, error) {
7187
var err error
7288
var cmds []parser.Command
@@ -96,38 +112,65 @@ func run(matrCachePath string, args ...string) error {
96112
return c.Run()
97113
}
98114

99-
func build(matrFilePath string, cmds []parser.Command) (string, error) {
100-
var b bytes.Buffer
115+
func build(matrFilePath string, noCache bool) (string, error) {
116+
// get absolute path to matrfile
117+
matrFilePath, err := filepath.Abs(matrFilePath)
118+
if err != nil {
119+
return "", err
120+
}
121+
122+
matrCachePath := filepath.Join(filepath.Dir(matrFilePath), ".matr")
101123

102-
matrPath, matrFile := filepath.Split(matrFilePath)
103-
matrCachePath := filepath.Join(matrPath, ".matr")
124+
// check if the matrfile has changed
125+
newHash, err := getSha256(matrFilePath)
126+
if err != nil {
127+
return "", err
128+
}
104129

130+
// read the hash from the matrfileSha256 file
131+
oldHash, err := os.ReadFile(filepath.Join(matrCachePath, "matrfile.sha256"))
132+
if err == nil && !noCache {
133+
// if the hash is the same, we can skip the build
134+
if ok := bytes.Equal(oldHash, newHash); ok {
135+
return matrCachePath, nil
136+
}
137+
}
138+
139+
// check if the cache folder exists
105140
if dir, err := os.Stat(matrCachePath); err != nil || !dir.IsDir() {
106141
if err := os.Mkdir(matrCachePath, 0777); err != nil {
107142
return "", err
108143
}
109144
}
110145

111-
f, err := os.OpenFile(filepath.Join(matrCachePath, "main.go"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
112-
if err != nil {
146+
// if the file doesn't exist, create it
147+
if err := os.WriteFile(filepath.Join(matrCachePath, "matrfile.sha256"), []byte(newHash), 0644); err != nil {
113148
return "", err
114149
}
115-
defer f.Close()
116150

117151
if !symlinkValid(matrCachePath) {
118152
os.Remove(filepath.Join(matrCachePath, defaultMatrFile))
119-
if err := os.Symlink(filepath.Join(matrPath, matrFile), filepath.Join(matrCachePath, defaultMatrFile)); err != nil {
153+
if err := os.Symlink(matrFilePath, filepath.Join(matrCachePath, defaultMatrFile)); err != nil {
120154
if os.IsExist(err) {
121155
return "", err
122156
}
123157
}
124158
}
125159

126-
if err := generate(cmds, &b); err != nil {
160+
// create the main.go file in the matr cache folder
161+
// for the generated code to write to
162+
f, err := os.OpenFile(filepath.Join(matrCachePath, "main.go"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
163+
if err != nil {
127164
return "", err
128165
}
166+
defer f.Close()
129167

130-
if _, err := io.Copy(f, &b); err != nil {
168+
cmds, err := parseMatrfile(matrFilePath)
169+
if err != nil {
170+
return "", err
171+
}
172+
173+
if err := generate(cmds, f); err != nil {
131174
return "", err
132175
}
133176

@@ -141,6 +184,20 @@ func build(matrFilePath string, cmds []parser.Command) (string, error) {
141184
return matrCachePath, cmd.Run()
142185
}
143186

187+
func getSha256(path string) ([]byte, error) {
188+
f, err := os.Open(path)
189+
if err != nil {
190+
return nil, err
191+
}
192+
193+
h := sha256.New()
194+
if _, err := io.Copy(h, f); err != nil {
195+
return nil, err
196+
}
197+
198+
return h.Sum(nil), nil
199+
}
200+
144201
func getMatrfilePath(matrFilePath string) (string, error) {
145202
matrFilePath, err := filepath.Abs(matrFilePath)
146203
if err != nil {

matr/matr.go

+12-16
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
"text/tabwriter"
99
)
1010

11-
var Version = "v0.1.0"
11+
var Version = "v0.2.0"
1212

1313
// ContextKey is used to identify matr values in the context
1414
type ContextKey string
@@ -55,7 +55,7 @@ func (m *Matr) PrintUsage(cmd string) {
5555
err = errors.New("ERROR: no handler found for target \"" + cmd + "\"")
5656
}
5757

58-
fmt.Println("\nUsage: matr <opts> [target] args...")
58+
fmt.Println("\nRun Task: matr <opts> [target] args...")
5959

6060
fmt.Println("\nTargets:")
6161
tw := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
@@ -87,33 +87,29 @@ func (m *Matr) Handle(task *Task) {
8787
// Run will execute the requested task function with the provided context and arguments.
8888
func (m *Matr) Run(ctx context.Context, args ...string) error {
8989
argsLen := len(args)
90-
if argsLen > 0 && args[0] == "-h" {
91-
cmd := ""
92-
if argsLen > 1 {
93-
cmd = args[1]
94-
}
95-
m.PrintUsage(cmd)
90+
if argsLen == 0 {
91+
m.PrintUsage("")
9692
return nil
9793
}
9894

9995
var handlerArgs []string
10096

101-
taskName := "default"
102-
103-
if argsLen != 0 {
104-
taskName = args[0]
105-
}
106-
10797
if argsLen > 1 {
10898
handlerArgs = args[1:]
99+
100+
if args[1] == "-h" {
101+
m.PrintUsage(args[0])
102+
return nil
103+
}
109104
}
110105

111106
ctx = context.WithValue(ctx, ctxArgsKey, handlerArgs)
112107

113-
task, ok := m.tasks[taskName]
108+
task, ok := m.tasks[args[0]]
114109
if !ok {
110+
fmt.Fprintf(os.Stderr, "ERROR: no handler found for target \""+args[0]+"\"\n")
115111
m.PrintUsage("")
116-
return fmt.Errorf("no handler found for target \"%s\"", taskName)
112+
return nil
117113
}
118114

119115
err := task.Handler(ctx, handlerArgs)

parser/parser_test.go

-55
Original file line numberDiff line numberDiff line change
@@ -1,56 +1 @@
1-
//go:build matr
2-
// +build matr
3-
41
package parser
5-
6-
import "context"
7-
8-
// Example handler for build:js that is used as a subtask
9-
func MultiVarIdentHandler(a, b, c string) error {
10-
return nil
11-
}
12-
13-
// Example handler for build:js that is used as a subtask
14-
func MultiVarStarIdentHandler(a, b, c *string) error {
15-
return nil
16-
}
17-
18-
// Example handler for build:js that is used as a subtask
19-
func multiVarSelHandler(a, b, c context.Context) error {
20-
return nil
21-
}
22-
23-
// Example handler for build:js that is used as a subtask
24-
func multiVarStarSelHandler(a, b, c *context.Context) error {
25-
return nil
26-
}
27-
28-
// Example handler for build:js that is used as a subtask
29-
func starSelHandler(ctx *context.Context) error {
30-
return nil
31-
}
32-
33-
// Example handler for build:js that is used as a subtask
34-
func spreadStarSelandler(ctx ...*context.Context) error {
35-
return nil
36-
}
37-
38-
// Example handler for build:js that is used as a subtask
39-
func spreadSelHandler(ctx ...context.Context) error {
40-
return nil
41-
}
42-
43-
// Example handler for build:js that is used as a subtask
44-
func spreadIdentHandler(s ...string) error {
45-
return nil
46-
}
47-
48-
// Example handler for build:js that is used as a subtask
49-
func IdentSpreadIdentHandler(a string, s ...string) error {
50-
return nil
51-
}
52-
53-
// Example handler for build:js that is used as a subtask
54-
func IdentSpreadIdentMultiReturnHandler(a string, s ...string) (string, int, error) {
55-
return nil
56-
}

0 commit comments

Comments
 (0)