...

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

View as plain text