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 5package work 6 7import ( 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// The Gccgo toolchain. 25 26type gccgoToolchain struct{} 27 28var GccgoName, GccgoBin string 29var gccgoErr error 30 31func init() { 32 GccgoName = cfg.Getenv("GCCGO") 33 if GccgoName == "" { 34 GccgoName = "gccgo" 35 } 36 GccgoBin, gccgoErr = cfg.LookPath(GccgoName) 37} 38 39func (gccgoToolchain) compiler() string { 40 checkGccgoBin() 41 return GccgoBin 42} 43 44func (gccgoToolchain) linker() string { 45 checkGccgoBin() 46 return GccgoBin 47} 48 49func (gccgoToolchain) ar() []string { 50 return envList("AR", "ar") 51} 52 53func 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 62func (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 // 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.) 142func 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 195func (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 217func (gccgoToolchain) symabis(b *Builder, a *Action, sfiles []string) (string, error) { 218 return "", nil 219} 220 221func 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 228func (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 253func (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 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 // support gfortran out of the box and let others pass the correct link options 517 // via CGO_LDFLAGS 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 537func (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 541func (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 545func (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// maybePIC adds -fPIC to the list of arguments if needed. 573func (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 581func gccgoPkgpath(p *load.Package) string { 582 if p.Internal.Build.IsCommand() && !p.Internal.ForceLibrary { 583 return "" 584 } 585 return p.ImportPath 586} 587 588var gccgoToSymbolFuncOnce sync.Once 589var gccgoToSymbolFunc func(string) string 590 591func (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 609var ( 610 gccgoSupportsCgoIncompleteOnce sync.Once 611 gccgoSupportsCgoIncomplete bool 612) 613 614const gccgoSupportsCgoIncompleteCode = ` 615package p 616 617import "runtime/cgo" 618 619type I cgo.Incomplete 620` 621 622// supportsCgoIncomplete reports whether the gccgo/GoLLVM compiler 623// being used supports cgo.Incomplete, which was added in GCC 13. 624// 625// This takes an Action only for output reporting purposes. 626// The result value is unrelated to the Action. 627func (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 // Since this function affects later builds, 657 // and only generates temporary files, 658 // we run the command even with -n. 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 // Show output. We always pass a nil err because errors are an 669 // expected outcome in this case. 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