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.
15 "cmd/go/internal/base"
17 "cmd/go/internal/load"
21 // The Gccgo toolchain.
23 type gccgoToolchain
struct{}
25 var GccgoName
, GccgoBin
string
29 GccgoName
= os
.Getenv("GCCGO")
31 GccgoName
= cfg
.DefaultGCCGO(cfg
.Goos
, cfg
.Goarch
)
33 GccgoBin
, gccgoErr
= exec
.LookPath(GccgoName
)
36 func (gccgoToolchain
) compiler() string {
41 func (gccgoToolchain
) linker() string {
46 func checkGccgoBin() {
50 fmt
.Fprintf(os
.Stderr
, "cmd/go: gccgo: %s\n", gccgoErr
)
54 func (tools gccgoToolchain
) gc(b
*Builder
, a
*Action
, archive
string, importcfg
[]byte, asmhdr
bool, gofiles
[]string) (ofile
string, output
[]byte, err error
) {
59 gcargs
:= []string{"-g"}
60 gcargs
= append(gcargs
, b
.gccArchArgs()...)
61 gcargs
= append(gcargs
, "-fdebug-prefix-map="+b
.WorkDir
+"=/tmp/go-build")
62 gcargs
= append(gcargs
, "-gno-record-gcc-switches")
63 if pkgpath
:= gccgoPkgpath(p
); pkgpath
!= "" {
64 gcargs
= append(gcargs
, "-fgo-pkgpath="+pkgpath
)
66 if p
.Internal
.LocalPrefix
!= "" {
67 gcargs
= append(gcargs
, "-fgo-relative-import-path="+p
.Internal
.LocalPrefix
)
70 args
:= str
.StringList(tools
.compiler(), "-c", gcargs
, "-o", ofile
, forcedGccgoflags
)
72 if b
.gccSupportsFlag(args
[:1], "-fgo-importcfg=/dev/null") {
73 if err
:= b
.writeFile(objdir
+"importcfg", importcfg
); err
!= nil {
76 args
= append(args
, "-fgo-importcfg="+objdir
+"importcfg")
78 root
:= objdir
+ "_importcfgroot_"
79 if err
:= buildImportcfgSymlinks(b
, root
, importcfg
); err
!= nil {
82 args
= append(args
, "-I", root
)
85 args
= append(args
, a
.Package
.Internal
.Gccgoflags
...)
86 for _
, f
:= range gofiles
{
87 args
= append(args
, mkAbs(p
.Dir
, f
))
90 output
, err
= b
.runOut(p
.Dir
, p
.ImportPath
, nil, args
)
91 return ofile
, output
, err
94 // buildImportcfgSymlinks builds in root a tree of symlinks
95 // implementing the directives from importcfg.
96 // This serves as a temporary transition mechanism until
97 // we can depend on gccgo reading an importcfg directly.
98 // (The Go 1.9 and later gc compilers already do.)
99 func buildImportcfgSymlinks(b
*Builder
, root
string, importcfg
[]byte) error
{
100 for lineNum
, line
:= range strings
.Split(string(importcfg
), "\n") {
102 line
= strings
.TrimSpace(line
)
106 if line
== "" || strings
.HasPrefix(line
, "#") {
109 var verb
, args
string
110 if i
:= strings
.Index(line
, " "); i
< 0 {
113 verb
, args
= line
[:i
], strings
.TrimSpace(line
[i
+1:])
115 var before
, after
string
116 if i
:= strings
.Index(args
, "="); i
>= 0 {
117 before
, after
= args
[:i
], args
[i
+1:]
121 base
.Fatalf("importcfg:%d: unknown directive %q", lineNum
, verb
)
123 if before
== "" || after
== "" {
124 return fmt
.Errorf(`importcfg:%d: invalid packagefile: syntax is "packagefile path=filename": %s`, lineNum
, line
)
126 archive
:= gccgoArchive(root
, before
)
127 if err
:= b
.Mkdir(filepath
.Dir(archive
)); err
!= nil {
130 if err
:= b
.Symlink(after
, archive
); err
!= nil {
134 if before
== "" || after
== "" {
135 return fmt
.Errorf(`importcfg:%d: invalid importmap: syntax is "importmap old=new": %s`, lineNum
, line
)
137 beforeA
:= gccgoArchive(root
, before
)
138 afterA
:= gccgoArchive(root
, after
)
139 if err
:= b
.Mkdir(filepath
.Dir(beforeA
)); err
!= nil {
142 if err
:= b
.Mkdir(filepath
.Dir(afterA
)); err
!= nil {
145 if err
:= b
.Symlink(afterA
, beforeA
); err
!= nil {
149 return fmt
.Errorf("gccgo -importcfg does not support shared libraries")
155 func (tools gccgoToolchain
) asm(b
*Builder
, a
*Action
, sfiles
[]string) ([]string, error
) {
158 for _
, sfile
:= range sfiles
{
159 base
:= filepath
.Base(sfile
)
160 ofile
:= a
.Objdir
+ base
[:len(base
)-len(".s")] + ".o"
161 ofiles
= append(ofiles
, ofile
)
162 sfile
= mkAbs(p
.Dir
, sfile
)
163 defs
:= []string{"-D", "GOOS_" + cfg
.Goos
, "-D", "GOARCH_" + cfg
.Goarch
}
164 if pkgpath
:= gccgoCleanPkgpath(p
); pkgpath
!= "" {
165 defs
= append(defs
, `-D`, `GOPKGPATH=`+pkgpath
)
167 defs
= tools
.maybePIC(defs
)
168 defs
= append(defs
, b
.gccArchArgs()...)
169 err
:= b
.run(a
, p
.Dir
, p
.ImportPath
, nil, tools
.compiler(), "-xassembler-with-cpp", "-I", a
.Objdir
, "-c", "-o", ofile
, defs
, sfile
)
177 func gccgoArchive(basedir
, imp
string) string {
178 end
:= filepath
.FromSlash(imp
+ ".a")
179 afile
:= filepath
.Join(basedir
, end
)
180 // add "lib" to the final element
181 return filepath
.Join(filepath
.Dir(afile
), "lib"+filepath
.Base(afile
))
184 func (gccgoToolchain
) pack(b
*Builder
, a
*Action
, afile
string, ofiles
[]string) error
{
187 var absOfiles
[]string
188 for _
, f
:= range ofiles
{
189 absOfiles
= append(absOfiles
, mkAbs(objdir
, f
))
191 absAfile
:= mkAbs(objdir
, afile
)
192 // Try with D modifier first, then without if that fails.
193 if cfg
.Goos
== "aix" || b
.run(a
, p
.Dir
, p
.ImportPath
, nil, "ar", "rcD", absAfile
, absOfiles
) != nil {
195 if cfg
.Goos
== "aix" && cfg
.Goarch
== "ppc64" {
196 // AIX puts both 32-bit and 64-bit objects in the same archive.
197 // Tell the AIX "ar" command to only care about 64-bit objects.
198 // AIX "ar" command does not know D option.
199 arArgs
= append(arArgs
, "-X64")
201 return b
.run(a
, p
.Dir
, p
.ImportPath
, nil, "ar", arArgs
, "rc", absAfile
, absOfiles
)
206 func (tools gccgoToolchain
) link(b
*Builder
, root
*Action
, out
, importcfg
string, allactions
[]*Action
, buildmode
, desc
string) error
{
207 // gccgo needs explicit linking with all package dependencies,
208 // and all LDFLAGS from cgo dependencies.
211 ldflags
:= b
.gccArchArgs()
212 cgoldflags
:= []string{}
217 if root
.Package
!= nil {
218 cxx
= len(root
.Package
.CXXFiles
) > 0 ||
len(root
.Package
.SwigCXXFiles
) > 0
219 objc
= len(root
.Package
.MFiles
) > 0
220 fortran
= len(root
.Package
.FFiles
) > 0
223 readCgoFlags
:= func(flagsFile
string) error
{
224 flags
, err
:= ioutil
.ReadFile(flagsFile
)
228 const ldflagsPrefix
= "_CGO_LDFLAGS="
229 for _
, line
:= range strings
.Split(string(flags
), "\n") {
230 if strings
.HasPrefix(line
, ldflagsPrefix
) {
231 line
= line
[len(ldflagsPrefix
):]
238 if quote
== 0 && (b
== ' ' || b
== '\t') {
240 cgoldflags
= append(cgoldflags
, string(nl
))
245 } else if b
== '"' || b
== '\'' {
247 } else if b
== quote
{
249 } else if quote
== 0 && start
&& b
== '-' && strings
.HasPrefix(line
, "g") {
252 } else if quote
== 0 && start
&& b
== '-' && strings
.HasPrefix(line
, "O") {
253 for len(line
) > 0 && line
[0] != ' ' && line
[0] != '\t' {
262 cgoldflags
= append(cgoldflags
, string(nl
))
270 readAndRemoveCgoFlags
:= func(archive
string) (string, error
) {
272 newArchive
:= root
.Objdir
+ fmt
.Sprintf("_pkg%d_.a", newID
)
273 if err
:= b
.copyFile(root
, newArchive
, archive
, 0666, false); err
!= nil {
277 // TODO(rsc): We could do better about showing the right _cgo_flags even in -n mode.
278 // Either the archive is already built and we can read them out,
279 // or we're printing commands to build the archive and can
280 // forward the _cgo_flags directly to this step.
281 b
.Showcmd("", "ar d %s _cgo_flags", newArchive
)
284 err
:= b
.run(root
, root
.Objdir
, desc
, nil, "ar", "x", newArchive
, "_cgo_flags")
288 err
= b
.run(root
, ".", desc
, nil, "ar", "d", newArchive
, "_cgo_flags")
292 err
= readCgoFlags(filepath
.Join(root
.Objdir
, "_cgo_flags"))
296 return newArchive
, nil
299 // If using -linkshared, find the shared library deps.
300 haveShlib
:= make(map[string]bool)
301 targetBase
:= filepath
.Base(root
.Target
)
302 if cfg
.BuildLinkshared
{
303 for _
, a
:= range root
.Deps
{
305 if p
== nil || p
.Shlib
== "" {
309 // The .a we are linking into this .so
310 // will have its Shlib set to this .so.
311 // Don't start thinking we want to link
312 // this .so into itself.
313 base
:= filepath
.Base(p
.Shlib
)
314 if base
!= targetBase
{
315 haveShlib
[base
] = true
320 // Arrange the deps into afiles and shlibs.
321 addedShlib
:= make(map[string]bool)
322 for _
, a
:= range root
.Deps
{
324 if p
!= nil && p
.Shlib
!= "" && haveShlib
[filepath
.Base(p
.Shlib
)] {
325 // This is a package linked into a shared
326 // library that we will put into shlibs.
330 if haveShlib
[filepath
.Base(a
.Target
)] {
331 // This is a shared library we want to link againt.
332 if !addedShlib
[a
.Target
] {
333 shlibs
= append(shlibs
, a
.Target
)
334 addedShlib
[a
.Target
] = true
341 if p
.UsesCgo() || p
.UsesSwig() {
343 target
, err
= readAndRemoveCgoFlags(target
)
349 afiles
= append(afiles
, target
)
353 for _
, a
:= range allactions
{
354 // Gather CgoLDFLAGS, but not from standard packages.
355 // The go tool can dig up runtime/cgo from GOROOT and
356 // think that it should use its CgoLDFLAGS, but gccgo
357 // doesn't use runtime/cgo.
358 if a
.Package
== nil {
361 if !a
.Package
.Standard
{
362 cgoldflags
= append(cgoldflags
, a
.Package
.CgoLDFLAGS
...)
364 if len(a
.Package
.CgoFiles
) > 0 {
367 if a
.Package
.UsesSwig() {
370 if len(a
.Package
.CXXFiles
) > 0 ||
len(a
.Package
.SwigCXXFiles
) > 0 {
373 if len(a
.Package
.MFiles
) > 0 {
376 if len(a
.Package
.FFiles
) > 0 {
381 wholeArchive
:= []string{"-Wl,--whole-archive"}
382 noWholeArchive
:= []string{"-Wl,--no-whole-archive"}
383 if cfg
.Goos
== "aix" {
387 ldflags
= append(ldflags
, wholeArchive
...)
388 ldflags
= append(ldflags
, afiles
...)
389 ldflags
= append(ldflags
, noWholeArchive
...)
391 ldflags
= append(ldflags
, cgoldflags
...)
392 ldflags
= append(ldflags
, envList("CGO_LDFLAGS", "")...)
393 if root
.Package
!= nil {
394 ldflags
= append(ldflags
, root
.Package
.CgoLDFLAGS
...)
397 if cfg
.Goos
!= "aix" {
398 ldflags
= str
.StringList("-Wl,-(", ldflags
, "-Wl,-)")
401 if root
.buildID
!= "" {
402 // On systems that normally use gold or the GNU linker,
403 // use the --build-id option to write a GNU build ID note.
405 case "android", "dragonfly", "linux", "netbsd":
406 ldflags
= append(ldflags
, fmt
.Sprintf("-Wl,--build-id=0x%x", root
.buildID
))
411 if cfg
.Goos
== "aix" {
412 rLibPath
= "-Wl,-blibpath="
414 rLibPath
= "-Wl,-rpath="
416 for _
, shlib
:= range shlibs
{
419 "-L"+filepath
.Dir(shlib
),
420 rLibPath
+filepath
.Dir(shlib
),
421 "-l"+strings
.TrimSuffix(
422 strings
.TrimPrefix(filepath
.Base(shlib
), "lib"),
427 goLibBegin
:= str
.StringList(wholeArchive
, "-lgolibbegin", noWholeArchive
)
430 if usesCgo
&& cfg
.Goos
== "linux" {
431 ldflags
= append(ldflags
, "-Wl,-E")
435 // Link the Go files into a single .o, and also link
438 // We need to use --whole-archive with -lgolibbegin
439 // because it doesn't define any symbols that will
440 // cause the contents to be pulled in; it's just
441 // initialization code.
443 // The user remains responsible for linking against
444 // -lgo -lpthread -lm in the final link. We can't use
445 // -r to pick them up because we can't combine
446 // split-stack and non-split-stack code in a single -r
447 // link, and libgo picks up non-split-stack code from
449 ldflags
= append(ldflags
, "-Wl,-r", "-nostdlib")
450 ldflags
= append(ldflags
, goLibBegin
...)
452 if nopie
:= b
.gccNoPie([]string{tools
.linker()}); nopie
!= "" {
453 ldflags
= append(ldflags
, nopie
)
456 // We are creating an object file, so we don't want a build ID.
457 if root
.buildID
== "" {
458 ldflags
= b
.disableBuildID(ldflags
)
465 ldflags
= append(ldflags
, "-shared", "-nostdlib")
466 ldflags
= append(ldflags
, goLibBegin
...)
467 ldflags
= append(ldflags
, "-lgo", "-lgcc_s", "-lgcc", "-lc", "-lgcc")
469 if cfg
.Goos
!= "aix" {
470 ldflags
= append(ldflags
, "-zdefs")
472 ldflags
= append(ldflags
, "-shared", "-nostdlib", "-lgo", "-lgcc_s", "-lgcc", "-lc")
475 ldflags
= append(ldflags
, "-pie")
478 base
.Fatalf("-buildmode=%s not supported for gccgo", buildmode
)
482 case "exe", "c-shared":
484 ldflags
= append(ldflags
, "-lstdc++")
487 ldflags
= append(ldflags
, "-lobjc")
490 fc
:= os
.Getenv("FC")
494 // support gfortran out of the box and let others pass the correct link options
496 if strings
.Contains(fc
, "gfortran") {
497 ldflags
= append(ldflags
, "-lgfortran")
502 if err
:= b
.run(root
, ".", desc
, nil, tools
.linker(), "-o", out
, ldflags
, forcedGccgoflags
, root
.Package
.Internal
.Gccgoflags
); err
!= nil {
508 if err
:= b
.run(root
, ".", desc
, nil, "ar", "rc", realOut
, out
); err
!= nil {
515 func (tools gccgoToolchain
) ld(b
*Builder
, root
*Action
, out
, importcfg
, mainpkg
string) error
{
516 return tools
.link(b
, root
, out
, importcfg
, root
.Deps
, ldBuildmode
, root
.Package
.ImportPath
)
519 func (tools gccgoToolchain
) ldShared(b
*Builder
, root
*Action
, toplevelactions
[]*Action
, out
, importcfg
string, allactions
[]*Action
) error
{
520 return tools
.link(b
, root
, out
, importcfg
, allactions
, "shared", out
)
523 func (tools gccgoToolchain
) cc(b
*Builder
, a
*Action
, ofile
, cfile
string) error
{
525 inc
:= filepath
.Join(cfg
.GOROOT
, "pkg", "include")
526 cfile
= mkAbs(p
.Dir
, cfile
)
527 defs
:= []string{"-D", "GOOS_" + cfg
.Goos
, "-D", "GOARCH_" + cfg
.Goarch
}
528 defs
= append(defs
, b
.gccArchArgs()...)
529 if pkgpath
:= gccgoCleanPkgpath(p
); pkgpath
!= "" {
530 defs
= append(defs
, `-D`, `GOPKGPATH="`+pkgpath
+`"`)
532 compiler
:= envList("CC", cfg
.DefaultCC(cfg
.Goos
, cfg
.Goarch
))
533 if b
.gccSupportsFlag(compiler
, "-fsplit-stack") {
534 defs
= append(defs
, "-fsplit-stack")
536 defs
= tools
.maybePIC(defs
)
537 if b
.gccSupportsFlag(compiler
, "-fdebug-prefix-map=a=b") {
538 defs
= append(defs
, "-fdebug-prefix-map="+b
.WorkDir
+"=/tmp/go-build")
540 if b
.gccSupportsFlag(compiler
, "-gno-record-gcc-switches") {
541 defs
= append(defs
, "-gno-record-gcc-switches")
543 return b
.run(a
, p
.Dir
, p
.ImportPath
, nil, compiler
, "-Wall", "-g",
544 "-I", a
.Objdir
, "-I", inc
, "-o", ofile
, defs
, "-c", cfile
)
547 // maybePIC adds -fPIC to the list of arguments if needed.
548 func (tools gccgoToolchain
) maybePIC(args
[]string) []string {
549 switch cfg
.BuildBuildmode
{
550 case "c-archive", "c-shared", "shared", "plugin":
551 args
= append(args
, "-fPIC")
556 func gccgoPkgpath(p
*load
.Package
) string {
557 if p
.Internal
.Build
.IsCommand() && !p
.Internal
.ForceLibrary
{
560 if p
.ImportPath
== "main" && p
.Internal
.ForceLibrary
{
566 func gccgoCleanPkgpath(p
*load
.Package
) string {
567 clean
:= func(r rune
) rune
{
569 case 'A' <= r
&& r
<= 'Z', 'a' <= r
&& r
<= 'z',
570 '0' <= r
&& r
<= '9':
575 return strings
.Map(clean
, gccgoPkgpath(p
))