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