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  }