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.
5 // Package build provides tools for building Go packages.
21 // Build produces a build Script for the given package.
22 func Build(tree *Tree, pkg string, info *DirInfo) (*Script, error) {
26 path: filepath.Join(tree.SrcDir(), pkg),
28 b.obj = b.abs("_obj") + string(filepath.Separator)
30 b.goarch = runtime.GOARCH
31 if g := os.Getenv("GOARCH"); g != "" {
35 b.arch, err = ArchChar(b.goarch)
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)
49 s.addInput(filepath.Join(t.PkgDir(), p+".a"))
52 // .go files to be built with gc
53 gofiles := b.abss(info.GoFiles...)
54 s.addInput(gofiles...)
56 var ofiles []string // object files to be linked or packed
58 // make build directory
60 s.addIntermediate(b.obj)
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...)
77 ofile := b.obj + "_go_." + b.arch
78 b.gc(ofile, gofiles...)
79 ofiles = append(ofiles, ofile)
80 s.addIntermediate(ofile)
84 for _, sfile := range info.SFiles {
85 ofile := b.obj + sfile[:len(sfile)-1] + b.arch
89 ofiles = append(ofiles, ofile)
90 s.addIntermediate(ofile)
94 return nil, errors.New("make: no object files to build")
100 // use the last part of the import path as binary name
101 _, bin := filepath.Split(pkg)
102 if runtime.GOOS == "windows" {
105 targ = filepath.Join(tree.BinDir(), bin)
107 targ = filepath.Join(tree.PkgDir(), pkg+".a")
110 // make target directory
111 targDir, _ := filepath.Split(targ)
114 // link binary or pack object
115 if info.IsCommand() {
116 b.ld(targ, ofiles...)
118 b.gopack(targ, ofiles...)
120 s.Output = append(s.Output, targ)
125 // A Script describes the build process for a Go package.
126 // The Input, Intermediate, and Output fields are lists of absolute paths.
130 Intermediate []string
134 func (s *Script) addInput(file ...string) {
135 s.Input = append(s.Input, file...)
138 func (s *Script) addIntermediate(file ...string) {
139 s.Intermediate = append(s.Intermediate, file...)
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 {
152 // Stale returns true if the build's inputs are newer than its outputs.
153 func (s *Script) Stale() bool {
155 // get latest mtime of outputs
156 for _, file := range s.Output {
157 fi, err := os.Stat(file)
159 // any error reading output files means stale
162 if mtime := fi.ModTime(); mtime.After(latest) {
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)
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 {
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 {
198 if e := s.Clean(); err == nil {
204 // A Cmd describes an individual build command.
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
214 func (c *Cmd) String() string {
215 return strings.Join(c.Args, " ")
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)
228 out := new(bytes.Buffer)
229 cmd := exec.Command(c.Args[0], c.Args[1:]...)
235 f, err := os.Create(c.Stdout)
242 if err := cmd.Run(); err != nil {
243 return fmt.Errorf("command %q: %v\n%v", c, err, out)
248 // ArchChar returns the architecture character for the given goarch.
249 // For example, ArchChar("amd64") returns "6".
250 func ArchChar(goarch string) (string, error) {
259 return "", errors.New("unsupported GOARCH " + goarch)
270 func (b *build) abs(file string) string {
271 if filepath.IsAbs(file) {
274 return filepath.Join(b.path, file)
277 func (b *build) abss(file ...string) []string {
278 s := make([]string, len(file))
279 for i, f := range file {
285 func (b *build) add(c Cmd) {
286 b.script.Cmd = append(b.script.Cmd, &c)
289 func (b *build) mkdir(name string) {
291 Args: []string{"mkdir", "-p", name},
292 Output: []string{name},
296 func (b *build) gc(ofile string, gofiles ...string) {
298 args := append([]string{gc, "-o", ofile}, gcImportArgs...)
299 args = append(args, gofiles...)
303 Output: []string{ofile},
307 func (b *build) asm(ofile string, sfile string) {
310 Args: []string{asm, "-o", ofile, sfile},
311 Input: []string{sfile},
312 Output: []string{ofile},
316 func (b *build) ld(targ string, ofiles ...string) {
318 args := append([]string{ld, "-o", targ}, ldImportArgs...)
319 args = append(args, ofiles...)
323 Output: []string{targ},
327 func (b *build) gopack(targ string, ofiles ...string) {
329 Args: append([]string{"gopack", "grc", targ}, ofiles...),
331 Output: []string{targ},
335 func (b *build) cc(ofile string, cfiles ...string) {
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}
341 Args: append(args, cfiles...),
343 Output: []string{ofile},
347 func (b *build) gccCompile(ofile, cfile string) {
349 Args: b.gccArgs("-o", ofile, "-c", cfile),
350 Input: []string{cfile},
351 Output: []string{ofile},
355 func (b *build) gccLink(ofile string, ofiles ...string) {
357 Args: append(b.gccArgs("-o", ofile), ofiles...),
359 Output: []string{ofile},
363 func (b *build) gccArgs(args ...string) []string {
364 // TODO(adg): HOST_CC
365 a := []string{"gcc", "-I", b.path, "-g", "-fPIC", "-O2"}
368 a = append(a, "-m32")
370 a = append(a, "-m64")
372 return append(a, args...)
375 var cgoRe = regexp.MustCompile(`[/\\:]`)
377 func (b *build) cgo(cgofiles, cgocfiles []string) (outGo, outObj []string) {
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")
388 defunC := b.obj + "_cgo_defun.c"
389 output := append([]string{defunC}, cfiles...)
390 output = append(output, gofiles...)
392 Args: append([]string{"cgo", "--"}, cgofiles...),
394 Env: append(os.Environ(), "GOARCH="+b.goarch),
398 outGo = append(outGo, gofiles...)
399 b.script.addIntermediate(defunC, b.obj+"_cgo_export.h", b.obj+"_cgo_flags")
400 b.script.addIntermediate(cfiles...)
403 defunObj := b.obj + "_cgo_defun." + b.arch
404 b.cc(defunObj, defunC)
405 outObj = append(outObj, defunObj)
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)
416 b.script.addIntermediate(ofile)
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)
425 dynObj := b.obj + "_cgo_.o"
426 b.gccLink(dynObj, linkobj...)
427 b.script.addIntermediate(dynObj)
430 importC := b.obj + "_cgo_import.c"
432 Args: []string{"cgo", "-dynimport", dynObj},
434 Input: []string{dynObj},
435 Output: []string{importC},
437 b.script.addIntermediate(importC)
439 // cc _cgo_import.ARCH
440 importObj := b.obj + "_cgo_import." + b.arch
441 b.cc(importObj, importC)
442 outObj = append(outObj, importObj)