github.com/kekek/gb@v0.4.5-0.20170222120241-d4ba64b0b297/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, 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, pkg.Dir, 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) 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 := pkg.bindir()
    95  	if err := mkdir(dir); err != nil {
    96  		return err
    97  	}
    98  	tmp, err := ioutil.TempFile(dir, ".gb-link")
    99  	if err != nil {
   100  		return err
   101  	}
   102  	tmp.Close()
   103  
   104  	args := append(pkg.ldflags, "-o", tmp.Name())
   105  	for _, d := range pkg.includePaths() {
   106  		args = append(args, "-L", d)
   107  	}
   108  	args = append(args, "-extld", linkCmd(pkg, "CC", defaultCC))
   109  	if goversion > 1.4 {
   110  		args = append(args, "-buildmode", pkg.buildmode)
   111  	}
   112  	args = append(args, pkg.objfile())
   113  
   114  	var buf bytes.Buffer
   115  	if err = runOut(&buf, ".", nil, t.ld, args...); err != nil {
   116  		os.Remove(tmp.Name()) // remove partial file
   117  		fmt.Fprintf(os.Stderr, "# %s\n", pkg.ImportPath)
   118  		io.Copy(os.Stderr, &buf)
   119  		return err
   120  	}
   121  	return os.Rename(tmp.Name(), pkg.Binfile())
   122  }
   123  
   124  func (t *gcToolchain) Cc(pkg *Package, ofile, cfile string) error {
   125  	if goversion > 1.4 {
   126  		return errors.Errorf("gc %f does not support cc", goversion)
   127  	}
   128  	args := []string{
   129  		"-F", "-V", "-w",
   130  		"-trimpath", pkg.Context.Workdir(),
   131  		"-I", pkg.Workdir(),
   132  		"-I", filepath.Join(runtime.GOROOT(), "pkg", pkg.gohostos+"_"+pkg.gohostarch), // for runtime.h
   133  		"-o", ofile,
   134  		"-D", "GOOS_" + pkg.gotargetos,
   135  		"-D", "GOARCH_" + pkg.gotargetarch,
   136  		cfile,
   137  	}
   138  	var buf bytes.Buffer
   139  	err := runOut(&buf, pkg.Dir, nil, t.cc, args...)
   140  	if err != nil {
   141  		fmt.Fprintf(os.Stderr, "# %s\n", pkg.ImportPath)
   142  		io.Copy(os.Stderr, &buf)
   143  	}
   144  	return err
   145  }
   146  
   147  func (t *gcToolchain) Pack(pkg *Package, afiles ...string) error {
   148  	args := []string{"r"}
   149  	args = append(args, afiles...)
   150  	dir := filepath.Dir(afiles[0])
   151  	var buf bytes.Buffer
   152  	err := runOut(&buf, dir, nil, t.pack, args...)
   153  	if err != nil {
   154  		fmt.Fprintf(os.Stderr, "# %s\n", pkg.ImportPath)
   155  		io.Copy(os.Stderr, &buf)
   156  	}
   157  	return err
   158  }
   159  
   160  func (t *gcToolchain) compiler() string { return t.gc }
   161  func (t *gcToolchain) linker() string   { return t.ld }
   162  
   163  func (t *gcToolchain) Gc(pkg *Package, files []string) error {
   164  	outfile := pkg.objfile()
   165  	args := append(pkg.gcflags, "-p", pkg.ImportPath, "-pack")
   166  	args = append(args, "-o", outfile)
   167  	for _, d := range pkg.includePaths() {
   168  		args = append(args, "-I", d)
   169  	}
   170  	if pkg.Goroot && 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.Goroot {
   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, pkg.Dir, 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  }