github.com/goproxy0/go@v0.0.0-20171111080102-49cc0c489d2c/src/cmd/go/internal/work/gccgo.go (about)

     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 work
     6  
     7  import (
     8  	"fmt"
     9  	"io/ioutil"
    10  	"os"
    11  	"os/exec"
    12  	"path/filepath"
    13  	"strings"
    14  
    15  	"cmd/go/internal/base"
    16  	"cmd/go/internal/cfg"
    17  	"cmd/go/internal/load"
    18  	"cmd/go/internal/str"
    19  )
    20  
    21  // The Gccgo toolchain.
    22  
    23  type gccgoToolchain struct{}
    24  
    25  var GccgoName, GccgoBin string
    26  var gccgoErr error
    27  
    28  func init() {
    29  	GccgoName = os.Getenv("GCCGO")
    30  	if GccgoName == "" {
    31  		GccgoName = "gccgo"
    32  	}
    33  	GccgoBin, gccgoErr = exec.LookPath(GccgoName)
    34  }
    35  
    36  func (gccgoToolchain) compiler() string {
    37  	checkGccgoBin()
    38  	return GccgoBin
    39  }
    40  
    41  func (gccgoToolchain) linker() string {
    42  	checkGccgoBin()
    43  	return GccgoBin
    44  }
    45  
    46  func checkGccgoBin() {
    47  	if gccgoErr == nil {
    48  		return
    49  	}
    50  	fmt.Fprintf(os.Stderr, "cmd/go: gccgo: %s\n", gccgoErr)
    51  	os.Exit(2)
    52  }
    53  
    54  func (tools gccgoToolchain) gc(b *Builder, a *Action, archive string, importcfg []byte, asmhdr bool, gofiles []string) (ofile string, output []byte, err error) {
    55  	p := a.Package
    56  	objdir := a.Objdir
    57  	out := "_go_.o"
    58  	ofile = objdir + out
    59  	gcargs := []string{"-g"}
    60  	gcargs = append(gcargs, b.gccArchArgs()...)
    61  	if pkgpath := gccgoPkgpath(p); pkgpath != "" {
    62  		gcargs = append(gcargs, "-fgo-pkgpath="+pkgpath)
    63  	}
    64  	if p.Internal.LocalPrefix != "" {
    65  		gcargs = append(gcargs, "-fgo-relative-import-path="+p.Internal.LocalPrefix)
    66  	}
    67  
    68  	args := str.StringList(tools.compiler(), "-c", gcargs, "-o", ofile, forcedGccgoflags)
    69  	if importcfg != nil {
    70  		if b.gccSupportsFlag(args[:1], "-fgo-importcfg=/dev/null") {
    71  			if err := b.writeFile(objdir+"importcfg", importcfg); err != nil {
    72  				return "", nil, err
    73  			}
    74  			args = append(args, "-fgo-importcfg="+objdir+"importcfg")
    75  		} else {
    76  			root := objdir + "_importcfgroot_"
    77  			if err := buildImportcfgSymlinks(b, root, importcfg); err != nil {
    78  				return "", nil, err
    79  			}
    80  			args = append(args, "-I", root)
    81  		}
    82  	}
    83  	args = append(args, a.Package.Internal.Gccgoflags...)
    84  	for _, f := range gofiles {
    85  		args = append(args, mkAbs(p.Dir, f))
    86  	}
    87  
    88  	output, err = b.runOut(p.Dir, p.ImportPath, nil, args)
    89  	return ofile, output, err
    90  }
    91  
    92  // buildImportcfgSymlinks builds in root a tree of symlinks
    93  // implementing the directives from importcfg.
    94  // This serves as a temporary transition mechanism until
    95  // we can depend on gccgo reading an importcfg directly.
    96  // (The Go 1.9 and later gc compilers already do.)
    97  func buildImportcfgSymlinks(b *Builder, root string, importcfg []byte) error {
    98  	for lineNum, line := range strings.Split(string(importcfg), "\n") {
    99  		lineNum++ // 1-based
   100  		line = strings.TrimSpace(line)
   101  		if line == "" {
   102  			continue
   103  		}
   104  		if line == "" || strings.HasPrefix(line, "#") {
   105  			continue
   106  		}
   107  		var verb, args string
   108  		if i := strings.Index(line, " "); i < 0 {
   109  			verb = line
   110  		} else {
   111  			verb, args = line[:i], strings.TrimSpace(line[i+1:])
   112  		}
   113  		var before, after string
   114  		if i := strings.Index(args, "="); i >= 0 {
   115  			before, after = args[:i], args[i+1:]
   116  		}
   117  		switch verb {
   118  		default:
   119  			base.Fatalf("importcfg:%d: unknown directive %q", lineNum, verb)
   120  		case "packagefile":
   121  			if before == "" || after == "" {
   122  				return fmt.Errorf(`importcfg:%d: invalid packagefile: syntax is "packagefile path=filename": %s`, lineNum, line)
   123  			}
   124  			archive := gccgoArchive(root, before)
   125  			if err := b.Mkdir(filepath.Dir(archive)); err != nil {
   126  				return err
   127  			}
   128  			if err := b.Symlink(after, archive); err != nil {
   129  				return err
   130  			}
   131  		case "importmap":
   132  			if before == "" || after == "" {
   133  				return fmt.Errorf(`importcfg:%d: invalid importmap: syntax is "importmap old=new": %s`, lineNum, line)
   134  			}
   135  			beforeA := gccgoArchive(root, before)
   136  			afterA := gccgoArchive(root, after)
   137  			if err := b.Mkdir(filepath.Dir(beforeA)); err != nil {
   138  				return err
   139  			}
   140  			if err := b.Mkdir(filepath.Dir(afterA)); err != nil {
   141  				return err
   142  			}
   143  			if err := b.Symlink(afterA, beforeA); err != nil {
   144  				return err
   145  			}
   146  		case "packageshlib":
   147  			return fmt.Errorf("gccgo -importcfg does not support shared libraries")
   148  		}
   149  	}
   150  	return nil
   151  }
   152  
   153  func (tools gccgoToolchain) asm(b *Builder, a *Action, sfiles []string) ([]string, error) {
   154  	p := a.Package
   155  	var ofiles []string
   156  	for _, sfile := range sfiles {
   157  		ofile := a.Objdir + sfile[:len(sfile)-len(".s")] + ".o"
   158  		ofiles = append(ofiles, ofile)
   159  		sfile = mkAbs(p.Dir, sfile)
   160  		defs := []string{"-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch}
   161  		if pkgpath := gccgoCleanPkgpath(p); pkgpath != "" {
   162  			defs = append(defs, `-D`, `GOPKGPATH=`+pkgpath)
   163  		}
   164  		defs = tools.maybePIC(defs)
   165  		defs = append(defs, b.gccArchArgs()...)
   166  		err := b.run(p.Dir, p.ImportPath, nil, tools.compiler(), "-xassembler-with-cpp", "-I", a.Objdir, "-c", "-o", ofile, defs, sfile)
   167  		if err != nil {
   168  			return nil, err
   169  		}
   170  	}
   171  	return ofiles, nil
   172  }
   173  
   174  func gccgoArchive(basedir, imp string) string {
   175  	end := filepath.FromSlash(imp + ".a")
   176  	afile := filepath.Join(basedir, end)
   177  	// add "lib" to the final element
   178  	return filepath.Join(filepath.Dir(afile), "lib"+filepath.Base(afile))
   179  }
   180  
   181  func (gccgoToolchain) pack(b *Builder, a *Action, afile string, ofiles []string) error {
   182  	p := a.Package
   183  	objdir := a.Objdir
   184  	var absOfiles []string
   185  	for _, f := range ofiles {
   186  		absOfiles = append(absOfiles, mkAbs(objdir, f))
   187  	}
   188  	return b.run(p.Dir, p.ImportPath, nil, "ar", "rc", mkAbs(objdir, afile), absOfiles)
   189  }
   190  
   191  func (tools gccgoToolchain) link(b *Builder, root *Action, out, importcfg string, allactions []*Action, buildmode, desc string) error {
   192  	// gccgo needs explicit linking with all package dependencies,
   193  	// and all LDFLAGS from cgo dependencies.
   194  	apackagePathsSeen := make(map[string]bool)
   195  	afiles := []string{}
   196  	shlibs := []string{}
   197  	ldflags := b.gccArchArgs()
   198  	cgoldflags := []string{}
   199  	usesCgo := false
   200  	cxx := false
   201  	objc := false
   202  	fortran := false
   203  	if root.Package != nil {
   204  		cxx = len(root.Package.CXXFiles) > 0 || len(root.Package.SwigCXXFiles) > 0
   205  		objc = len(root.Package.MFiles) > 0
   206  		fortran = len(root.Package.FFiles) > 0
   207  	}
   208  
   209  	readCgoFlags := func(flagsFile string) error {
   210  		flags, err := ioutil.ReadFile(flagsFile)
   211  		if err != nil {
   212  			return err
   213  		}
   214  		const ldflagsPrefix = "_CGO_LDFLAGS="
   215  		for _, line := range strings.Split(string(flags), "\n") {
   216  			if strings.HasPrefix(line, ldflagsPrefix) {
   217  				newFlags := strings.Fields(line[len(ldflagsPrefix):])
   218  				for _, flag := range newFlags {
   219  					// Every _cgo_flags file has -g and -O2 in _CGO_LDFLAGS
   220  					// but they don't mean anything to the linker so filter
   221  					// them out.
   222  					if flag != "-g" && !strings.HasPrefix(flag, "-O") {
   223  						cgoldflags = append(cgoldflags, flag)
   224  					}
   225  				}
   226  			}
   227  		}
   228  		return nil
   229  	}
   230  
   231  	newID := 0
   232  	readAndRemoveCgoFlags := func(archive string) (string, error) {
   233  		newID++
   234  		newArchive := root.Objdir + fmt.Sprintf("_pkg%d_.a", newID)
   235  		if err := b.copyFile(root, newArchive, archive, 0666, false); err != nil {
   236  			return "", err
   237  		}
   238  		if cfg.BuildN || cfg.BuildX {
   239  			b.Showcmd("", "ar d %s _cgo_flags", newArchive)
   240  			if cfg.BuildN {
   241  				// TODO(rsc): We could do better about showing the right _cgo_flags even in -n mode.
   242  				// Either the archive is already built and we can read them out,
   243  				// or we're printing commands to build the archive and can
   244  				// forward the _cgo_flags directly to this step.
   245  				return "", nil
   246  			}
   247  		}
   248  		err := b.run(root.Objdir, desc, nil, "ar", "x", newArchive, "_cgo_flags")
   249  		if err != nil {
   250  			return "", err
   251  		}
   252  		err = b.run(".", desc, nil, "ar", "d", newArchive, "_cgo_flags")
   253  		if err != nil {
   254  			return "", err
   255  		}
   256  		err = readCgoFlags(filepath.Join(root.Objdir, "_cgo_flags"))
   257  		if err != nil {
   258  			return "", err
   259  		}
   260  		return newArchive, nil
   261  	}
   262  
   263  	actionsSeen := make(map[*Action]bool)
   264  	// Make a pre-order depth-first traversal of the action graph, taking note of
   265  	// whether a shared library action has been seen on the way to an action (the
   266  	// construction of the graph means that if any path to a node passes through
   267  	// a shared library action, they all do).
   268  	var walk func(a *Action, seenShlib bool)
   269  	var err error
   270  	walk = func(a *Action, seenShlib bool) {
   271  		if actionsSeen[a] {
   272  			return
   273  		}
   274  		actionsSeen[a] = true
   275  		if a.Package != nil && !seenShlib {
   276  			if a.Package.Standard {
   277  				return
   278  			}
   279  			// We record the target of the first time we see a .a file
   280  			// for a package to make sure that we prefer the 'install'
   281  			// rather than the 'build' location (which may not exist any
   282  			// more). We still need to traverse the dependencies of the
   283  			// build action though so saying
   284  			// if apackagePathsSeen[a.Package.ImportPath] { return }
   285  			// doesn't work.
   286  			if !apackagePathsSeen[a.Package.ImportPath] {
   287  				apackagePathsSeen[a.Package.ImportPath] = true
   288  				target := a.Target
   289  				if len(a.Package.CgoFiles) > 0 || a.Package.UsesSwig() {
   290  					target, err = readAndRemoveCgoFlags(target)
   291  					if err != nil {
   292  						return
   293  					}
   294  				}
   295  				afiles = append(afiles, target)
   296  			}
   297  		}
   298  		if strings.HasSuffix(a.Target, ".so") {
   299  			shlibs = append(shlibs, a.Target)
   300  			seenShlib = true
   301  		}
   302  		for _, a1 := range a.Deps {
   303  			walk(a1, seenShlib)
   304  			if err != nil {
   305  				return
   306  			}
   307  		}
   308  	}
   309  	for _, a1 := range root.Deps {
   310  		walk(a1, false)
   311  		if err != nil {
   312  			return err
   313  		}
   314  	}
   315  
   316  	for _, a := range allactions {
   317  		// Gather CgoLDFLAGS, but not from standard packages.
   318  		// The go tool can dig up runtime/cgo from GOROOT and
   319  		// think that it should use its CgoLDFLAGS, but gccgo
   320  		// doesn't use runtime/cgo.
   321  		if a.Package == nil {
   322  			continue
   323  		}
   324  		if !a.Package.Standard {
   325  			cgoldflags = append(cgoldflags, a.Package.CgoLDFLAGS...)
   326  		}
   327  		if len(a.Package.CgoFiles) > 0 {
   328  			usesCgo = true
   329  		}
   330  		if a.Package.UsesSwig() {
   331  			usesCgo = true
   332  		}
   333  		if len(a.Package.CXXFiles) > 0 || len(a.Package.SwigCXXFiles) > 0 {
   334  			cxx = true
   335  		}
   336  		if len(a.Package.MFiles) > 0 {
   337  			objc = true
   338  		}
   339  		if len(a.Package.FFiles) > 0 {
   340  			fortran = true
   341  		}
   342  	}
   343  
   344  	ldflags = append(ldflags, "-Wl,--whole-archive")
   345  	ldflags = append(ldflags, afiles...)
   346  	ldflags = append(ldflags, "-Wl,--no-whole-archive")
   347  
   348  	ldflags = append(ldflags, cgoldflags...)
   349  	ldflags = append(ldflags, envList("CGO_LDFLAGS", "")...)
   350  	if root.Package != nil {
   351  		ldflags = append(ldflags, root.Package.CgoLDFLAGS...)
   352  	}
   353  
   354  	ldflags = str.StringList("-Wl,-(", ldflags, "-Wl,-)")
   355  
   356  	for _, shlib := range shlibs {
   357  		ldflags = append(
   358  			ldflags,
   359  			"-L"+filepath.Dir(shlib),
   360  			"-Wl,-rpath="+filepath.Dir(shlib),
   361  			"-l"+strings.TrimSuffix(
   362  				strings.TrimPrefix(filepath.Base(shlib), "lib"),
   363  				".so"))
   364  	}
   365  
   366  	var realOut string
   367  	switch buildmode {
   368  	case "exe":
   369  		if usesCgo && cfg.Goos == "linux" {
   370  			ldflags = append(ldflags, "-Wl,-E")
   371  		}
   372  
   373  	case "c-archive":
   374  		// Link the Go files into a single .o, and also link
   375  		// in -lgolibbegin.
   376  		//
   377  		// We need to use --whole-archive with -lgolibbegin
   378  		// because it doesn't define any symbols that will
   379  		// cause the contents to be pulled in; it's just
   380  		// initialization code.
   381  		//
   382  		// The user remains responsible for linking against
   383  		// -lgo -lpthread -lm in the final link. We can't use
   384  		// -r to pick them up because we can't combine
   385  		// split-stack and non-split-stack code in a single -r
   386  		// link, and libgo picks up non-split-stack code from
   387  		// libffi.
   388  		ldflags = append(ldflags, "-Wl,-r", "-nostdlib", "-Wl,--whole-archive", "-lgolibbegin", "-Wl,--no-whole-archive")
   389  
   390  		if nopie := b.gccNoPie([]string{tools.linker()}); nopie != "" {
   391  			ldflags = append(ldflags, nopie)
   392  		}
   393  
   394  		// We are creating an object file, so we don't want a build ID.
   395  		ldflags = b.disableBuildID(ldflags)
   396  
   397  		realOut = out
   398  		out = out + ".o"
   399  
   400  	case "c-shared":
   401  		ldflags = append(ldflags, "-shared", "-nostdlib", "-Wl,--whole-archive", "-lgolibbegin", "-Wl,--no-whole-archive", "-lgo", "-lgcc_s", "-lgcc", "-lc", "-lgcc")
   402  	case "shared":
   403  		ldflags = append(ldflags, "-zdefs", "-shared", "-nostdlib", "-lgo", "-lgcc_s", "-lgcc", "-lc")
   404  
   405  	default:
   406  		base.Fatalf("-buildmode=%s not supported for gccgo", buildmode)
   407  	}
   408  
   409  	switch buildmode {
   410  	case "exe", "c-shared":
   411  		if cxx {
   412  			ldflags = append(ldflags, "-lstdc++")
   413  		}
   414  		if objc {
   415  			ldflags = append(ldflags, "-lobjc")
   416  		}
   417  		if fortran {
   418  			fc := os.Getenv("FC")
   419  			if fc == "" {
   420  				fc = "gfortran"
   421  			}
   422  			// support gfortran out of the box and let others pass the correct link options
   423  			// via CGO_LDFLAGS
   424  			if strings.Contains(fc, "gfortran") {
   425  				ldflags = append(ldflags, "-lgfortran")
   426  			}
   427  		}
   428  	}
   429  
   430  	if err := b.run(".", desc, nil, tools.linker(), "-o", out, ldflags, forcedGccgoflags, root.Package.Internal.Gccgoflags); err != nil {
   431  		return err
   432  	}
   433  
   434  	switch buildmode {
   435  	case "c-archive":
   436  		if err := b.run(".", desc, nil, "ar", "rc", realOut, out); err != nil {
   437  			return err
   438  		}
   439  	}
   440  	return nil
   441  }
   442  
   443  func (tools gccgoToolchain) ld(b *Builder, root *Action, out, importcfg, mainpkg string) error {
   444  	return tools.link(b, root, out, importcfg, root.Deps, ldBuildmode, root.Package.ImportPath)
   445  }
   446  
   447  func (tools gccgoToolchain) ldShared(b *Builder, root *Action, toplevelactions []*Action, out, importcfg string, allactions []*Action) error {
   448  	fakeRoot := *root
   449  	fakeRoot.Deps = toplevelactions
   450  	return tools.link(b, &fakeRoot, out, importcfg, allactions, "shared", out)
   451  }
   452  
   453  func (tools gccgoToolchain) cc(b *Builder, a *Action, ofile, cfile string) error {
   454  	p := a.Package
   455  	inc := filepath.Join(cfg.GOROOT, "pkg", "include")
   456  	cfile = mkAbs(p.Dir, cfile)
   457  	defs := []string{"-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch}
   458  	defs = append(defs, b.gccArchArgs()...)
   459  	if pkgpath := gccgoCleanPkgpath(p); pkgpath != "" {
   460  		defs = append(defs, `-D`, `GOPKGPATH="`+pkgpath+`"`)
   461  	}
   462  	switch cfg.Goarch {
   463  	case "386", "amd64":
   464  		defs = append(defs, "-fsplit-stack")
   465  	}
   466  	defs = tools.maybePIC(defs)
   467  	return b.run(p.Dir, p.ImportPath, nil, envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch)), "-Wall", "-g",
   468  		"-I", a.Objdir, "-I", inc, "-o", ofile, defs, "-c", cfile)
   469  }
   470  
   471  // maybePIC adds -fPIC to the list of arguments if needed.
   472  func (tools gccgoToolchain) maybePIC(args []string) []string {
   473  	switch cfg.BuildBuildmode {
   474  	case "c-shared", "shared", "plugin":
   475  		args = append(args, "-fPIC")
   476  	}
   477  	return args
   478  }
   479  
   480  func gccgoPkgpath(p *load.Package) string {
   481  	if p.Internal.Build.IsCommand() && !p.Internal.ForceLibrary {
   482  		return ""
   483  	}
   484  	return p.ImportPath
   485  }
   486  
   487  func gccgoCleanPkgpath(p *load.Package) string {
   488  	clean := func(r rune) rune {
   489  		switch {
   490  		case 'A' <= r && r <= 'Z', 'a' <= r && r <= 'z',
   491  			'0' <= r && r <= '9':
   492  			return r
   493  		}
   494  		return '_'
   495  	}
   496  	return strings.Map(clean, gccgoPkgpath(p))
   497  }