Skip to content

Commit 6e123ae

Browse files
author
Mario L Gutierrez
committed
compile Godofile only when it changes
1 parent 651893c commit 6e123ae

File tree

8 files changed

+159
-74
lines changed

8 files changed

+159
-74
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,5 @@ tmp/
44
_*
55
node_modules
66
example/dist
7+
tasks/godobin
8+
tasks/godobin.exe

README.md

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import (
2424
)
2525

2626
func tasks(p *Project) {
27-
Env = "GOPATH=.vendor::$GOPATH PG_PASSWORD=dev"
27+
Env = `GOPATH=.vendor::$GOPATH`
2828

2929
p.Task("default", D{"hello", "build"})
3030

@@ -37,19 +37,26 @@ func tasks(p *Project) {
3737
}
3838
})
3939

40-
p.Task("build", func() {
41-
Run("GOOS=linux GOARCH=amd64 go build", In{"cmd/server"})
42-
}).Watch("**/*.go")
40+
p.Task("assets?", func() {
41+
// The "?" tells Godo to run this task ONLY ONCE regardless of
42+
// how many tasks depend on it. In this case watchify watches
43+
// on its own.
44+
Run("watchify public/js/index.js d -o dist/js/app.bundle.js")
45+
}).Watch("public/**/*.{css,js,html}")
4346

44-
p.Task("views", func() {
45-
Run("razor templates")
46-
}).Watch("templates/**/*.go.html")
47+
p.Task("build", D{"views", "assets"}, func() error {
48+
return Run("GOOS=linux GOARCH=amd64 go build", In{"cmd/server"})
49+
}).Watch("**/*.go")
4750

48-
p.Task("server", D{"views"}, func() {
49-
// rebuilds and restarts the process when a watched file changes
51+
p.Task("server", D{"views", "assets"}, func() {
52+
// rebuilds and restarts when a watched file changes
5053
Start("main.go", In{"cmd/server"})
5154
}).Watch("server/**/*.go", "cmd/server/*.{go,json}").
5255
Debounce(3000)
56+
57+
p.Task("views", func() error {
58+
return Run("razor templates")
59+
}).Watch("templates/**/*.go.html")
5360
}
5461

5562
func main() {
@@ -61,19 +68,19 @@ To run "server" task from parent dir of `tasks/`
6168

6269
godo server
6370

64-
To rerun "server" and its dependencies whenever any `*.go.html`, `*.go` or `*.json` file changes
71+
To rerun "server" and its dependencies whenever any of their watched files change
6572

6673
godo server --watch
6774

68-
To run the "default" task which runs "hello" and "views"
75+
To run the "default" task which runs "hello" and "build"
6976

7077
godo
7178

7279
Task names may add a "?" suffix to execute only once even when watching
7380

7481
```go
7582
// build once regardless of number of dependents
76-
p.Task("build?", func() {})
83+
p.Task("assets?", func() {})
7784
```
7885

7986
Task options
@@ -91,13 +98,13 @@ Task options
9198

9299
Task handlers
93100

94-
func() - Simple function handler
95-
func(c *Context) - Handler which accepts the current context
96-
func() error
97-
func(c *Context) error
101+
func() - Simple function handler, don't care about return
102+
func() error - Simple function handler
103+
func(c *Context) - Task with context, don't care about return
104+
func(c *Context) error - Task with context
98105

99106
Any error return in task or its dependencies stops the pipeline and
100-
`godo` exits with 1 except in watch mode.
107+
`godo` exits with status code of 1, except in watch mode.
101108

102109
### Task Arguments
103110

@@ -149,6 +156,12 @@ c.Args.MustInt("number", "n")
149156
c.Args.ZeroInt("number", "n")
150157
```
151158

159+
## godobin
160+
161+
`godo` compiles `Godofile.go` to `godobin` (`godobin.exe` on Windows) whenever
162+
`Godofile.go` changes. The binary file is built into the same directory as
163+
`Godofile.go` and should be ignored by adding the path to `.gitignore`.
164+
152165
## Exec functions
153166

154167
### Bash

VERSION.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
package godo
22

33
// Version is the current version
4-
var Version = "1.3.2"
4+
var Version = "1.4.0"

cmd/godo/main.go

Lines changed: 85 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,18 @@ import (
77
"os"
88
"path/filepath"
99
"regexp"
10+
"runtime"
1011
"strings"
12+
"time"
1113

14+
"github.com/mgutz/minimist"
1215
"github.com/mgutz/str"
1316
"gopkg.in/godo.v1"
1417
"gopkg.in/godo.v1/util"
1518
)
1619

20+
var isWindows = runtime.GOOS == "windows"
21+
1722
func checkError(err error, format string, args ...interface{}) {
1823
if err != nil {
1924
util.Error("ERR", format, args...)
@@ -34,6 +39,18 @@ func isPackageMain(data []byte) bool {
3439
}
3540

3641
func main() {
42+
// cfg := profile.Config{
43+
// BlockProfile: true,
44+
// CPUProfile: true,
45+
// MemProfile: true,
46+
// NoShutdownHook: true, // do not hook SIGINT
47+
// }
48+
49+
// // p.Stop() must be called before the program exits to
50+
// // ensure profiling information is written to disk.
51+
// p := profile.Start(&cfg)
52+
// defer p.Stop()
53+
3754
// legacy version used tasks/
3855
godoFiles := []string{"Gododir/Godofile.go", "tasks/Godofile.go"}
3956
src := ""
@@ -57,67 +74,89 @@ func main() {
5774
os.Exit(1)
5875
}
5976

60-
mainFile := buildMain(rel)
61-
if mainFile != "" {
62-
src = mainFile
63-
defer os.RemoveAll(filepath.Dir(mainFile))
64-
}
65-
cmd := "go run " + src + " " + strings.Join(os.Args[1:], " ")
77+
exe := buildMain(rel)
78+
cmd := exe + " " + strings.Join(os.Args[1:], " ")
79+
cmd = str.Clean(cmd)
6680
// errors are displayed by tasks
81+
6782
godo.Run(cmd)
6883
}
6984

70-
func buildMain(src string) string {
71-
tempFile := ""
85+
type godorc struct {
86+
ModTime time.Time
87+
Size int64
88+
}
89+
90+
func mustBeMain(src string) {
7291
data, err := ioutil.ReadFile(src)
7392
if err != nil {
7493
fmt.Fprintln(os.Stderr, err)
7594
os.Exit(1)
7695
}
7796

7897
if !hasMain(data) {
79-
if isPackageMain(data) {
80-
msg := `%s is not runnable. Rename package OR make it runnable by adding
81-
82-
func main() {
83-
godo.Godo(Tasks)
84-
}
85-
`
86-
fmt.Printf(msg, src)
87-
os.Exit(1)
88-
}
98+
msg := `%s is not runnable. Rename package OR make it runnable by adding
99+
100+
func main() {
101+
godo.Godo(tasks)
102+
}
103+
`
104+
fmt.Printf(msg, src)
105+
os.Exit(1)
106+
}
107+
108+
if !isPackageMain(data) {
109+
msg := `%s is not runnable. It must be package main`
110+
fmt.Printf(msg, src)
111+
os.Exit(1)
112+
}
113+
}
114+
115+
func buildMain(src string) string {
116+
mustBeMain(src)
117+
118+
exeFile := "godobin"
119+
if isWindows {
120+
exeFile = "godobin.exe"
121+
}
122+
123+
dir := filepath.Dir(src)
124+
exe := filepath.Join(dir, exeFile)
125+
reasonFormat := "Godofile changed. Rebuilding %s...\n"
126+
127+
argm := minimist.Parse()
128+
rebuild := argm.ZeroBool("rebuild")
129+
if rebuild {
130+
os.Remove(exe)
131+
}
132+
133+
// see if last run exists .godoinfo
134+
fiExe, err := os.Stat(exe)
135+
build := os.IsNotExist(err)
136+
if build {
137+
reasonFormat = "Building %s...\n"
138+
}
139+
140+
fiGodofile, err := os.Stat(src)
141+
if os.IsNotExist(err) {
142+
log.Fatalln(err)
143+
os.Exit(1)
144+
}
145+
build = build || fiExe.ModTime().Before(fiGodofile.ModTime())
146+
147+
if build {
148+
util.Debug("godo", reasonFormat, exe)
89149

90-
template := `
91-
package main
92-
import (
93-
"gopkg.in/godo.v1"
94-
pkg "{{package}}"
95-
)
96-
func main() {
97-
godo.Godo(pkg.Tasks)
98-
}
99-
`
100-
packageName, err := util.PackageName(src)
150+
err := godo.Run("go build -a -o "+exeFile, godo.In{dir})
101151
if err != nil {
102152
panic(err)
103153
}
104-
code := str.Template(template, map[string]interface{}{
105-
"package": filepath.ToSlash(packageName),
106-
})
107-
//log.Println("DBG template", code)
108-
tempDir, err := ioutil.TempDir("", "godo")
109-
if err != nil {
110-
panic("Could not create temp directory")
111-
}
112-
//log.Printf("code\n %s\n", code)
113-
tempFile = filepath.Join(tempDir, "Godofile_main.go")
114-
err = ioutil.WriteFile(tempFile, []byte(code), 0644)
115-
if err != nil {
116-
log.Panicf("Could not write temp file %s\n", tempFile)
117-
}
154+
}
118155

119-
src = tempFile
120-
return src
156+
if rebuild {
157+
util.Info("godo", "ok")
158+
os.Exit(0)
121159
}
122-
return ""
160+
161+
return exe
123162
}

doc.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,5 +70,4 @@
7070
//
7171
// func() {} - Simple function handler
7272
// func(c *Context) {} - Handler which accepts the current context
73-
7473
package godo

env_test.go

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,26 @@
11
package godo
22

33
import (
4+
"fmt"
45
"os"
6+
"runtime"
57
"testing"
8+
9+
"github.com/mgutz/str"
610
)
711

12+
var isWindows = runtime.GOOS == "windows"
13+
814
func TestEnvironment(t *testing.T) {
9-
SetEnviron("USER=$USER:godo", true)
15+
var user string
16+
if isWindows {
17+
user = os.Getenv("USERNAME")
18+
os.Setenv("USER", user)
19+
} else {
20+
user = os.Getenv("USER")
21+
}
1022

11-
user := os.Getenv("USER")
23+
SetEnviron("USER=$USER:godo", true)
1224
env := effectiveEnv(nil)
1325
if !sliceContains(env, "USER="+user+":godo") {
1426
t.Error("Environment interpolation failed", env)
@@ -92,8 +104,17 @@ func TestExpansion(t *testing.T) {
92104
func TestInheritedRunEnv(t *testing.T) {
93105
os.Setenv("TEST_RUN_ENV", "fubar")
94106
SetEnviron("", true)
95-
output, _ := RunOutput(`FOO=bar BAH=baz bash -c "echo -n $TEST_RUN_ENV $FOO"`)
96-
if output != "fubar bar" {
97-
t.Error("Environment was not inherited! Got", output)
107+
108+
var output string
109+
110+
if isWindows {
111+
output, _ = RunOutput(`FOO=bar BAH=baz cmd /C "echo %TEST_RUN_ENV% %FOO%"`)
112+
} else {
113+
output, _ = RunOutput(`FOO=bar BAH=baz bash -c "echo -n $TEST_RUN_ENV $FOO"`)
114+
}
115+
116+
117+
if str.Clean(output) != "fubar bar" {
118+
t.Error("Environment was not inherited! Got", fmt.Sprintf("%q", output))
98119
}
99120
}

project_test.go

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"strings"
88
"testing"
99

10+
"github.com/mgutz/str"
1011
"github.com/stretchr/testify/assert"
1112
)
1213

@@ -168,10 +169,16 @@ func TestCalculateWatchPaths(t *testing.T) {
168169
}
169170

170171
func TestInside(t *testing.T) {
171-
Inside("./test", func() {
172-
out, _ := RunOutput("bash foo.sh")
173-
if out != "FOOBAR" {
174-
t.Error("Inside failed")
172+
Inside("test", func() {
173+
var out string
174+
if isWindows {
175+
out, _ = RunOutput("foo.cmd")
176+
} else {
177+
out, _ = RunOutput("bash foo.sh")
178+
}
179+
180+
if str.Clean(out) != "FOOBAR" {
181+
t.Error("Inside failed. Got", fmt.Sprintf("%q", out))
175182
}
176183
})
177184

@@ -182,6 +189,9 @@ func TestInside(t *testing.T) {
182189
}
183190

184191
func TestBash(t *testing.T) {
192+
if isWindows {
193+
return
194+
}
185195
out, _ := BashOutput(`echo -n foobar`)
186196
if out != "foobar" {
187197
t.Error("Simple bash failed. Got", out)

test/foo.cmd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@echo FOOBAR

0 commit comments

Comments
 (0)