Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(393)

Delta Between Two Patch Sets: src/cmd/go/build.go

Issue 5504062: code review 5504062: build: fixes for Windows (Closed)
Left Patch Set: diff -r 498835bf2505 https://go.googlecode.com/hg/ Created 12 years, 3 months ago
Right Patch Set: diff -r 498835bf2505 https://go.googlecode.com/hg/ Created 12 years, 3 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « no previous file | src/make.bash » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
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 main
6
7 import (
8 "bytes"
9 "container/heap"
10 "errors"
11 "fmt"
12 "go/build"
13 "io"
14 "io/ioutil"
15 "os"
16 "os/exec"
17 "path/filepath"
18 "regexp"
19 "runtime"
20 "strings"
21 "sync"
22 )
23
24 // Break init cycles
25 func init() {
26 cmdBuild.Run = runBuild
27 cmdInstall.Run = runInstall
28 }
29
30 var cmdBuild = &Command{
31 UsageLine: "build [-a] [-n] [-x] [-o output] [importpath... | gofiles... ]",
32 Short: "compile packages and dependencies",
33 Long: `
34 Build compiles the packages named by the import paths,
35 along with their dependencies, but it does not install the results.
36
37 If the arguments are a list of .go files, build treats them as a list
38 of source files specifying a single package.
39
40 When the command line specifies a single main package,
41 build writes the resulting executable to output (default a.out).
42 Otherwise build compiles the packages but discards the results,
43 serving only as a check that the packages can be built.
44
45 The -a flag forces rebuilding of packages that are already up-to-date.
46 The -n flag prints the commands but does not run them.
47 The -x flag prints the commands.
48 The -o flag specifies the output file name.
49 It is an error to use -o when the command line specifies multiple packages.
50
51 For more about import paths, see 'go help importpath'.
52
53 See also: go install, go get, go clean.
54 `,
55 }
56
57 var buildA = cmdBuild.Flag.Bool("a", false, "")
58 var buildN = cmdBuild.Flag.Bool("n", false, "")
59 var buildX = cmdBuild.Flag.Bool("x", false, "")
60 var buildO = cmdBuild.Flag.String("o", "", "output file")
61
62 func runBuild(cmd *Command, args []string) {
63 var b builder
64 b.init(*buildA, *buildN, *buildX)
65
66 var pkgs []*Package
67 if len(args) > 0 && strings.HasSuffix(args[0], ".go") {
68 pkg := goFilesPackage(args, "")
69 pkgs = append(pkgs, pkg)
70 } else {
71 pkgs = packages(args)
72 }
73
74 if len(pkgs) == 1 && pkgs[0].Name == "main" && *buildO == "" {
75 *buildO = "a.out"
76 }
77
78 if *buildO != "" {
79 if len(pkgs) > 1 {
80 fatalf("go build: cannot use -o with multiple packages")
81 }
82 p := pkgs[0]
83 p.target = "" // must build - not up to date
84 a := b.action(modeInstall, modeBuild, p)
85 a.target = *buildO
86 b.do(a)
87 return
88 }
89
90 a := &action{}
91 for _, p := range packages(args) {
92 a.deps = append(a.deps, b.action(modeBuild, modeBuild, p))
93 }
94 b.do(a)
95 }
96
97 var cmdInstall = &Command{
98 UsageLine: "install [-a] [-n] [-x] [importpath...]",
99 Short: "compile and install packages and dependencies",
100 Long: `
101 Install compiles and installs the packages named by the import paths,
102 along with their dependencies.
103
104 The -a flag forces reinstallation of packages that are already up-to-date.
105 The -n flag prints the commands but does not run them.
106 The -x flag prints the commands.
107
108 For more about import paths, see 'go help importpath'.
109
110 See also: go build, go get, go clean.
111 `,
112 }
113
114 var installA = cmdInstall.Flag.Bool("a", false, "")
115 var installN = cmdInstall.Flag.Bool("n", false, "")
116 var installX = cmdInstall.Flag.Bool("x", false, "")
117
118 func runInstall(cmd *Command, args []string) {
119 var b builder
120 b.init(*installA, *installN, *installX)
121 a := &action{}
122 for _, p := range packages(args) {
123 a.deps = append(a.deps, b.action(modeInstall, modeInstall, p))
124 }
125 b.do(a)
126 }
127
128 // A builder holds global state about a build.
129 // It does not hold per-package state, because eventually we will
130 // build packages in parallel, and the builder will be shared.
131 type builder struct {
132 work string // the temporary work directory (ends i n filepath.Separator)
133 aflag bool // the -a flag
134 nflag bool // the -n flag
135 xflag bool // the -x flag
136 arch string // e.g., "6"
137 goroot string // the $GOROOT
138 goarch string // the $GOARCH
139 goos string // the $GOOS
140 gobin string // the $GOBIN
141 exe string // the executable suffix - "" or ".exe"
142 gcflags []string // additional flags for Go compiler
143 actionCache map[cacheKey]*action // a cache of already-constructed actio ns
144 mkdirCache map[string]bool // a cache of created directories
145
146 output sync.Mutex
147 scriptDir string // current directory in printed script
148
149 exec sync.Mutex
150 readySema chan bool
151 ready actionQueue
152 }
153
154 // An action represents a single action in the action graph.
155 type action struct {
156 p *Package // the package this action works on
157 deps []*action // actions that must happen before this one
158 triggers []*action // inverse of deps
159 cgo *action // action for cgo binary if needed
160
161 f func(*builder, *action) error // the action itself (nil = no- op)
162 ignoreFail bool // whether to run f even if dep endencies fail
163
164 // Generated files, directories.
165 link bool // target is executable, not just package
166 pkgdir string // the -I or -L argument to use when importing this packag e
167 objdir string // directory for intermediate objects
168 objpkg string // the intermediate package .a file created during the act ion
169 target string // goal of the action: the created package or executable
170
171 // Execution state.
172 pending int // number of deps yet to complete
173 priority int // relative execution priority
174 failed bool // whether the action failed
175 }
176
177 // cacheKey is the key for the action cache.
178 type cacheKey struct {
179 mode buildMode
180 p *Package
181 }
182
183 // buildMode specifies the build mode:
184 // are we just building things or also installing the results?
185 type buildMode int
186
187 const (
188 modeBuild buildMode = iota
189 modeInstall
190 )
191
192 func (b *builder) init(aflag, nflag, xflag bool) {
193 var err error
194 b.aflag = aflag
195 b.nflag = nflag
196 b.xflag = xflag
197 b.actionCache = make(map[cacheKey]*action)
198 b.mkdirCache = make(map[string]bool)
199 b.goarch = build.DefaultContext.GOARCH
200 b.goos = build.DefaultContext.GOOS
201 b.goroot = build.Path[0].Path
202 b.gobin = build.Path[0].BinDir()
203 if b.goos == "windows" {
204 b.exe = ".exe"
205 }
206 b.gcflags = strings.Fields(os.Getenv("GCFLAGS"))
207
208 b.arch, err = build.ArchChar(b.goarch)
209 if err != nil {
210 fatalf("%s", err)
211 }
212
213 if nflag {
214 b.work = "$WORK"
215 } else {
216 b.work, err = ioutil.TempDir("", "go-build")
217 if err != nil {
218 fatalf("%s", err)
219 }
220 if b.xflag {
221 fmt.Printf("WORK=%s\n", b.work)
222 }
223 atexit(func() { os.RemoveAll(b.work) })
224 }
225 }
226
227 // goFilesPackage creates a package for building a collection of Go files
228 // (typically named on the command line). If target is given, the package
229 // target is target. Otherwise, the target is named p.a for
230 // package p or named after the first Go file for package main.
231 func goFilesPackage(gofiles []string, target string) *Package {
232 // TODO: Remove this restriction.
233 for _, f := range gofiles {
234 if !strings.HasSuffix(f, ".go") || strings.Contains(f, "/") || s trings.Contains(f, string(filepath.Separator)) {
235 fatalf("named files must be in current directory and .go files")
236 }
237 }
238
239 // Synthesize fake "directory" that only shows those two files,
240 // to make it look like this is a standard package or
241 // command directory.
242 var dir []os.FileInfo
243 for _, file := range gofiles {
244 fi, err := os.Stat(file)
245 if err != nil {
246 fatalf("%s", err)
247 }
248 if fi.IsDir() {
249 fatalf("%s is a directory, should be a Go file", file)
250 }
251 dir = append(dir, fi)
252 }
253 ctxt := build.DefaultContext
254 ctxt.ReadDir = func(string) ([]os.FileInfo, error) { return dir, nil }
255 pwd, _ := os.Getwd()
256 pkg, err := scanPackage(&ctxt, &build.Tree{Path: "."}, "<command line>", "<command line>", pwd)
257 if err != nil {
258 fatalf("%s", err)
259 }
260 if target != "" {
261 pkg.target = target
262 } else if pkg.Name == "main" {
263 pkg.target = gofiles[0][:len(gofiles[0])-len(".go")]
264 } else {
265 pkg.target = pkg.Name + ".a"
266 }
267 pkg.ImportPath = "_/" + pkg.target
268 return pkg
269 }
270
271 // action returns the action for applying the given operation (mode) to the pack age.
272 // depMode is the action to use when building dependencies.
273 func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action {
274 key := cacheKey{mode, p}
275 a := b.actionCache[key]
276 if a != nil {
277 return a
278 }
279
280 a = &action{p: p, pkgdir: p.t.PkgDir()}
281 if p.pkgdir != "" { // overrides p.t
282 a.pkgdir = p.pkgdir
283 }
284
285 b.actionCache[key] = a
286
287 for _, p1 := range p.imports {
288 a.deps = append(a.deps, b.action(depMode, depMode, p1))
289 }
290
291 if len(p.CgoFiles) > 0 {
292 p1, err := loadPackage("cmd/cgo")
293 if err != nil {
294 fatalf("load cmd/cgo: %v", err)
295 }
296 a.cgo = b.action(depMode, depMode, p1)
297 a.deps = append(a.deps, a.cgo)
298 }
299
300 if p.Standard {
301 switch p.ImportPath {
302 case "builtin", "unsafe":
303 // Fake packages - nothing to build.
304 return a
305 }
306 }
307
308 if !p.Stale && !b.aflag && p.target != "" {
309 // p.Stale==false implies that p.target is up-to-date.
310 // Record target name for use by actions depending on this one.
311 a.target = p.target
312 return a
313 }
314
315 a.objdir = filepath.Join(b.work, filepath.FromSlash(a.p.ImportPath+"/_ob j")) + string(filepath.Separator)
316 a.objpkg = filepath.Join(b.work, filepath.FromSlash(a.p.ImportPath+".a") )
317 a.link = p.Name == "main"
318
319 switch mode {
320 case modeInstall:
321 a.f = (*builder).install
322 a.deps = []*action{b.action(modeBuild, depMode, p)}
323 a.target = a.p.target
324 case modeBuild:
325 a.f = (*builder).build
326 a.target = a.objpkg
327 if a.link {
328 // An executable file.
329 // Have to use something other than .a for the suffix.
330 // It is easier on Windows if we use .exe, so use .exe e verywhere.
331 // (This is the name of a temporary file.)
332 a.target = a.objdir + "a.out" + b.exe
333 }
334 }
335
336 return a
337 }
338
339 // do runs the action graph rooted at root.
340 func (b *builder) do(root *action) {
341 // Build list of all actions, assigning depth-first post-order priority.
342 // The original implementation here was a true queue
343 // (using a channel) but it had the effect of getting
344 // distracted by low-level leaf actions to the detriment
345 // of completing higher-level actions. The order of
346 // work does not matter much to overall execution time,
347 // but when running "go test std" it is nice to see each test
348 // results as soon as possible. The priorities assigned
349 // ensure that, all else being equal, the execution prefers
350 // to do what it would have done first in a simple depth-first
351 // dependency order traversal.
352 all := map[*action]bool{}
353 priority := 0
354 var walk func(*action)
355 walk = func(a *action) {
356 if all[a] {
357 return
358 }
359 all[a] = true
360 priority++
361 for _, a1 := range a.deps {
362 walk(a1)
363 }
364 a.priority = priority
365 }
366 walk(root)
367
368 b.readySema = make(chan bool, len(all))
369 done := make(chan bool)
370
371 // Initialize per-action execution state.
372 for a := range all {
373 for _, a1 := range a.deps {
374 a1.triggers = append(a1.triggers, a)
375 }
376 a.pending = len(a.deps)
377 if a.pending == 0 {
378 b.ready.push(a)
379 b.readySema <- true
380 }
381 }
382
383 // Handle runs a single action and takes care of triggering
384 // any actions that are runnable as a result.
385 handle := func(a *action) {
386 var err error
387 if a.f != nil && (!a.failed || a.ignoreFail) {
388 err = a.f(b, a)
389 }
390
391 // The actions run in parallel but all the updates to the
392 // shared work state are serialized through b.exec.
393 b.exec.Lock()
394 defer b.exec.Unlock()
395
396 if err != nil {
397 if err == errPrintedOutput {
398 exitStatus = 2
399 } else {
400 errorf("%s", err)
401 }
402 a.failed = true
403 }
404
405 for _, a0 := range a.triggers {
406 if a.failed {
407 a0.failed = true
408 }
409 if a0.pending--; a0.pending == 0 {
410 b.ready.push(a0)
411 b.readySema <- true
412 }
413 }
414
415 if a == root {
416 close(b.readySema)
417 done <- true
418 }
419 }
420
421 // TODO: Turn this knob for parallelism.
422 for i := 0; i < 1; i++ {
423 go func() {
424 for _ = range b.readySema {
425 // Receiving a value from b.sema entitles
426 // us to take from the ready queue.
427 b.exec.Lock()
428 a := b.ready.pop()
429 b.exec.Unlock()
430 handle(a)
431 }
432 }()
433 }
434
435 <-done
436 }
437
438 // build is the action for building a single package or command.
439 func (b *builder) build(a *action) error {
440 if b.nflag {
441 // In -n mode, print a banner between packages.
442 // The banner is five lines so that when changes to
443 // different sections of the bootstrap script have to
444 // be merged, the banners give patch something
445 // to use to find its context.
446 fmt.Printf("\n#\n# %s\n#\n\n", a.p.ImportPath)
447 }
448
449 // make build directory
450 obj := a.objdir
451 if err := b.mkdir(obj); err != nil {
452 return err 1 return err
453 } 2 }
454 3
455 » var gofiles, cfiles, sfiles, objects, cgoObjects []string 4 » var gofiles, cfiles, sfiles, objects []string
456 gofiles = append(gofiles, a.p.GoFiles...) 5 gofiles = append(gofiles, a.p.GoFiles...)
457 cfiles = append(cfiles, a.p.CFiles...) 6 cfiles = append(cfiles, a.p.CFiles...)
458 sfiles = append(sfiles, a.p.SFiles...) 7 sfiles = append(sfiles, a.p.SFiles...)
459
460 // run cgo
461 if len(a.p.CgoFiles) > 0 {
462 // In a package using cgo, cgo compiles the C and assembly files with gcc.··
463 // There is one exception: runtime/cgo's job is to bridge the
464 // cgo and non-cgo worlds, so it necessarily has files in both.
465 // In that case gcc only gets the gcc_* files.
466 var gccfiles []string
467 println("std?", a.p.Standard, a.p.ImportPath)
468 if a.p.Standard && a.p.ImportPath == "runtime/cgo" {
469 filter := func(files, nongcc, gcc []string) ([]string, [ ]string) {
470 for _, f := range files {
471 if strings.HasPrefix(f, "gcc_") {
472 gcc = append(gcc, f)
473 } else {
474 nongcc = append(nongcc, f)
475 }
476 }
477 return nongcc, gcc
478 }
479 cfiles, gccfiles = filter(cfiles, cfiles[:0], gccfiles)
480 sfiles, gccfiles = filter(sfiles, sfiles[:0], gccfiles)
481 } else {
482 gccfiles = append(cfiles, sfiles...)
483 cfiles = nil
484 sfiles = nil
485 }
486
487 outGo, outObj, err := b.cgo(a.p, a.cgo.target, obj, gccfiles)
488 if err != nil {
489 return err
490 }
491 cgoObjects = append(cgoObjects, outObj...)
492 gofiles = append(gofiles, outGo...)
493 }
494
495 // prepare Go import path list
496 inc := []string{}
497 incMap := map[string]bool{}
498
499 incMap[b.work] = true // handled later
500 incMap[build.Path[0].PkgDir()] = true // goroot
501 incMap[""] = true // ignore empty strings
502
503 // build package directories of dependencies
504 for _, a1 := range a.deps {
505 if pkgdir := a1.pkgdir; !incMap[pkgdir] {
506 incMap[pkgdir] = true
507 inc = append(inc, "-I", pkgdir)
508 }
509 }
510
511 // work directory
512 inc = append(inc, "-I", b.work)
513
514 // then installed package directories of dependencies
515 for _, a1 := range a.deps {
516 if pkgdir := a1.p.t.PkgDir(); !incMap[pkgdir] {
517 incMap[pkgdir] = true
518 inc = append(inc, "-I", pkgdir)
519 }
520 }
521
522 // compile Go
523 if len(gofiles) > 0 {
524 out := "_go_." + b.arch
525 gcargs := []string{"-p", a.p.ImportPath}
526 if a.p.Standard && a.p.ImportPath == "runtime" {
527 // runtime compiles with a special 6g flag to emit
528 // additional reflect type data.
529 gcargs = append(gcargs, "-+")
530 }
531 if err := b.gc(a.p, obj+out, gcargs, inc, gofiles); err != nil {
532 return err
533 }
534 objects = append(objects, out)
535 }
536
537 // copy .h files named for goos or goarch or goos_goarch
538 // to names using GOOS and GOARCH.
539 // For example, defs_linux_amd64.h becomes defs_GOOS_GOARCH.h.
540 _goos_goarch := "_" + b.goos + "_" + b.goarch + ".h"
541 _goos := "_" + b.goos + ".h"
542 _goarch := "_" + b.goarch + ".h"
543 for _, file := range a.p.HFiles {
544 switch {
545 case strings.HasSuffix(file, _goos_goarch):
546 targ := file[:len(file)-len(_goos_goarch)] + "_GOOS_GOAR CH.h"
547 if err := b.copyFile(obj+targ, filepath.Join(a.p.Dir, fi le), 0666); err != nil {
548 return err
549 }
550 case strings.HasSuffix(file, _goarch):
551 targ := file[:len(file)-len(_goarch)] + "_GOARCH.h"
552 if err := b.copyFile(obj+targ, filepath.Join(a.p.Dir, fi le), 0666); err != nil {
553 return err
554 }
555 case strings.HasSuffix(file, _goos):
556 targ := file[:len(file)-len(_goos)] + "_GOOS.h"
557 if err := b.copyFile(obj+targ, filepath.Join(a.p.Dir, fi le), 0666); err != nil {
558 return err
559 }
560 }
561 }
562
563 for _, file := range cfiles {
564 out := file[:len(file)-len(".c")] + "." + b.arch
565 if err := b.cc(a.p, obj, obj+out, file); err != nil {
566 return err
567 }
568 objects = append(objects, out)
569 }
570
571 // assemble .s files
572 for _, file := range sfiles {
573 out := file[:len(file)-len(".s")] + "." + b.arch
574 if err := b.asm(a.p, obj, obj+out, file); err != nil {
575 return err
576 }
577 objects = append(objects, out)
578 }
579
580 // NOTE(rsc): On Windows, it is critically important that the
581 // gcc-compiled objects (cgoObjects) be listed after the ordinary
582 // objects in the archive. I do not know why this is, and I am
583 // not sure I want to know. http://golang.org/issue/2601
r 2011/12/21 20:48:38 take out the editorializing by s/,.*know/./
584 objects = append(objects, cgoObjects...)
585
586 // pack into archive in obj directory
587 if err := b.gopack(a.p, obj, a.objpkg, objects); err != nil {
588 return err
589 }
590
591 // link if needed.
592 if a.link {
593 // command.
594 // import paths for compiler are introduced by -I.
595 // for linker, they are introduced by -L.
596 for i := 0; i < len(inc); i += 2 {
597 inc[i] = "-L"
598 }
599 if err := b.ld(a.p, a.target, inc, a.objpkg); err != nil {
600 return err
601 }
602 }
603
604 return nil
605 }
606
607 // install is the action for installing a single package or executable.
608 func (b *builder) install(a *action) error {
609 a1 := a.deps[0]
610 perm := uint32(0666)
611 if a1.link {
612 perm = 0777
613 }
614
615 // make target directory
616 dir, _ := filepath.Split(a.target)
617 if dir != "" {
618 if err := b.mkdir(dir); err != nil {
619 return err
620 }
621 }
622
623 return b.copyFile(a.target, a1.target, perm)
624 }
625
626 // removeByRenaming removes file name by moving it to a tmp
627 // directory and deleting the target if possible.
628 func removeByRenaming(name string) error {
629 f, err := ioutil.TempFile("", "")
630 if err != nil {
631 return err
632 }
633 tmpname := f.Name()
634 f.Close()
635 err = os.Remove(tmpname)
636 if err != nil {
637 return err
638 }
639 err = os.Rename(name, tmpname)
640 if err != nil {
641 // assume name file does not exists,
642 // otherwise later code will fail.
643 return nil
644 }
645 err = os.Remove(tmpname)
646 if err != nil {
647 // TODO(brainman): file is locked and can't be deleted.
648 // We need to come up with a better way of doing it.·
649 }
650 return nil
651 }
652
653 // copyFile is like 'cp src dst'.
654 func (b *builder) copyFile(dst, src string, perm uint32) error {
655 if b.nflag || b.xflag {
656 b.showcmd("", "cp %s %s", src, dst)
657 if b.nflag {
658 return nil
659 }
660 }
661
662 sf, err := os.Open(src)
663 if err != nil {
664 return err
665 }
666 defer sf.Close()
667 os.Remove(dst)
668 df, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
669 if err != nil {
670 if runtime.GOOS != "windows" {
671 return err
672 }
673 // Windows does not allow to replace binary file
674 // while it is executing. We will cheat.
675 err = removeByRenaming(dst)
676 if err != nil {
677 return err
678 }
679 df, err = os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, p erm)
680 if err != nil {
681 return err
682 }
683 }
684 _, err = io.Copy(df, sf)
685 df.Close()
686 if err != nil {
687 os.Remove(dst)
688 return err
689 }
690 return nil
691 }
692
693 // fmtcmd formats a command in the manner of fmt.Sprintf but also:
694 //
695 // If dir is non-empty and the script is not in dir right now,
696 // fmtcmd inserts "cd dir\n" before the command.
697 //
698 // fmtcmd replaces the value of b.work with $WORK.
699 // fmtcmd replaces the value of b.goroot with $GOROOT.
700 // fmtcmd replaces the value of b.gobin with $GOBIN.
701 //
702 // fmtcmd replaces the name of the current directory with dot (.)
703 // but only when it is at the beginning of a space-separated token.
704 //
705 func (b *builder) fmtcmd(dir string, format string, args ...interface{}) string {
706 cmd := fmt.Sprintf(format, args...)
707 if dir != "" {
708 cmd = strings.Replace(" "+cmd, " "+dir, " .", -1)[1:]
709 if b.scriptDir != dir {
710 b.scriptDir = dir
711 cmd = "cd " + dir + "\n" + cmd
712 }
713 }
714 cmd = strings.Replace(cmd, b.work, "$WORK", -1)
715 cmd = strings.Replace(cmd, b.gobin, "$GOBIN", -1)
716 cmd = strings.Replace(cmd, b.goroot, "$GOROOT", -1)
717 return cmd
718 }
719
720 // showcmd prints the given command to standard output
721 // for the implementation of -n or -x.
722 func (b *builder) showcmd(dir string, format string, args ...interface{}) {
723 b.output.Lock()
724 defer b.output.Unlock()
725 fmt.Println(b.fmtcmd(dir, format, args...))
726 }
727
728 // showOutput prints "# desc" followed by the given output.
729 // The output is expected to contain references to 'dir', usually
730 // the source directory for the package that has failed to build.
731 // showOutput rewrites mentions of dir with a relative path to dir
732 // when the relative path is shorter. This is usually more pleasant.
733 // For example, if fmt doesn't compile and we are in src/pkg/html,
734 // the output is
735 //
736 // $ go build
737 // # fmt
738 // ../fmt/print.go:1090: undefined: asdf
739 // $
740 //
741 // instead of
742 //
743 // $ go build
744 // # fmt
745 // /usr/gopher/go/src/pkg/fmt/print.go:1090: undefined: asdf
746 // $
747 //
748 // showOutput also replaces references to the work directory with $WORK.
749 //
750 func (b *builder) showOutput(dir, desc, out string) {
751 prefix := "# " + desc
752 suffix := "\n" + out
753 pwd, _ := os.Getwd()
754 if reldir, err := filepath.Rel(pwd, dir); err == nil && len(reldir) < le n(dir) {
755 suffix = strings.Replace(suffix, " "+dir, " "+reldir, -1)
756 suffix = strings.Replace(suffix, "\n"+dir, "\n"+reldir, -1)
757 }
758 suffix = strings.Replace(suffix, " "+b.work, " $WORK", -1)
759
760 b.output.Lock()
761 defer b.output.Unlock()
762 fmt.Print(prefix, suffix)
763 }
764
765 // errPrintedOutput is a special error indicating that a command failed
766 // but that it generated output as well, and that output has already
767 // been printed, so there's no point showing 'exit status 1' or whatever
768 // the wait status was. The main executor, builder.do, knows not to
769 // print this error.
770 var errPrintedOutput = errors.New("already printed output - no need to show erro r")
771
772 // run runs the command given by cmdline in the directory dir.
773 // If the commnd fails, run prints information about the failure
774 // and returns a non-nil error.
775 func (b *builder) run(dir string, desc string, cmdline ...string) error {
776 if b.nflag || b.xflag {
777 b.showcmd(dir, "%s", strings.Join(cmdline, " "))
778 if b.nflag {
779 return nil
780 }
781 }
782
783 var buf bytes.Buffer
784 cmd := exec.Command(cmdline[0], cmdline[1:]...)
785 cmd.Stdout = &buf
786 cmd.Stderr = &buf
787 cmd.Dir = dir
788 // TODO: cmd.Env
789 err := cmd.Run()
790 if buf.Len() > 0 {
791 out := buf.Bytes()
792 if out[len(out)-1] != '\n' {
793 out = append(out, '\n')
794 }
795 if desc == "" {
796 desc = b.fmtcmd(dir, "%s", strings.Join(cmdline, " "))
797 }
798 b.showOutput(dir, desc, string(out))
799 if err != nil {
800 err = errPrintedOutput
801 }
802 }
803 return err
804 }
805
806 // mkdir makes the named directory.
807 func (b *builder) mkdir(dir string) error {
808 // We can be a little aggressive about being
809 // sure directories exist. Skip repeated calls.
810 if b.mkdirCache[dir] {
811 return nil
812 }
813 b.mkdirCache[dir] = true
814
815 if b.nflag || b.xflag {
816 b.showcmd("", "mkdir -p %s", dir)
817 if b.nflag {
818 return nil
819 }
820 }
821
822 if err := os.MkdirAll(dir, 0777); err != nil {
823 return err
824 }
825 return nil
826 }
827
828 // mkAbs returns an absolute path corresponding to
829 // evaluating f in the directory dir.
830 // We always pass absolute paths of source files so that
831 // the error messages will include the full path to a file
832 // in need of attention.
833 func mkAbs(dir, f string) string {
834 // Leave absolute paths alone.
835 // Also, during -n mode we use the pseudo-directory $WORK
836 // instead of creating an actual work directory that won't be used.
837 // Leave paths beginning with $WORK alone too.
838 if filepath.IsAbs(f) || strings.HasPrefix(f, "$WORK") {
839 return f
840 }
841 return filepath.Join(dir, f)
842 }
843
844 // gc runs the Go compiler in a specific directory on a set of files
845 // to generate the named output file.·
846 func (b *builder) gc(p *Package, ofile string, gcargs, importArgs []string, gofi les []string) error {
847 args := []string{b.arch + "g", "-o", ofile}
848 args = append(args, b.gcflags...)
849 args = append(args, gcargs...)
850 args = append(args, importArgs...)
851 for _, f := range gofiles {
852 args = append(args, mkAbs(p.Dir, f))
853 }
854 return b.run(p.Dir, p.ImportPath, args...)
855 }
856
857 // asm runs the assembler in a specific directory on a specific file
858 // to generate the named output file.·
859 func (b *builder) asm(p *Package, obj, ofile, sfile string) error {
860 sfile = mkAbs(p.Dir, sfile)
861 return b.run(p.Dir, p.ImportPath, b.arch+"a", "-I", obj, "-o", ofile, "- DGOOS_"+b.goos, "-DGOARCH_"+b.goarch, sfile)
862 }
863
864 // gopack runs the assembler in a specific directory to create
865 // an archive from a set of object files.
866 // typically it is run in the object directory.
867 func (b *builder) gopack(p *Package, objDir, afile string, ofiles []string) erro r {
868 cmd := []string{"gopack", "grc"}
869 cmd = append(cmd, mkAbs(objDir, afile))
870 for _, f := range ofiles {
871 cmd = append(cmd, mkAbs(objDir, f))
872 }
873 return b.run(p.Dir, p.ImportPath, cmd...)
874 }
875
876 // ld runs the linker to create a package starting at mainpkg.
877 func (b *builder) ld(p *Package, out string, importArgs []string, mainpkg string ) error {
878 return b.run(p.Dir, p.ImportPath, append(append([]string{b.arch + "l", " -o", out}, importArgs...), mainpkg)...)
879 }
880
881 // cc runs the gc-toolchain C compiler in a directory on a C file
882 // to produce an output file.
883 func (b *builder) cc(p *Package, objdir, ofile, cfile string) error {
884 inc := filepath.Join(b.goroot, "pkg", fmt.Sprintf("%s_%s", b.goos, b.goa rch))
885 cfile = mkAbs(p.Dir, cfile)
886 return b.run(p.Dir, p.ImportPath, b.arch+"c", "-FVw", "-I", objdir, "-I" , inc, "-o", ofile, "-DGOOS_"+b.goos, "-DGOARCH_"+b.goarch, cfile)
887 }
888
889 // gcc runs the gcc C compiler to create an object from a single C file.
890 func (b *builder) gcc(p *Package, out string, flags []string, cfile string) erro r {
891 cfile = mkAbs(p.Dir, cfile)
892 return b.run(p.Dir, p.ImportPath, b.gccCmd(p.Dir, flags, "-o", out, "-c" , cfile)...)
893 }
894
895 // gccld runs the gcc linker to create an executable from a set of object files
896 func (b *builder) gccld(p *Package, out string, flags []string, obj []string) er ror {
897 return b.run(p.Dir, p.ImportPath, append(b.gccCmd(p.Dir, flags, "-o", ou t), obj...)...)
898 }
899
900 // gccCmd returns a gcc command line ending with args
901 func (b *builder) gccCmd(objdir string, flags []string, args ...string) []string {
902 // TODO: HOST_CC?
903 a := []string{"gcc", "-I", objdir, "-g", "-O2"}
904
905 // Definitely want -fPIC but on Windows gcc complains
906 // "-fPIC ignored for target (all code is position independent)"
907 if b.goos != "windows" {
908 a = append(a, "-fPIC")
909 }
910 switch b.arch {
911 case "8":
912 a = append(a, "-m32")
913 case "6":
914 a = append(a, "-m64")
915 }
916 a = append(a, flags...)
917 return append(a, args...)
918 }
919
920 var cgoRe = regexp.MustCompile(`[/\\:]`)
921
922 func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, outObj []string, err error) {
923 if b.goos != runtime.GOOS {
924 return nil, nil, errors.New("cannot use cgo when compiling for a different operating system")
925 }
926
927 outObj = append(outObj, "") // for importObj, at end of function
928
929 // cgo
930 // TODO: CGOPKGPATH, CGO_FLAGS?
931 gofiles := []string{obj + "_cgo_gotypes.go"}
932 cfiles := []string{"_cgo_main.c", "_cgo_export.c"}
933 for _, fn := range p.CgoFiles {
934 f := cgoRe.ReplaceAllString(fn[:len(fn)-2], "_")
935 gofiles = append(gofiles, obj+f+"cgo1.go")
936 cfiles = append(cfiles, f+"cgo2.c")
937 }
938 defunC := obj + "_cgo_defun.c"
939 // TODO: make cgo not depend on $GOARCH?
940 // TODO: make cgo write to obj
941 cgoArgs := []string{cgoExe, "-objdir", obj}
942 if p.Standard && p.ImportPath == "runtime/cgo" {
943 cgoArgs = append(cgoArgs, "-import_runtime_cgo=false")
944 }
945 cgoArgs = append(cgoArgs, "--")
946 cgoArgs = append(cgoArgs, p.CgoFiles...)
947 if err := b.run(p.Dir, p.ImportPath, cgoArgs...); err != nil {
948 return nil, nil, err
949 }
950 outGo = append(outGo, gofiles...)
951
952 // cc _cgo_defun.c
953 defunObj := obj + "_cgo_defun." + b.arch
954 if err := b.cc(p, obj, defunObj, defunC); err != nil {
955 return nil, nil, err
956 }
957 outObj = append(outObj, defunObj)
958
959 // gcc
960 var linkobj []string
961 for _, cfile := range cfiles {
962 ofile := obj + cfile[:len(cfile)-1] + "o"
963 if err := b.gcc(p, ofile, p.info.CgoCFLAGS, obj+cfile); err != n il {
964 return nil, nil, err
965 }
966 linkobj = append(linkobj, ofile)
967 if !strings.HasSuffix(ofile, "_cgo_main.o") {
968 outObj = append(outObj, ofile)
969 }
970 }
971 for _, file := range gccfiles {
972 ofile := obj + cgoRe.ReplaceAllString(file[:len(file)-1], "_") + "o"
973 if err := b.gcc(p, ofile, p.info.CgoCFLAGS, file); err != nil {
974 return nil, nil, err
975 }
976 linkobj = append(linkobj, ofile)
977 outObj = append(outObj, ofile)
978 }
979 dynobj := obj + "_cgo_.o"
980 if err := b.gccld(p, dynobj, p.info.CgoLDFLAGS, linkobj); err != nil {
981 return nil, nil, err
982 }
983
984 // cgo -dynimport
985 importC := obj + "_cgo_import.c"
986 if err := b.run(p.Dir, p.ImportPath, cgoExe, "-objdir", obj, "-dynimport ", dynobj, "-dynout", importC); err != nil {
987 return nil, nil, err
988 }
989
990 // cc _cgo_import.ARCH
991 importObj := obj + "_cgo_import." + b.arch
992 if err := b.cc(p, obj, importObj, importC); err != nil {
993 return nil, nil, err
994 }
995
996 // NOTE(rsc): The importObj is a 5c/6c/8c object and on Windows
997 // must be processed before the gcc-generated objects.
998 // Put it first. We left room above. http://golang.org/issue/2601
999 outObj[0] = importObj
1000
1001 return outGo, outObj, nil
1002 }
1003
1004 // An actionQueue is a priority queue of actions.
1005 type actionQueue []*action
1006
1007 // Implement heap.Interface
1008 func (q *actionQueue) Len() int { return len(*q) }
1009 func (q *actionQueue) Swap(i, j int) { (*q)[i], (*q)[j] = (*q)[j], (*q)[i] }
1010 func (q *actionQueue) Less(i, j int) bool { return (*q)[i].priority < (*q)[j].pr iority }
1011 func (q *actionQueue) Push(x interface{}) { *q = append(*q, x.(*action)) }
1012 func (q *actionQueue) Pop() interface{} {
1013 n := len(*q) - 1
1014 x := (*q)[n]
1015 *q = (*q)[:n]
1016 return x
1017 }
1018
1019 func (q *actionQueue) push(a *action) {
1020 heap.Push(q, a)
1021 }
1022
1023 func (q *actionQueue) pop() *action {
1024 return heap.Pop(q).(*action)
1025 }
LEFTRIGHT
« no previous file | src/make.bash » ('j') | Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Toggle Comments ('s')

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b