forked from constabulary/gb
-
Notifications
You must be signed in to change notification settings - Fork 0
/
gb.go
161 lines (141 loc) · 4.24 KB
/
gb.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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
// Package gb is a tool kit for building Go packages and programs.
//
// The executable, cmd/gb, is located in the respective subdirectory
// along with several plugin programs.
package gb
import (
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
)
// Toolchain represents a standardised set of command line tools
// used to build and test Go programs.
type Toolchain interface {
Gc(pkg *Package, searchpaths []string, importpath, srcdir, outfile string, files []string) error
Asm(pkg *Package, srcdir, ofile, sfile string) error
Pack(pkg *Package, afiles ...string) error
Ld(*Package, []string, string, string) error
Cc(pkg *Package, ofile string, cfile string) error
// compiler returns the location of the compiler for .go source code
compiler() string
// linker returns the location of the linker for this toolchain
linker() string
}
// Actions and Tasks.
//
// Actions and Tasks allow gb to separate the role of describing the
// order in which work will be done, from the work itself.
// Actions are the former, they describe the graph of dependencies
// between actions, and thus the work to be done. By traversing the action
// graph, we can do the work, executing Tasks in a sane order.
//
// Tasks describe the work to be done, without being concerned with
// the order in which the work is done -- that is up to the code that
// places Tasks into actions. Tasks also know more intimate details about
// filesystems, processes, file lists, etc, that Actions do not.
//
// Action graphs (they are not strictly trees as branchs converge on base actions)
// contain only work to be performed, there are no Actions with empty Tasks
// or Tasks which do no work.
//
// Actions are executed by Executors, but can also be transformed, mutated,
// or even graphed.
// An Action describes a task to be performed and a set
// of Actions that the task depends on.
type Action struct {
// Name describes the action.
Name string
// Deps identifies the Actions that this Action depends.
Deps []*Action
// Task identifies the that this action represents.
Task
}
// Task represents some work to be performed. It contains a single method
// Run, which is expected to be executed at most once.
type Task interface {
// Run will initiate the work that this task represents and
// block until the work is complete.
Run() error
}
// TaskFn is a Task that can execute itself.
type TaskFn func() error
func (fn TaskFn) Run() error { return fn() }
func mktmpdir() string {
d, err := ioutil.TempDir("", "gb")
if err != nil {
Fatalf("could not create temporary directory: %v", err)
}
return d
}
func mkdir(path string) error {
return os.MkdirAll(path, 0755)
}
func copyfile(dst, src string) error {
err := mkdir(filepath.Dir(dst))
if err != nil {
return fmt.Errorf("copyfile: mkdirall: %v", err)
}
r, err := os.Open(src)
if err != nil {
return fmt.Errorf("copyfile: open(%q): %v", src, err)
}
defer r.Close()
w, err := os.Create(dst)
if err != nil {
return fmt.Errorf("copyfile: create(%q): %v", dst, err)
}
Debugf("copyfile(dst: %v, src: %v)", dst, src)
_, err = io.Copy(w, r)
return err
}
// joinlist joins a []string representing path items
// using the operating system specific list separator.
func joinlist(l []string) string {
return strings.Join(l, string(filepath.ListSeparator))
}
func splitQuotedFields(s string) ([]string, error) {
// Split fields allowing '' or "" around elements.
// Quotes further inside the string do not count.
var f []string
for len(s) > 0 {
for len(s) > 0 && isWhitespace(s[0]) {
s = s[1:]
}
if len(s) == 0 {
break
}
// Accepted quoted string. No unescaping inside.
if s[0] == '"' || s[0] == '\'' {
quote := s[0]
s = s[1:]
i := 0
for i < len(s) && s[i] != quote {
i++
}
if i >= len(s) {
return nil, fmt.Errorf("unterminated %c string", quote)
}
f = append(f, s[:i])
s = s[i+1:]
continue
}
i := 0
for i < len(s) && !isWhitespace(s[i]) {
i++
}
f = append(f, s[:i])
s = s[i:]
}
return f, nil
}
func isWhitespace(c byte) bool {
return c == ' ' || c == '\t' || c == '\n' || c == '\r'
}
// stripext strips the extension from a filename.
// The extension is defined by filepath.Ext.
func stripext(path string) string {
return path[:len(path)-len(filepath.Ext(path))]
}