OSDN Git Service

9515a7e645224f41d032ad227e0ff4e4cf89ad1f
[pf3gnuchains/gcc-fork.git] / libgo / go / go / build / build.go
1 // Copyright 2011 The Go Authors.  All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 // Package build provides tools for building Go packages.
6 package build
7
8 import (
9         "bytes"
10         "errors"
11         "fmt"
12         "os"
13         "os/exec"
14         "path/filepath"
15         "regexp"
16         "runtime"
17         "strings"
18         "time"
19 )
20
21 // Build produces a build Script for the given package.
22 func Build(tree *Tree, pkg string, info *DirInfo) (*Script, error) {
23         s := &Script{}
24         b := &build{
25                 script: s,
26                 path:   filepath.Join(tree.SrcDir(), pkg),
27         }
28         b.obj = b.abs("_obj") + string(filepath.Separator)
29
30         b.goarch = runtime.GOARCH
31         if g := os.Getenv("GOARCH"); g != "" {
32                 b.goarch = g
33         }
34         var err error
35         b.arch, err = ArchChar(b.goarch)
36         if err != nil {
37                 return nil, err
38         }
39
40         // add import object files to list of Inputs
41         for _, pkg := range info.Imports {
42                 t, p, err := FindTree(pkg)
43                 if err != nil && err != ErrNotFound {
44                         // FindTree should always be able to suggest an import
45                         // path and tree. The path must be malformed
46                         // (for example, an absolute or relative path).
47                         return nil, errors.New("build: invalid import: " + pkg)
48                 }
49                 s.addInput(filepath.Join(t.PkgDir(), p+".a"))
50         }
51
52         // .go files to be built with gc
53         gofiles := b.abss(info.GoFiles...)
54         s.addInput(gofiles...)
55
56         var ofiles []string // object files to be linked or packed
57
58         // make build directory
59         b.mkdir(b.obj)
60         s.addIntermediate(b.obj)
61
62         // cgo
63         if len(info.CgoFiles) > 0 {
64                 cgoFiles := b.abss(info.CgoFiles...)
65                 s.addInput(cgoFiles...)
66                 cgoCFiles := b.abss(info.CFiles...)
67                 s.addInput(cgoCFiles...)
68                 outGo, outObj := b.cgo(cgoFiles, cgoCFiles)
69                 gofiles = append(gofiles, outGo...)
70                 ofiles = append(ofiles, outObj...)
71                 s.addIntermediate(outGo...)
72                 s.addIntermediate(outObj...)
73         }
74
75         // compile
76         if len(gofiles) > 0 {
77                 ofile := b.obj + "_go_." + b.arch
78                 b.gc(ofile, gofiles...)
79                 ofiles = append(ofiles, ofile)
80                 s.addIntermediate(ofile)
81         }
82
83         // assemble
84         for _, sfile := range info.SFiles {
85                 ofile := b.obj + sfile[:len(sfile)-1] + b.arch
86                 sfile = b.abs(sfile)
87                 s.addInput(sfile)
88                 b.asm(ofile, sfile)
89                 ofiles = append(ofiles, ofile)
90                 s.addIntermediate(ofile)
91         }
92
93         if len(ofiles) == 0 {
94                 return nil, errors.New("make: no object files to build")
95         }
96
97         // choose target file
98         var targ string
99         if info.IsCommand() {
100                 // use the last part of the import path as binary name
101                 _, bin := filepath.Split(pkg)
102                 if runtime.GOOS == "windows" {
103                         bin += ".exe"
104                 }
105                 targ = filepath.Join(tree.BinDir(), bin)
106         } else {
107                 targ = filepath.Join(tree.PkgDir(), pkg+".a")
108         }
109
110         // make target directory
111         targDir, _ := filepath.Split(targ)
112         b.mkdir(targDir)
113
114         // link binary or pack object
115         if info.IsCommand() {
116                 b.ld(targ, ofiles...)
117         } else {
118                 b.gopack(targ, ofiles...)
119         }
120         s.Output = append(s.Output, targ)
121
122         return b.script, nil
123 }
124
125 // A Script describes the build process for a Go package.
126 // The Input, Intermediate, and Output fields are lists of absolute paths.
127 type Script struct {
128         Cmd          []*Cmd
129         Input        []string
130         Intermediate []string
131         Output       []string
132 }
133
134 func (s *Script) addInput(file ...string) {
135         s.Input = append(s.Input, file...)
136 }
137
138 func (s *Script) addIntermediate(file ...string) {
139         s.Intermediate = append(s.Intermediate, file...)
140 }
141
142 // Run runs the Script's Cmds in order.
143 func (s *Script) Run() error {
144         for _, c := range s.Cmd {
145                 if err := c.Run(); err != nil {
146                         return err
147                 }
148         }
149         return nil
150 }
151
152 // Stale returns true if the build's inputs are newer than its outputs.
153 func (s *Script) Stale() bool {
154         var latest time.Time
155         // get latest mtime of outputs
156         for _, file := range s.Output {
157                 fi, err := os.Stat(file)
158                 if err != nil {
159                         // any error reading output files means stale
160                         return true
161                 }
162                 if mtime := fi.ModTime(); mtime.After(latest) {
163                         latest = mtime
164                 }
165         }
166         for _, file := range s.Input {
167                 fi, err := os.Stat(file)
168                 if err != nil || fi.ModTime().After(latest) {
169                         // any error reading input files means stale
170                         // (attempt to rebuild to figure out why)
171                         return true
172                 }
173         }
174         return false
175 }
176
177 // Clean removes the Script's Intermediate files.
178 // It tries to remove every file and returns the first error it encounters.
179 func (s *Script) Clean() (err error) {
180         // Reverse order so that directories get removed after the files they contain.
181         for i := len(s.Intermediate) - 1; i >= 0; i-- {
182                 if e := os.Remove(s.Intermediate[i]); err == nil {
183                         err = e
184                 }
185         }
186         return
187 }
188
189 // Nuke removes the Script's Intermediate and Output files.
190 // It tries to remove every file and returns the first error it encounters.
191 func (s *Script) Nuke() (err error) {
192         // Reverse order so that directories get removed after the files they contain.
193         for i := len(s.Output) - 1; i >= 0; i-- {
194                 if e := os.Remove(s.Output[i]); err == nil {
195                         err = e
196                 }
197         }
198         if e := s.Clean(); err == nil {
199                 err = e
200         }
201         return
202 }
203
204 // A Cmd describes an individual build command.
205 type Cmd struct {
206         Args   []string // command-line
207         Stdout string   // write standard output to this file, "" is passthrough
208         Dir    string   // working directory
209         Env    []string // environment
210         Input  []string // file paths (dependencies)
211         Output []string // file paths
212 }
213
214 func (c *Cmd) String() string {
215         return strings.Join(c.Args, " ")
216 }
217
218 // Run executes the Cmd.
219 func (c *Cmd) Run() error {
220         if c.Args[0] == "mkdir" {
221                 for _, p := range c.Output {
222                         if err := os.MkdirAll(p, 0777); err != nil {
223                                 return fmt.Errorf("command %q: %v", c, err)
224                         }
225                 }
226                 return nil
227         }
228         out := new(bytes.Buffer)
229         cmd := exec.Command(c.Args[0], c.Args[1:]...)
230         cmd.Dir = c.Dir
231         cmd.Env = c.Env
232         cmd.Stdout = out
233         cmd.Stderr = out
234         if c.Stdout != "" {
235                 f, err := os.Create(c.Stdout)
236                 if err != nil {
237                         return err
238                 }
239                 defer f.Close()
240                 cmd.Stdout = f
241         }
242         if err := cmd.Run(); err != nil {
243                 return fmt.Errorf("command %q: %v\n%v", c, err, out)
244         }
245         return nil
246 }
247
248 // ArchChar returns the architecture character for the given goarch.
249 // For example, ArchChar("amd64") returns "6".
250 func ArchChar(goarch string) (string, error) {
251         switch goarch {
252         case "386":
253                 return "8", nil
254         case "amd64":
255                 return "6", nil
256         case "arm":
257                 return "5", nil
258         }
259         return "", errors.New("unsupported GOARCH " + goarch)
260 }
261
262 type build struct {
263         script *Script
264         path   string
265         obj    string
266         goarch string
267         arch   string
268 }
269
270 func (b *build) abs(file string) string {
271         if filepath.IsAbs(file) {
272                 return file
273         }
274         return filepath.Join(b.path, file)
275 }
276
277 func (b *build) abss(file ...string) []string {
278         s := make([]string, len(file))
279         for i, f := range file {
280                 s[i] = b.abs(f)
281         }
282         return s
283 }
284
285 func (b *build) add(c Cmd) {
286         b.script.Cmd = append(b.script.Cmd, &c)
287 }
288
289 func (b *build) mkdir(name string) {
290         b.add(Cmd{
291                 Args:   []string{"mkdir", "-p", name},
292                 Output: []string{name},
293         })
294 }
295
296 func (b *build) gc(ofile string, gofiles ...string) {
297         gc := b.arch + "g"
298         args := append([]string{gc, "-o", ofile}, gcImportArgs...)
299         args = append(args, gofiles...)
300         b.add(Cmd{
301                 Args:   args,
302                 Input:  gofiles,
303                 Output: []string{ofile},
304         })
305 }
306
307 func (b *build) asm(ofile string, sfile string) {
308         asm := b.arch + "a"
309         b.add(Cmd{
310                 Args:   []string{asm, "-o", ofile, sfile},
311                 Input:  []string{sfile},
312                 Output: []string{ofile},
313         })
314 }
315
316 func (b *build) ld(targ string, ofiles ...string) {
317         ld := b.arch + "l"
318         args := append([]string{ld, "-o", targ}, ldImportArgs...)
319         args = append(args, ofiles...)
320         b.add(Cmd{
321                 Args:   args,
322                 Input:  ofiles,
323                 Output: []string{targ},
324         })
325 }
326
327 func (b *build) gopack(targ string, ofiles ...string) {
328         b.add(Cmd{
329                 Args:   append([]string{"gopack", "grc", targ}, ofiles...),
330                 Input:  ofiles,
331                 Output: []string{targ},
332         })
333 }
334
335 func (b *build) cc(ofile string, cfiles ...string) {
336         cc := b.arch + "c"
337         dir := fmt.Sprintf("%s_%s", runtime.GOOS, runtime.GOARCH)
338         inc := filepath.Join(runtime.GOROOT(), "pkg", dir)
339         args := []string{cc, "-FVw", "-I", inc, "-o", ofile}
340         b.add(Cmd{
341                 Args:   append(args, cfiles...),
342                 Input:  cfiles,
343                 Output: []string{ofile},
344         })
345 }
346
347 func (b *build) gccCompile(ofile, cfile string) {
348         b.add(Cmd{
349                 Args:   b.gccArgs("-o", ofile, "-c", cfile),
350                 Input:  []string{cfile},
351                 Output: []string{ofile},
352         })
353 }
354
355 func (b *build) gccLink(ofile string, ofiles ...string) {
356         b.add(Cmd{
357                 Args:   append(b.gccArgs("-o", ofile), ofiles...),
358                 Input:  ofiles,
359                 Output: []string{ofile},
360         })
361 }
362
363 func (b *build) gccArgs(args ...string) []string {
364         // TODO(adg): HOST_CC
365         a := []string{"gcc", "-I", b.path, "-g", "-fPIC", "-O2"}
366         switch b.arch {
367         case "8":
368                 a = append(a, "-m32")
369         case "6":
370                 a = append(a, "-m64")
371         }
372         return append(a, args...)
373 }
374
375 var cgoRe = regexp.MustCompile(`[/\\:]`)
376
377 func (b *build) cgo(cgofiles, cgocfiles []string) (outGo, outObj []string) {
378         // cgo
379         // TODO(adg): CGOPKGPATH
380         // TODO(adg): CGO_FLAGS
381         gofiles := []string{b.obj + "_cgo_gotypes.go"}
382         cfiles := []string{b.obj + "_cgo_main.c", b.obj + "_cgo_export.c"}
383         for _, fn := range cgofiles {
384                 f := b.obj + cgoRe.ReplaceAllString(fn[:len(fn)-2], "_")
385                 gofiles = append(gofiles, f+"cgo1.go")
386                 cfiles = append(cfiles, f+"cgo2.c")
387         }
388         defunC := b.obj + "_cgo_defun.c"
389         output := append([]string{defunC}, cfiles...)
390         output = append(output, gofiles...)
391         b.add(Cmd{
392                 Args:   append([]string{"cgo", "--"}, cgofiles...),
393                 Dir:    b.path,
394                 Env:    append(os.Environ(), "GOARCH="+b.goarch),
395                 Input:  cgofiles,
396                 Output: output,
397         })
398         outGo = append(outGo, gofiles...)
399         b.script.addIntermediate(defunC, b.obj+"_cgo_export.h", b.obj+"_cgo_flags")
400         b.script.addIntermediate(cfiles...)
401
402         // cc _cgo_defun.c
403         defunObj := b.obj + "_cgo_defun." + b.arch
404         b.cc(defunObj, defunC)
405         outObj = append(outObj, defunObj)
406
407         // gcc
408         linkobj := make([]string, 0, len(cfiles))
409         for _, cfile := range cfiles {
410                 ofile := cfile[:len(cfile)-1] + "o"
411                 b.gccCompile(ofile, cfile)
412                 linkobj = append(linkobj, ofile)
413                 if !strings.HasSuffix(ofile, "_cgo_main.o") {
414                         outObj = append(outObj, ofile)
415                 } else {
416                         b.script.addIntermediate(ofile)
417                 }
418         }
419         for _, cfile := range cgocfiles {
420                 ofile := b.obj + cgoRe.ReplaceAllString(cfile[:len(cfile)-1], "_") + "o"
421                 b.gccCompile(ofile, cfile)
422                 linkobj = append(linkobj, ofile)
423                 outObj = append(outObj, ofile)
424         }
425         dynObj := b.obj + "_cgo_.o"
426         b.gccLink(dynObj, linkobj...)
427         b.script.addIntermediate(dynObj)
428
429         // cgo -dynimport
430         importC := b.obj + "_cgo_import.c"
431         b.add(Cmd{
432                 Args:   []string{"cgo", "-dynimport", dynObj},
433                 Stdout: importC,
434                 Input:  []string{dynObj},
435                 Output: []string{importC},
436         })
437         b.script.addIntermediate(importC)
438
439         // cc _cgo_import.ARCH
440         importObj := b.obj + "_cgo_import." + b.arch
441         b.cc(importObj, importC)
442         outObj = append(outObj, importObj)
443
444         return
445 }