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