github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/go/not-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 "io/ioutil" 10 "os" 11 "os/exec" 12 "path/filepath" 13 "strings" 14 15 "github.com/gagliardetto/golang-go/cmd/go/not-internal/base" 16 "github.com/gagliardetto/golang-go/cmd/go/not-internal/cfg" 17 "github.com/gagliardetto/golang-go/cmd/go/not-internal/load" 18 "github.com/gagliardetto/golang-go/cmd/go/not-internal/str" 19 ) 20 21 // The Gccgo toolchain. 22 23 type gccgoToolchain struct{} 24 25 var GccgoName, GccgoBin string 26 var gccgoErr error 27 28 func init() { 29 GccgoName = cfg.Getenv("GCCGO") 30 if GccgoName == "" { 31 GccgoName = "gccgo" 32 } 33 GccgoBin, gccgoErr = exec.LookPath(GccgoName) 34 } 35 36 func (gccgoToolchain) compiler() string { 37 checkGccgoBin() 38 return GccgoBin 39 } 40 41 func (gccgoToolchain) linker() string { 42 checkGccgoBin() 43 return GccgoBin 44 } 45 46 func (gccgoToolchain) ar() string { 47 ar := cfg.Getenv("AR") 48 if ar == "" { 49 ar = "ar" 50 } 51 return ar 52 } 53 54 func checkGccgoBin() { 55 if gccgoErr == nil { 56 return 57 } 58 fmt.Fprintf(os.Stderr, "github.com/gagliardetto/golang-go/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 []byte, symabis string, asmhdr bool, gofiles []string) (ofile string, output []byte, err error) { 64 p := a.Package 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 := b.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(b, root, importcfg); err != nil { 89 return "", nil, err 90 } 91 args = append(args, "-I", root) 92 } 93 } 94 if cfg.BuildTrimpath && b.gccSupportsFlag(args[:1], "-ffile-prefix-map=a=b") { 95 args = append(args, "-ffile-prefix-map="+base.Cwd+"=.") 96 args = append(args, "-ffile-prefix-map="+b.WorkDir+"=/tmp/go-build") 97 } 98 args = append(args, a.Package.Internal.Gccgoflags...) 99 for _, f := range gofiles { 100 args = append(args, mkAbs(p.Dir, f)) 101 } 102 103 output, err = b.runOut(a, p.Dir, nil, args) 104 return ofile, output, err 105 } 106 107 // buildImportcfgSymlinks builds in root a tree of symlinks 108 // implementing the directives from importcfg. 109 // This serves as a temporary transition mechanism until 110 // we can depend on gccgo reading an importcfg directly. 111 // (The Go 1.9 and later gc compilers already do.) 112 func buildImportcfgSymlinks(b *Builder, root string, importcfg []byte) error { 113 for lineNum, line := range strings.Split(string(importcfg), "\n") { 114 lineNum++ // 1-based 115 line = strings.TrimSpace(line) 116 if line == "" { 117 continue 118 } 119 if line == "" || strings.HasPrefix(line, "#") { 120 continue 121 } 122 var verb, args string 123 if i := strings.Index(line, " "); i < 0 { 124 verb = line 125 } else { 126 verb, args = line[:i], strings.TrimSpace(line[i+1:]) 127 } 128 var before, after string 129 if i := strings.Index(args, "="); i >= 0 { 130 before, after = args[:i], args[i+1:] 131 } 132 switch verb { 133 default: 134 base.Fatalf("importcfg:%d: unknown directive %q", lineNum, verb) 135 case "packagefile": 136 if before == "" || after == "" { 137 return fmt.Errorf(`importcfg:%d: invalid packagefile: syntax is "packagefile path=filename": %s`, lineNum, line) 138 } 139 archive := gccgoArchive(root, before) 140 if err := b.Mkdir(filepath.Dir(archive)); err != nil { 141 return err 142 } 143 if err := b.Symlink(after, archive); err != nil { 144 return err 145 } 146 case "importmap": 147 if before == "" || after == "" { 148 return fmt.Errorf(`importcfg:%d: invalid importmap: syntax is "importmap old=new": %s`, lineNum, line) 149 } 150 beforeA := gccgoArchive(root, before) 151 afterA := gccgoArchive(root, after) 152 if err := b.Mkdir(filepath.Dir(beforeA)); err != nil { 153 return err 154 } 155 if err := b.Mkdir(filepath.Dir(afterA)); err != nil { 156 return err 157 } 158 if err := b.Symlink(afterA, beforeA); err != nil { 159 return err 160 } 161 case "packageshlib": 162 return fmt.Errorf("gccgo -importcfg does not support shared libraries") 163 } 164 } 165 return nil 166 } 167 168 func (tools gccgoToolchain) asm(b *Builder, a *Action, sfiles []string) ([]string, error) { 169 p := a.Package 170 var ofiles []string 171 for _, sfile := range sfiles { 172 base := filepath.Base(sfile) 173 ofile := a.Objdir + base[:len(base)-len(".s")] + ".o" 174 ofiles = append(ofiles, ofile) 175 sfile = mkAbs(p.Dir, sfile) 176 defs := []string{"-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch} 177 if pkgpath := gccgoCleanPkgpath(p); pkgpath != "" { 178 defs = append(defs, `-D`, `GOPKGPATH=`+pkgpath) 179 } 180 defs = tools.maybePIC(defs) 181 defs = append(defs, b.gccArchArgs()...) 182 err := b.run(a, p.Dir, p.ImportPath, nil, tools.compiler(), "-xassembler-with-cpp", "-I", a.Objdir, "-c", "-o", ofile, defs, sfile) 183 if err != nil { 184 return nil, err 185 } 186 } 187 return ofiles, nil 188 } 189 190 func (gccgoToolchain) symabis(b *Builder, a *Action, sfiles []string) (string, error) { 191 return "", nil 192 } 193 194 func gccgoArchive(basedir, imp string) string { 195 end := filepath.FromSlash(imp + ".a") 196 afile := filepath.Join(basedir, end) 197 // add "lib" to the final element 198 return filepath.Join(filepath.Dir(afile), "lib"+filepath.Base(afile)) 199 } 200 201 func (tools gccgoToolchain) pack(b *Builder, a *Action, afile string, ofiles []string) error { 202 p := a.Package 203 objdir := a.Objdir 204 var absOfiles []string 205 for _, f := range ofiles { 206 absOfiles = append(absOfiles, mkAbs(objdir, f)) 207 } 208 var arArgs []string 209 if cfg.Goos == "aix" && cfg.Goarch == "ppc64" { 210 // AIX puts both 32-bit and 64-bit objects in the same archive. 211 // Tell the AIX "ar" command to only care about 64-bit objects. 212 arArgs = []string{"-X64"} 213 } 214 absAfile := mkAbs(objdir, afile) 215 // Try with D modifier first, then without if that fails. 216 output, err := b.runOut(a, p.Dir, nil, tools.ar(), arArgs, "rcD", absAfile, absOfiles) 217 if err != nil { 218 return b.run(a, p.Dir, p.ImportPath, nil, tools.ar(), arArgs, "rc", absAfile, absOfiles) 219 } 220 221 if len(output) > 0 { 222 // Show the output if there is any even without errors. 223 b.showOutput(a, p.Dir, p.ImportPath, b.processOutput(output)) 224 } 225 226 return nil 227 } 228 229 func (tools gccgoToolchain) link(b *Builder, root *Action, out, importcfg string, allactions []*Action, buildmode, desc string) error { 230 // gccgo needs explicit linking with all package dependencies, 231 // and all LDFLAGS from cgo dependencies. 232 afiles := []string{} 233 shlibs := []string{} 234 ldflags := b.gccArchArgs() 235 cgoldflags := []string{} 236 usesCgo := false 237 cxx := false 238 objc := false 239 fortran := false 240 if root.Package != nil { 241 cxx = len(root.Package.CXXFiles) > 0 || len(root.Package.SwigCXXFiles) > 0 242 objc = len(root.Package.MFiles) > 0 243 fortran = len(root.Package.FFiles) > 0 244 } 245 246 readCgoFlags := func(flagsFile string) error { 247 flags, err := ioutil.ReadFile(flagsFile) 248 if err != nil { 249 return err 250 } 251 const ldflagsPrefix = "_CGO_LDFLAGS=" 252 for _, line := range strings.Split(string(flags), "\n") { 253 if strings.HasPrefix(line, ldflagsPrefix) { 254 newFlags := strings.Fields(line[len(ldflagsPrefix):]) 255 for _, flag := range newFlags { 256 // Every _cgo_flags file has -g and -O2 in _CGO_LDFLAGS 257 // but they don't mean anything to the linker so filter 258 // them out. 259 if flag != "-g" && !strings.HasPrefix(flag, "-O") { 260 cgoldflags = append(cgoldflags, flag) 261 } 262 } 263 } 264 } 265 return nil 266 } 267 268 var arArgs []string 269 if cfg.Goos == "aix" && cfg.Goarch == "ppc64" { 270 // AIX puts both 32-bit and 64-bit objects in the same archive. 271 // Tell the AIX "ar" command to only care about 64-bit objects. 272 arArgs = []string{"-X64"} 273 } 274 275 newID := 0 276 readAndRemoveCgoFlags := func(archive string) (string, error) { 277 newID++ 278 newArchive := root.Objdir + fmt.Sprintf("_pkg%d_.a", newID) 279 if err := b.copyFile(newArchive, archive, 0666, false); err != nil { 280 return "", err 281 } 282 if cfg.BuildN || cfg.BuildX { 283 b.Showcmd("", "ar d %s _cgo_flags", newArchive) 284 if cfg.BuildN { 285 // TODO(rsc): We could do better about showing the right _cgo_flags even in -n mode. 286 // Either the archive is already built and we can read them out, 287 // or we're printing commands to build the archive and can 288 // forward the _cgo_flags directly to this step. 289 return "", nil 290 } 291 } 292 err := b.run(root, root.Objdir, desc, nil, tools.ar(), arArgs, "x", newArchive, "_cgo_flags") 293 if err != nil { 294 return "", err 295 } 296 err = b.run(root, ".", desc, nil, tools.ar(), arArgs, "d", newArchive, "_cgo_flags") 297 if err != nil { 298 return "", err 299 } 300 err = readCgoFlags(filepath.Join(root.Objdir, "_cgo_flags")) 301 if err != nil { 302 return "", err 303 } 304 return newArchive, nil 305 } 306 307 // If using -linkshared, find the shared library deps. 308 haveShlib := make(map[string]bool) 309 targetBase := filepath.Base(root.Target) 310 if cfg.BuildLinkshared { 311 for _, a := range root.Deps { 312 p := a.Package 313 if p == nil || p.Shlib == "" { 314 continue 315 } 316 317 // The .a we are linking into this .so 318 // will have its Shlib set to this .so. 319 // Don't start thinking we want to link 320 // this .so into itself. 321 base := filepath.Base(p.Shlib) 322 if base != targetBase { 323 haveShlib[base] = true 324 } 325 } 326 } 327 328 // Arrange the deps into afiles and shlibs. 329 addedShlib := make(map[string]bool) 330 for _, a := range root.Deps { 331 p := a.Package 332 if p != nil && p.Shlib != "" && haveShlib[filepath.Base(p.Shlib)] { 333 // This is a package linked into a shared 334 // library that we will put into shlibs. 335 continue 336 } 337 338 if haveShlib[filepath.Base(a.Target)] { 339 // This is a shared library we want to link against. 340 if !addedShlib[a.Target] { 341 shlibs = append(shlibs, a.Target) 342 addedShlib[a.Target] = true 343 } 344 continue 345 } 346 347 if p != nil { 348 target := a.built 349 if p.UsesCgo() || p.UsesSwig() { 350 var err error 351 target, err = readAndRemoveCgoFlags(target) 352 if err != nil { 353 continue 354 } 355 } 356 357 afiles = append(afiles, target) 358 } 359 } 360 361 for _, a := range allactions { 362 // Gather CgoLDFLAGS, but not from standard packages. 363 // The go tool can dig up runtime/cgo from GOROOT and 364 // think that it should use its CgoLDFLAGS, but gccgo 365 // doesn't use runtime/cgo. 366 if a.Package == nil { 367 continue 368 } 369 if !a.Package.Standard { 370 cgoldflags = append(cgoldflags, a.Package.CgoLDFLAGS...) 371 } 372 if len(a.Package.CgoFiles) > 0 { 373 usesCgo = true 374 } 375 if a.Package.UsesSwig() { 376 usesCgo = true 377 } 378 if len(a.Package.CXXFiles) > 0 || len(a.Package.SwigCXXFiles) > 0 { 379 cxx = true 380 } 381 if len(a.Package.MFiles) > 0 { 382 objc = true 383 } 384 if len(a.Package.FFiles) > 0 { 385 fortran = true 386 } 387 } 388 389 wholeArchive := []string{"-Wl,--whole-archive"} 390 noWholeArchive := []string{"-Wl,--no-whole-archive"} 391 if cfg.Goos == "aix" { 392 wholeArchive = nil 393 noWholeArchive = nil 394 } 395 ldflags = append(ldflags, wholeArchive...) 396 ldflags = append(ldflags, afiles...) 397 ldflags = append(ldflags, noWholeArchive...) 398 399 ldflags = append(ldflags, cgoldflags...) 400 ldflags = append(ldflags, envList("CGO_LDFLAGS", "")...) 401 if root.Package != nil { 402 ldflags = append(ldflags, root.Package.CgoLDFLAGS...) 403 } 404 if cfg.Goos != "aix" { 405 ldflags = str.StringList("-Wl,-(", ldflags, "-Wl,-)") 406 } 407 408 if root.buildID != "" { 409 // On systems that normally use gold or the GNU linker, 410 // use the --build-id option to write a GNU build ID note. 411 switch cfg.Goos { 412 case "android", "dragonfly", "linux", "netbsd": 413 ldflags = append(ldflags, fmt.Sprintf("-Wl,--build-id=0x%x", root.buildID)) 414 } 415 } 416 417 var rLibPath string 418 if cfg.Goos == "aix" { 419 rLibPath = "-Wl,-blibpath=" 420 } else { 421 rLibPath = "-Wl,-rpath=" 422 } 423 for _, shlib := range shlibs { 424 ldflags = append( 425 ldflags, 426 "-L"+filepath.Dir(shlib), 427 rLibPath+filepath.Dir(shlib), 428 "-l"+strings.TrimSuffix( 429 strings.TrimPrefix(filepath.Base(shlib), "lib"), 430 ".so")) 431 } 432 433 var realOut string 434 goLibBegin := str.StringList(wholeArchive, "-lgolibbegin", noWholeArchive) 435 switch buildmode { 436 case "exe": 437 if usesCgo && cfg.Goos == "linux" { 438 ldflags = append(ldflags, "-Wl,-E") 439 } 440 441 case "c-archive": 442 // Link the Go files into a single .o, and also link 443 // in -lgolibbegin. 444 // 445 // We need to use --whole-archive with -lgolibbegin 446 // because it doesn't define any symbols that will 447 // cause the contents to be pulled in; it's just 448 // initialization code. 449 // 450 // The user remains responsible for linking against 451 // -lgo -lpthread -lm in the final link. We can't use 452 // -r to pick them up because we can't combine 453 // split-stack and non-split-stack code in a single -r 454 // link, and libgo picks up non-split-stack code from 455 // libffi. 456 ldflags = append(ldflags, "-Wl,-r", "-nostdlib") 457 ldflags = append(ldflags, goLibBegin...) 458 459 if nopie := b.gccNoPie([]string{tools.linker()}); nopie != "" { 460 ldflags = append(ldflags, nopie) 461 } 462 463 // We are creating an object file, so we don't want a build ID. 464 if root.buildID == "" { 465 ldflags = b.disableBuildID(ldflags) 466 } 467 468 realOut = out 469 out = out + ".o" 470 471 case "c-shared": 472 ldflags = append(ldflags, "-shared", "-nostdlib") 473 ldflags = append(ldflags, goLibBegin...) 474 ldflags = append(ldflags, "-lgo", "-lgcc_s", "-lgcc", "-lc", "-lgcc") 475 476 case "shared": 477 if cfg.Goos != "aix" { 478 ldflags = append(ldflags, "-zdefs") 479 } 480 ldflags = append(ldflags, "-shared", "-nostdlib", "-lgo", "-lgcc_s", "-lgcc", "-lc") 481 482 default: 483 base.Fatalf("-buildmode=%s not supported for gccgo", buildmode) 484 } 485 486 switch buildmode { 487 case "exe", "c-shared": 488 if cxx { 489 ldflags = append(ldflags, "-lstdc++") 490 } 491 if objc { 492 ldflags = append(ldflags, "-lobjc") 493 } 494 if fortran { 495 fc := cfg.Getenv("FC") 496 if fc == "" { 497 fc = "gfortran" 498 } 499 // support gfortran out of the box and let others pass the correct link options 500 // via CGO_LDFLAGS 501 if strings.Contains(fc, "gfortran") { 502 ldflags = append(ldflags, "-lgfortran") 503 } 504 } 505 } 506 507 if err := b.run(root, ".", desc, nil, tools.linker(), "-o", out, ldflags, forcedGccgoflags, root.Package.Internal.Gccgoflags); err != nil { 508 return err 509 } 510 511 switch buildmode { 512 case "c-archive": 513 if err := b.run(root, ".", desc, nil, tools.ar(), arArgs, "rc", realOut, out); err != nil { 514 return err 515 } 516 } 517 return nil 518 } 519 520 func (tools gccgoToolchain) ld(b *Builder, root *Action, out, importcfg, mainpkg string) error { 521 return tools.link(b, root, out, importcfg, root.Deps, ldBuildmode, root.Package.ImportPath) 522 } 523 524 func (tools gccgoToolchain) ldShared(b *Builder, root *Action, toplevelactions []*Action, out, importcfg string, allactions []*Action) error { 525 return tools.link(b, root, out, importcfg, allactions, "shared", out) 526 } 527 528 func (tools gccgoToolchain) cc(b *Builder, a *Action, ofile, cfile string) error { 529 p := a.Package 530 inc := filepath.Join(cfg.GOROOT, "pkg", "include") 531 cfile = mkAbs(p.Dir, cfile) 532 defs := []string{"-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch} 533 defs = append(defs, b.gccArchArgs()...) 534 if pkgpath := gccgoCleanPkgpath(p); pkgpath != "" { 535 defs = append(defs, `-D`, `GOPKGPATH="`+pkgpath+`"`) 536 } 537 compiler := envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch)) 538 if b.gccSupportsFlag(compiler, "-fsplit-stack") { 539 defs = append(defs, "-fsplit-stack") 540 } 541 defs = tools.maybePIC(defs) 542 if b.gccSupportsFlag(compiler, "-ffile-prefix-map=a=b") { 543 defs = append(defs, "-ffile-prefix-map="+base.Cwd+"=.") 544 defs = append(defs, "-ffile-prefix-map="+b.WorkDir+"=/tmp/go-build") 545 } else if b.gccSupportsFlag(compiler, "-fdebug-prefix-map=a=b") { 546 defs = append(defs, "-fdebug-prefix-map="+b.WorkDir+"=/tmp/go-build") 547 } 548 if b.gccSupportsFlag(compiler, "-gno-record-gcc-switches") { 549 defs = append(defs, "-gno-record-gcc-switches") 550 } 551 return b.run(a, p.Dir, p.ImportPath, nil, compiler, "-Wall", "-g", 552 "-I", a.Objdir, "-I", inc, "-o", ofile, defs, "-c", cfile) 553 } 554 555 // maybePIC adds -fPIC to the list of arguments if needed. 556 func (tools gccgoToolchain) maybePIC(args []string) []string { 557 switch cfg.BuildBuildmode { 558 case "c-shared", "shared", "plugin": 559 args = append(args, "-fPIC") 560 } 561 return args 562 } 563 564 func gccgoPkgpath(p *load.Package) string { 565 if p.Internal.Build.IsCommand() && !p.Internal.ForceLibrary { 566 return "" 567 } 568 return p.ImportPath 569 } 570 571 func gccgoCleanPkgpath(p *load.Package) string { 572 clean := func(r rune) rune { 573 switch { 574 case 'A' <= r && r <= 'Z', 'a' <= r && r <= 'z', 575 '0' <= r && r <= '9': 576 return r 577 } 578 return '_' 579 } 580 return strings.Map(clean, gccgoPkgpath(p)) 581 }