1
2
3
4
5 package work
6
7 import (
8 "fmt"
9 exec "internal/execabs"
10 "os"
11 "path/filepath"
12 "strings"
13 "sync"
14
15 "cmd/go/internal/base"
16 "cmd/go/internal/cfg"
17 "cmd/go/internal/fsys"
18 "cmd/go/internal/load"
19 "cmd/go/internal/str"
20 "cmd/internal/pkgpath"
21 )
22
23
24
25 type gccgoToolchain struct{}
26
27 var GccgoName, GccgoBin string
28 var gccgoErr error
29
30 func init() {
31 GccgoName = cfg.Getenv("GCCGO")
32 if GccgoName == "" {
33 GccgoName = "gccgo"
34 }
35 GccgoBin, gccgoErr = exec.LookPath(GccgoName)
36 }
37
38 func (gccgoToolchain) compiler() string {
39 checkGccgoBin()
40 return GccgoBin
41 }
42
43 func (gccgoToolchain) linker() string {
44 checkGccgoBin()
45 return GccgoBin
46 }
47
48 func (gccgoToolchain) ar() string {
49 ar := cfg.Getenv("AR")
50 if ar == "" {
51 ar = "ar"
52 }
53 return ar
54 }
55
56 func checkGccgoBin() {
57 if gccgoErr == nil {
58 return
59 }
60 fmt.Fprintf(os.Stderr, "cmd/go: gccgo: %s\n", gccgoErr)
61 base.SetExitStatus(2)
62 base.Exit()
63 }
64
65 func (tools gccgoToolchain) gc(b *Builder, a *Action, archive string, importcfg, embedcfg []byte, symabis string, asmhdr bool, gofiles []string) (ofile string, output []byte, err error) {
66 p := a.Package
67 objdir := a.Objdir
68 out := "_go_.o"
69 ofile = objdir + out
70 gcargs := []string{"-g"}
71 gcargs = append(gcargs, b.gccArchArgs()...)
72 gcargs = append(gcargs, "-fdebug-prefix-map="+b.WorkDir+"=/tmp/go-build")
73 gcargs = append(gcargs, "-gno-record-gcc-switches")
74 if pkgpath := gccgoPkgpath(p); pkgpath != "" {
75 gcargs = append(gcargs, "-fgo-pkgpath="+pkgpath)
76 }
77 if p.Internal.LocalPrefix != "" {
78 gcargs = append(gcargs, "-fgo-relative-import-path="+p.Internal.LocalPrefix)
79 }
80
81 args := str.StringList(tools.compiler(), "-c", gcargs, "-o", ofile, forcedGccgoflags)
82 if importcfg != nil {
83 if b.gccSupportsFlag(args[:1], "-fgo-importcfg=/dev/null") {
84 if err := b.writeFile(objdir+"importcfg", importcfg); err != nil {
85 return "", nil, err
86 }
87 args = append(args, "-fgo-importcfg="+objdir+"importcfg")
88 } else {
89 root := objdir + "_importcfgroot_"
90 if err := buildImportcfgSymlinks(b, root, importcfg); err != nil {
91 return "", nil, err
92 }
93 args = append(args, "-I", root)
94 }
95 }
96 if embedcfg != nil && b.gccSupportsFlag(args[:1], "-fgo-embedcfg=/dev/null") {
97 if err := b.writeFile(objdir+"embedcfg", embedcfg); err != nil {
98 return "", nil, err
99 }
100 args = append(args, "-fgo-embedcfg="+objdir+"embedcfg")
101 }
102
103 if b.gccSupportsFlag(args[:1], "-ffile-prefix-map=a=b") {
104 if cfg.BuildTrimpath {
105 args = append(args, "-ffile-prefix-map="+base.Cwd()+"=.")
106 args = append(args, "-ffile-prefix-map="+b.WorkDir+"=/tmp/go-build")
107 }
108 if fsys.OverlayFile != "" {
109 for _, name := range gofiles {
110 absPath := mkAbs(p.Dir, name)
111 overlayPath, ok := fsys.OverlayPath(absPath)
112 if !ok {
113 continue
114 }
115 toPath := absPath
116
117
118 if cfg.BuildTrimpath && str.HasFilePathPrefix(toPath, base.Cwd()) {
119 toPath = "." + toPath[len(base.Cwd()):]
120 }
121 args = append(args, "-ffile-prefix-map="+overlayPath+"="+toPath)
122 }
123 }
124 }
125
126 args = append(args, a.Package.Internal.Gccgoflags...)
127 for _, f := range gofiles {
128 f := mkAbs(p.Dir, f)
129
130
131 f, _ = fsys.OverlayPath(f)
132 args = append(args, f)
133 }
134
135 output, err = b.runOut(a, p.Dir, nil, args)
136 return ofile, output, err
137 }
138
139
140
141
142
143
144 func buildImportcfgSymlinks(b *Builder, root string, importcfg []byte) error {
145 for lineNum, line := range strings.Split(string(importcfg), "\n") {
146 lineNum++
147 line = strings.TrimSpace(line)
148 if line == "" {
149 continue
150 }
151 if line == "" || strings.HasPrefix(line, "#") {
152 continue
153 }
154 var verb, args string
155 if i := strings.Index(line, " "); i < 0 {
156 verb = line
157 } else {
158 verb, args = line[:i], strings.TrimSpace(line[i+1:])
159 }
160 var before, after string
161 if i := strings.Index(args, "="); i >= 0 {
162 before, after = args[:i], args[i+1:]
163 }
164 switch verb {
165 default:
166 base.Fatalf("importcfg:%d: unknown directive %q", lineNum, verb)
167 case "packagefile":
168 if before == "" || after == "" {
169 return fmt.Errorf(`importcfg:%d: invalid packagefile: syntax is "packagefile path=filename": %s`, lineNum, line)
170 }
171 archive := gccgoArchive(root, before)
172 if err := b.Mkdir(filepath.Dir(archive)); err != nil {
173 return err
174 }
175 if err := b.Symlink(after, archive); err != nil {
176 return err
177 }
178 case "importmap":
179 if before == "" || after == "" {
180 return fmt.Errorf(`importcfg:%d: invalid importmap: syntax is "importmap old=new": %s`, lineNum, line)
181 }
182 beforeA := gccgoArchive(root, before)
183 afterA := gccgoArchive(root, after)
184 if err := b.Mkdir(filepath.Dir(beforeA)); err != nil {
185 return err
186 }
187 if err := b.Mkdir(filepath.Dir(afterA)); err != nil {
188 return err
189 }
190 if err := b.Symlink(afterA, beforeA); err != nil {
191 return err
192 }
193 case "packageshlib":
194 return fmt.Errorf("gccgo -importcfg does not support shared libraries")
195 }
196 }
197 return nil
198 }
199
200 func (tools gccgoToolchain) asm(b *Builder, a *Action, sfiles []string) ([]string, error) {
201 p := a.Package
202 var ofiles []string
203 for _, sfile := range sfiles {
204 base := filepath.Base(sfile)
205 ofile := a.Objdir + base[:len(base)-len(".s")] + ".o"
206 ofiles = append(ofiles, ofile)
207 sfile, _ = fsys.OverlayPath(mkAbs(p.Dir, sfile))
208 defs := []string{"-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch}
209 if pkgpath := tools.gccgoCleanPkgpath(b, p); pkgpath != "" {
210 defs = append(defs, `-D`, `GOPKGPATH=`+pkgpath)
211 }
212 defs = tools.maybePIC(defs)
213 defs = append(defs, b.gccArchArgs()...)
214 err := b.run(a, p.Dir, p.ImportPath, nil, tools.compiler(), "-xassembler-with-cpp", "-I", a.Objdir, "-c", "-o", ofile, defs, sfile)
215 if err != nil {
216 return nil, err
217 }
218 }
219 return ofiles, nil
220 }
221
222 func (gccgoToolchain) symabis(b *Builder, a *Action, sfiles []string) (string, error) {
223 return "", nil
224 }
225
226 func gccgoArchive(basedir, imp string) string {
227 end := filepath.FromSlash(imp + ".a")
228 afile := filepath.Join(basedir, end)
229
230 return filepath.Join(filepath.Dir(afile), "lib"+filepath.Base(afile))
231 }
232
233 func (tools gccgoToolchain) pack(b *Builder, a *Action, afile string, ofiles []string) error {
234 p := a.Package
235 objdir := a.Objdir
236 var absOfiles []string
237 for _, f := range ofiles {
238 absOfiles = append(absOfiles, mkAbs(objdir, f))
239 }
240 var arArgs []string
241 if cfg.Goos == "aix" && cfg.Goarch == "ppc64" {
242
243
244 arArgs = []string{"-X64"}
245 }
246 absAfile := mkAbs(objdir, afile)
247
248 output, err := b.runOut(a, p.Dir, nil, tools.ar(), arArgs, "rcD", absAfile, absOfiles)
249 if err != nil {
250 return b.run(a, p.Dir, p.ImportPath, nil, tools.ar(), arArgs, "rc", absAfile, absOfiles)
251 }
252
253 if len(output) > 0 {
254
255 b.showOutput(a, p.Dir, p.ImportPath, b.processOutput(output))
256 }
257
258 return nil
259 }
260
261 func (tools gccgoToolchain) link(b *Builder, root *Action, out, importcfg string, allactions []*Action, buildmode, desc string) error {
262
263
264 afiles := []string{}
265 shlibs := []string{}
266 ldflags := b.gccArchArgs()
267 cgoldflags := []string{}
268 usesCgo := false
269 cxx := false
270 objc := false
271 fortran := false
272 if root.Package != nil {
273 cxx = len(root.Package.CXXFiles) > 0 || len(root.Package.SwigCXXFiles) > 0
274 objc = len(root.Package.MFiles) > 0
275 fortran = len(root.Package.FFiles) > 0
276 }
277
278 readCgoFlags := func(flagsFile string) error {
279 flags, err := os.ReadFile(flagsFile)
280 if err != nil {
281 return err
282 }
283 const ldflagsPrefix = "_CGO_LDFLAGS="
284 for _, line := range strings.Split(string(flags), "\n") {
285 if strings.HasPrefix(line, ldflagsPrefix) {
286 newFlags := strings.Fields(line[len(ldflagsPrefix):])
287 for _, flag := range newFlags {
288
289
290
291 if flag != "-g" && !strings.HasPrefix(flag, "-O") {
292 cgoldflags = append(cgoldflags, flag)
293 }
294 }
295 }
296 }
297 return nil
298 }
299
300 var arArgs []string
301 if cfg.Goos == "aix" && cfg.Goarch == "ppc64" {
302
303
304 arArgs = []string{"-X64"}
305 }
306
307 newID := 0
308 readAndRemoveCgoFlags := func(archive string) (string, error) {
309 newID++
310 newArchive := root.Objdir + fmt.Sprintf("_pkg%d_.a", newID)
311 if err := b.copyFile(newArchive, archive, 0666, false); err != nil {
312 return "", err
313 }
314 if cfg.BuildN || cfg.BuildX {
315 b.Showcmd("", "ar d %s _cgo_flags", newArchive)
316 if cfg.BuildN {
317
318
319
320
321 return "", nil
322 }
323 }
324 err := b.run(root, root.Objdir, desc, nil, tools.ar(), arArgs, "x", newArchive, "_cgo_flags")
325 if err != nil {
326 return "", err
327 }
328 err = b.run(root, ".", desc, nil, tools.ar(), arArgs, "d", newArchive, "_cgo_flags")
329 if err != nil {
330 return "", err
331 }
332 err = readCgoFlags(filepath.Join(root.Objdir, "_cgo_flags"))
333 if err != nil {
334 return "", err
335 }
336 return newArchive, nil
337 }
338
339
340 haveShlib := make(map[string]bool)
341 targetBase := filepath.Base(root.Target)
342 if cfg.BuildLinkshared {
343 for _, a := range root.Deps {
344 p := a.Package
345 if p == nil || p.Shlib == "" {
346 continue
347 }
348
349
350
351
352
353 base := filepath.Base(p.Shlib)
354 if base != targetBase {
355 haveShlib[base] = true
356 }
357 }
358 }
359
360
361 addedShlib := make(map[string]bool)
362 for _, a := range root.Deps {
363 p := a.Package
364 if p != nil && p.Shlib != "" && haveShlib[filepath.Base(p.Shlib)] {
365
366
367 continue
368 }
369
370 if haveShlib[filepath.Base(a.Target)] {
371
372 if !addedShlib[a.Target] {
373 shlibs = append(shlibs, a.Target)
374 addedShlib[a.Target] = true
375 }
376 continue
377 }
378
379 if p != nil {
380 target := a.built
381 if p.UsesCgo() || p.UsesSwig() {
382 var err error
383 target, err = readAndRemoveCgoFlags(target)
384 if err != nil {
385 continue
386 }
387 }
388
389 afiles = append(afiles, target)
390 }
391 }
392
393 for _, a := range allactions {
394
395
396
397
398 if a.Package == nil {
399 continue
400 }
401 if !a.Package.Standard {
402 cgoldflags = append(cgoldflags, a.Package.CgoLDFLAGS...)
403 }
404 if len(a.Package.CgoFiles) > 0 {
405 usesCgo = true
406 }
407 if a.Package.UsesSwig() {
408 usesCgo = true
409 }
410 if len(a.Package.CXXFiles) > 0 || len(a.Package.SwigCXXFiles) > 0 {
411 cxx = true
412 }
413 if len(a.Package.MFiles) > 0 {
414 objc = true
415 }
416 if len(a.Package.FFiles) > 0 {
417 fortran = true
418 }
419 }
420
421 wholeArchive := []string{"-Wl,--whole-archive"}
422 noWholeArchive := []string{"-Wl,--no-whole-archive"}
423 if cfg.Goos == "aix" {
424 wholeArchive = nil
425 noWholeArchive = nil
426 }
427 ldflags = append(ldflags, wholeArchive...)
428 ldflags = append(ldflags, afiles...)
429 ldflags = append(ldflags, noWholeArchive...)
430
431 ldflags = append(ldflags, cgoldflags...)
432 ldflags = append(ldflags, envList("CGO_LDFLAGS", "")...)
433 if root.Package != nil {
434 ldflags = append(ldflags, root.Package.CgoLDFLAGS...)
435 }
436 if cfg.Goos != "aix" {
437 ldflags = str.StringList("-Wl,-(", ldflags, "-Wl,-)")
438 }
439
440 if root.buildID != "" {
441
442
443 switch cfg.Goos {
444 case "android", "dragonfly", "linux", "netbsd":
445 ldflags = append(ldflags, fmt.Sprintf("-Wl,--build-id=0x%x", root.buildID))
446 }
447 }
448
449 var rLibPath string
450 if cfg.Goos == "aix" {
451 rLibPath = "-Wl,-blibpath="
452 } else {
453 rLibPath = "-Wl,-rpath="
454 }
455 for _, shlib := range shlibs {
456 ldflags = append(
457 ldflags,
458 "-L"+filepath.Dir(shlib),
459 rLibPath+filepath.Dir(shlib),
460 "-l"+strings.TrimSuffix(
461 strings.TrimPrefix(filepath.Base(shlib), "lib"),
462 ".so"))
463 }
464
465 var realOut string
466 goLibBegin := str.StringList(wholeArchive, "-lgolibbegin", noWholeArchive)
467 switch buildmode {
468 case "exe":
469 if usesCgo && cfg.Goos == "linux" {
470 ldflags = append(ldflags, "-Wl,-E")
471 }
472
473 case "c-archive":
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488 ldflags = append(ldflags, "-Wl,-r", "-nostdlib")
489 ldflags = append(ldflags, goLibBegin...)
490
491 if nopie := b.gccNoPie([]string{tools.linker()}); nopie != "" {
492 ldflags = append(ldflags, nopie)
493 }
494
495
496 if root.buildID == "" {
497 ldflags = b.disableBuildID(ldflags)
498 }
499
500 realOut = out
501 out = out + ".o"
502
503 case "c-shared":
504 ldflags = append(ldflags, "-shared", "-nostdlib")
505 ldflags = append(ldflags, goLibBegin...)
506 ldflags = append(ldflags, "-lgo", "-lgcc_s", "-lgcc", "-lc", "-lgcc")
507
508 case "shared":
509 if cfg.Goos != "aix" {
510 ldflags = append(ldflags, "-zdefs")
511 }
512 ldflags = append(ldflags, "-shared", "-nostdlib", "-lgo", "-lgcc_s", "-lgcc", "-lc")
513
514 default:
515 base.Fatalf("-buildmode=%s not supported for gccgo", buildmode)
516 }
517
518 switch buildmode {
519 case "exe", "c-shared":
520 if cxx {
521 ldflags = append(ldflags, "-lstdc++")
522 }
523 if objc {
524 ldflags = append(ldflags, "-lobjc")
525 }
526 if fortran {
527 fc := cfg.Getenv("FC")
528 if fc == "" {
529 fc = "gfortran"
530 }
531
532
533 if strings.Contains(fc, "gfortran") {
534 ldflags = append(ldflags, "-lgfortran")
535 }
536 }
537 }
538
539 if err := b.run(root, ".", desc, nil, tools.linker(), "-o", out, ldflags, forcedGccgoflags, root.Package.Internal.Gccgoflags); err != nil {
540 return err
541 }
542
543 switch buildmode {
544 case "c-archive":
545 if err := b.run(root, ".", desc, nil, tools.ar(), arArgs, "rc", realOut, out); err != nil {
546 return err
547 }
548 }
549 return nil
550 }
551
552 func (tools gccgoToolchain) ld(b *Builder, root *Action, out, importcfg, mainpkg string) error {
553 return tools.link(b, root, out, importcfg, root.Deps, ldBuildmode, root.Package.ImportPath)
554 }
555
556 func (tools gccgoToolchain) ldShared(b *Builder, root *Action, toplevelactions []*Action, out, importcfg string, allactions []*Action) error {
557 return tools.link(b, root, out, importcfg, allactions, "shared", out)
558 }
559
560 func (tools gccgoToolchain) cc(b *Builder, a *Action, ofile, cfile string) error {
561 p := a.Package
562 inc := filepath.Join(cfg.GOROOT, "pkg", "include")
563 cfile = mkAbs(p.Dir, cfile)
564 defs := []string{"-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch}
565 defs = append(defs, b.gccArchArgs()...)
566 if pkgpath := tools.gccgoCleanPkgpath(b, p); pkgpath != "" {
567 defs = append(defs, `-D`, `GOPKGPATH="`+pkgpath+`"`)
568 }
569 compiler := envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch))
570 if b.gccSupportsFlag(compiler, "-fsplit-stack") {
571 defs = append(defs, "-fsplit-stack")
572 }
573 defs = tools.maybePIC(defs)
574 if b.gccSupportsFlag(compiler, "-ffile-prefix-map=a=b") {
575 defs = append(defs, "-ffile-prefix-map="+base.Cwd()+"=.")
576 defs = append(defs, "-ffile-prefix-map="+b.WorkDir+"=/tmp/go-build")
577 } else if b.gccSupportsFlag(compiler, "-fdebug-prefix-map=a=b") {
578 defs = append(defs, "-fdebug-prefix-map="+b.WorkDir+"=/tmp/go-build")
579 }
580 if b.gccSupportsFlag(compiler, "-gno-record-gcc-switches") {
581 defs = append(defs, "-gno-record-gcc-switches")
582 }
583 return b.run(a, p.Dir, p.ImportPath, nil, compiler, "-Wall", "-g",
584 "-I", a.Objdir, "-I", inc, "-o", ofile, defs, "-c", cfile)
585 }
586
587
588 func (tools gccgoToolchain) maybePIC(args []string) []string {
589 switch cfg.BuildBuildmode {
590 case "c-shared", "shared", "plugin":
591 args = append(args, "-fPIC")
592 }
593 return args
594 }
595
596 func gccgoPkgpath(p *load.Package) string {
597 if p.Internal.Build.IsCommand() && !p.Internal.ForceLibrary {
598 return ""
599 }
600 return p.ImportPath
601 }
602
603 var gccgoToSymbolFuncOnce sync.Once
604 var gccgoToSymbolFunc func(string) string
605
606 func (tools gccgoToolchain) gccgoCleanPkgpath(b *Builder, p *load.Package) string {
607 gccgoToSymbolFuncOnce.Do(func() {
608 fn, err := pkgpath.ToSymbolFunc(tools.compiler(), b.WorkDir)
609 if err != nil {
610 fmt.Fprintf(os.Stderr, "cmd/go: %v\n", err)
611 base.SetExitStatus(2)
612 base.Exit()
613 }
614 gccgoToSymbolFunc = fn
615 })
616
617 return gccgoToSymbolFunc(gccgoPkgpath(p))
618 }
619
View as plain text