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