github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/go/not-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  	"github.com/gagliardetto/golang-go/cmd/go/not-internal/base"
    16  	"github.com/gagliardetto/golang-go/cmd/go/not-internal/cfg"
    17  	"github.com/gagliardetto/golang-go/cmd/go/not-internal/load"
    18  	"github.com/gagliardetto/golang-go/cmd/go/not-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 = cfg.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 (gccgoToolchain) ar() string {
    47  	ar := cfg.Getenv("AR")
    48  	if ar == "" {
    49  		ar = "ar"
    50  	}
    51  	return ar
    52  }
    53  
    54  func checkGccgoBin() {
    55  	if gccgoErr == nil {
    56  		return
    57  	}
    58  	fmt.Fprintf(os.Stderr, "github.com/gagliardetto/golang-go/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 []byte, symabis string, asmhdr bool, gofiles []string) (ofile string, output []byte, err error) {
    64  	p := a.Package
    65  	objdir := a.Objdir
    66  	out := "_go_.o"
    67  	ofile = objdir + out
    68  	gcargs := []string{"-g"}
    69  	gcargs = append(gcargs, b.gccArchArgs()...)
    70  	gcargs = append(gcargs, "-fdebug-prefix-map="+b.WorkDir+"=/tmp/go-build")
    71  	gcargs = append(gcargs, "-gno-record-gcc-switches")
    72  	if pkgpath := gccgoPkgpath(p); pkgpath != "" {
    73  		gcargs = append(gcargs, "-fgo-pkgpath="+pkgpath)
    74  	}
    75  	if p.Internal.LocalPrefix != "" {
    76  		gcargs = append(gcargs, "-fgo-relative-import-path="+p.Internal.LocalPrefix)
    77  	}
    78  
    79  	args := str.StringList(tools.compiler(), "-c", gcargs, "-o", ofile, forcedGccgoflags)
    80  	if importcfg != nil {
    81  		if b.gccSupportsFlag(args[:1], "-fgo-importcfg=/dev/null") {
    82  			if err := b.writeFile(objdir+"importcfg", importcfg); err != nil {
    83  				return "", nil, err
    84  			}
    85  			args = append(args, "-fgo-importcfg="+objdir+"importcfg")
    86  		} else {
    87  			root := objdir + "_importcfgroot_"
    88  			if err := buildImportcfgSymlinks(b, root, importcfg); err != nil {
    89  				return "", nil, err
    90  			}
    91  			args = append(args, "-I", root)
    92  		}
    93  	}
    94  	if cfg.BuildTrimpath && b.gccSupportsFlag(args[:1], "-ffile-prefix-map=a=b") {
    95  		args = append(args, "-ffile-prefix-map="+base.Cwd+"=.")
    96  		args = append(args, "-ffile-prefix-map="+b.WorkDir+"=/tmp/go-build")
    97  	}
    98  	args = append(args, a.Package.Internal.Gccgoflags...)
    99  	for _, f := range gofiles {
   100  		args = append(args, mkAbs(p.Dir, f))
   101  	}
   102  
   103  	output, err = b.runOut(a, p.Dir, nil, args)
   104  	return ofile, output, err
   105  }
   106  
   107  // buildImportcfgSymlinks builds in root a tree of symlinks
   108  // implementing the directives from importcfg.
   109  // This serves as a temporary transition mechanism until
   110  // we can depend on gccgo reading an importcfg directly.
   111  // (The Go 1.9 and later gc compilers already do.)
   112  func buildImportcfgSymlinks(b *Builder, root string, importcfg []byte) error {
   113  	for lineNum, line := range strings.Split(string(importcfg), "\n") {
   114  		lineNum++ // 1-based
   115  		line = strings.TrimSpace(line)
   116  		if line == "" {
   117  			continue
   118  		}
   119  		if line == "" || strings.HasPrefix(line, "#") {
   120  			continue
   121  		}
   122  		var verb, args string
   123  		if i := strings.Index(line, " "); i < 0 {
   124  			verb = line
   125  		} else {
   126  			verb, args = line[:i], strings.TrimSpace(line[i+1:])
   127  		}
   128  		var before, after string
   129  		if i := strings.Index(args, "="); i >= 0 {
   130  			before, after = args[:i], args[i+1:]
   131  		}
   132  		switch verb {
   133  		default:
   134  			base.Fatalf("importcfg:%d: unknown directive %q", lineNum, verb)
   135  		case "packagefile":
   136  			if before == "" || after == "" {
   137  				return fmt.Errorf(`importcfg:%d: invalid packagefile: syntax is "packagefile path=filename": %s`, lineNum, line)
   138  			}
   139  			archive := gccgoArchive(root, before)
   140  			if err := b.Mkdir(filepath.Dir(archive)); err != nil {
   141  				return err
   142  			}
   143  			if err := b.Symlink(after, archive); err != nil {
   144  				return err
   145  			}
   146  		case "importmap":
   147  			if before == "" || after == "" {
   148  				return fmt.Errorf(`importcfg:%d: invalid importmap: syntax is "importmap old=new": %s`, lineNum, line)
   149  			}
   150  			beforeA := gccgoArchive(root, before)
   151  			afterA := gccgoArchive(root, after)
   152  			if err := b.Mkdir(filepath.Dir(beforeA)); err != nil {
   153  				return err
   154  			}
   155  			if err := b.Mkdir(filepath.Dir(afterA)); err != nil {
   156  				return err
   157  			}
   158  			if err := b.Symlink(afterA, beforeA); err != nil {
   159  				return err
   160  			}
   161  		case "packageshlib":
   162  			return fmt.Errorf("gccgo -importcfg does not support shared libraries")
   163  		}
   164  	}
   165  	return nil
   166  }
   167  
   168  func (tools gccgoToolchain) asm(b *Builder, a *Action, sfiles []string) ([]string, error) {
   169  	p := a.Package
   170  	var ofiles []string
   171  	for _, sfile := range sfiles {
   172  		base := filepath.Base(sfile)
   173  		ofile := a.Objdir + base[:len(base)-len(".s")] + ".o"
   174  		ofiles = append(ofiles, ofile)
   175  		sfile = mkAbs(p.Dir, sfile)
   176  		defs := []string{"-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch}
   177  		if pkgpath := gccgoCleanPkgpath(p); pkgpath != "" {
   178  			defs = append(defs, `-D`, `GOPKGPATH=`+pkgpath)
   179  		}
   180  		defs = tools.maybePIC(defs)
   181  		defs = append(defs, b.gccArchArgs()...)
   182  		err := b.run(a, p.Dir, p.ImportPath, nil, tools.compiler(), "-xassembler-with-cpp", "-I", a.Objdir, "-c", "-o", ofile, defs, sfile)
   183  		if err != nil {
   184  			return nil, err
   185  		}
   186  	}
   187  	return ofiles, nil
   188  }
   189  
   190  func (gccgoToolchain) symabis(b *Builder, a *Action, sfiles []string) (string, error) {
   191  	return "", nil
   192  }
   193  
   194  func gccgoArchive(basedir, imp string) string {
   195  	end := filepath.FromSlash(imp + ".a")
   196  	afile := filepath.Join(basedir, end)
   197  	// add "lib" to the final element
   198  	return filepath.Join(filepath.Dir(afile), "lib"+filepath.Base(afile))
   199  }
   200  
   201  func (tools gccgoToolchain) pack(b *Builder, a *Action, afile string, ofiles []string) error {
   202  	p := a.Package
   203  	objdir := a.Objdir
   204  	var absOfiles []string
   205  	for _, f := range ofiles {
   206  		absOfiles = append(absOfiles, mkAbs(objdir, f))
   207  	}
   208  	var arArgs []string
   209  	if cfg.Goos == "aix" && cfg.Goarch == "ppc64" {
   210  		// AIX puts both 32-bit and 64-bit objects in the same archive.
   211  		// Tell the AIX "ar" command to only care about 64-bit objects.
   212  		arArgs = []string{"-X64"}
   213  	}
   214  	absAfile := mkAbs(objdir, afile)
   215  	// Try with D modifier first, then without if that fails.
   216  	output, err := b.runOut(a, p.Dir, nil, tools.ar(), arArgs, "rcD", absAfile, absOfiles)
   217  	if err != nil {
   218  		return b.run(a, p.Dir, p.ImportPath, nil, tools.ar(), arArgs, "rc", absAfile, absOfiles)
   219  	}
   220  
   221  	if len(output) > 0 {
   222  		// Show the output if there is any even without errors.
   223  		b.showOutput(a, p.Dir, p.ImportPath, b.processOutput(output))
   224  	}
   225  
   226  	return nil
   227  }
   228  
   229  func (tools gccgoToolchain) link(b *Builder, root *Action, out, importcfg string, allactions []*Action, buildmode, desc string) error {
   230  	// gccgo needs explicit linking with all package dependencies,
   231  	// and all LDFLAGS from cgo dependencies.
   232  	afiles := []string{}
   233  	shlibs := []string{}
   234  	ldflags := b.gccArchArgs()
   235  	cgoldflags := []string{}
   236  	usesCgo := false
   237  	cxx := false
   238  	objc := false
   239  	fortran := false
   240  	if root.Package != nil {
   241  		cxx = len(root.Package.CXXFiles) > 0 || len(root.Package.SwigCXXFiles) > 0
   242  		objc = len(root.Package.MFiles) > 0
   243  		fortran = len(root.Package.FFiles) > 0
   244  	}
   245  
   246  	readCgoFlags := func(flagsFile string) error {
   247  		flags, err := ioutil.ReadFile(flagsFile)
   248  		if err != nil {
   249  			return err
   250  		}
   251  		const ldflagsPrefix = "_CGO_LDFLAGS="
   252  		for _, line := range strings.Split(string(flags), "\n") {
   253  			if strings.HasPrefix(line, ldflagsPrefix) {
   254  				newFlags := strings.Fields(line[len(ldflagsPrefix):])
   255  				for _, flag := range newFlags {
   256  					// Every _cgo_flags file has -g and -O2 in _CGO_LDFLAGS
   257  					// but they don't mean anything to the linker so filter
   258  					// them out.
   259  					if flag != "-g" && !strings.HasPrefix(flag, "-O") {
   260  						cgoldflags = append(cgoldflags, flag)
   261  					}
   262  				}
   263  			}
   264  		}
   265  		return nil
   266  	}
   267  
   268  	var arArgs []string
   269  	if cfg.Goos == "aix" && cfg.Goarch == "ppc64" {
   270  		// AIX puts both 32-bit and 64-bit objects in the same archive.
   271  		// Tell the AIX "ar" command to only care about 64-bit objects.
   272  		arArgs = []string{"-X64"}
   273  	}
   274  
   275  	newID := 0
   276  	readAndRemoveCgoFlags := func(archive string) (string, error) {
   277  		newID++
   278  		newArchive := root.Objdir + fmt.Sprintf("_pkg%d_.a", newID)
   279  		if err := b.copyFile(newArchive, archive, 0666, false); err != nil {
   280  			return "", err
   281  		}
   282  		if cfg.BuildN || cfg.BuildX {
   283  			b.Showcmd("", "ar d %s _cgo_flags", newArchive)
   284  			if cfg.BuildN {
   285  				// TODO(rsc): We could do better about showing the right _cgo_flags even in -n mode.
   286  				// Either the archive is already built and we can read them out,
   287  				// or we're printing commands to build the archive and can
   288  				// forward the _cgo_flags directly to this step.
   289  				return "", nil
   290  			}
   291  		}
   292  		err := b.run(root, root.Objdir, desc, nil, tools.ar(), arArgs, "x", newArchive, "_cgo_flags")
   293  		if err != nil {
   294  			return "", err
   295  		}
   296  		err = b.run(root, ".", desc, nil, tools.ar(), arArgs, "d", newArchive, "_cgo_flags")
   297  		if err != nil {
   298  			return "", err
   299  		}
   300  		err = readCgoFlags(filepath.Join(root.Objdir, "_cgo_flags"))
   301  		if err != nil {
   302  			return "", err
   303  		}
   304  		return newArchive, nil
   305  	}
   306  
   307  	// If using -linkshared, find the shared library deps.
   308  	haveShlib := make(map[string]bool)
   309  	targetBase := filepath.Base(root.Target)
   310  	if cfg.BuildLinkshared {
   311  		for _, a := range root.Deps {
   312  			p := a.Package
   313  			if p == nil || p.Shlib == "" {
   314  				continue
   315  			}
   316  
   317  			// The .a we are linking into this .so
   318  			// will have its Shlib set to this .so.
   319  			// Don't start thinking we want to link
   320  			// this .so into itself.
   321  			base := filepath.Base(p.Shlib)
   322  			if base != targetBase {
   323  				haveShlib[base] = true
   324  			}
   325  		}
   326  	}
   327  
   328  	// Arrange the deps into afiles and shlibs.
   329  	addedShlib := make(map[string]bool)
   330  	for _, a := range root.Deps {
   331  		p := a.Package
   332  		if p != nil && p.Shlib != "" && haveShlib[filepath.Base(p.Shlib)] {
   333  			// This is a package linked into a shared
   334  			// library that we will put into shlibs.
   335  			continue
   336  		}
   337  
   338  		if haveShlib[filepath.Base(a.Target)] {
   339  			// This is a shared library we want to link against.
   340  			if !addedShlib[a.Target] {
   341  				shlibs = append(shlibs, a.Target)
   342  				addedShlib[a.Target] = true
   343  			}
   344  			continue
   345  		}
   346  
   347  		if p != nil {
   348  			target := a.built
   349  			if p.UsesCgo() || p.UsesSwig() {
   350  				var err error
   351  				target, err = readAndRemoveCgoFlags(target)
   352  				if err != nil {
   353  					continue
   354  				}
   355  			}
   356  
   357  			afiles = append(afiles, target)
   358  		}
   359  	}
   360  
   361  	for _, a := range allactions {
   362  		// Gather CgoLDFLAGS, but not from standard packages.
   363  		// The go tool can dig up runtime/cgo from GOROOT and
   364  		// think that it should use its CgoLDFLAGS, but gccgo
   365  		// doesn't use runtime/cgo.
   366  		if a.Package == nil {
   367  			continue
   368  		}
   369  		if !a.Package.Standard {
   370  			cgoldflags = append(cgoldflags, a.Package.CgoLDFLAGS...)
   371  		}
   372  		if len(a.Package.CgoFiles) > 0 {
   373  			usesCgo = true
   374  		}
   375  		if a.Package.UsesSwig() {
   376  			usesCgo = true
   377  		}
   378  		if len(a.Package.CXXFiles) > 0 || len(a.Package.SwigCXXFiles) > 0 {
   379  			cxx = true
   380  		}
   381  		if len(a.Package.MFiles) > 0 {
   382  			objc = true
   383  		}
   384  		if len(a.Package.FFiles) > 0 {
   385  			fortran = true
   386  		}
   387  	}
   388  
   389  	wholeArchive := []string{"-Wl,--whole-archive"}
   390  	noWholeArchive := []string{"-Wl,--no-whole-archive"}
   391  	if cfg.Goos == "aix" {
   392  		wholeArchive = nil
   393  		noWholeArchive = nil
   394  	}
   395  	ldflags = append(ldflags, wholeArchive...)
   396  	ldflags = append(ldflags, afiles...)
   397  	ldflags = append(ldflags, noWholeArchive...)
   398  
   399  	ldflags = append(ldflags, cgoldflags...)
   400  	ldflags = append(ldflags, envList("CGO_LDFLAGS", "")...)
   401  	if root.Package != nil {
   402  		ldflags = append(ldflags, root.Package.CgoLDFLAGS...)
   403  	}
   404  	if cfg.Goos != "aix" {
   405  		ldflags = str.StringList("-Wl,-(", ldflags, "-Wl,-)")
   406  	}
   407  
   408  	if root.buildID != "" {
   409  		// On systems that normally use gold or the GNU linker,
   410  		// use the --build-id option to write a GNU build ID note.
   411  		switch cfg.Goos {
   412  		case "android", "dragonfly", "linux", "netbsd":
   413  			ldflags = append(ldflags, fmt.Sprintf("-Wl,--build-id=0x%x", root.buildID))
   414  		}
   415  	}
   416  
   417  	var rLibPath string
   418  	if cfg.Goos == "aix" {
   419  		rLibPath = "-Wl,-blibpath="
   420  	} else {
   421  		rLibPath = "-Wl,-rpath="
   422  	}
   423  	for _, shlib := range shlibs {
   424  		ldflags = append(
   425  			ldflags,
   426  			"-L"+filepath.Dir(shlib),
   427  			rLibPath+filepath.Dir(shlib),
   428  			"-l"+strings.TrimSuffix(
   429  				strings.TrimPrefix(filepath.Base(shlib), "lib"),
   430  				".so"))
   431  	}
   432  
   433  	var realOut string
   434  	goLibBegin := str.StringList(wholeArchive, "-lgolibbegin", noWholeArchive)
   435  	switch buildmode {
   436  	case "exe":
   437  		if usesCgo && cfg.Goos == "linux" {
   438  			ldflags = append(ldflags, "-Wl,-E")
   439  		}
   440  
   441  	case "c-archive":
   442  		// Link the Go files into a single .o, and also link
   443  		// in -lgolibbegin.
   444  		//
   445  		// We need to use --whole-archive with -lgolibbegin
   446  		// because it doesn't define any symbols that will
   447  		// cause the contents to be pulled in; it's just
   448  		// initialization code.
   449  		//
   450  		// The user remains responsible for linking against
   451  		// -lgo -lpthread -lm in the final link. We can't use
   452  		// -r to pick them up because we can't combine
   453  		// split-stack and non-split-stack code in a single -r
   454  		// link, and libgo picks up non-split-stack code from
   455  		// libffi.
   456  		ldflags = append(ldflags, "-Wl,-r", "-nostdlib")
   457  		ldflags = append(ldflags, goLibBegin...)
   458  
   459  		if nopie := b.gccNoPie([]string{tools.linker()}); nopie != "" {
   460  			ldflags = append(ldflags, nopie)
   461  		}
   462  
   463  		// We are creating an object file, so we don't want a build ID.
   464  		if root.buildID == "" {
   465  			ldflags = b.disableBuildID(ldflags)
   466  		}
   467  
   468  		realOut = out
   469  		out = out + ".o"
   470  
   471  	case "c-shared":
   472  		ldflags = append(ldflags, "-shared", "-nostdlib")
   473  		ldflags = append(ldflags, goLibBegin...)
   474  		ldflags = append(ldflags, "-lgo", "-lgcc_s", "-lgcc", "-lc", "-lgcc")
   475  
   476  	case "shared":
   477  		if cfg.Goos != "aix" {
   478  			ldflags = append(ldflags, "-zdefs")
   479  		}
   480  		ldflags = append(ldflags, "-shared", "-nostdlib", "-lgo", "-lgcc_s", "-lgcc", "-lc")
   481  
   482  	default:
   483  		base.Fatalf("-buildmode=%s not supported for gccgo", buildmode)
   484  	}
   485  
   486  	switch buildmode {
   487  	case "exe", "c-shared":
   488  		if cxx {
   489  			ldflags = append(ldflags, "-lstdc++")
   490  		}
   491  		if objc {
   492  			ldflags = append(ldflags, "-lobjc")
   493  		}
   494  		if fortran {
   495  			fc := cfg.Getenv("FC")
   496  			if fc == "" {
   497  				fc = "gfortran"
   498  			}
   499  			// support gfortran out of the box and let others pass the correct link options
   500  			// via CGO_LDFLAGS
   501  			if strings.Contains(fc, "gfortran") {
   502  				ldflags = append(ldflags, "-lgfortran")
   503  			}
   504  		}
   505  	}
   506  
   507  	if err := b.run(root, ".", desc, nil, tools.linker(), "-o", out, ldflags, forcedGccgoflags, root.Package.Internal.Gccgoflags); err != nil {
   508  		return err
   509  	}
   510  
   511  	switch buildmode {
   512  	case "c-archive":
   513  		if err := b.run(root, ".", desc, nil, tools.ar(), arArgs, "rc", realOut, out); err != nil {
   514  			return err
   515  		}
   516  	}
   517  	return nil
   518  }
   519  
   520  func (tools gccgoToolchain) ld(b *Builder, root *Action, out, importcfg, mainpkg string) error {
   521  	return tools.link(b, root, out, importcfg, root.Deps, ldBuildmode, root.Package.ImportPath)
   522  }
   523  
   524  func (tools gccgoToolchain) ldShared(b *Builder, root *Action, toplevelactions []*Action, out, importcfg string, allactions []*Action) error {
   525  	return tools.link(b, root, out, importcfg, allactions, "shared", out)
   526  }
   527  
   528  func (tools gccgoToolchain) cc(b *Builder, a *Action, ofile, cfile string) error {
   529  	p := a.Package
   530  	inc := filepath.Join(cfg.GOROOT, "pkg", "include")
   531  	cfile = mkAbs(p.Dir, cfile)
   532  	defs := []string{"-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch}
   533  	defs = append(defs, b.gccArchArgs()...)
   534  	if pkgpath := gccgoCleanPkgpath(p); pkgpath != "" {
   535  		defs = append(defs, `-D`, `GOPKGPATH="`+pkgpath+`"`)
   536  	}
   537  	compiler := envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch))
   538  	if b.gccSupportsFlag(compiler, "-fsplit-stack") {
   539  		defs = append(defs, "-fsplit-stack")
   540  	}
   541  	defs = tools.maybePIC(defs)
   542  	if b.gccSupportsFlag(compiler, "-ffile-prefix-map=a=b") {
   543  		defs = append(defs, "-ffile-prefix-map="+base.Cwd+"=.")
   544  		defs = append(defs, "-ffile-prefix-map="+b.WorkDir+"=/tmp/go-build")
   545  	} else if b.gccSupportsFlag(compiler, "-fdebug-prefix-map=a=b") {
   546  		defs = append(defs, "-fdebug-prefix-map="+b.WorkDir+"=/tmp/go-build")
   547  	}
   548  	if b.gccSupportsFlag(compiler, "-gno-record-gcc-switches") {
   549  		defs = append(defs, "-gno-record-gcc-switches")
   550  	}
   551  	return b.run(a, p.Dir, p.ImportPath, nil, compiler, "-Wall", "-g",
   552  		"-I", a.Objdir, "-I", inc, "-o", ofile, defs, "-c", cfile)
   553  }
   554  
   555  // maybePIC adds -fPIC to the list of arguments if needed.
   556  func (tools gccgoToolchain) maybePIC(args []string) []string {
   557  	switch cfg.BuildBuildmode {
   558  	case "c-shared", "shared", "plugin":
   559  		args = append(args, "-fPIC")
   560  	}
   561  	return args
   562  }
   563  
   564  func gccgoPkgpath(p *load.Package) string {
   565  	if p.Internal.Build.IsCommand() && !p.Internal.ForceLibrary {
   566  		return ""
   567  	}
   568  	return p.ImportPath
   569  }
   570  
   571  func gccgoCleanPkgpath(p *load.Package) string {
   572  	clean := func(r rune) rune {
   573  		switch {
   574  		case 'A' <= r && r <= 'Z', 'a' <= r && r <= 'z',
   575  			'0' <= r && r <= '9':
   576  			return r
   577  		}
   578  		return '_'
   579  	}
   580  	return strings.Map(clean, gccgoPkgpath(p))
   581  }