github.com/goproxy0/go@v0.0.0-20171111080102-49cc0c489d2c/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 ofile := a.Objdir + sfile[:len(sfile)-len(".s")] + ".o" 158 ofiles = append(ofiles, ofile) 159 sfile = mkAbs(p.Dir, sfile) 160 defs := []string{"-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch} 161 if pkgpath := gccgoCleanPkgpath(p); pkgpath != "" { 162 defs = append(defs, `-D`, `GOPKGPATH=`+pkgpath) 163 } 164 defs = tools.maybePIC(defs) 165 defs = append(defs, b.gccArchArgs()...) 166 err := b.run(p.Dir, p.ImportPath, nil, tools.compiler(), "-xassembler-with-cpp", "-I", a.Objdir, "-c", "-o", ofile, defs, sfile) 167 if err != nil { 168 return nil, err 169 } 170 } 171 return ofiles, nil 172 } 173 174 func gccgoArchive(basedir, imp string) string { 175 end := filepath.FromSlash(imp + ".a") 176 afile := filepath.Join(basedir, end) 177 // add "lib" to the final element 178 return filepath.Join(filepath.Dir(afile), "lib"+filepath.Base(afile)) 179 } 180 181 func (gccgoToolchain) pack(b *Builder, a *Action, afile string, ofiles []string) error { 182 p := a.Package 183 objdir := a.Objdir 184 var absOfiles []string 185 for _, f := range ofiles { 186 absOfiles = append(absOfiles, mkAbs(objdir, f)) 187 } 188 return b.run(p.Dir, p.ImportPath, nil, "ar", "rc", mkAbs(objdir, afile), absOfiles) 189 } 190 191 func (tools gccgoToolchain) link(b *Builder, root *Action, out, importcfg string, allactions []*Action, buildmode, desc string) error { 192 // gccgo needs explicit linking with all package dependencies, 193 // and all LDFLAGS from cgo dependencies. 194 apackagePathsSeen := make(map[string]bool) 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.Objdir, desc, nil, "ar", "x", newArchive, "_cgo_flags") 249 if err != nil { 250 return "", err 251 } 252 err = b.run(".", 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 actionsSeen := make(map[*Action]bool) 264 // Make a pre-order depth-first traversal of the action graph, taking note of 265 // whether a shared library action has been seen on the way to an action (the 266 // construction of the graph means that if any path to a node passes through 267 // a shared library action, they all do). 268 var walk func(a *Action, seenShlib bool) 269 var err error 270 walk = func(a *Action, seenShlib bool) { 271 if actionsSeen[a] { 272 return 273 } 274 actionsSeen[a] = true 275 if a.Package != nil && !seenShlib { 276 if a.Package.Standard { 277 return 278 } 279 // We record the target of the first time we see a .a file 280 // for a package to make sure that we prefer the 'install' 281 // rather than the 'build' location (which may not exist any 282 // more). We still need to traverse the dependencies of the 283 // build action though so saying 284 // if apackagePathsSeen[a.Package.ImportPath] { return } 285 // doesn't work. 286 if !apackagePathsSeen[a.Package.ImportPath] { 287 apackagePathsSeen[a.Package.ImportPath] = true 288 target := a.Target 289 if len(a.Package.CgoFiles) > 0 || a.Package.UsesSwig() { 290 target, err = readAndRemoveCgoFlags(target) 291 if err != nil { 292 return 293 } 294 } 295 afiles = append(afiles, target) 296 } 297 } 298 if strings.HasSuffix(a.Target, ".so") { 299 shlibs = append(shlibs, a.Target) 300 seenShlib = true 301 } 302 for _, a1 := range a.Deps { 303 walk(a1, seenShlib) 304 if err != nil { 305 return 306 } 307 } 308 } 309 for _, a1 := range root.Deps { 310 walk(a1, false) 311 if err != nil { 312 return err 313 } 314 } 315 316 for _, a := range allactions { 317 // Gather CgoLDFLAGS, but not from standard packages. 318 // The go tool can dig up runtime/cgo from GOROOT and 319 // think that it should use its CgoLDFLAGS, but gccgo 320 // doesn't use runtime/cgo. 321 if a.Package == nil { 322 continue 323 } 324 if !a.Package.Standard { 325 cgoldflags = append(cgoldflags, a.Package.CgoLDFLAGS...) 326 } 327 if len(a.Package.CgoFiles) > 0 { 328 usesCgo = true 329 } 330 if a.Package.UsesSwig() { 331 usesCgo = true 332 } 333 if len(a.Package.CXXFiles) > 0 || len(a.Package.SwigCXXFiles) > 0 { 334 cxx = true 335 } 336 if len(a.Package.MFiles) > 0 { 337 objc = true 338 } 339 if len(a.Package.FFiles) > 0 { 340 fortran = true 341 } 342 } 343 344 ldflags = append(ldflags, "-Wl,--whole-archive") 345 ldflags = append(ldflags, afiles...) 346 ldflags = append(ldflags, "-Wl,--no-whole-archive") 347 348 ldflags = append(ldflags, cgoldflags...) 349 ldflags = append(ldflags, envList("CGO_LDFLAGS", "")...) 350 if root.Package != nil { 351 ldflags = append(ldflags, root.Package.CgoLDFLAGS...) 352 } 353 354 ldflags = str.StringList("-Wl,-(", ldflags, "-Wl,-)") 355 356 for _, shlib := range shlibs { 357 ldflags = append( 358 ldflags, 359 "-L"+filepath.Dir(shlib), 360 "-Wl,-rpath="+filepath.Dir(shlib), 361 "-l"+strings.TrimSuffix( 362 strings.TrimPrefix(filepath.Base(shlib), "lib"), 363 ".so")) 364 } 365 366 var realOut string 367 switch buildmode { 368 case "exe": 369 if usesCgo && cfg.Goos == "linux" { 370 ldflags = append(ldflags, "-Wl,-E") 371 } 372 373 case "c-archive": 374 // Link the Go files into a single .o, and also link 375 // in -lgolibbegin. 376 // 377 // We need to use --whole-archive with -lgolibbegin 378 // because it doesn't define any symbols that will 379 // cause the contents to be pulled in; it's just 380 // initialization code. 381 // 382 // The user remains responsible for linking against 383 // -lgo -lpthread -lm in the final link. We can't use 384 // -r to pick them up because we can't combine 385 // split-stack and non-split-stack code in a single -r 386 // link, and libgo picks up non-split-stack code from 387 // libffi. 388 ldflags = append(ldflags, "-Wl,-r", "-nostdlib", "-Wl,--whole-archive", "-lgolibbegin", "-Wl,--no-whole-archive") 389 390 if nopie := b.gccNoPie([]string{tools.linker()}); nopie != "" { 391 ldflags = append(ldflags, nopie) 392 } 393 394 // We are creating an object file, so we don't want a build ID. 395 ldflags = b.disableBuildID(ldflags) 396 397 realOut = out 398 out = out + ".o" 399 400 case "c-shared": 401 ldflags = append(ldflags, "-shared", "-nostdlib", "-Wl,--whole-archive", "-lgolibbegin", "-Wl,--no-whole-archive", "-lgo", "-lgcc_s", "-lgcc", "-lc", "-lgcc") 402 case "shared": 403 ldflags = append(ldflags, "-zdefs", "-shared", "-nostdlib", "-lgo", "-lgcc_s", "-lgcc", "-lc") 404 405 default: 406 base.Fatalf("-buildmode=%s not supported for gccgo", buildmode) 407 } 408 409 switch buildmode { 410 case "exe", "c-shared": 411 if cxx { 412 ldflags = append(ldflags, "-lstdc++") 413 } 414 if objc { 415 ldflags = append(ldflags, "-lobjc") 416 } 417 if fortran { 418 fc := os.Getenv("FC") 419 if fc == "" { 420 fc = "gfortran" 421 } 422 // support gfortran out of the box and let others pass the correct link options 423 // via CGO_LDFLAGS 424 if strings.Contains(fc, "gfortran") { 425 ldflags = append(ldflags, "-lgfortran") 426 } 427 } 428 } 429 430 if err := b.run(".", desc, nil, tools.linker(), "-o", out, ldflags, forcedGccgoflags, root.Package.Internal.Gccgoflags); err != nil { 431 return err 432 } 433 434 switch buildmode { 435 case "c-archive": 436 if err := b.run(".", desc, nil, "ar", "rc", realOut, out); err != nil { 437 return err 438 } 439 } 440 return nil 441 } 442 443 func (tools gccgoToolchain) ld(b *Builder, root *Action, out, importcfg, mainpkg string) error { 444 return tools.link(b, root, out, importcfg, root.Deps, ldBuildmode, root.Package.ImportPath) 445 } 446 447 func (tools gccgoToolchain) ldShared(b *Builder, root *Action, toplevelactions []*Action, out, importcfg string, allactions []*Action) error { 448 fakeRoot := *root 449 fakeRoot.Deps = toplevelactions 450 return tools.link(b, &fakeRoot, out, importcfg, allactions, "shared", out) 451 } 452 453 func (tools gccgoToolchain) cc(b *Builder, a *Action, ofile, cfile string) error { 454 p := a.Package 455 inc := filepath.Join(cfg.GOROOT, "pkg", "include") 456 cfile = mkAbs(p.Dir, cfile) 457 defs := []string{"-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch} 458 defs = append(defs, b.gccArchArgs()...) 459 if pkgpath := gccgoCleanPkgpath(p); pkgpath != "" { 460 defs = append(defs, `-D`, `GOPKGPATH="`+pkgpath+`"`) 461 } 462 switch cfg.Goarch { 463 case "386", "amd64": 464 defs = append(defs, "-fsplit-stack") 465 } 466 defs = tools.maybePIC(defs) 467 return b.run(p.Dir, p.ImportPath, nil, envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch)), "-Wall", "-g", 468 "-I", a.Objdir, "-I", inc, "-o", ofile, defs, "-c", cfile) 469 } 470 471 // maybePIC adds -fPIC to the list of arguments if needed. 472 func (tools gccgoToolchain) maybePIC(args []string) []string { 473 switch cfg.BuildBuildmode { 474 case "c-shared", "shared", "plugin": 475 args = append(args, "-fPIC") 476 } 477 return args 478 } 479 480 func gccgoPkgpath(p *load.Package) string { 481 if p.Internal.Build.IsCommand() && !p.Internal.ForceLibrary { 482 return "" 483 } 484 return p.ImportPath 485 } 486 487 func gccgoCleanPkgpath(p *load.Package) string { 488 clean := func(r rune) rune { 489 switch { 490 case 'A' <= r && r <= 'Z', 'a' <= r && r <= 'z', 491 '0' <= r && r <= '9': 492 return r 493 } 494 return '_' 495 } 496 return strings.Map(clean, gccgoPkgpath(p)) 497 }