github.com/seh/gb@v0.4.4-0.20160724065125-065d2b2b1ba1/gc.go (about) 1 package gb 2 3 import ( 4 "bytes" 5 "fmt" 6 "go/build" 7 "io" 8 "io/ioutil" 9 "os" 10 "path/filepath" 11 "runtime" 12 "strings" 13 14 "github.com/pkg/errors" 15 ) 16 17 // gc toolchain 18 19 type gcToolchain struct { 20 gc, cc, ld, as, pack string 21 } 22 23 func GcToolchain() func(c *Context) error { 24 return func(c *Context) error { 25 // TODO(dfc) this should come from the context, not the runtime. 26 goroot := runtime.GOROOT() 27 28 if goversion == 1.4 && (c.gohostos != c.gotargetos || c.gohostarch != c.gotargetarch) { 29 // cross-compliation is not supported yet #31 30 return errors.Errorf("cross compilation from host %s/%s to target %s/%s not supported with Go 1.4", c.gohostos, c.gohostarch, c.gotargetos, c.gotargetarch) 31 } 32 33 tooldir := filepath.Join(goroot, "pkg", "tool", c.gohostos+"_"+c.gohostarch) 34 exe := "" 35 if c.gohostos == "windows" { 36 exe += ".exe" 37 } 38 switch { 39 case goversion == 1.4: 40 archchar, err := build.ArchChar(c.gotargetarch) 41 if err != nil { 42 return err 43 } 44 c.tc = &gcToolchain{ 45 gc: filepath.Join(tooldir, archchar+"g"+exe), 46 ld: filepath.Join(tooldir, archchar+"l"+exe), 47 as: filepath.Join(tooldir, archchar+"a"+exe), 48 cc: filepath.Join(tooldir, archchar+"c"+exe), 49 pack: filepath.Join(tooldir, "pack"+exe), 50 } 51 case goversion > 1.4: 52 c.tc = &gcToolchain{ 53 gc: filepath.Join(tooldir, "compile"+exe), 54 ld: filepath.Join(tooldir, "link"+exe), 55 as: filepath.Join(tooldir, "asm"+exe), 56 pack: filepath.Join(tooldir, "pack"+exe), 57 } 58 default: 59 return errors.Errorf("unsupported Go version: %v", runtime.Version) 60 } 61 return nil 62 } 63 } 64 65 func (t *gcToolchain) Asm(pkg *Package, srcdir, ofile, sfile string) error { 66 args := []string{"-o", ofile, "-D", "GOOS_" + pkg.gotargetos, "-D", "GOARCH_" + pkg.gotargetarch} 67 switch { 68 case goversion == 1.4: 69 includedir := filepath.Join(runtime.GOROOT(), "pkg", pkg.gotargetos+"_"+pkg.gotargetarch) 70 args = append(args, "-I", includedir) 71 case goversion > 1.4: 72 odir := filepath.Join(filepath.Dir(ofile)) 73 includedir := filepath.Join(runtime.GOROOT(), "pkg", "include") 74 args = append(args, "-I", odir, "-I", includedir) 75 default: 76 return errors.Errorf("unsupported Go version: %v", runtime.Version) 77 } 78 args = append(args, sfile) 79 if err := mkdir(filepath.Dir(ofile)); err != nil { 80 return errors.Errorf("gc:asm: %v", err) 81 } 82 var buf bytes.Buffer 83 err := runOut(&buf, srcdir, nil, t.as, args...) 84 if err != nil { 85 fmt.Fprintf(os.Stderr, "# %s\n", pkg.ImportPath) 86 io.Copy(os.Stderr, &buf) 87 } 88 return err 89 } 90 91 func (t *gcToolchain) Ld(pkg *Package, searchpaths []string, outfile, afile string) error { 92 // to ensure we don't write a partial binary, link the binary to a temporary file in 93 // in the target directory, then rename. 94 dir := filepath.Dir(outfile) 95 tmp, err := ioutil.TempFile(dir, ".gb-link") 96 if err != nil { 97 return err 98 } 99 tmp.Close() 100 101 args := append(pkg.ldflags, "-o", tmp.Name()) 102 for _, d := range searchpaths { 103 args = append(args, "-L", d) 104 } 105 args = append(args, "-extld", linkCmd(pkg, "CC", defaultCC)) 106 if goversion > 1.4 { 107 args = append(args, "-buildmode", pkg.buildmode) 108 } 109 args = append(args, afile) 110 111 if err := mkdir(dir); err != nil { 112 return err 113 } 114 115 var buf bytes.Buffer 116 if err = runOut(&buf, ".", nil, t.ld, args...); err != nil { 117 os.Remove(tmp.Name()) // remove partial file 118 fmt.Fprintf(os.Stderr, "# %s\n", pkg.ImportPath) 119 io.Copy(os.Stderr, &buf) 120 return err 121 } 122 return os.Rename(tmp.Name(), outfile) 123 } 124 125 func (t *gcToolchain) Cc(pkg *Package, ofile, cfile string) error { 126 if goversion > 1.4 { 127 return errors.Errorf("gc %f does not support cc", goversion) 128 } 129 args := []string{ 130 "-F", "-V", "-w", 131 "-trimpath", pkg.Workdir(), 132 "-I", Workdir(pkg), 133 "-I", filepath.Join(runtime.GOROOT(), "pkg", pkg.gohostos+"_"+pkg.gohostarch), // for runtime.h 134 "-o", ofile, 135 "-D", "GOOS_" + pkg.gotargetos, 136 "-D", "GOARCH_" + pkg.gotargetarch, 137 cfile, 138 } 139 var buf bytes.Buffer 140 err := runOut(&buf, pkg.Dir, nil, t.cc, args...) 141 if err != nil { 142 fmt.Fprintf(os.Stderr, "# %s\n", pkg.ImportPath) 143 io.Copy(os.Stderr, &buf) 144 } 145 return err 146 } 147 148 func (t *gcToolchain) Pack(pkg *Package, afiles ...string) error { 149 args := []string{"r"} 150 args = append(args, afiles...) 151 dir := filepath.Dir(afiles[0]) 152 var buf bytes.Buffer 153 err := runOut(&buf, dir, nil, t.pack, args...) 154 if err != nil { 155 fmt.Fprintf(os.Stderr, "# %s\n", pkg.ImportPath) 156 io.Copy(os.Stderr, &buf) 157 } 158 return err 159 } 160 161 func (t *gcToolchain) compiler() string { return t.gc } 162 func (t *gcToolchain) linker() string { return t.ld } 163 164 func (t *gcToolchain) Gc(pkg *Package, searchpaths []string, importpath, srcdir, outfile string, files []string) error { 165 args := append(pkg.gcflags, "-p", importpath, "-pack") 166 args = append(args, "-o", outfile) 167 for _, d := range searchpaths { 168 args = append(args, "-I", d) 169 } 170 if pkg.Standard && pkg.ImportPath == "runtime" { 171 // runtime compiles with a special gc flag to emit 172 // additional reflect type data. 173 args = append(args, "-+") 174 } 175 176 switch { 177 case pkg.Complete(): 178 args = append(args, "-complete") 179 case goversion > 1.4: 180 asmhdr := filepath.Join(filepath.Dir(outfile), pkg.Name, "go_asm.h") 181 args = append(args, "-asmhdr", asmhdr) 182 } 183 184 // If there are vendored components, create an -importmap to map the import statement 185 // to the vendored import path. The possibilities for abusing this flag are endless. 186 if goversion > 1.5 && pkg.Standard { 187 for _, path := range pkg.Package.Imports { 188 if i := strings.LastIndex(path, "/vendor/"); i >= 0 { 189 args = append(args, "-importmap", path[i+len("/vendor/"):]+"="+path) 190 } else if strings.HasPrefix(path, "vendor/") { 191 args = append(args, "-importmap", path[len("vendor/"):]+"="+path) 192 } 193 } 194 } 195 196 args = append(args, files...) 197 if err := mkdir(filepath.Join(filepath.Dir(outfile), pkg.Name)); err != nil { 198 return errors.Wrap(err, "mkdir") 199 } 200 var buf bytes.Buffer 201 err := runOut(&buf, srcdir, nil, t.gc, args...) 202 if err != nil { 203 fmt.Fprintf(os.Stderr, "# %s\n", pkg.ImportPath) 204 io.Copy(os.Stderr, &buf) 205 } 206 return err 207 }