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