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