github.com/bir3/gocompiler@v0.3.205/src/cmd/gocmd/internal/work/gccgo.go (about) 1 // Copyright 2011 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package work 6 7 import ( 8 "fmt" 9 "os" 10 "os/exec" 11 "path/filepath" 12 "strings" 13 "sync" 14 15 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/base" 16 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/cfg" 17 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/fsys" 18 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/load" 19 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/str" 20 "github.com/bir3/gocompiler/src/cmd/internal/pkgpath" 21 ) 22 23 // The Gccgo toolchain. 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 // gccgo only applies the last matching rule, so also handle the case where 117 // BuildTrimpath is true and the path is relative to base.Cwd(). 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 // Overlay files if necessary. 130 // See comment on gctoolchain.gc about overlay TODOs 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 // buildImportcfgSymlinks builds in root a tree of symlinks 140 // implementing the directives from importcfg. 141 // This serves as a temporary transition mechanism until 142 // we can depend on gccgo reading an importcfg directly. 143 // (The Go 1.9 and later gc compilers already do.) 144 func buildImportcfgSymlinks(b *Builder, root string, importcfg []byte) error { 145 for lineNum, line := range strings.Split(string(importcfg), "\n") { 146 lineNum++ // 1-based 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 before, after, _ := strings.Cut(args, "=") 161 switch verb { 162 default: 163 base.Fatalf("importcfg:%d: unknown directive %q", lineNum, verb) 164 case "packagefile": 165 if before == "" || after == "" { 166 return fmt.Errorf(`importcfg:%d: invalid packagefile: syntax is "packagefile path=filename": %s`, lineNum, line) 167 } 168 archive := gccgoArchive(root, before) 169 if err := b.Mkdir(filepath.Dir(archive)); err != nil { 170 return err 171 } 172 if err := b.Symlink(after, archive); err != nil { 173 return err 174 } 175 case "importmap": 176 if before == "" || after == "" { 177 return fmt.Errorf(`importcfg:%d: invalid importmap: syntax is "importmap old=new": %s`, lineNum, line) 178 } 179 beforeA := gccgoArchive(root, before) 180 afterA := gccgoArchive(root, after) 181 if err := b.Mkdir(filepath.Dir(beforeA)); err != nil { 182 return err 183 } 184 if err := b.Mkdir(filepath.Dir(afterA)); err != nil { 185 return err 186 } 187 if err := b.Symlink(afterA, beforeA); err != nil { 188 return err 189 } 190 case "packageshlib": 191 return fmt.Errorf("gccgo -importcfg does not support shared libraries") 192 } 193 } 194 return nil 195 } 196 197 func (tools gccgoToolchain) asm(b *Builder, a *Action, sfiles []string) ([]string, error) { 198 p := a.Package 199 var ofiles []string 200 for _, sfile := range sfiles { 201 base := filepath.Base(sfile) 202 ofile := a.Objdir + base[:len(base)-len(".s")] + ".o" 203 ofiles = append(ofiles, ofile) 204 sfile, _ = fsys.OverlayPath(mkAbs(p.Dir, sfile)) 205 defs := []string{"-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch} 206 if pkgpath := tools.gccgoCleanPkgpath(b, p); pkgpath != "" { 207 defs = append(defs, `-D`, `GOPKGPATH=`+pkgpath) 208 } 209 defs = tools.maybePIC(defs) 210 defs = append(defs, b.gccArchArgs()...) 211 err := b.run(a, p.Dir, p.ImportPath, nil, tools.compiler(), "-xassembler-with-cpp", "-I", a.Objdir, "-c", "-o", ofile, defs, sfile) 212 if err != nil { 213 return nil, err 214 } 215 } 216 return ofiles, nil 217 } 218 219 func (gccgoToolchain) symabis(b *Builder, a *Action, sfiles []string) (string, error) { 220 return "", nil 221 } 222 223 func gccgoArchive(basedir, imp string) string { 224 end := filepath.FromSlash(imp + ".a") 225 afile := filepath.Join(basedir, end) 226 // add "lib" to the final element 227 return filepath.Join(filepath.Dir(afile), "lib"+filepath.Base(afile)) 228 } 229 230 func (tools gccgoToolchain) pack(b *Builder, a *Action, afile string, ofiles []string) error { 231 p := a.Package 232 objdir := a.Objdir 233 var absOfiles []string 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 // AIX puts both 32-bit and 64-bit objects in the same archive. 240 // Tell the AIX "ar" command to only care about 64-bit objects. 241 arArgs = []string{"-X64"} 242 } 243 absAfile := mkAbs(objdir, afile) 244 // Try with D modifier first, then without if that fails. 245 output, err := b.runOut(a, p.Dir, nil, tools.ar(), arArgs, "rcD", absAfile, absOfiles) 246 if err != nil { 247 return b.run(a, p.Dir, p.ImportPath, nil, tools.ar(), arArgs, "rc", absAfile, absOfiles) 248 } 249 250 if len(output) > 0 { 251 // Show the output if there is any even without errors. 252 b.showOutput(a, p.Dir, p.ImportPath, b.processOutput(output)) 253 } 254 255 return nil 256 } 257 258 func (tools gccgoToolchain) link(b *Builder, root *Action, out, importcfg string, allactions []*Action, buildmode, desc string) error { 259 // gccgo needs explicit linking with all package dependencies, 260 // and all LDFLAGS from cgo dependencies. 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 // Every _cgo_flags file has -g and -O2 in _CGO_LDFLAGS 285 // but they don't mean anything to the linker so filter 286 // them out. 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 // AIX puts both 32-bit and 64-bit objects in the same archive. 298 // Tell the AIX "ar" command to only care about 64-bit objects. 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 := b.copyFile(newArchive, archive, 0666, false); err != nil { 307 return "", err 308 } 309 if cfg.BuildN || cfg.BuildX { 310 b.Showcmd("", "ar d %s _cgo_flags", newArchive) 311 if cfg.BuildN { 312 // TODO(rsc): We could do better about showing the right _cgo_flags even in -n mode. 313 // Either the archive is already built and we can read them out, 314 // or we're printing commands to build the archive and can 315 // forward the _cgo_flags directly to this step. 316 return "", nil 317 } 318 } 319 err := b.run(root, root.Objdir, desc, nil, tools.ar(), arArgs, "x", newArchive, "_cgo_flags") 320 if err != nil { 321 return "", err 322 } 323 err = b.run(root, ".", 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 // If using -linkshared, find the shared library deps. 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 // The .a we are linking into this .so 345 // will have its Shlib set to this .so. 346 // Don't start thinking we want to link 347 // this .so into itself. 348 base := filepath.Base(p.Shlib) 349 if base != targetBase { 350 haveShlib[base] = true 351 } 352 } 353 } 354 355 // Arrange the deps into afiles and shlibs. 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 // This is a package linked into a shared 361 // library that we will put into shlibs. 362 continue 363 } 364 365 if haveShlib[filepath.Base(a.Target)] { 366 // This is a shared library we want to link against. 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 // Gather CgoLDFLAGS, but not from standard packages. 390 // The go tool can dig up runtime/cgo from GOROOT and 391 // think that it should use its CgoLDFLAGS, but gccgo 392 // doesn't use runtime/cgo. 393 if a.Package == nil { 394 continue 395 } 396 if !a.Package.Standard { 397 cgoldflags = append(cgoldflags, a.Package.CgoLDFLAGS...) 398 } 399 if len(a.Package.CgoFiles) > 0 { 400 usesCgo = true 401 } 402 if a.Package.UsesSwig() { 403 usesCgo = true 404 } 405 if len(a.Package.CXXFiles) > 0 || len(a.Package.SwigCXXFiles) > 0 { 406 cxx = true 407 } 408 if len(a.Package.MFiles) > 0 { 409 objc = true 410 } 411 if len(a.Package.FFiles) > 0 { 412 fortran = true 413 } 414 } 415 416 wholeArchive := []string{"-Wl,--whole-archive"} 417 noWholeArchive := []string{"-Wl,--no-whole-archive"} 418 if cfg.Goos == "aix" { 419 wholeArchive = nil 420 noWholeArchive = nil 421 } 422 ldflags = append(ldflags, wholeArchive...) 423 ldflags = append(ldflags, afiles...) 424 ldflags = append(ldflags, noWholeArchive...) 425 426 ldflags = append(ldflags, cgoldflags...) 427 ldflags = append(ldflags, envList("CGO_LDFLAGS", "")...) 428 if root.Package != nil { 429 ldflags = append(ldflags, root.Package.CgoLDFLAGS...) 430 } 431 if cfg.Goos != "aix" { 432 ldflags = str.StringList("-Wl,-(", ldflags, "-Wl,-)") 433 } 434 435 if root.buildID != "" { 436 // On systems that normally use gold or the GNU linker, 437 // use the --build-id option to write a GNU build ID note. 438 switch cfg.Goos { 439 case "android", "dragonfly", "linux", "netbsd": 440 ldflags = append(ldflags, fmt.Sprintf("-Wl,--build-id=0x%x", root.buildID)) 441 } 442 } 443 444 var rLibPath string 445 if cfg.Goos == "aix" { 446 rLibPath = "-Wl,-blibpath=" 447 } else { 448 rLibPath = "-Wl,-rpath=" 449 } 450 for _, shlib := range shlibs { 451 ldflags = append( 452 ldflags, 453 "-L"+filepath.Dir(shlib), 454 rLibPath+filepath.Dir(shlib), 455 "-l"+strings.TrimSuffix( 456 strings.TrimPrefix(filepath.Base(shlib), "lib"), 457 ".so")) 458 } 459 460 var realOut string 461 goLibBegin := str.StringList(wholeArchive, "-lgolibbegin", noWholeArchive) 462 switch buildmode { 463 case "exe": 464 if usesCgo && cfg.Goos == "linux" { 465 ldflags = append(ldflags, "-Wl,-E") 466 } 467 468 case "c-archive": 469 // Link the Go files into a single .o, and also link 470 // in -lgolibbegin. 471 // 472 // We need to use --whole-archive with -lgolibbegin 473 // because it doesn't define any symbols that will 474 // cause the contents to be pulled in; it's just 475 // initialization code. 476 // 477 // The user remains responsible for linking against 478 // -lgo -lpthread -lm in the final link. We can't use 479 // -r to pick them up because we can't combine 480 // split-stack and non-split-stack code in a single -r 481 // link, and libgo picks up non-split-stack code from 482 // libffi. 483 ldflags = append(ldflags, "-Wl,-r", "-nostdlib") 484 ldflags = append(ldflags, goLibBegin...) 485 486 if nopie := b.gccNoPie([]string{tools.linker()}); nopie != "" { 487 ldflags = append(ldflags, nopie) 488 } 489 490 // We are creating an object file, so we don't want a build ID. 491 if root.buildID == "" { 492 ldflags = b.disableBuildID(ldflags) 493 } 494 495 realOut = out 496 out = out + ".o" 497 498 case "c-shared": 499 ldflags = append(ldflags, "-shared", "-nostdlib") 500 ldflags = append(ldflags, goLibBegin...) 501 ldflags = append(ldflags, "-lgo", "-lgcc_s", "-lgcc", "-lc", "-lgcc") 502 503 case "shared": 504 if cfg.Goos != "aix" { 505 ldflags = append(ldflags, "-zdefs") 506 } 507 ldflags = append(ldflags, "-shared", "-nostdlib", "-lgo", "-lgcc_s", "-lgcc", "-lc") 508 509 default: 510 base.Fatalf("-buildmode=%s not supported for gccgo", buildmode) 511 } 512 513 switch buildmode { 514 case "exe", "c-shared": 515 if cxx { 516 ldflags = append(ldflags, "-lstdc++") 517 } 518 if objc { 519 ldflags = append(ldflags, "-lobjc") 520 } 521 if fortran { 522 fc := cfg.Getenv("FC") 523 if fc == "" { 524 fc = "gfortran" 525 } 526 // support gfortran out of the box and let others pass the correct link options 527 // via CGO_LDFLAGS 528 if strings.Contains(fc, "gfortran") { 529 ldflags = append(ldflags, "-lgfortran") 530 } 531 } 532 } 533 534 if err := b.run(root, ".", desc, nil, tools.linker(), "-o", out, ldflags, forcedGccgoflags, root.Package.Internal.Gccgoflags); err != nil { 535 return err 536 } 537 538 switch buildmode { 539 case "c-archive": 540 if err := b.run(root, ".", desc, nil, tools.ar(), arArgs, "rc", realOut, out); err != nil { 541 return err 542 } 543 } 544 return nil 545 } 546 547 func (tools gccgoToolchain) ld(b *Builder, root *Action, out, importcfg, mainpkg string) error { 548 return tools.link(b, root, out, importcfg, root.Deps, ldBuildmode, root.Package.ImportPath) 549 } 550 551 func (tools gccgoToolchain) ldShared(b *Builder, root *Action, toplevelactions []*Action, out, importcfg string, allactions []*Action) error { 552 return tools.link(b, root, out, importcfg, allactions, "shared", out) 553 } 554 555 func (tools gccgoToolchain) cc(b *Builder, a *Action, ofile, cfile string) error { 556 p := a.Package 557 inc := filepath.Join(cfg.GOROOT, "pkg", "include") 558 cfile = mkAbs(p.Dir, cfile) 559 defs := []string{"-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch} 560 defs = append(defs, b.gccArchArgs()...) 561 if pkgpath := tools.gccgoCleanPkgpath(b, p); pkgpath != "" { 562 defs = append(defs, `-D`, `GOPKGPATH="`+pkgpath+`"`) 563 } 564 compiler := envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch)) 565 if b.gccSupportsFlag(compiler, "-fsplit-stack") { 566 defs = append(defs, "-fsplit-stack") 567 } 568 defs = tools.maybePIC(defs) 569 if b.gccSupportsFlag(compiler, "-ffile-prefix-map=a=b") { 570 defs = append(defs, "-ffile-prefix-map="+base.Cwd()+"=.") 571 defs = append(defs, "-ffile-prefix-map="+b.WorkDir+"=/tmp/go-build") 572 } else if b.gccSupportsFlag(compiler, "-fdebug-prefix-map=a=b") { 573 defs = append(defs, "-fdebug-prefix-map="+b.WorkDir+"=/tmp/go-build") 574 } 575 if b.gccSupportsFlag(compiler, "-gno-record-gcc-switches") { 576 defs = append(defs, "-gno-record-gcc-switches") 577 } 578 return b.run(a, p.Dir, p.ImportPath, nil, compiler, "-Wall", "-g", 579 "-I", a.Objdir, "-I", inc, "-o", ofile, defs, "-c", cfile) 580 } 581 582 // maybePIC adds -fPIC to the list of arguments if needed. 583 func (tools gccgoToolchain) maybePIC(args []string) []string { 584 switch cfg.BuildBuildmode { 585 case "c-shared", "shared", "plugin": 586 args = append(args, "-fPIC") 587 } 588 return args 589 } 590 591 func gccgoPkgpath(p *load.Package) string { 592 if p.Internal.Build.IsCommand() && !p.Internal.ForceLibrary { 593 return "" 594 } 595 return p.ImportPath 596 } 597 598 var gccgoToSymbolFuncOnce sync.Once 599 var gccgoToSymbolFunc func(string) string 600 601 func (tools gccgoToolchain) gccgoCleanPkgpath(b *Builder, p *load.Package) string { 602 gccgoToSymbolFuncOnce.Do(func() { 603 tmpdir := b.WorkDir 604 if cfg.BuildN { 605 tmpdir = os.TempDir() 606 } 607 fn, err := pkgpath.ToSymbolFunc(tools.compiler(), tmpdir) 608 if err != nil { 609 fmt.Fprintf(os.Stderr, "cmd/go: %v\n", err) 610 base.SetExitStatus(2) 611 base.Exit() 612 } 613 gccgoToSymbolFunc = fn 614 }) 615 616 return gccgoToSymbolFunc(gccgoPkgpath(p)) 617 } 618 619 var ( 620 gccgoSupportsCgoIncompleteOnce sync.Once 621 gccgoSupportsCgoIncomplete bool 622 ) 623 624 const gccgoSupportsCgoIncompleteCode = ` 625 package p 626 627 import "runtime/cgo" 628 629 type I cgo.Incomplete 630 ` 631 632 // supportsCgoIncomplete reports whether the gccgo/GoLLVM compiler 633 // being used supports cgo.Incomplete, which was added in GCC 13. 634 func (tools gccgoToolchain) supportsCgoIncomplete(b *Builder) bool { 635 gccgoSupportsCgoIncompleteOnce.Do(func() { 636 fail := func(err error) { 637 fmt.Fprintf(os.Stderr, "cmd/go: %v\n", err) 638 base.SetExitStatus(2) 639 base.Exit() 640 } 641 642 tmpdir := b.WorkDir 643 if cfg.BuildN { 644 tmpdir = os.TempDir() 645 } 646 f, err := os.CreateTemp(tmpdir, "*_gccgo_cgoincomplete.go") 647 if err != nil { 648 fail(err) 649 } 650 fn := f.Name() 651 f.Close() 652 defer os.Remove(fn) 653 654 if err := os.WriteFile(fn, []byte(gccgoSupportsCgoIncompleteCode), 0644); err != nil { 655 fail(err) 656 } 657 658 on := strings.TrimSuffix(fn, ".go") + ".o" 659 if cfg.BuildN || cfg.BuildX { 660 b.Showcmd(tmpdir, "%s -c -o %s %s || true", tools.compiler(), on, fn) 661 // Since this function affects later builds, 662 // and only generates temporary files, 663 // we run the command even with -n. 664 } 665 cmd := exec.Command(tools.compiler(), "-c", "-o", on, fn) 666 cmd.Dir = tmpdir 667 var buf strings.Builder 668 cmd.Stdout = &buf 669 cmd.Stderr = &buf 670 err = cmd.Run() 671 if out := buf.String(); len(out) > 0 { 672 b.showOutput(nil, tmpdir, b.fmtcmd(tmpdir, "%s -c -o %s %s", tools.compiler(), on, fn), buf.String()) 673 } 674 gccgoSupportsCgoIncomplete = err == nil 675 }) 676 return gccgoSupportsCgoIncomplete 677 }