/
builder.go
113 lines (102 loc) · 2.37 KB
/
builder.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
package command
import (
"bytes"
"errors"
"os"
"sync"
"os/exec"
"path/filepath"
"github.com/ysugimoto/ginger/entity"
"github.com/ysugimoto/ginger/logger"
)
const parallelBuildNum = 5
// builder builds go application dynamically.
// It's funny go application executes `go build` command :-)
type builder struct {
src string
dest string
libPath string
}
func newBuilder(src, dest, libPath string) *builder {
return &builder{
src: src,
dest: dest,
libPath: libPath,
}
}
// build builds go application by each functions
func (b *builder) build(targets []*entity.Function) error {
log := logger.WithNamespace("ginger.build")
// Parallel build by each functions
index := 0
errorBuilds := []error{}
for {
var end int
if len(targets) < index+parallelBuildNum {
end = len(targets)
} else {
end = index + 5
}
var wg sync.WaitGroup
// var mu sync.Mutex
for _, fn := range targets[index:end] {
wg.Add(1)
log.Printf("Building function: %s...\n", fn.Name)
success := make(chan struct{})
err := make(chan error)
go b.compile(fn.Name, success, err)
go func() {
defer func() {
wg.Done()
close(success)
close(err)
}()
select {
case e := <-err:
log.Errorf("Failed to build function: %s\n", e.Error())
errorBuilds = append(errorBuilds, e)
return
case <-success:
log.Infof("Function %s built successfully\n", fn.Name)
return
}
}()
}
wg.Wait()
if end == len(targets) {
break
}
index += parallelBuildNum
}
if len(errorBuilds) > 0 {
return errors.New("Either function build failed")
}
return nil
}
// compile compiles go application by `go build` command.
// Note that runtime in AWS Lambda is linux, so we have to build as linux amd64 target.
func (b *builder) compile(name string, successChan chan struct{}, errChan chan error) {
buffer := new(bytes.Buffer)
out := filepath.Join(b.dest, name)
src := filepath.Join(b.src, name)
gopath := os.Getenv("GOPATH")
if gopath == "" {
gopath = b.libPath
} else {
gopath += ":" + b.libPath
}
cmd := exec.Command("go", "build", "-o", out)
cmd.Dir = src
cmd.Env = buildEnv(map[string]string{
"GOOS": "linux",
"GOARCH": "amd64",
"GOPATH": gopath,
})
cmd.Stdout = os.Stdout
cmd.Stderr = buffer
if err := cmd.Run(); err != nil {
errChan <- errors.New(string(buffer.Bytes()))
} else {
successChan <- struct{}{}
}
}