github.com/bir3/gocompiler@v0.9.2202/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 "bytes" 9 "fmt" 10 "os" 11 "github.com/bir3/gocompiler/exec" 12 "path/filepath" 13 "strings" 14 "sync" 15 16 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/base" 17 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/cfg" 18 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/fsys" 19 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/load" 20 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/str" 21 "github.com/bir3/gocompiler/src/cmd/internal/pkgpath" 22 ) 23 24 // The Gccgo toolchain. 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, 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 // gccgo only applies the last matching rule, so also handle the case where 115 // BuildTrimpath is true and the path is relative to base.Cwd(). 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 // Overlay files if necessary. 128 // See comment on gctoolchain.gc about overlay TODOs 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 // buildImportcfgSymlinks builds in root a tree of symlinks 138 // implementing the directives from importcfg. 139 // This serves as a temporary transition mechanism until 140 // we can depend on gccgo reading an importcfg directly. 141 // (The Go 1.9 and later gc compilers already do.) 142 func buildImportcfgSymlinks(sh *Shell, root string, importcfg []byte) error { 143 for lineNum, line := range strings.Split(string(importcfg), "\n") { 144 lineNum++ // 1-based 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 // add "lib" to the final element 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 // AIX puts both 32-bit and 64-bit objects in the same archive. 239 // Tell the AIX "ar" command to only care about 64-bit objects. 240 arArgs = []string{"-X64"} 241 } 242 absAfile := mkAbs(objdir, afile) 243 // Try with D modifier first, then without if that fails. 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 // Show the output if there is any even without errors. 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 // gccgo needs explicit linking with all package dependencies, 257 // and all LDFLAGS from cgo dependencies. 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 // Every _cgo_flags file has -g and -O2 in _CGO_LDFLAGS 282 // but they don't mean anything to the linker so filter 283 // them out. 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 // AIX puts both 32-bit and 64-bit objects in the same archive. 295 // Tell the AIX "ar" command to only care about 64-bit objects. 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 // TODO(rsc): We could do better about showing the right _cgo_flags even in -n mode. 310 // Either the archive is already built and we can read them out, 311 // or we're printing commands to build the archive and can 312 // forward the _cgo_flags directly to this step. 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 // If using -linkshared, find the shared library deps. 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 // The .a we are linking into this .so 342 // will have its Shlib set to this .so. 343 // Don't start thinking we want to link 344 // this .so into itself. 345 base := filepath.Base(p.Shlib) 346 if base != targetBase { 347 haveShlib[base] = true 348 } 349 } 350 } 351 352 // Arrange the deps into afiles and shlibs. 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 // This is a package linked into a shared 358 // library that we will put into shlibs. 359 continue 360 } 361 362 if haveShlib[filepath.Base(a.Target)] { 363 // This is a shared library we want to link against. 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 // On systems that normally use gold or the GNU linker, 424 // use the --build-id option to write a GNU build ID note. 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 // Link the Go files into a single .o, and also link 457 // in -lgolibbegin. 458 // 459 // We need to use --whole-archive with -lgolibbegin 460 // because it doesn't define any symbols that will 461 // cause the contents to be pulled in; it's just 462 // initialization code. 463 // 464 // The user remains responsible for linking against 465 // -lgo -lpthread -lm in the final link. We can't use 466 // -r to pick them up because we can't combine 467 // split-stack and non-split-stack code in a single -r 468 // link, and libgo picks up non-split-stack code from 469 // libffi. 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 // We are creating an object file, so we don't want a build ID. 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 ldflags = append(ldflags, goLibBegin...) 488 ldflags = append(ldflags, "-lgo", "-lgcc_s", "-lgcc", "-lc", "-lgcc") 489 490 case "shared": 491 if cfg.Goos != "aix" { 492 ldflags = append(ldflags, "-zdefs") 493 } 494 ldflags = append(ldflags, "-shared", "-nostdlib", "-lgo", "-lgcc_s", "-lgcc", "-lc") 495 496 default: 497 base.Fatalf("-buildmode=%s not supported for gccgo", buildmode) 498 } 499 500 switch buildmode { 501 case "exe", "c-shared": 502 if cxx { 503 ldflags = append(ldflags, "-lstdc++") 504 } 505 if objc { 506 ldflags = append(ldflags, "-lobjc") 507 } 508 if fortran { 509 fc := cfg.Getenv("FC") 510 if fc == "" { 511 fc = "gfortran" 512 } 513 // support gfortran out of the box and let others pass the correct link options 514 // via CGO_LDFLAGS 515 if strings.Contains(fc, "gfortran") { 516 ldflags = append(ldflags, "-lgfortran") 517 } 518 } 519 } 520 521 if err := sh.run(".", desc, nil, tools.linker(), "-o", out, ldflags, forcedGccgoflags, root.Package.Internal.Gccgoflags); err != nil { 522 return err 523 } 524 525 switch buildmode { 526 case "c-archive": 527 if err := sh.run(".", desc, nil, tools.ar(), arArgs, "rc", realOut, out); err != nil { 528 return err 529 } 530 } 531 return nil 532 } 533 534 func (tools gccgoToolchain) ld(b *Builder, root *Action, targetPath, importcfg, mainpkg string) error { 535 return tools.link(b, root, targetPath, importcfg, root.Deps, ldBuildmode, root.Package.ImportPath) 536 } 537 538 func (tools gccgoToolchain) ldShared(b *Builder, root *Action, toplevelactions []*Action, targetPath, importcfg string, allactions []*Action) error { 539 return tools.link(b, root, targetPath, importcfg, allactions, "shared", targetPath) 540 } 541 542 func (tools gccgoToolchain) cc(b *Builder, a *Action, ofile, cfile string) error { 543 p := a.Package 544 inc := filepath.Join(cfg.GOROOT, "pkg", "include") 545 cfile = mkAbs(p.Dir, cfile) 546 defs := []string{"-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch} 547 defs = append(defs, b.gccArchArgs()...) 548 if pkgpath := tools.gccgoCleanPkgpath(b, p); pkgpath != "" { 549 defs = append(defs, `-D`, `GOPKGPATH="`+pkgpath+`"`) 550 } 551 compiler := envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch)) 552 if b.gccSupportsFlag(compiler, "-fsplit-stack") { 553 defs = append(defs, "-fsplit-stack") 554 } 555 defs = tools.maybePIC(defs) 556 if b.gccSupportsFlag(compiler, "-ffile-prefix-map=a=b") { 557 defs = append(defs, "-ffile-prefix-map="+base.Cwd()+"=.") 558 defs = append(defs, "-ffile-prefix-map="+b.WorkDir+"=/tmp/go-build") 559 } else if b.gccSupportsFlag(compiler, "-fdebug-prefix-map=a=b") { 560 defs = append(defs, "-fdebug-prefix-map="+b.WorkDir+"=/tmp/go-build") 561 } 562 if b.gccSupportsFlag(compiler, "-gno-record-gcc-switches") { 563 defs = append(defs, "-gno-record-gcc-switches") 564 } 565 return b.Shell(a).run(p.Dir, p.ImportPath, nil, compiler, "-Wall", "-g", 566 "-I", a.Objdir, "-I", inc, "-o", ofile, defs, "-c", cfile) 567 } 568 569 // maybePIC adds -fPIC to the list of arguments if needed. 570 func (tools gccgoToolchain) maybePIC(args []string) []string { 571 switch cfg.BuildBuildmode { 572 case "c-shared", "shared", "plugin": 573 args = append(args, "-fPIC") 574 } 575 return args 576 } 577 578 func gccgoPkgpath(p *load.Package) string { 579 if p.Internal.Build.IsCommand() && !p.Internal.ForceLibrary { 580 return "" 581 } 582 return p.ImportPath 583 } 584 585 var gccgoToSymbolFuncOnce sync.Once 586 var gccgoToSymbolFunc func(string) string 587 588 func (tools gccgoToolchain) gccgoCleanPkgpath(b *Builder, p *load.Package) string { 589 gccgoToSymbolFuncOnce.Do(func() { 590 tmpdir := b.WorkDir 591 if cfg.BuildN { 592 tmpdir = os.TempDir() 593 } 594 fn, err := pkgpath.ToSymbolFunc(tools.compiler(), tmpdir) 595 if err != nil { 596 fmt.Fprintf(os.Stderr, "cmd/go: %v\n", err) 597 base.SetExitStatus(2) 598 base.Exit() 599 } 600 gccgoToSymbolFunc = fn 601 }) 602 603 return gccgoToSymbolFunc(gccgoPkgpath(p)) 604 } 605 606 var ( 607 gccgoSupportsCgoIncompleteOnce sync.Once 608 gccgoSupportsCgoIncomplete bool 609 ) 610 611 const gccgoSupportsCgoIncompleteCode = ` 612 package p 613 614 import "runtime/cgo" 615 616 type I cgo.Incomplete 617 ` 618 619 // supportsCgoIncomplete reports whether the gccgo/GoLLVM compiler 620 // being used supports cgo.Incomplete, which was added in GCC 13. 621 // 622 // This takes an Action only for output reporting purposes. 623 // The result value is unrelated to the Action. 624 func (tools gccgoToolchain) supportsCgoIncomplete(b *Builder, a *Action) bool { 625 gccgoSupportsCgoIncompleteOnce.Do(func() { 626 sh := b.Shell(a) 627 628 fail := func(err error) { 629 fmt.Fprintf(os.Stderr, "cmd/go: %v\n", err) 630 base.SetExitStatus(2) 631 base.Exit() 632 } 633 634 tmpdir := b.WorkDir 635 if cfg.BuildN { 636 tmpdir = os.TempDir() 637 } 638 f, err := os.CreateTemp(tmpdir, "*_gccgo_cgoincomplete.go") 639 if err != nil { 640 fail(err) 641 } 642 fn := f.Name() 643 f.Close() 644 defer os.Remove(fn) 645 646 if err := os.WriteFile(fn, []byte(gccgoSupportsCgoIncompleteCode), 0644); err != nil { 647 fail(err) 648 } 649 650 on := strings.TrimSuffix(fn, ".go") + ".o" 651 if cfg.BuildN || cfg.BuildX { 652 sh.ShowCmd(tmpdir, "%s -c -o %s %s || true", tools.compiler(), on, fn) 653 // Since this function affects later builds, 654 // and only generates temporary files, 655 // we run the command even with -n. 656 } 657 cmd := exec.Command(tools.compiler(), "-c", "-o", on, fn) 658 cmd.Dir = tmpdir 659 var buf bytes.Buffer 660 cmd.Stdout = &buf 661 cmd.Stderr = &buf 662 err = cmd.Run() 663 gccgoSupportsCgoIncomplete = err == nil 664 if cfg.BuildN || cfg.BuildX { 665 // Show output. We always pass a nil err because errors are an 666 // expected outcome in this case. 667 desc := sh.fmtCmd(tmpdir, "%s -c -o %s %s", tools.compiler(), on, fn) 668 sh.reportCmd(desc, tmpdir, buf.Bytes(), nil) 669 } 670 }) 671 return gccgoSupportsCgoIncomplete 672 }