Source file src/pkg/cmd/go/internal/work/gccgo.go
1
2
3
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
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
104
105
106
107
108 func buildImportcfgSymlinks(b *Builder, root string, importcfg []byte) error {
109 for lineNum, line := range strings.Split(string(importcfg), "\n") {
110 lineNum++
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
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
207
208 arArgs = []string{"-X64"}
209 }
210 absAfile := mkAbs(objdir, afile)
211
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
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
227
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
253
254
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
267
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
282
283
284
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
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
314
315
316
317 base := filepath.Base(p.Shlib)
318 if base != targetBase {
319 haveShlib[base] = true
320 }
321 }
322 }
323
324
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
330
331 continue
332 }
333
334 if haveShlib[filepath.Base(a.Target)] {
335
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
359
360
361
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
406
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
439
440
441
442
443
444
445
446
447
448
449
450
451
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
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
496
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
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