LEFT | RIGHT |
(no file at all) | |
1 // Copyright 2011 The Go Authors. All rights reserved. | 1 // Copyright 2011 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 // Package build provides tools for building Go packages. | 5 // Package build provides tools for building Go packages. |
6 package build | 6 package build |
7 | 7 |
8 import ( | 8 import ( |
9 "bytes" | 9 "bytes" |
10 "exec" | 10 "exec" |
11 "fmt" | 11 "fmt" |
12 "os" | 12 "os" |
13 "path/filepath" | 13 "path/filepath" |
14 "runtime" | 14 "runtime" |
15 "strings" | 15 "strings" |
16 ) | 16 ) |
17 | 17 |
18 func (d *DirInfo) Build(targ string) ([]*Cmd, os.Error) { | 18 // Build produces a build Script for the given package. |
19 » b := &build{obj: "_obj/"} | 19 func Build(tree *Tree, pkg string, info *DirInfo) (*Script, os.Error) { |
| 20 » s := &Script{} |
| 21 » b := &build{ |
| 22 » » script: s, |
| 23 » » path: filepath.Join(tree.SrcDir(), pkg), |
| 24 » } |
| 25 » b.obj = b.abs("_obj") + "/" |
20 | 26 |
21 goarch := runtime.GOARCH | 27 goarch := runtime.GOARCH |
22 if g := os.Getenv("GOARCH"); g != "" { | 28 if g := os.Getenv("GOARCH"); g != "" { |
23 goarch = g | 29 goarch = g |
24 } | 30 } |
25 var err os.Error | 31 var err os.Error |
26 b.arch, err = ArchChar(goarch) | 32 b.arch, err = ArchChar(goarch) |
27 if err != nil { | 33 if err != nil { |
28 return nil, err | 34 return nil, err |
29 } | 35 } |
30 | 36 |
31 » var gofiles = d.GoFiles // .go files to be built with gc | 37 » // .go files to be built with gc |
32 » var ofiles []string // *.GOARCH files to be linked or packed | 38 » gofiles := b.abss(info.GoFiles...) |
| 39 » s.addInput(gofiles...) |
| 40 |
| 41 » var ofiles []string // object files to be linked or packed |
33 | 42 |
34 // make build directory | 43 // make build directory |
35 b.mkdir(b.obj) | 44 b.mkdir(b.obj) |
| 45 s.addIntermediate(b.obj) |
36 | 46 |
37 // cgo | 47 // cgo |
38 » if len(d.CgoFiles) > 0 { | 48 » if len(info.CgoFiles) > 0 { |
39 » » outGo, outObj := b.cgo(d.CgoFiles) | 49 » » cgoFiles := b.abss(info.CgoFiles...) |
| 50 » » s.addInput(cgoFiles...) |
| 51 » » outGo, outObj := b.cgo(cgoFiles) |
40 gofiles = append(gofiles, outGo...) | 52 gofiles = append(gofiles, outGo...) |
41 ofiles = append(ofiles, outObj...) | 53 ofiles = append(ofiles, outObj...) |
| 54 s.addIntermediate(outGo...) |
| 55 s.addIntermediate(outObj...) |
42 } | 56 } |
43 | 57 |
44 // compile | 58 // compile |
45 if len(gofiles) > 0 { | 59 if len(gofiles) > 0 { |
46 ofile := b.obj + "_go_." + b.arch | 60 ofile := b.obj + "_go_." + b.arch |
47 b.gc(ofile, gofiles...) | 61 b.gc(ofile, gofiles...) |
48 ofiles = append(ofiles, ofile) | 62 ofiles = append(ofiles, ofile) |
| 63 s.addIntermediate(ofile) |
49 } | 64 } |
50 | 65 |
51 // assemble | 66 // assemble |
52 » for _, sfile := range d.SFiles { | 67 » for _, sfile := range info.SFiles { |
53 ofile := b.obj + sfile[:len(sfile)-1] + b.arch | 68 ofile := b.obj + sfile[:len(sfile)-1] + b.arch |
| 69 sfile = b.abs(sfile) |
| 70 s.addInput(sfile) |
54 b.asm(ofile, sfile) | 71 b.asm(ofile, sfile) |
55 ofiles = append(ofiles, ofile) | 72 ofiles = append(ofiles, ofile) |
| 73 s.addIntermediate(sfile, ofile) |
56 } | 74 } |
57 | 75 |
58 if len(ofiles) == 0 { | 76 if len(ofiles) == 0 { |
59 return nil, os.NewError("make: no object files to build") | 77 return nil, os.NewError("make: no object files to build") |
60 } | 78 } |
61 | 79 |
62 » if d.IsCommand() { | 80 » // choose target file |
| 81 » var targ string |
| 82 » if info.IsCommand() { |
| 83 » » // use the last part of the import path as binary name |
| 84 » » _, bin := filepath.Split(pkg) |
| 85 » » targ = filepath.Join(tree.BinDir(), bin) |
| 86 » } else { |
| 87 » » targ = filepath.Join(tree.PkgDir(), pkg+".a") |
| 88 » } |
| 89 |
| 90 » // make target directory |
| 91 » targDir, _ := filepath.Split(targ) |
| 92 » b.mkdir(targDir) |
| 93 |
| 94 » // link binary or pack object |
| 95 » if info.IsCommand() { |
63 b.ld(targ, ofiles...) | 96 b.ld(targ, ofiles...) |
64 } else { | 97 } else { |
65 b.gopack(targ, ofiles...) | 98 b.gopack(targ, ofiles...) |
66 } | 99 } |
67 | 100 » s.Output = append(s.Output, targ) |
68 » return b.cmds, nil | 101 |
69 } | 102 » return b.script, nil |
70 | 103 } |
| 104 |
| 105 // A Script describes the build process for a Go package. |
| 106 // The Input, Intermediate, and Output fields are lists of absolute paths. |
| 107 type Script struct { |
| 108 » Cmd []*Cmd |
| 109 » Input []string |
| 110 » Intermediate []string |
| 111 » Output []string |
| 112 } |
| 113 |
| 114 func (s *Script) addInput(file ...string) { |
| 115 » s.Input = append(s.Input, file...) |
| 116 } |
| 117 |
| 118 func (s *Script) addIntermediate(file ...string) { |
| 119 » s.Intermediate = append(s.Intermediate, file...) |
| 120 } |
| 121 |
| 122 // Run runs the Script's Cmds in order. |
| 123 func (s *Script) Run() os.Error { |
| 124 » for _, c := range s.Cmd { |
| 125 » » if err := c.Run(); err != nil { |
| 126 » » » return err |
| 127 » » } |
| 128 » } |
| 129 » return nil |
| 130 } |
| 131 |
| 132 // Stale returns true if the build's inputs are newer than its outputs. |
| 133 func (s *Script) Stale() bool { |
| 134 » var latest int64 |
| 135 » // get latest mtime of outputs |
| 136 » for _, file := range s.Output { |
| 137 » » fi, err := os.Stat(file) |
| 138 » » if err != nil { |
| 139 » » » // any error reading output files means stale |
| 140 » » » return true |
| 141 » » } |
| 142 » » if m := fi.Mtime_ns; m > latest { |
| 143 » » » latest = m |
| 144 » » } |
| 145 » } |
| 146 » for _, file := range s.Input { |
| 147 » » fi, err := os.Stat(file) |
| 148 » » if err != nil || fi.Mtime_ns > latest { |
| 149 » » » // any error reading input files means stale |
| 150 » » » // (attempt to rebuild to figure out why) |
| 151 » » » return true |
| 152 » » } |
| 153 » } |
| 154 » return false |
| 155 } |
| 156 |
| 157 // Clean removes the Script's Intermediate files. |
| 158 // It tries to remove every file and returns the first error it encounters. |
| 159 func (s *Script) Clean() (err os.Error) { |
| 160 » for i := len(s.Intermediate) - 1; i >= 0; i-- { |
| 161 » » if e := os.Remove(s.Intermediate[i]); err == nil { |
| 162 » » » err = e |
| 163 » » } |
| 164 » } |
| 165 » return |
| 166 } |
| 167 |
| 168 // Clean removes the Script's Intermediate and Output files. |
| 169 // It tries to remove every file and returns the first error it encounters. |
| 170 func (s *Script) Nuke() (err os.Error) { |
| 171 » for i := len(s.Output) - 1; i >= 0; i-- { |
| 172 » » if e := os.Remove(s.Output[i]); err == nil { |
| 173 » » » err = e |
| 174 » » } |
| 175 » } |
| 176 » if e := s.Clean(); err == nil { |
| 177 » » err = e |
| 178 » } |
| 179 » return |
| 180 } |
| 181 |
| 182 // A Cmd describes an individual build command. |
71 type Cmd struct { | 183 type Cmd struct { |
72 Args []string // command-line | 184 Args []string // command-line |
73 Stdout string // write standard output to this file, "" is passthrough | 185 Stdout string // write standard output to this file, "" is passthrough |
| 186 Dir string // working directory |
74 Input []string // file paths (dependencies) | 187 Input []string // file paths (dependencies) |
75 Output []string // file paths | 188 Output []string // file paths |
76 } | 189 } |
77 | 190 |
78 func (c *Cmd) String() string { | 191 func (c *Cmd) String() string { |
79 return strings.Join(c.Args, " ") | 192 return strings.Join(c.Args, " ") |
80 } | 193 } |
81 | 194 |
82 func (c *Cmd) Run(dir string) os.Error { | 195 // Run executes the Cmd. |
| 196 func (c *Cmd) Run() os.Error { |
83 out := new(bytes.Buffer) | 197 out := new(bytes.Buffer) |
84 cmd := exec.Command(c.Args[0], c.Args[1:]...) | 198 cmd := exec.Command(c.Args[0], c.Args[1:]...) |
85 » cmd.Dir = dir | 199 » cmd.Dir = c.Dir |
86 cmd.Stdout = out | 200 cmd.Stdout = out |
87 cmd.Stderr = out | 201 cmd.Stderr = out |
88 if c.Stdout != "" { | 202 if c.Stdout != "" { |
89 » » f, err := os.Create(filepath.Join(dir, c.Stdout)) | 203 » » f, err := os.Create(c.Stdout) |
90 if err != nil { | 204 if err != nil { |
91 return err | 205 return err |
92 } | 206 } |
93 defer f.Close() | 207 defer f.Close() |
94 cmd.Stdout = f | 208 cmd.Stdout = f |
95 } | 209 } |
96 if err := cmd.Run(); err != nil { | 210 if err := cmd.Run(); err != nil { |
97 return fmt.Errorf("command %q: %v\n%v", c, err, out) | 211 return fmt.Errorf("command %q: %v\n%v", c, err, out) |
98 } | 212 } |
99 return nil | 213 return nil |
100 } | |
101 | |
102 func (c *Cmd) Clean(dir string) (err os.Error) { | |
103 for _, fn := range c.Output { | |
104 if e := os.RemoveAll(fn); err == nil { | |
105 err = e | |
106 } | |
107 } | |
108 return | |
109 } | 214 } |
110 | 215 |
111 // ArchChar returns the architecture character for the given goarch. | 216 // ArchChar returns the architecture character for the given goarch. |
112 // For example, ArchChar("amd64") returns "6". | 217 // For example, ArchChar("amd64") returns "6". |
113 func ArchChar(goarch string) (string, os.Error) { | 218 func ArchChar(goarch string) (string, os.Error) { |
114 switch goarch { | 219 switch goarch { |
115 case "386": | 220 case "386": |
116 return "8", nil | 221 return "8", nil |
117 case "amd64": | 222 case "amd64": |
118 return "6", nil | 223 return "6", nil |
119 case "arm": | 224 case "arm": |
120 return "5", nil | 225 return "5", nil |
121 } | 226 } |
122 return "", os.NewError("unsupported GOARCH " + goarch) | 227 return "", os.NewError("unsupported GOARCH " + goarch) |
123 } | 228 } |
124 | 229 |
125 type build struct { | 230 type build struct { |
126 » cmds []*Cmd | 231 » script *Script |
127 » obj string | 232 » path string |
128 » arch string | 233 » obj string |
| 234 » arch string |
| 235 } |
| 236 |
| 237 func (b *build) abs(file string) string { |
| 238 » if filepath.IsAbs(file) { |
| 239 » » return file |
| 240 » } |
| 241 » return filepath.Join(b.path, file) |
| 242 } |
| 243 |
| 244 func (b *build) abss(file ...string) []string { |
| 245 » s := make([]string, len(file)) |
| 246 » for i, f := range file { |
| 247 » » s[i] = b.abs(f) |
| 248 » } |
| 249 » return s |
129 } | 250 } |
130 | 251 |
131 func (b *build) add(c Cmd) { | 252 func (b *build) add(c Cmd) { |
132 » b.cmds = append(b.cmds, &c) | 253 » b.script.Cmd = append(b.script.Cmd, &c) |
133 } | 254 } |
134 | 255 |
135 func (b *build) mkdir(name string) { | 256 func (b *build) mkdir(name string) { |
136 b.add(Cmd{ | 257 b.add(Cmd{ |
137 Args: []string{"mkdir", "-p", name}, | 258 Args: []string{"mkdir", "-p", name}, |
138 Output: []string{name}, | 259 Output: []string{name}, |
139 }) | 260 }) |
140 } | 261 } |
141 | 262 |
142 func (b *build) gc(ofile string, gofiles ...string) { | 263 func (b *build) gc(ofile string, gofiles ...string) { |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
215 return append([]string{"gcc", m, "-I", ".", "-g", "-fPIC", "-O2"}, args.
..) | 336 return append([]string{"gcc", m, "-I", ".", "-g", "-fPIC", "-O2"}, args.
..) |
216 } | 337 } |
217 | 338 |
218 func (b *build) cgo(cgofiles []string) (outGo, outObj []string) { | 339 func (b *build) cgo(cgofiles []string) (outGo, outObj []string) { |
219 // cgo | 340 // cgo |
220 // TODO(adg): CGOPKGPATH | 341 // TODO(adg): CGOPKGPATH |
221 // TODO(adg): CGO_FLAGS | 342 // TODO(adg): CGO_FLAGS |
222 gofiles := []string{b.obj + "_cgo_gotypes.go"} | 343 gofiles := []string{b.obj + "_cgo_gotypes.go"} |
223 cfiles := []string{b.obj + "_cgo_main.c", b.obj + "_cgo_export.c"} | 344 cfiles := []string{b.obj + "_cgo_main.c", b.obj + "_cgo_export.c"} |
224 for _, fn := range cgofiles { | 345 for _, fn := range cgofiles { |
| 346 fn = filepath.Base(fn) |
225 f := b.obj + fn[:len(fn)-2] | 347 f := b.obj + fn[:len(fn)-2] |
226 gofiles = append(gofiles, f+"cgo1.go") | 348 gofiles = append(gofiles, f+"cgo1.go") |
227 cfiles = append(cfiles, f+"cgo2.c") | 349 cfiles = append(cfiles, f+"cgo2.c") |
228 } | 350 } |
229 defunC := b.obj + "_cgo_defun.c" | 351 defunC := b.obj + "_cgo_defun.c" |
230 output := append([]string{defunC}, gofiles...) | 352 output := append([]string{defunC}, gofiles...) |
231 output = append(output, cfiles...) | 353 output = append(output, cfiles...) |
232 b.add(Cmd{ | 354 b.add(Cmd{ |
233 Args: append([]string{"cgo", "--"}, cgofiles...), | 355 Args: append([]string{"cgo", "--"}, cgofiles...), |
234 Input: cgofiles, | 356 Input: cgofiles, |
(...skipping 28 matching lines...) Expand all Loading... |
263 Output: []string{importC}, | 385 Output: []string{importC}, |
264 }) | 386 }) |
265 | 387 |
266 // cc _cgo_import.ARCH | 388 // cc _cgo_import.ARCH |
267 importObj := b.obj + "_cgo_import." + b.arch | 389 importObj := b.obj + "_cgo_import." + b.arch |
268 b.cc(importObj, importC) | 390 b.cc(importObj, importC) |
269 outObj = append(outObj, importObj) | 391 outObj = append(outObj, importObj) |
270 | 392 |
271 return | 393 return |
272 } | 394 } |
LEFT | RIGHT |