forked from bldy/build
-
Notifications
You must be signed in to change notification settings - Fork 0
/
build.go
131 lines (109 loc) · 2.82 KB
/
build.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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// Copyright 2015-2016 Sevki <s@sevki.org>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package build defines build target and build context structures
package build
import (
"bytes"
"context"
"io"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
"sync"
)
// Target defines the interface that rules must implement for becoming build targets.
type Target interface {
GetName() string
GetDependencies() []string
Hash() []byte
Build(*Context) error
Installs() map[string]string
}
// Context defines the context in which a target will be built, it
// provide helper functions for shelling out without having to worry
// about stdout or stderr outputs.
type Context struct {
wd string
stderr, stdout *bytes.Buffer
logger *log.Logger
buf *bytes.Buffer
}
// NewContext initializes and returns a new build.Context
func NewContext(dir string) *Context {
buf := bytes.Buffer{}
return &Context{
wd: dir,
stderr: &buf,
stdout: &buf,
logger: log.New(&buf, "", log.Lmicroseconds),
buf: &buf,
}
}
func (c *Context) Stdout() io.Reader {
return c.buf
}
func (c *Context) StdErr() io.Reader {
return c.buf
}
func (c *Context) Printf(format string, v ...interface{}) {
c.logger.Printf(format, v)
}
func (c *Context) Println(v ...interface{}) {
c.logger.Println(v)
}
// Exec executes a command writing it's outputs to the context
func (c *Context) Exec(cmd string, env, params []string) error {
c.Println(strings.Join(append([]string{cmd}, params...), "\n"))
var stdOut, stdErr io.ReadCloser
var wg sync.WaitGroup
x := exec.Command(cmd, params...)
x.Dir = c.wd
x.Env = env
stdErr, err := x.StderrPipe()
if err != nil {
return err
}
stdOut, err = x.StdoutPipe()
if err != nil {
return err
}
wg.Add(2)
go func() {
io.Copy(c.stdout, stdOut)
wg.Done()
}()
go func() {
io.Copy(c.stderr, stdErr)
wg.Done()
}()
if err := x.Run(); err != nil {
return err
}
wg.Wait()
return nil
}
// Run executes a command writing it's outputs to the context
func (c *Context) Run(ctx context.Context, cmd string, env, params []string) *exec.Cmd {
c.Println(strings.Join(append([]string{cmd}, params...), " "))
x := exec.CommandContext(ctx, cmd, params...)
x.Dir = c.wd
x.Env = env
return x
}
// Create creates and returns a new file with the given name in the context
func (c *Context) Create(name string) (*os.File, error) {
return os.Create(filepath.Join(c.wd, name))
}
// Open creates and returns a new file with the given name in the context
func (c *Context) Open(name string) (*os.File, error) {
if filepath.IsAbs(name) {
return os.Open(name)
}
return os.Open(filepath.Join(c.wd, name))
}
func (c *Context) Mkdir(name string) error {
return os.MkdirAll(filepath.Join(c.wd, name), os.ModeDir|os.ModePerm)
}