github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/go/not-internal/work/gc.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  	"bufio"
     9  	"bytes"
    10  	"fmt"
    11  	"io"
    12  	"io/ioutil"
    13  	"log"
    14  	"os"
    15  	"path/filepath"
    16  	"runtime"
    17  	"strings"
    18  
    19  	"github.com/gagliardetto/golang-go/cmd/go/not-internal/base"
    20  	"github.com/gagliardetto/golang-go/cmd/go/not-internal/cfg"
    21  	"github.com/gagliardetto/golang-go/cmd/go/not-internal/load"
    22  	"github.com/gagliardetto/golang-go/cmd/go/not-internal/str"
    23  	"github.com/gagliardetto/golang-go/cmd/internal/objabi"
    24  	"github.com/gagliardetto/golang-go/cmd/internal/sys"
    25  	"crypto/sha1"
    26  )
    27  
    28  // The Go toolchain.
    29  
    30  type gcToolchain struct{}
    31  
    32  func (gcToolchain) compiler() string {
    33  	return base.Tool("compile")
    34  }
    35  
    36  func (gcToolchain) linker() string {
    37  	return base.Tool("link")
    38  }
    39  
    40  func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg []byte, symabis string, asmhdr bool, gofiles []string) (ofile string, output []byte, err error) {
    41  	p := a.Package
    42  	objdir := a.Objdir
    43  	if archive != "" {
    44  		ofile = archive
    45  	} else {
    46  		out := "_go_.o"
    47  		ofile = objdir + out
    48  	}
    49  
    50  	pkgpath := p.ImportPath
    51  	if cfg.BuildBuildmode == "plugin" {
    52  		pkgpath = pluginPath(a)
    53  	} else if p.Name == "main" && !p.Internal.ForceLibrary {
    54  		pkgpath = "main"
    55  	}
    56  	gcargs := []string{"-p", pkgpath}
    57  	if p.Module != nil && p.Module.GoVersion != "" && allowedVersion(p.Module.GoVersion) {
    58  		gcargs = append(gcargs, "-lang=go"+p.Module.GoVersion)
    59  	}
    60  	if p.Standard {
    61  		gcargs = append(gcargs, "-std")
    62  	}
    63  	compilingRuntime := p.Standard && (p.ImportPath == "runtime" || strings.HasPrefix(p.ImportPath, "runtime/internal"))
    64  	// The runtime package imports a couple of general internal packages.
    65  	if p.Standard && (p.ImportPath == "github.com/gagliardetto/golang-go/not-internal/cpu" || p.ImportPath == "github.com/gagliardetto/golang-go/not-internal/bytealg") {
    66  		compilingRuntime = true
    67  	}
    68  	if compilingRuntime {
    69  		// runtime compiles with a special gc flag to check for
    70  		// memory allocations that are invalid in the runtime package,
    71  		// and to implement some special compiler pragmas.
    72  		gcargs = append(gcargs, "-+")
    73  	}
    74  
    75  	// If we're giving the compiler the entire package (no C etc files), tell it that,
    76  	// so that it can give good error messages about forward declarations.
    77  	// Exceptions: a few standard packages have forward declarations for
    78  	// pieces supplied behind-the-scenes by package runtime.
    79  	extFiles := len(p.CgoFiles) + len(p.CFiles) + len(p.CXXFiles) + len(p.MFiles) + len(p.FFiles) + len(p.SFiles) + len(p.SysoFiles) + len(p.SwigFiles) + len(p.SwigCXXFiles)
    80  	if p.Standard {
    81  		switch p.ImportPath {
    82  		case "bytes", "github.com/gagliardetto/golang-go/not-internal/poll", "net", "os", "runtime/pprof", "runtime/trace", "sync", "syscall", "time":
    83  			extFiles++
    84  		}
    85  	}
    86  	if extFiles == 0 {
    87  		gcargs = append(gcargs, "-complete")
    88  	}
    89  	if cfg.BuildContext.InstallSuffix != "" {
    90  		gcargs = append(gcargs, "-installsuffix", cfg.BuildContext.InstallSuffix)
    91  	}
    92  	if a.buildID != "" {
    93  		gcargs = append(gcargs, "-buildid", a.buildID)
    94  	}
    95  	if p.Internal.OmitDebug || cfg.Goos == "plan9" || cfg.Goarch == "wasm" {
    96  		gcargs = append(gcargs, "-dwarf=false")
    97  	}
    98  	if strings.HasPrefix(runtimeVersion, "go1") && !strings.Contains(os.Args[0], "go_bootstrap") {
    99  		gcargs = append(gcargs, "-goversion", runtimeVersion)
   100  	}
   101  	if symabis != "" {
   102  		gcargs = append(gcargs, "-symabis", symabis)
   103  	}
   104  
   105  	gcflags := str.StringList(forcedGcflags, p.Internal.Gcflags)
   106  	if compilingRuntime {
   107  		// Remove -N, if present.
   108  		// It is not possible to build the runtime with no optimizations,
   109  		// because the compiler cannot eliminate enough write barriers.
   110  		for i := 0; i < len(gcflags); i++ {
   111  			if gcflags[i] == "-N" {
   112  				copy(gcflags[i:], gcflags[i+1:])
   113  				gcflags = gcflags[:len(gcflags)-1]
   114  				i--
   115  			}
   116  		}
   117  	}
   118  
   119  	args := []interface{}{cfg.BuildToolexec, base.Tool("compile"), "-o", ofile, "-trimpath", a.trimpath(), gcflags, gcargs, "-D", p.Internal.LocalPrefix}
   120  	if importcfg != nil {
   121  		if err := b.writeFile(objdir+"importcfg", importcfg); err != nil {
   122  			return "", nil, err
   123  		}
   124  		args = append(args, "-importcfg", objdir+"importcfg")
   125  	}
   126  	if ofile == archive {
   127  		args = append(args, "-pack")
   128  	}
   129  	if asmhdr {
   130  		args = append(args, "-asmhdr", objdir+"go_asm.h")
   131  	}
   132  
   133  	// Add -c=N to use concurrent backend compilation, if possible.
   134  	if c := gcBackendConcurrency(gcflags); c > 1 {
   135  		args = append(args, fmt.Sprintf("-c=%d", c))
   136  	}
   137  
   138  	for _, f := range gofiles {
   139  		args = append(args, mkAbs(p.Dir, f))
   140  	}
   141  
   142  	output, err = b.runOut(a, p.Dir, nil, args...)
   143  	return ofile, output, err
   144  }
   145  
   146  // gcBackendConcurrency returns the backend compiler concurrency level for a package compilation.
   147  func gcBackendConcurrency(gcflags []string) int {
   148  	// First, check whether we can use -c at all for this compilation.
   149  	canDashC := concurrentGCBackendCompilationEnabledByDefault
   150  
   151  	switch e := os.Getenv("GO19CONCURRENTCOMPILATION"); e {
   152  	case "0":
   153  		canDashC = false
   154  	case "1":
   155  		canDashC = true
   156  	case "":
   157  		// Not set. Use default.
   158  	default:
   159  		log.Fatalf("GO19CONCURRENTCOMPILATION must be 0, 1, or unset, got %q", e)
   160  	}
   161  
   162  CheckFlags:
   163  	for _, flag := range gcflags {
   164  		// Concurrent compilation is presumed incompatible with any gcflags,
   165  		// except for a small whitelist of commonly used flags.
   166  		// If the user knows better, they can manually add their own -c to the gcflags.
   167  		switch flag {
   168  		case "-N", "-l", "-S", "-B", "-C", "-I":
   169  			// OK
   170  		default:
   171  			canDashC = false
   172  			break CheckFlags
   173  		}
   174  	}
   175  
   176  	// TODO: Test and delete these conditions.
   177  	if objabi.Fieldtrack_enabled != 0 || objabi.Preemptibleloops_enabled != 0 {
   178  		canDashC = false
   179  	}
   180  
   181  	if !canDashC {
   182  		return 1
   183  	}
   184  
   185  	// Decide how many concurrent backend compilations to allow.
   186  	//
   187  	// If we allow too many, in theory we might end up with p concurrent processes,
   188  	// each with c concurrent backend compiles, all fighting over the same resources.
   189  	// However, in practice, that seems not to happen too much.
   190  	// Most build graphs are surprisingly serial, so p==1 for much of the build.
   191  	// Furthermore, concurrent backend compilation is only enabled for a part
   192  	// of the overall compiler execution, so c==1 for much of the build.
   193  	// So don't worry too much about that interaction for now.
   194  	//
   195  	// However, in practice, setting c above 4 tends not to help very much.
   196  	// See the analysis in CL 41192.
   197  	//
   198  	// TODO(josharian): attempt to detect whether this particular compilation
   199  	// is likely to be a bottleneck, e.g. when:
   200  	//   - it has no successor packages to compile (usually package main)
   201  	//   - all paths through the build graph pass through it
   202  	//   - critical path scheduling says it is high priority
   203  	// and in such a case, set c to runtime.NumCPU.
   204  	// We do this now when p==1.
   205  	if cfg.BuildP == 1 {
   206  		// No process parallelism. Max out c.
   207  		return runtime.NumCPU()
   208  	}
   209  	// Some process parallelism. Set c to min(4, numcpu).
   210  	c := 4
   211  	if ncpu := runtime.NumCPU(); ncpu < c {
   212  		c = ncpu
   213  	}
   214  	return c
   215  }
   216  
   217  // trimpath returns the -trimpath argument to use
   218  // when compiling the action.
   219  func (a *Action) trimpath() string {
   220  	// Strip the object directory entirely.
   221  	objdir := a.Objdir
   222  	if len(objdir) > 1 && objdir[len(objdir)-1] == filepath.Separator {
   223  		objdir = objdir[:len(objdir)-1]
   224  	}
   225  	rewrite := objdir + "=>"
   226  
   227  	// For "go build -trimpath", rewrite package source directory
   228  	// to a file system-independent path (just the import path).
   229  	if cfg.BuildTrimpath {
   230  		if m := a.Package.Module; m != nil && m.Version != "" {
   231  			rewrite += ";" + a.Package.Dir + "=>" + m.Path + "@" + m.Version + strings.TrimPrefix(a.Package.ImportPath, m.Path)
   232  		} else {
   233  			rewrite += ";" + a.Package.Dir + "=>" + a.Package.ImportPath
   234  		}
   235  	}
   236  
   237  	return rewrite
   238  }
   239  
   240  func asmArgs(a *Action, p *load.Package) []interface{} {
   241  	// Add -I pkg/GOOS_GOARCH so #include "textflag.h" works in .s files.
   242  	inc := filepath.Join(cfg.GOROOT, "pkg", "include")
   243  	args := []interface{}{cfg.BuildToolexec, base.Tool("asm"), "-trimpath", a.trimpath(), "-I", a.Objdir, "-I", inc, "-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch, forcedAsmflags, p.Internal.Asmflags}
   244  	if p.ImportPath == "runtime" && cfg.Goarch == "386" {
   245  		for _, arg := range forcedAsmflags {
   246  			if arg == "-dynlink" {
   247  				args = append(args, "-D=GOBUILDMODE_shared=1")
   248  			}
   249  		}
   250  	}
   251  
   252  	if cfg.Goarch == "mips" || cfg.Goarch == "mipsle" {
   253  		// Define GOMIPS_value from cfg.GOMIPS.
   254  		args = append(args, "-D", "GOMIPS_"+cfg.GOMIPS)
   255  	}
   256  
   257  	if cfg.Goarch == "mips64" || cfg.Goarch == "mips64le" {
   258  		// Define GOMIPS64_value from cfg.GOMIPS64.
   259  		args = append(args, "-D", "GOMIPS64_"+cfg.GOMIPS64)
   260  	}
   261  
   262  	return args
   263  }
   264  
   265  func (gcToolchain) asm(b *Builder, a *Action, sfiles []string) ([]string, error) {
   266  	p := a.Package
   267  	args := asmArgs(a, p)
   268  
   269  	var ofiles []string
   270  	for _, sfile := range sfiles {
   271  		ofile := a.Objdir + sfile[:len(sfile)-len(".s")] + ".o"
   272  		ofiles = append(ofiles, ofile)
   273  		args1 := append(args, "-o", ofile, mkAbs(p.Dir, sfile))
   274  		if err := b.run(a, p.Dir, p.ImportPath, nil, args1...); err != nil {
   275  			return nil, err
   276  		}
   277  	}
   278  	return ofiles, nil
   279  }
   280  
   281  func (gcToolchain) symabis(b *Builder, a *Action, sfiles []string) (string, error) {
   282  	mkSymabis := func(p *load.Package, sfiles []string, path string) error {
   283  		args := asmArgs(a, p)
   284  		args = append(args, "-gensymabis", "-o", path)
   285  		for _, sfile := range sfiles {
   286  			if p.ImportPath == "runtime/cgo" && strings.HasPrefix(sfile, "gcc_") {
   287  				continue
   288  			}
   289  			args = append(args, mkAbs(p.Dir, sfile))
   290  		}
   291  
   292  		// Supply an empty go_asm.h as if the compiler had been run.
   293  		// -gensymabis parsing is lax enough that we don't need the
   294  		// actual definitions that would appear in go_asm.h.
   295  		if err := b.writeFile(a.Objdir+"go_asm.h", nil); err != nil {
   296  			return err
   297  		}
   298  
   299  		return b.run(a, p.Dir, p.ImportPath, nil, args...)
   300  	}
   301  
   302  	var symabis string // Only set if we actually create the file
   303  	p := a.Package
   304  	if len(sfiles) != 0 {
   305  		symabis = a.Objdir + "symabis"
   306  		if err := mkSymabis(p, sfiles, symabis); err != nil {
   307  			return "", err
   308  		}
   309  	}
   310  
   311  	return symabis, nil
   312  }
   313  
   314  // toolVerify checks that the command line args writes the same output file
   315  // if run using newTool instead.
   316  // Unused now but kept around for future use.
   317  func toolVerify(a *Action, b *Builder, p *load.Package, newTool string, ofile string, args []interface{}) error {
   318  	newArgs := make([]interface{}, len(args))
   319  	copy(newArgs, args)
   320  	newArgs[1] = base.Tool(newTool)
   321  	newArgs[3] = ofile + ".new" // x.6 becomes x.6.new
   322  	if err := b.run(a, p.Dir, p.ImportPath, nil, newArgs...); err != nil {
   323  		return err
   324  	}
   325  	data1, err := ioutil.ReadFile(ofile)
   326  	if err != nil {
   327  		return err
   328  	}
   329  	data2, err := ioutil.ReadFile(ofile + ".new")
   330  	if err != nil {
   331  		return err
   332  	}
   333  	if !bytes.Equal(data1, data2) {
   334  		return fmt.Errorf("%s and %s produced different output files:\n%s\n%s", filepath.Base(args[1].(string)), newTool, strings.Join(str.StringList(args...), " "), strings.Join(str.StringList(newArgs...), " "))
   335  	}
   336  	os.Remove(ofile + ".new")
   337  	return nil
   338  }
   339  
   340  func (gcToolchain) pack(b *Builder, a *Action, afile string, ofiles []string) error {
   341  	var absOfiles []string
   342  	for _, f := range ofiles {
   343  		absOfiles = append(absOfiles, mkAbs(a.Objdir, f))
   344  	}
   345  	absAfile := mkAbs(a.Objdir, afile)
   346  
   347  	// The archive file should have been created by the compiler.
   348  	// Since it used to not work that way, verify.
   349  	if !cfg.BuildN {
   350  		if _, err := os.Stat(absAfile); err != nil {
   351  			base.Fatalf("os.Stat of archive file failed: %v", err)
   352  		}
   353  	}
   354  
   355  	p := a.Package
   356  	if cfg.BuildN || cfg.BuildX {
   357  		cmdline := str.StringList(base.Tool("pack"), "r", absAfile, absOfiles)
   358  		b.Showcmd(p.Dir, "%s # internal", joinUnambiguously(cmdline))
   359  	}
   360  	if cfg.BuildN {
   361  		return nil
   362  	}
   363  	if err := packInternal(absAfile, absOfiles); err != nil {
   364  		b.showOutput(a, p.Dir, p.Desc(), err.Error()+"\n")
   365  		return errPrintedOutput
   366  	}
   367  	return nil
   368  }
   369  
   370  func packInternal(afile string, ofiles []string) error {
   371  	dst, err := os.OpenFile(afile, os.O_WRONLY|os.O_APPEND, 0)
   372  	if err != nil {
   373  		return err
   374  	}
   375  	defer dst.Close() // only for error returns or panics
   376  	w := bufio.NewWriter(dst)
   377  
   378  	for _, ofile := range ofiles {
   379  		src, err := os.Open(ofile)
   380  		if err != nil {
   381  			return err
   382  		}
   383  		fi, err := src.Stat()
   384  		if err != nil {
   385  			src.Close()
   386  			return err
   387  		}
   388  		// Note: Not using %-16.16s format because we care
   389  		// about bytes, not runes.
   390  		name := fi.Name()
   391  		if len(name) > 16 {
   392  			name = name[:16]
   393  		} else {
   394  			name += strings.Repeat(" ", 16-len(name))
   395  		}
   396  		size := fi.Size()
   397  		fmt.Fprintf(w, "%s%-12d%-6d%-6d%-8o%-10d`\n",
   398  			name, 0, 0, 0, 0644, size)
   399  		n, err := io.Copy(w, src)
   400  		src.Close()
   401  		if err == nil && n < size {
   402  			err = io.ErrUnexpectedEOF
   403  		} else if err == nil && n > size {
   404  			err = fmt.Errorf("file larger than size reported by stat")
   405  		}
   406  		if err != nil {
   407  			return fmt.Errorf("copying %s to %s: %v", ofile, afile, err)
   408  		}
   409  		if size&1 != 0 {
   410  			w.WriteByte(0)
   411  		}
   412  	}
   413  
   414  	if err := w.Flush(); err != nil {
   415  		return err
   416  	}
   417  	return dst.Close()
   418  }
   419  
   420  // setextld sets the appropriate linker flags for the specified compiler.
   421  func setextld(ldflags []string, compiler []string) []string {
   422  	for _, f := range ldflags {
   423  		if f == "-extld" || strings.HasPrefix(f, "-extld=") {
   424  			// don't override -extld if supplied
   425  			return ldflags
   426  		}
   427  	}
   428  	ldflags = append(ldflags, "-extld="+compiler[0])
   429  	if len(compiler) > 1 {
   430  		extldflags := false
   431  		add := strings.Join(compiler[1:], " ")
   432  		for i, f := range ldflags {
   433  			if f == "-extldflags" && i+1 < len(ldflags) {
   434  				ldflags[i+1] = add + " " + ldflags[i+1]
   435  				extldflags = true
   436  				break
   437  			} else if strings.HasPrefix(f, "-extldflags=") {
   438  				ldflags[i] = "-extldflags=" + add + " " + ldflags[i][len("-extldflags="):]
   439  				extldflags = true
   440  				break
   441  			}
   442  		}
   443  		if !extldflags {
   444  			ldflags = append(ldflags, "-extldflags="+add)
   445  		}
   446  	}
   447  	return ldflags
   448  }
   449  
   450  // pluginPath computes the package path for a plugin main package.
   451  //
   452  // This is typically the import path of the main package p, unless the
   453  // plugin is being built directly from source files. In that case we
   454  // combine the package build ID with the contents of the main package
   455  // source files. This allows us to identify two different plugins
   456  // built from two source files with the same name.
   457  func pluginPath(a *Action) string {
   458  	p := a.Package
   459  	if p.ImportPath != "command-line-arguments" {
   460  		return p.ImportPath
   461  	}
   462  	h := sha1.New()
   463  	buildID := a.buildID
   464  	if a.Mode == "link" {
   465  		// For linking, use the main package's build ID instead of
   466  		// the binary's build ID, so it is the same hash used in
   467  		// compiling and linking.
   468  		// When compiling, we use actionID/actionID (instead of
   469  		// actionID/contentID) as a temporary build ID to compute
   470  		// the hash. Do the same here. (See buildid.go:useCache)
   471  		// The build ID matters because it affects the overall hash
   472  		// in the plugin's pseudo-import path returned below.
   473  		// We need to use the same import path when compiling and linking.
   474  		id := strings.Split(buildID, buildIDSeparator)
   475  		buildID = id[1] + buildIDSeparator + id[1]
   476  	}
   477  	fmt.Fprintf(h, "build ID: %s\n", buildID)
   478  	for _, file := range str.StringList(p.GoFiles, p.CgoFiles, p.SFiles) {
   479  		data, err := ioutil.ReadFile(filepath.Join(p.Dir, file))
   480  		if err != nil {
   481  			base.Fatalf("go: %s", err)
   482  		}
   483  		h.Write(data)
   484  	}
   485  	return fmt.Sprintf("plugin/unnamed-%x", h.Sum(nil))
   486  }
   487  
   488  func (gcToolchain) ld(b *Builder, root *Action, out, importcfg, mainpkg string) error {
   489  	cxx := len(root.Package.CXXFiles) > 0 || len(root.Package.SwigCXXFiles) > 0
   490  	for _, a := range root.Deps {
   491  		if a.Package != nil && (len(a.Package.CXXFiles) > 0 || len(a.Package.SwigCXXFiles) > 0) {
   492  			cxx = true
   493  		}
   494  	}
   495  	var ldflags []string
   496  	if cfg.BuildContext.InstallSuffix != "" {
   497  		ldflags = append(ldflags, "-installsuffix", cfg.BuildContext.InstallSuffix)
   498  	}
   499  	if root.Package.Internal.OmitDebug {
   500  		ldflags = append(ldflags, "-s", "-w")
   501  	}
   502  	if cfg.BuildBuildmode == "plugin" {
   503  		ldflags = append(ldflags, "-pluginpath", pluginPath(root))
   504  	}
   505  
   506  	// Store BuildID inside toolchain binaries as a unique identifier of the
   507  	// tool being run, for use by content-based staleness determination.
   508  	if root.Package.Goroot && strings.HasPrefix(root.Package.ImportPath, "github.com/gagliardetto/golang-go/cmd/") {
   509  		// External linking will include our build id in the external
   510  		// linker's build id, which will cause our build id to not
   511  		// match the next time the tool is built.
   512  		// Rely on the external build id instead.
   513  		if !sys.MustLinkExternal(cfg.Goos, cfg.Goarch) {
   514  			ldflags = append(ldflags, "-X=cmd/internal/objabi.buildID="+root.buildID)
   515  		}
   516  	}
   517  
   518  	// If the user has not specified the -extld option, then specify the
   519  	// appropriate linker. In case of C++ code, use the compiler named
   520  	// by the CXX environment variable or defaultCXX if CXX is not set.
   521  	// Else, use the CC environment variable and defaultCC as fallback.
   522  	var compiler []string
   523  	if cxx {
   524  		compiler = envList("CXX", cfg.DefaultCXX(cfg.Goos, cfg.Goarch))
   525  	} else {
   526  		compiler = envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch))
   527  	}
   528  	ldflags = append(ldflags, "-buildmode="+ldBuildmode)
   529  	if root.buildID != "" {
   530  		ldflags = append(ldflags, "-buildid="+root.buildID)
   531  	}
   532  	ldflags = append(ldflags, forcedLdflags...)
   533  	ldflags = append(ldflags, root.Package.Internal.Ldflags...)
   534  	ldflags = setextld(ldflags, compiler)
   535  
   536  	// On OS X when using external linking to build a shared library,
   537  	// the argument passed here to -o ends up recorded in the final
   538  	// shared library in the LC_ID_DYLIB load command.
   539  	// To avoid putting the temporary output directory name there
   540  	// (and making the resulting shared library useless),
   541  	// run the link in the output directory so that -o can name
   542  	// just the final path element.
   543  	// On Windows, DLL file name is recorded in PE file
   544  	// export section, so do like on OS X.
   545  	dir := "."
   546  	if (cfg.Goos == "darwin" || cfg.Goos == "windows") && cfg.BuildBuildmode == "c-shared" {
   547  		dir, out = filepath.Split(out)
   548  	}
   549  
   550  	env := []string{}
   551  	if cfg.BuildTrimpath {
   552  		env = append(env, "GOROOT_FINAL=go")
   553  	}
   554  	return b.run(root, dir, root.Package.ImportPath, env, cfg.BuildToolexec, base.Tool("link"), "-o", out, "-importcfg", importcfg, ldflags, mainpkg)
   555  }
   556  
   557  func (gcToolchain) ldShared(b *Builder, root *Action, toplevelactions []*Action, out, importcfg string, allactions []*Action) error {
   558  	ldflags := []string{"-installsuffix", cfg.BuildContext.InstallSuffix}
   559  	ldflags = append(ldflags, "-buildmode=shared")
   560  	ldflags = append(ldflags, forcedLdflags...)
   561  	ldflags = append(ldflags, root.Package.Internal.Ldflags...)
   562  	cxx := false
   563  	for _, a := range allactions {
   564  		if a.Package != nil && (len(a.Package.CXXFiles) > 0 || len(a.Package.SwigCXXFiles) > 0) {
   565  			cxx = true
   566  		}
   567  	}
   568  	// If the user has not specified the -extld option, then specify the
   569  	// appropriate linker. In case of C++ code, use the compiler named
   570  	// by the CXX environment variable or defaultCXX if CXX is not set.
   571  	// Else, use the CC environment variable and defaultCC as fallback.
   572  	var compiler []string
   573  	if cxx {
   574  		compiler = envList("CXX", cfg.DefaultCXX(cfg.Goos, cfg.Goarch))
   575  	} else {
   576  		compiler = envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch))
   577  	}
   578  	ldflags = setextld(ldflags, compiler)
   579  	for _, d := range toplevelactions {
   580  		if !strings.HasSuffix(d.Target, ".a") { // omit unsafe etc and actions for other shared libraries
   581  			continue
   582  		}
   583  		ldflags = append(ldflags, d.Package.ImportPath+"="+d.Target)
   584  	}
   585  	return b.run(root, ".", out, nil, cfg.BuildToolexec, base.Tool("link"), "-o", out, "-importcfg", importcfg, ldflags)
   586  }
   587  
   588  func (gcToolchain) cc(b *Builder, a *Action, ofile, cfile string) error {
   589  	return fmt.Errorf("%s: C source files not supported without cgo", mkAbs(a.Package.Dir, cfile))
   590  }