cmd/go: on AIX, pass -X64 first when invoking ar
[official-gcc.git] / libgo / go / cmd / go / internal / work / gccgo.go
blobe8dab19147839f3ecc7d651ac53b67f016dac511
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.
5 package work
7 import (
8 "fmt"
9 "io/ioutil"
10 "os"
11 "os/exec"
12 "path/filepath"
13 "strings"
15 "cmd/go/internal/base"
16 "cmd/go/internal/cfg"
17 "cmd/go/internal/load"
18 "cmd/go/internal/str"
21 // The Gccgo toolchain.
23 type gccgoToolchain struct{}
25 var GccgoName, GccgoBin string
26 var gccgoErr error
28 func init() {
29 GccgoName = os.Getenv("GCCGO")
30 if GccgoName == "" {
31 GccgoName = cfg.DefaultGCCGO(cfg.Goos, cfg.Goarch)
33 GccgoBin, gccgoErr = exec.LookPath(GccgoName)
36 func (gccgoToolchain) compiler() string {
37 checkGccgoBin()
38 return GccgoBin
41 func (gccgoToolchain) linker() string {
42 checkGccgoBin()
43 return GccgoBin
46 func checkGccgoBin() {
47 if gccgoErr == nil {
48 return
50 fmt.Fprintf(os.Stderr, "cmd/go: gccgo: %s\n", gccgoErr)
51 os.Exit(2)
54 func (tools gccgoToolchain) gc(b *Builder, a *Action, archive string, importcfg []byte, asmhdr bool, gofiles []string) (ofile string, output []byte, err error) {
55 p := a.Package
56 objdir := a.Objdir
57 out := "_go_.o"
58 ofile = objdir + out
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)
71 if importcfg != nil {
72 if b.gccSupportsFlag(args[:1], "-fgo-importcfg=/dev/null") {
73 if err := b.writeFile(objdir+"importcfg", importcfg); err != nil {
74 return "", nil, err
76 args = append(args, "-fgo-importcfg="+objdir+"importcfg")
77 } else {
78 root := objdir + "_importcfgroot_"
79 if err := buildImportcfgSymlinks(b, root, importcfg); err != nil {
80 return "", nil, err
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") {
101 lineNum++ // 1-based
102 line = strings.TrimSpace(line)
103 if line == "" {
104 continue
106 if line == "" || strings.HasPrefix(line, "#") {
107 continue
109 var verb, args string
110 if i := strings.Index(line, " "); i < 0 {
111 verb = line
112 } else {
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:]
119 switch verb {
120 default:
121 base.Fatalf("importcfg:%d: unknown directive %q", lineNum, verb)
122 case "packagefile":
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 {
128 return err
130 if err := b.Symlink(after, archive); err != nil {
131 return err
133 case "importmap":
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 {
140 return err
142 if err := b.Mkdir(filepath.Dir(afterA)); err != nil {
143 return err
145 if err := b.Symlink(afterA, beforeA); err != nil {
146 return err
148 case "packageshlib":
149 return fmt.Errorf("gccgo -importcfg does not support shared libraries")
152 return nil
155 func (tools gccgoToolchain) asm(b *Builder, a *Action, sfiles []string) ([]string, error) {
156 p := a.Package
157 var ofiles []string
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)
170 if err != nil {
171 return nil, err
174 return ofiles, nil
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 {
185 p := a.Package
186 objdir := a.Objdir
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 {
194 var arArgs []string
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)
203 return nil
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.
209 afiles := []string{}
210 shlibs := []string{}
211 ldflags := b.gccArchArgs()
212 cgoldflags := []string{}
213 usesCgo := false
214 cxx := false
215 objc := false
216 fortran := false
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)
225 if err != nil {
226 return err
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):]
232 quote := byte(0)
233 start := true
234 var nl []byte
235 for len(line) > 0 {
236 b := line[0]
237 line = line[1:]
238 if quote == 0 && (b == ' ' || b == '\t') {
239 if len(nl) > 0 {
240 cgoldflags = append(cgoldflags, string(nl))
241 nl = nil
243 start = true
244 continue
245 } else if b == '"' || b == '\'' {
246 quote = b
247 } else if b == quote {
248 quote = 0
249 } else if quote == 0 && start && b == '-' && strings.HasPrefix(line, "g") {
250 line = line[1:]
251 continue
252 } else if quote == 0 && start && b == '-' && strings.HasPrefix(line, "O") {
253 for len(line) > 0 && line[0] != ' ' && line[0] != '\t' {
254 line = line[1:]
256 continue
258 nl = append(nl, b)
259 start = false
261 if len(nl) > 0 {
262 cgoldflags = append(cgoldflags, string(nl))
266 return nil
269 newID := 0
270 readAndRemoveCgoFlags := func(archive string) (string, error) {
271 newID++
272 newArchive := root.Objdir + fmt.Sprintf("_pkg%d_.a", newID)
273 if err := b.copyFile(root, newArchive, archive, 0666, false); err != nil {
274 return "", err
276 if cfg.BuildN {
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)
282 return "", nil
284 err := b.run(root, root.Objdir, desc, nil, "ar", "x", newArchive, "_cgo_flags")
285 if err != nil {
286 return "", err
288 err = b.run(root, ".", desc, nil, "ar", "d", newArchive, "_cgo_flags")
289 if err != nil {
290 return "", err
292 err = readCgoFlags(filepath.Join(root.Objdir, "_cgo_flags"))
293 if err != nil {
294 return "", err
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 {
304 p := a.Package
305 if p == nil || p.Shlib == "" {
306 continue
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 {
323 p := a.Package
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.
327 continue
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
336 continue
339 if p != nil {
340 target := a.built
341 if p.UsesCgo() || p.UsesSwig() {
342 var err error
343 target, err = readAndRemoveCgoFlags(target)
344 if err != nil {
345 continue
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 {
359 continue
361 if !a.Package.Standard {
362 cgoldflags = append(cgoldflags, a.Package.CgoLDFLAGS...)
364 if len(a.Package.CgoFiles) > 0 {
365 usesCgo = true
367 if a.Package.UsesSwig() {
368 usesCgo = true
370 if len(a.Package.CXXFiles) > 0 || len(a.Package.SwigCXXFiles) > 0 {
371 cxx = true
373 if len(a.Package.MFiles) > 0 {
374 objc = true
376 if len(a.Package.FFiles) > 0 {
377 fortran = true
381 wholeArchive := []string{"-Wl,--whole-archive"}
382 noWholeArchive := []string{"-Wl,--no-whole-archive"}
383 if cfg.Goos == "aix" {
384 wholeArchive = nil
385 noWholeArchive = nil
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.
404 switch cfg.Goos {
405 case "android", "dragonfly", "linux", "netbsd":
406 ldflags = append(ldflags, fmt.Sprintf("-Wl,--build-id=0x%x", root.buildID))
410 var rLibPath string
411 if cfg.Goos == "aix" {
412 rLibPath = "-Wl,-blibpath="
413 } else {
414 rLibPath = "-Wl,-rpath="
416 for _, shlib := range shlibs {
417 ldflags = append(
418 ldflags,
419 "-L"+filepath.Dir(shlib),
420 rLibPath+filepath.Dir(shlib),
421 "-l"+strings.TrimSuffix(
422 strings.TrimPrefix(filepath.Base(shlib), "lib"),
423 ".so"))
426 var realOut string
427 goLibBegin := str.StringList(wholeArchive, "-lgolibbegin", noWholeArchive)
428 switch buildmode {
429 case "exe":
430 if usesCgo && cfg.Goos == "linux" {
431 ldflags = append(ldflags, "-Wl,-E")
434 case "c-archive":
435 // Link the Go files into a single .o, and also link
436 // in -lgolibbegin.
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
448 // libffi.
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)
461 realOut = out
462 out = out + ".o"
464 case "c-shared":
465 ldflags = append(ldflags, "-shared", "-nostdlib")
466 ldflags = append(ldflags, goLibBegin...)
467 ldflags = append(ldflags, "-lgo", "-lgcc_s", "-lgcc", "-lc", "-lgcc")
468 case "shared":
469 if cfg.Goos != "aix" {
470 ldflags = append(ldflags, "-zdefs")
472 ldflags = append(ldflags, "-shared", "-nostdlib", "-lgo", "-lgcc_s", "-lgcc", "-lc")
474 case "pie":
475 ldflags = append(ldflags, "-pie")
477 default:
478 base.Fatalf("-buildmode=%s not supported for gccgo", buildmode)
481 switch buildmode {
482 case "exe", "c-shared":
483 if cxx {
484 ldflags = append(ldflags, "-lstdc++")
486 if objc {
487 ldflags = append(ldflags, "-lobjc")
489 if fortran {
490 fc := os.Getenv("FC")
491 if fc == "" {
492 fc = "gfortran"
494 // support gfortran out of the box and let others pass the correct link options
495 // via CGO_LDFLAGS
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 {
503 return err
506 switch buildmode {
507 case "c-archive":
508 if err := b.run(root, ".", desc, nil, "ar", "rc", realOut, out); err != nil {
509 return err
512 return 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 {
524 p := a.Package
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")
553 return args
556 func gccgoPkgpath(p *load.Package) string {
557 if p.Internal.Build.IsCommand() && !p.Internal.ForceLibrary {
558 return ""
560 if p.ImportPath == "main" && p.Internal.ForceLibrary {
561 return "testmain"
563 return p.ImportPath
566 func gccgoCleanPkgpath(p *load.Package) string {
567 clean := func(r rune) rune {
568 switch {
569 case 'A' <= r && r <= 'Z', 'a' <= r && r <= 'z',
570 '0' <= r && r <= '9':
571 return r
573 return '_'
575 return strings.Map(clean, gccgoPkgpath(p))