Source file src/cmd/go/internal/work/gccgo.go

     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  	"bytes"
     9  	"fmt"
    10  	"os"
    11  	"os/exec"
    12  	"path/filepath"
    13  	"strings"
    14  	"sync"
    15  
    16  	"cmd/go/internal/base"
    17  	"cmd/go/internal/cfg"
    18  	"cmd/go/internal/fsys"
    19  	"cmd/go/internal/load"
    20  	"cmd/go/internal/str"
    21  	"cmd/internal/pathcache"
    22  	"cmd/internal/pkgpath"
    23  )
    24  
    25  // The Gccgo toolchain.
    26  
    27  type gccgoToolchain struct{}
    28  
    29  var GccgoName, GccgoBin string
    30  var gccgoErr error
    31  
    32  func init() {
    33  	GccgoName = cfg.Getenv("GCCGO")
    34  	if GccgoName == "" {
    35  		GccgoName = "gccgo"
    36  	}
    37  	GccgoBin, gccgoErr = pathcache.LookPath(GccgoName)
    38  }
    39  
    40  func (gccgoToolchain) compiler() string {
    41  	checkGccgoBin()
    42  	return GccgoBin
    43  }
    44  
    45  func (gccgoToolchain) linker() string {
    46  	checkGccgoBin()
    47  	return GccgoBin
    48  }
    49  
    50  func (gccgoToolchain) ar() []string {
    51  	return envList("AR", "ar")
    52  }
    53  
    54  func checkGccgoBin() {
    55  	if gccgoErr == nil {
    56  		return
    57  	}
    58  	fmt.Fprintf(os.Stderr, "cmd/go: gccgo: %s\n", gccgoErr)
    59  	base.SetExitStatus(2)
    60  	base.Exit()
    61  }
    62  
    63  func (tools gccgoToolchain) gc(b *Builder, a *Action, archive string, importcfg, embedcfg []byte, symabis string, asmhdr bool, pgoProfile string, gofiles []string) (ofile string, output []byte, err error) {
    64  	p := a.Package
    65  	sh := b.Shell(a)
    66  	objdir := a.Objdir
    67  	out := "_go_.o"
    68  	ofile = objdir + out
    69  	gcargs := []string{"-g"}
    70  	gcargs = append(gcargs, b.gccArchArgs()...)
    71  	gcargs = append(gcargs, "-fdebug-prefix-map="+b.WorkDir+"=/tmp/go-build")
    72  	gcargs = append(gcargs, "-gno-record-gcc-switches")
    73  	if pkgpath := gccgoPkgpath(p); pkgpath != "" {
    74  		gcargs = append(gcargs, "-fgo-pkgpath="+pkgpath)
    75  	}
    76  	if p.Internal.LocalPrefix != "" {
    77  		gcargs = append(gcargs, "-fgo-relative-import-path="+p.Internal.LocalPrefix)
    78  	}
    79  
    80  	args := str.StringList(tools.compiler(), "-c", gcargs, "-o", ofile, forcedGccgoflags)
    81  	if importcfg != nil {
    82  		if b.gccSupportsFlag(args[:1], "-fgo-importcfg=/dev/null") {
    83  			if err := sh.writeFile(objdir+"importcfg", importcfg); err != nil {
    84  				return "", nil, err
    85  			}
    86  			args = append(args, "-fgo-importcfg="+objdir+"importcfg")
    87  		} else {
    88  			root := objdir + "_importcfgroot_"
    89  			if err := buildImportcfgSymlinks(sh, root, importcfg); err != nil {
    90  				return "", nil, err
    91  			}
    92  			args = append(args, "-I", root)
    93  		}
    94  	}
    95  	if embedcfg != nil && b.gccSupportsFlag(args[:1], "-fgo-embedcfg=/dev/null") {
    96  		if err := sh.writeFile(objdir+"embedcfg", embedcfg); err != nil {
    97  			return "", nil, err
    98  		}
    99  		args = append(args, "-fgo-embedcfg="+objdir+"embedcfg")
   100  	}
   101  
   102  	if b.gccSupportsFlag(args[:1], "-ffile-prefix-map=a=b") {
   103  		if cfg.BuildTrimpath {
   104  			args = append(args, "-ffile-prefix-map="+base.Cwd()+"=.")
   105  			args = append(args, "-ffile-prefix-map="+b.WorkDir+"=/tmp/go-build")
   106  		}
   107  		if fsys.OverlayFile != "" {
   108  			for _, name := range gofiles {
   109  				absPath := mkAbs(p.Dir, name)
   110  				overlayPath, ok := fsys.OverlayPath(absPath)
   111  				if !ok {
   112  					continue
   113  				}
   114  				toPath := absPath
   115  				// gccgo only applies the last matching rule, so also handle the case where
   116  				// BuildTrimpath is true and the path is relative to base.Cwd().
   117  				if cfg.BuildTrimpath && str.HasFilePathPrefix(toPath, base.Cwd()) {
   118  					toPath = "." + toPath[len(base.Cwd()):]
   119  				}
   120  				args = append(args, "-ffile-prefix-map="+overlayPath+"="+toPath)
   121  			}
   122  		}
   123  	}
   124  
   125  	args = append(args, a.Package.Internal.Gccgoflags...)
   126  	for _, f := range gofiles {
   127  		f := mkAbs(p.Dir, f)
   128  		// Overlay files if necessary.
   129  		// See comment on gctoolchain.gc about overlay TODOs
   130  		f, _ = fsys.OverlayPath(f)
   131  		args = append(args, f)
   132  	}
   133  
   134  	output, err = sh.runOut(p.Dir, nil, args)
   135  	return ofile, output, err
   136  }
   137  
   138  // buildImportcfgSymlinks builds in root a tree of symlinks
   139  // implementing the directives from importcfg.
   140  // This serves as a temporary transition mechanism until
   141  // we can depend on gccgo reading an importcfg directly.
   142  // (The Go 1.9 and later gc compilers already do.)
   143  func buildImportcfgSymlinks(sh *Shell, root string, importcfg []byte) error {
   144  	for lineNum, line := range strings.Split(string(importcfg), "\n") {
   145  		lineNum++ // 1-based
   146  		line = strings.TrimSpace(line)
   147  		if line == "" {
   148  			continue
   149  		}
   150  		if line == "" || strings.HasPrefix(line, "#") {
   151  			continue
   152  		}
   153  		var verb, args string
   154  		if i := strings.Index(line, " "); i < 0 {
   155  			verb = line
   156  		} else {
   157  			verb, args = line[:i], strings.TrimSpace(line[i+1:])
   158  		}
   159  		before, after, _ := strings.Cut(args, "=")
   160  		switch verb {
   161  		default:
   162  			base.Fatalf("importcfg:%d: unknown directive %q", lineNum, verb)
   163  		case "packagefile":
   164  			if before == "" || after == "" {
   165  				return fmt.Errorf(`importcfg:%d: invalid packagefile: syntax is "packagefile path=filename": %s`, lineNum, line)
   166  			}
   167  			archive := gccgoArchive(root, before)
   168  			if err := sh.Mkdir(filepath.Dir(archive)); err != nil {
   169  				return err
   170  			}
   171  			if err := sh.Symlink(after, archive); err != nil {
   172  				return err
   173  			}
   174  		case "importmap":
   175  			if before == "" || after == "" {
   176  				return fmt.Errorf(`importcfg:%d: invalid importmap: syntax is "importmap old=new": %s`, lineNum, line)
   177  			}
   178  			beforeA := gccgoArchive(root, before)
   179  			afterA := gccgoArchive(root, after)
   180  			if err := sh.Mkdir(filepath.Dir(beforeA)); err != nil {
   181  				return err
   182  			}
   183  			if err := sh.Mkdir(filepath.Dir(afterA)); err != nil {
   184  				return err
   185  			}
   186  			if err := sh.Symlink(afterA, beforeA); err != nil {
   187  				return err
   188  			}
   189  		case "packageshlib":
   190  			return fmt.Errorf("gccgo -importcfg does not support shared libraries")
   191  		}
   192  	}
   193  	return nil
   194  }
   195  
   196  func (tools gccgoToolchain) asm(b *Builder, a *Action, sfiles []string) ([]string, error) {
   197  	p := a.Package
   198  	var ofiles []string
   199  	for _, sfile := range sfiles {
   200  		base := filepath.Base(sfile)
   201  		ofile := a.Objdir + base[:len(base)-len(".s")] + ".o"
   202  		ofiles = append(ofiles, ofile)
   203  		sfile, _ = fsys.OverlayPath(mkAbs(p.Dir, sfile))
   204  		defs := []string{"-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch}
   205  		if pkgpath := tools.gccgoCleanPkgpath(b, p); pkgpath != "" {
   206  			defs = append(defs, `-D`, `GOPKGPATH=`+pkgpath)
   207  		}
   208  		defs = tools.maybePIC(defs)
   209  		defs = append(defs, b.gccArchArgs()...)
   210  		err := b.Shell(a).run(p.Dir, p.ImportPath, nil, tools.compiler(), "-xassembler-with-cpp", "-I", a.Objdir, "-c", "-o", ofile, defs, sfile)
   211  		if err != nil {
   212  			return nil, err
   213  		}
   214  	}
   215  	return ofiles, nil
   216  }
   217  
   218  func (gccgoToolchain) symabis(b *Builder, a *Action, sfiles []string) (string, error) {
   219  	return "", nil
   220  }
   221  
   222  func gccgoArchive(basedir, imp string) string {
   223  	end := filepath.FromSlash(imp + ".a")
   224  	afile := filepath.Join(basedir, end)
   225  	// add "lib" to the final element
   226  	return filepath.Join(filepath.Dir(afile), "lib"+filepath.Base(afile))
   227  }
   228  
   229  func (tools gccgoToolchain) pack(b *Builder, a *Action, afile string, ofiles []string) error {
   230  	p := a.Package
   231  	sh := b.Shell(a)
   232  	objdir := a.Objdir
   233  	absOfiles := make([]string, 0, len(ofiles))
   234  	for _, f := range ofiles {
   235  		absOfiles = append(absOfiles, mkAbs(objdir, f))
   236  	}
   237  	var arArgs []string
   238  	if cfg.Goos == "aix" && cfg.Goarch == "ppc64" {
   239  		// AIX puts both 32-bit and 64-bit objects in the same archive.
   240  		// Tell the AIX "ar" command to only care about 64-bit objects.
   241  		arArgs = []string{"-X64"}
   242  	}
   243  	absAfile := mkAbs(objdir, afile)
   244  	// Try with D modifier first, then without if that fails.
   245  	output, err := sh.runOut(p.Dir, nil, tools.ar(), arArgs, "rcD", absAfile, absOfiles)
   246  	if err != nil {
   247  		return sh.run(p.Dir, p.ImportPath, nil, tools.ar(), arArgs, "rc", absAfile, absOfiles)
   248  	}
   249  
   250  	// Show the output if there is any even without errors.
   251  	return sh.reportCmd("", "", output, nil)
   252  }
   253  
   254  func (tools gccgoToolchain) link(b *Builder, root *Action, out, importcfg string, allactions []*Action, buildmode, desc string) error {
   255  	sh := b.Shell(root)
   256  
   257  	// gccgo needs explicit linking with all package dependencies,
   258  	// and all LDFLAGS from cgo dependencies.
   259  	afiles := []string{}
   260  	shlibs := []string{}
   261  	ldflags := b.gccArchArgs()
   262  	cgoldflags := []string{}
   263  	usesCgo := false
   264  	cxx := false
   265  	objc := false
   266  	fortran := false
   267  	if root.Package != nil {
   268  		cxx = len(root.Package.CXXFiles) > 0 || len(root.Package.SwigCXXFiles) > 0
   269  		objc = len(root.Package.MFiles) > 0
   270  		fortran = len(root.Package.FFiles) > 0
   271  	}
   272  
   273  	readCgoFlags := func(flagsFile string) error {
   274  		flags, err := os.ReadFile(flagsFile)
   275  		if err != nil {
   276  			return err
   277  		}
   278  		const ldflagsPrefix = "_CGO_LDFLAGS="
   279  		for _, line := range strings.Split(string(flags), "\n") {
   280  			if strings.HasPrefix(line, ldflagsPrefix) {
   281  				flag := line[len(ldflagsPrefix):]
   282  				// Every _cgo_flags file has -g and -O2 in _CGO_LDFLAGS
   283  				// but they don't mean anything to the linker so filter
   284  				// them out.
   285  				if flag != "-g" && !strings.HasPrefix(flag, "-O") {
   286  					cgoldflags = append(cgoldflags, flag)
   287  				}
   288  			}
   289  		}
   290  		return nil
   291  	}
   292  
   293  	var arArgs []string
   294  	if cfg.Goos == "aix" && cfg.Goarch == "ppc64" {
   295  		// AIX puts both 32-bit and 64-bit objects in the same archive.
   296  		// Tell the AIX "ar" command to only care about 64-bit objects.
   297  		arArgs = []string{"-X64"}
   298  	}
   299  
   300  	newID := 0
   301  	readAndRemoveCgoFlags := func(archive string) (string, error) {
   302  		newID++
   303  		newArchive := root.Objdir + fmt.Sprintf("_pkg%d_.a", newID)
   304  		if err := sh.CopyFile(newArchive, archive, 0666, false); err != nil {
   305  			return "", err
   306  		}
   307  		if cfg.BuildN || cfg.BuildX {
   308  			sh.ShowCmd("", "ar d %s _cgo_flags", newArchive)
   309  			if cfg.BuildN {
   310  				// TODO(rsc): We could do better about showing the right _cgo_flags even in -n mode.
   311  				// Either the archive is already built and we can read them out,
   312  				// or we're printing commands to build the archive and can
   313  				// forward the _cgo_flags directly to this step.
   314  				return "", nil
   315  			}
   316  		}
   317  		err := sh.run(root.Objdir, desc, nil, tools.ar(), arArgs, "x", newArchive, "_cgo_flags")
   318  		if err != nil {
   319  			return "", err
   320  		}
   321  		err = sh.run(".", desc, nil, tools.ar(), arArgs, "d", newArchive, "_cgo_flags")
   322  		if err != nil {
   323  			return "", err
   324  		}
   325  		err = readCgoFlags(filepath.Join(root.Objdir, "_cgo_flags"))
   326  		if err != nil {
   327  			return "", err
   328  		}
   329  		return newArchive, nil
   330  	}
   331  
   332  	// If using -linkshared, find the shared library deps.
   333  	haveShlib := make(map[string]bool)
   334  	targetBase := filepath.Base(root.Target)
   335  	if cfg.BuildLinkshared {
   336  		for _, a := range root.Deps {
   337  			p := a.Package
   338  			if p == nil || p.Shlib == "" {
   339  				continue
   340  			}
   341  
   342  			// The .a we are linking into this .so
   343  			// will have its Shlib set to this .so.
   344  			// Don't start thinking we want to link
   345  			// this .so into itself.
   346  			base := filepath.Base(p.Shlib)
   347  			if base != targetBase {
   348  				haveShlib[base] = true
   349  			}
   350  		}
   351  	}
   352  
   353  	// Arrange the deps into afiles and shlibs.
   354  	addedShlib := make(map[string]bool)
   355  	for _, a := range root.Deps {
   356  		p := a.Package
   357  		if p != nil && p.Shlib != "" && haveShlib[filepath.Base(p.Shlib)] {
   358  			// This is a package linked into a shared
   359  			// library that we will put into shlibs.
   360  			continue
   361  		}
   362  
   363  		if haveShlib[filepath.Base(a.Target)] {
   364  			// This is a shared library we want to link against.
   365  			if !addedShlib[a.Target] {
   366  				shlibs = append(shlibs, a.Target)
   367  				addedShlib[a.Target] = true
   368  			}
   369  			continue
   370  		}
   371  
   372  		if p != nil {
   373  			target := a.built
   374  			if p.UsesCgo() || p.UsesSwig() {
   375  				var err error
   376  				target, err = readAndRemoveCgoFlags(target)
   377  				if err != nil {
   378  					continue
   379  				}
   380  			}
   381  
   382  			afiles = append(afiles, target)
   383  		}
   384  	}
   385  
   386  	for _, a := range allactions {
   387  		if a.Package == nil {
   388  			continue
   389  		}
   390  		if len(a.Package.CgoFiles) > 0 {
   391  			usesCgo = true
   392  		}
   393  		if a.Package.UsesSwig() {
   394  			usesCgo = true
   395  		}
   396  		if len(a.Package.CXXFiles) > 0 || len(a.Package.SwigCXXFiles) > 0 {
   397  			cxx = true
   398  		}
   399  		if len(a.Package.MFiles) > 0 {
   400  			objc = true
   401  		}
   402  		if len(a.Package.FFiles) > 0 {
   403  			fortran = true
   404  		}
   405  	}
   406  
   407  	wholeArchive := []string{"-Wl,--whole-archive"}
   408  	noWholeArchive := []string{"-Wl,--no-whole-archive"}
   409  	if cfg.Goos == "aix" {
   410  		wholeArchive = nil
   411  		noWholeArchive = nil
   412  	}
   413  	ldflags = append(ldflags, wholeArchive...)
   414  	ldflags = append(ldflags, afiles...)
   415  	ldflags = append(ldflags, noWholeArchive...)
   416  
   417  	ldflags = append(ldflags, cgoldflags...)
   418  	ldflags = append(ldflags, envList("CGO_LDFLAGS", "")...)
   419  	if cfg.Goos != "aix" {
   420  		ldflags = str.StringList("-Wl,-(", ldflags, "-Wl,-)")
   421  	}
   422  
   423  	if root.buildID != "" {
   424  		// On systems that normally use gold or the GNU linker,
   425  		// use the --build-id option to write a GNU build ID note.
   426  		switch cfg.Goos {
   427  		case "android", "dragonfly", "linux", "netbsd":
   428  			ldflags = append(ldflags, fmt.Sprintf("-Wl,--build-id=0x%x", root.buildID))
   429  		}
   430  	}
   431  
   432  	var rLibPath string
   433  	if cfg.Goos == "aix" {
   434  		rLibPath = "-Wl,-blibpath="
   435  	} else {
   436  		rLibPath = "-Wl,-rpath="
   437  	}
   438  	for _, shlib := range shlibs {
   439  		ldflags = append(
   440  			ldflags,
   441  			"-L"+filepath.Dir(shlib),
   442  			rLibPath+filepath.Dir(shlib),
   443  			"-l"+strings.TrimSuffix(
   444  				strings.TrimPrefix(filepath.Base(shlib), "lib"),
   445  				".so"))
   446  	}
   447  
   448  	var realOut string
   449  	goLibBegin := str.StringList(wholeArchive, "-lgolibbegin", noWholeArchive)
   450  	switch buildmode {
   451  	case "exe":
   452  		if usesCgo && cfg.Goos == "linux" {
   453  			ldflags = append(ldflags, "-Wl,-E")
   454  		}
   455  
   456  	case "c-archive":
   457  		// Link the Go files into a single .o, and also link
   458  		// in -lgolibbegin.
   459  		//
   460  		// We need to use --whole-archive with -lgolibbegin
   461  		// because it doesn't define any symbols that will
   462  		// cause the contents to be pulled in; it's just
   463  		// initialization code.
   464  		//
   465  		// The user remains responsible for linking against
   466  		// -lgo -lpthread -lm in the final link. We can't use
   467  		// -r to pick them up because we can't combine
   468  		// split-stack and non-split-stack code in a single -r
   469  		// link, and libgo picks up non-split-stack code from
   470  		// libffi.
   471  		ldflags = append(ldflags, "-Wl,-r", "-nostdlib")
   472  		ldflags = append(ldflags, goLibBegin...)
   473  
   474  		if nopie := b.gccNoPie([]string{tools.linker()}); nopie != "" {
   475  			ldflags = append(ldflags, nopie)
   476  		}
   477  
   478  		// We are creating an object file, so we don't want a build ID.
   479  		if root.buildID == "" {
   480  			ldflags = b.disableBuildID(ldflags)
   481  		}
   482  
   483  		realOut = out
   484  		out = out + ".o"
   485  
   486  	case "c-shared":
   487  		ldflags = append(ldflags, "-shared", "-nostdlib")
   488  		if cfg.Goos != "windows" {
   489  			ldflags = append(ldflags, "-Wl,-z,nodelete")
   490  		}
   491  		ldflags = append(ldflags, goLibBegin...)
   492  		ldflags = append(ldflags, "-lgo", "-lgcc_s", "-lgcc", "-lc", "-lgcc")
   493  
   494  	case "shared":
   495  		if cfg.Goos != "aix" {
   496  			ldflags = append(ldflags, "-zdefs")
   497  		}
   498  		ldflags = append(ldflags, "-shared", "-nostdlib", "-lgo", "-lgcc_s", "-lgcc", "-lc")
   499  
   500  	default:
   501  		base.Fatalf("-buildmode=%s not supported for gccgo", buildmode)
   502  	}
   503  
   504  	switch buildmode {
   505  	case "exe", "c-shared":
   506  		if cxx {
   507  			ldflags = append(ldflags, "-lstdc++")
   508  		}
   509  		if objc {
   510  			ldflags = append(ldflags, "-lobjc")
   511  		}
   512  		if fortran {
   513  			fc := cfg.Getenv("FC")
   514  			if fc == "" {
   515  				fc = "gfortran"
   516  			}
   517  			// support gfortran out of the box and let others pass the correct link options
   518  			// via CGO_LDFLAGS
   519  			if strings.Contains(fc, "gfortran") {
   520  				ldflags = append(ldflags, "-lgfortran")
   521  			}
   522  		}
   523  	}
   524  
   525  	if err := sh.run(".", desc, nil, tools.linker(), "-o", out, ldflags, forcedGccgoflags, root.Package.Internal.Gccgoflags); err != nil {
   526  		return err
   527  	}
   528  
   529  	switch buildmode {
   530  	case "c-archive":
   531  		if err := sh.run(".", desc, nil, tools.ar(), arArgs, "rc", realOut, out); err != nil {
   532  			return err
   533  		}
   534  	}
   535  	return nil
   536  }
   537  
   538  func (tools gccgoToolchain) ld(b *Builder, root *Action, targetPath, importcfg, mainpkg string) error {
   539  	return tools.link(b, root, targetPath, importcfg, root.Deps, ldBuildmode, root.Package.ImportPath)
   540  }
   541  
   542  func (tools gccgoToolchain) ldShared(b *Builder, root *Action, toplevelactions []*Action, targetPath, importcfg string, allactions []*Action) error {
   543  	return tools.link(b, root, targetPath, importcfg, allactions, "shared", targetPath)
   544  }
   545  
   546  func (tools gccgoToolchain) cc(b *Builder, a *Action, ofile, cfile string) error {
   547  	p := a.Package
   548  	inc := filepath.Join(cfg.GOROOT, "pkg", "include")
   549  	cfile = mkAbs(p.Dir, cfile)
   550  	defs := []string{"-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch}
   551  	defs = append(defs, b.gccArchArgs()...)
   552  	if pkgpath := tools.gccgoCleanPkgpath(b, p); pkgpath != "" {
   553  		defs = append(defs, `-D`, `GOPKGPATH="`+pkgpath+`"`)
   554  	}
   555  	compiler := envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch))
   556  	if b.gccSupportsFlag(compiler, "-fsplit-stack") {
   557  		defs = append(defs, "-fsplit-stack")
   558  	}
   559  	defs = tools.maybePIC(defs)
   560  	if b.gccSupportsFlag(compiler, "-ffile-prefix-map=a=b") {
   561  		defs = append(defs, "-ffile-prefix-map="+base.Cwd()+"=.")
   562  		defs = append(defs, "-ffile-prefix-map="+b.WorkDir+"=/tmp/go-build")
   563  	} else if b.gccSupportsFlag(compiler, "-fdebug-prefix-map=a=b") {
   564  		defs = append(defs, "-fdebug-prefix-map="+b.WorkDir+"=/tmp/go-build")
   565  	}
   566  	if b.gccSupportsFlag(compiler, "-gno-record-gcc-switches") {
   567  		defs = append(defs, "-gno-record-gcc-switches")
   568  	}
   569  	return b.Shell(a).run(p.Dir, p.ImportPath, nil, compiler, "-Wall", "-g",
   570  		"-I", a.Objdir, "-I", inc, "-o", ofile, defs, "-c", cfile)
   571  }
   572  
   573  // maybePIC adds -fPIC to the list of arguments if needed.
   574  func (tools gccgoToolchain) maybePIC(args []string) []string {
   575  	switch cfg.BuildBuildmode {
   576  	case "c-shared", "shared", "plugin":
   577  		args = append(args, "-fPIC")
   578  	}
   579  	return args
   580  }
   581  
   582  func gccgoPkgpath(p *load.Package) string {
   583  	if p.Internal.Build.IsCommand() && !p.Internal.ForceLibrary {
   584  		return ""
   585  	}
   586  	return p.ImportPath
   587  }
   588  
   589  var gccgoToSymbolFuncOnce sync.Once
   590  var gccgoToSymbolFunc func(string) string
   591  
   592  func (tools gccgoToolchain) gccgoCleanPkgpath(b *Builder, p *load.Package) string {
   593  	gccgoToSymbolFuncOnce.Do(func() {
   594  		tmpdir := b.WorkDir
   595  		if cfg.BuildN {
   596  			tmpdir = os.TempDir()
   597  		}
   598  		fn, err := pkgpath.ToSymbolFunc(tools.compiler(), tmpdir)
   599  		if err != nil {
   600  			fmt.Fprintf(os.Stderr, "cmd/go: %v\n", err)
   601  			base.SetExitStatus(2)
   602  			base.Exit()
   603  		}
   604  		gccgoToSymbolFunc = fn
   605  	})
   606  
   607  	return gccgoToSymbolFunc(gccgoPkgpath(p))
   608  }
   609  
   610  var (
   611  	gccgoSupportsCgoIncompleteOnce sync.Once
   612  	gccgoSupportsCgoIncomplete     bool
   613  )
   614  
   615  const gccgoSupportsCgoIncompleteCode = `
   616  package p
   617  
   618  import "runtime/cgo"
   619  
   620  type I cgo.Incomplete
   621  `
   622  
   623  // supportsCgoIncomplete reports whether the gccgo/GoLLVM compiler
   624  // being used supports cgo.Incomplete, which was added in GCC 13.
   625  //
   626  // This takes an Action only for output reporting purposes.
   627  // The result value is unrelated to the Action.
   628  func (tools gccgoToolchain) supportsCgoIncomplete(b *Builder, a *Action) bool {
   629  	gccgoSupportsCgoIncompleteOnce.Do(func() {
   630  		sh := b.Shell(a)
   631  
   632  		fail := func(err error) {
   633  			fmt.Fprintf(os.Stderr, "cmd/go: %v\n", err)
   634  			base.SetExitStatus(2)
   635  			base.Exit()
   636  		}
   637  
   638  		tmpdir := b.WorkDir
   639  		if cfg.BuildN {
   640  			tmpdir = os.TempDir()
   641  		}
   642  		f, err := os.CreateTemp(tmpdir, "*_gccgo_cgoincomplete.go")
   643  		if err != nil {
   644  			fail(err)
   645  		}
   646  		fn := f.Name()
   647  		f.Close()
   648  		defer os.Remove(fn)
   649  
   650  		if err := os.WriteFile(fn, []byte(gccgoSupportsCgoIncompleteCode), 0644); err != nil {
   651  			fail(err)
   652  		}
   653  
   654  		on := strings.TrimSuffix(fn, ".go") + ".o"
   655  		if cfg.BuildN || cfg.BuildX {
   656  			sh.ShowCmd(tmpdir, "%s -c -o %s %s || true", tools.compiler(), on, fn)
   657  			// Since this function affects later builds,
   658  			// and only generates temporary files,
   659  			// we run the command even with -n.
   660  		}
   661  		cmd := exec.Command(tools.compiler(), "-c", "-o", on, fn)
   662  		cmd.Dir = tmpdir
   663  		var buf bytes.Buffer
   664  		cmd.Stdout = &buf
   665  		cmd.Stderr = &buf
   666  		err = cmd.Run()
   667  		gccgoSupportsCgoIncomplete = err == nil
   668  		if cfg.BuildN || cfg.BuildX {
   669  			// Show output. We always pass a nil err because errors are an
   670  			// expected outcome in this case.
   671  			desc := sh.fmtCmd(tmpdir, "%s -c -o %s %s", tools.compiler(), on, fn)
   672  			sh.reportCmd(desc, tmpdir, buf.Bytes(), nil)
   673  		}
   674  	})
   675  	return gccgoSupportsCgoIncomplete
   676  }
   677  

View as plain text