github.com/bir3/gocompiler@v0.9.2202/src/cmd/gocmd/internal/work/gc.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 "bufio" 9 "bytes" 10 "fmt" 11 "github.com/bir3/gocompiler/src/internal/platform" 12 "io" 13 "log" 14 "os" 15 "path/filepath" 16 "runtime" 17 "strings" 18 19 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/base" 20 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/cfg" 21 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/fsys" 22 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/gover" 23 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/load" 24 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/str" 25 "github.com/bir3/gocompiler/src/cmd/internal/quoted" 26 "crypto/sha1" 27 ) 28 29 // Tests can override this by setting $TESTGO_TOOLCHAIN_VERSION. 30 var ToolchainVersion = runtime.Version() 31 32 // The 'path' used for GOROOT_FINAL when -trimpath is specified 33 const trimPathGoRootFinal string = "$GOROOT" 34 35 // The Go toolchain. 36 37 type gcToolchain struct{} 38 39 func (gcToolchain) compiler() string { 40 return base.Tool("compile") 41 } 42 43 func (gcToolchain) linker() string { 44 return base.Tool("link") 45 } 46 47 func pkgPath(a *Action) string { 48 p := a.Package 49 ppath := p.ImportPath 50 if cfg.BuildBuildmode == "plugin" { 51 ppath = pluginPath(a) 52 } else if p.Name == "main" && !p.Internal.ForceLibrary { 53 ppath = "main" 54 } 55 return ppath 56 } 57 58 func (gcToolchain) gc(b *Builder, a *Action, archive string, importcfg, embedcfg []byte, symabis string, asmhdr bool, gofiles []string) (ofile string, output []byte, err error) { 59 p := a.Package 60 sh := b.Shell(a) 61 objdir := a.Objdir 62 if archive != "" { 63 ofile = archive 64 } else { 65 out := "_go_.o" 66 ofile = objdir + out 67 } 68 69 pkgpath := pkgPath(a) 70 defaultGcFlags := []string{"-p", pkgpath} 71 if p.Module != nil { 72 v := p.Module.GoVersion 73 if v == "" { 74 v = gover.DefaultGoModVersion 75 } 76 if allowedVersion(v) { 77 defaultGcFlags = append(defaultGcFlags, "-lang=go"+gover.Lang(v)) 78 } 79 } 80 if p.Standard { 81 defaultGcFlags = append(defaultGcFlags, "-std") 82 } 83 84 // If we're giving the compiler the entire package (no C etc files), tell it that, 85 // so that it can give good error messages about forward declarations. 86 // Exceptions: a few standard packages have forward declarations for 87 // pieces supplied behind-the-scenes by package runtime. 88 extFiles := len(p.CgoFiles) + len(p.CFiles) + len(p.CXXFiles) + len(p.MFiles) + len(p.FFiles) + len(p.SFiles) + len(p.SysoFiles) + len(p.SwigFiles) + len(p.SwigCXXFiles) 89 if p.Standard { 90 switch p.ImportPath { 91 case "bytes", "internal/poll", "net", "os": 92 fallthrough 93 case "runtime/metrics", "runtime/pprof", "runtime/trace": 94 fallthrough 95 case "sync", "syscall", "time": 96 extFiles++ 97 } 98 } 99 if extFiles == 0 { 100 defaultGcFlags = append(defaultGcFlags, "-complete") 101 } 102 if cfg.BuildContext.InstallSuffix != "" { 103 defaultGcFlags = append(defaultGcFlags, "-installsuffix", cfg.BuildContext.InstallSuffix) 104 } 105 if a.buildID != "" { 106 defaultGcFlags = append(defaultGcFlags, "-buildid", a.buildID) 107 } 108 if p.Internal.OmitDebug || cfg.Goos == "plan9" || cfg.Goarch == "wasm" { 109 defaultGcFlags = append(defaultGcFlags, "-dwarf=false") 110 } 111 if strings.HasPrefix(ToolchainVersion, "go1") && !strings.Contains(os.Args[0], "go_bootstrap") { 112 defaultGcFlags = append(defaultGcFlags, "-goversion", ToolchainVersion) 113 } 114 if p.Internal.Cover.Cfg != "" { 115 defaultGcFlags = append(defaultGcFlags, "-coveragecfg="+p.Internal.Cover.Cfg) 116 } 117 if p.Internal.PGOProfile != "" { 118 defaultGcFlags = append(defaultGcFlags, "-pgoprofile="+p.Internal.PGOProfile) 119 } 120 if symabis != "" { 121 defaultGcFlags = append(defaultGcFlags, "-symabis", symabis) 122 } 123 124 gcflags := str.StringList(forcedGcflags, p.Internal.Gcflags) 125 if p.Internal.FuzzInstrument { 126 gcflags = append(gcflags, fuzzInstrumentFlags()...) 127 } 128 // Add -c=N to use concurrent backend compilation, if possible. 129 if c := gcBackendConcurrency(gcflags); c > 1 { 130 defaultGcFlags = append(defaultGcFlags, fmt.Sprintf("-c=%d", c)) 131 } 132 133 args := []any{cfg.BuildToolexec, base.Tool("compile"), "-o", ofile, "-trimpath", a.trimpath(), defaultGcFlags, gcflags} 134 if p.Internal.LocalPrefix == "" { 135 args = append(args, "-nolocalimports") 136 } else { 137 args = append(args, "-D", p.Internal.LocalPrefix) 138 } 139 if importcfg != nil { 140 if err := sh.writeFile(objdir+"importcfg", importcfg); err != nil { 141 return "", nil, err 142 } 143 args = append(args, "-importcfg", objdir+"importcfg") 144 } 145 if embedcfg != nil { 146 if err := sh.writeFile(objdir+"embedcfg", embedcfg); err != nil { 147 return "", nil, err 148 } 149 args = append(args, "-embedcfg", objdir+"embedcfg") 150 } 151 if ofile == archive { 152 args = append(args, "-pack") 153 } 154 if asmhdr { 155 args = append(args, "-asmhdr", objdir+"go_asm.h") 156 } 157 158 for _, f := range gofiles { 159 f := mkAbs(p.Dir, f) 160 161 // Handle overlays. Convert path names using OverlayPath 162 // so these paths can be handed directly to tools. 163 // Deleted files won't show up in when scanning directories earlier, 164 // so OverlayPath will never return "" (meaning a deleted file) here. 165 // TODO(#39958): Handle cases where the package directory 166 // doesn't exist on disk (this can happen when all the package's 167 // files are in an overlay): the code expects the package directory 168 // to exist and runs some tools in that directory. 169 // TODO(#39958): Process the overlays when the 170 // gofiles, cgofiles, cfiles, sfiles, and cxxfiles variables are 171 // created in (*Builder).build. Doing that requires rewriting the 172 // code that uses those values to expect absolute paths. 173 f, _ = fsys.OverlayPath(f) 174 175 args = append(args, f) 176 } 177 178 output, err = sh.runOut(base.Cwd(), nil, args...) 179 return ofile, output, err 180 } 181 182 // gcBackendConcurrency returns the backend compiler concurrency level for a package compilation. 183 func gcBackendConcurrency(gcflags []string) int { 184 // First, check whether we can use -c at all for this compilation. 185 canDashC := concurrentGCBackendCompilationEnabledByDefault 186 187 switch e := os.Getenv("GO19CONCURRENTCOMPILATION"); e { 188 case "0": 189 canDashC = false 190 case "1": 191 canDashC = true 192 case "": 193 // Not set. Use default. 194 default: 195 log.Fatalf("GO19CONCURRENTCOMPILATION must be 0, 1, or unset, got %q", e) 196 } 197 198 // TODO: Test and delete these conditions. 199 if cfg.ExperimentErr != nil || cfg.Experiment.FieldTrack || cfg.Experiment.PreemptibleLoops { 200 canDashC = false 201 } 202 203 if !canDashC { 204 return 1 205 } 206 207 // Decide how many concurrent backend compilations to allow. 208 // 209 // If we allow too many, in theory we might end up with p concurrent processes, 210 // each with c concurrent backend compiles, all fighting over the same resources. 211 // However, in practice, that seems not to happen too much. 212 // Most build graphs are surprisingly serial, so p==1 for much of the build. 213 // Furthermore, concurrent backend compilation is only enabled for a part 214 // of the overall compiler execution, so c==1 for much of the build. 215 // So don't worry too much about that interaction for now. 216 // 217 // However, in practice, setting c above 4 tends not to help very much. 218 // See the analysis in CL 41192. 219 // 220 // TODO(josharian): attempt to detect whether this particular compilation 221 // is likely to be a bottleneck, e.g. when: 222 // - it has no successor packages to compile (usually package main) 223 // - all paths through the build graph pass through it 224 // - critical path scheduling says it is high priority 225 // and in such a case, set c to runtime.GOMAXPROCS(0). 226 // By default this is the same as runtime.NumCPU. 227 // We do this now when p==1. 228 // To limit parallelism, set GOMAXPROCS below numCPU; this may be useful 229 // on a low-memory builder, or if a deterministic build order is required. 230 c := runtime.GOMAXPROCS(0) 231 if cfg.BuildP == 1 { 232 // No process parallelism, do not cap compiler parallelism. 233 return c 234 } 235 // Some process parallelism. Set c to min(4, maxprocs). 236 if c > 4 { 237 c = 4 238 } 239 return c 240 } 241 242 // trimpath returns the -trimpath argument to use 243 // when compiling the action. 244 func (a *Action) trimpath() string { 245 // Keep in sync with Builder.ccompile 246 // The trimmed paths are a little different, but we need to trim in the 247 // same situations. 248 249 // Strip the object directory entirely. 250 objdir := a.Objdir 251 if len(objdir) > 1 && objdir[len(objdir)-1] == filepath.Separator { 252 objdir = objdir[:len(objdir)-1] 253 } 254 rewrite := "" 255 256 rewriteDir := a.Package.Dir 257 if cfg.BuildTrimpath { 258 importPath := a.Package.Internal.OrigImportPath 259 if m := a.Package.Module; m != nil && m.Version != "" { 260 rewriteDir = m.Path + "@" + m.Version + strings.TrimPrefix(importPath, m.Path) 261 } else { 262 rewriteDir = importPath 263 } 264 rewrite += a.Package.Dir + "=>" + rewriteDir + ";" 265 } 266 267 // Add rewrites for overlays. The 'from' and 'to' paths in overlays don't need to have 268 // same basename, so go from the overlay contents file path (passed to the compiler) 269 // to the path the disk path would be rewritten to. 270 271 cgoFiles := make(map[string]bool) 272 for _, f := range a.Package.CgoFiles { 273 cgoFiles[f] = true 274 } 275 276 // TODO(matloob): Higher up in the stack, when the logic for deciding when to make copies 277 // of c/c++/m/f/hfiles is consolidated, use the same logic that Build uses to determine 278 // whether to create the copies in objdir to decide whether to rewrite objdir to the 279 // package directory here. 280 var overlayNonGoRewrites string // rewrites for non-go files 281 hasCgoOverlay := false 282 if fsys.OverlayFile != "" { 283 for _, filename := range a.Package.AllFiles() { 284 path := filename 285 if !filepath.IsAbs(path) { 286 path = filepath.Join(a.Package.Dir, path) 287 } 288 base := filepath.Base(path) 289 isGo := strings.HasSuffix(filename, ".go") || strings.HasSuffix(filename, ".s") 290 isCgo := cgoFiles[filename] || !isGo 291 overlayPath, isOverlay := fsys.OverlayPath(path) 292 if isCgo && isOverlay { 293 hasCgoOverlay = true 294 } 295 if !isCgo && isOverlay { 296 rewrite += overlayPath + "=>" + filepath.Join(rewriteDir, base) + ";" 297 } else if isCgo { 298 // Generate rewrites for non-Go files copied to files in objdir. 299 if filepath.Dir(path) == a.Package.Dir { 300 // This is a file copied to objdir. 301 overlayNonGoRewrites += filepath.Join(objdir, base) + "=>" + filepath.Join(rewriteDir, base) + ";" 302 } 303 } else { 304 // Non-overlay Go files are covered by the a.Package.Dir rewrite rule above. 305 } 306 } 307 } 308 if hasCgoOverlay { 309 rewrite += overlayNonGoRewrites 310 } 311 rewrite += objdir + "=>" 312 313 return rewrite 314 } 315 316 func asmArgs(a *Action, p *load.Package) []any { 317 // Add -I pkg/GOOS_GOARCH so #include "textflag.h" works in .s files. 318 inc := filepath.Join(cfg.GOROOT, "pkg", "include") 319 pkgpath := pkgPath(a) 320 args := []any{cfg.BuildToolexec, base.Tool("asm"), "-p", pkgpath, "-trimpath", a.trimpath(), "-I", a.Objdir, "-I", inc, "-D", "GOOS_" + cfg.Goos, "-D", "GOARCH_" + cfg.Goarch, forcedAsmflags, p.Internal.Asmflags} 321 if p.ImportPath == "runtime" && cfg.Goarch == "386" { 322 for _, arg := range forcedAsmflags { 323 if arg == "-dynlink" { 324 args = append(args, "-D=GOBUILDMODE_shared=1") 325 } 326 } 327 } 328 329 if cfg.Goarch == "386" { 330 // Define GO386_value from cfg.GO386. 331 args = append(args, "-D", "GO386_"+cfg.GO386) 332 } 333 334 if cfg.Goarch == "amd64" { 335 // Define GOAMD64_value from cfg.GOAMD64. 336 args = append(args, "-D", "GOAMD64_"+cfg.GOAMD64) 337 } 338 339 if cfg.Goarch == "mips" || cfg.Goarch == "mipsle" { 340 // Define GOMIPS_value from cfg.GOMIPS. 341 args = append(args, "-D", "GOMIPS_"+cfg.GOMIPS) 342 } 343 344 if cfg.Goarch == "mips64" || cfg.Goarch == "mips64le" { 345 // Define GOMIPS64_value from cfg.GOMIPS64. 346 args = append(args, "-D", "GOMIPS64_"+cfg.GOMIPS64) 347 } 348 349 if cfg.Goarch == "ppc64" || cfg.Goarch == "ppc64le" { 350 // Define GOPPC64_power8..N from cfg.PPC64. 351 // We treat each powerpc version as a superset of functionality. 352 switch cfg.GOPPC64 { 353 case "power10": 354 args = append(args, "-D", "GOPPC64_power10") 355 fallthrough 356 case "power9": 357 args = append(args, "-D", "GOPPC64_power9") 358 fallthrough 359 default: // This should always be power8. 360 args = append(args, "-D", "GOPPC64_power8") 361 } 362 } 363 364 if cfg.Goarch == "arm" { 365 // Define GOARM_value from cfg.GOARM. 366 switch cfg.GOARM { 367 case "7": 368 args = append(args, "-D", "GOARM_7") 369 fallthrough 370 case "6": 371 args = append(args, "-D", "GOARM_6") 372 fallthrough 373 default: 374 args = append(args, "-D", "GOARM_5") 375 } 376 } 377 378 return args 379 } 380 381 func (gcToolchain) asm(b *Builder, a *Action, sfiles []string) ([]string, error) { 382 p := a.Package 383 args := asmArgs(a, p) 384 385 var ofiles []string 386 for _, sfile := range sfiles { 387 overlayPath, _ := fsys.OverlayPath(mkAbs(p.Dir, sfile)) 388 ofile := a.Objdir + sfile[:len(sfile)-len(".s")] + ".o" 389 ofiles = append(ofiles, ofile) 390 args1 := append(args, "-o", ofile, overlayPath) 391 if err := b.Shell(a).run(p.Dir, p.ImportPath, nil, args1...); err != nil { 392 return nil, err 393 } 394 } 395 return ofiles, nil 396 } 397 398 func (gcToolchain) symabis(b *Builder, a *Action, sfiles []string) (string, error) { 399 sh := b.Shell(a) 400 401 mkSymabis := func(p *load.Package, sfiles []string, path string) error { 402 args := asmArgs(a, p) 403 args = append(args, "-gensymabis", "-o", path) 404 for _, sfile := range sfiles { 405 if p.ImportPath == "runtime/cgo" && strings.HasPrefix(sfile, "gcc_") { 406 continue 407 } 408 op, _ := fsys.OverlayPath(mkAbs(p.Dir, sfile)) 409 args = append(args, op) 410 } 411 412 // Supply an empty go_asm.h as if the compiler had been run. 413 // -gensymabis parsing is lax enough that we don't need the 414 // actual definitions that would appear in go_asm.h. 415 if err := sh.writeFile(a.Objdir+"go_asm.h", nil); err != nil { 416 return err 417 } 418 419 return sh.run(p.Dir, p.ImportPath, nil, args...) 420 } 421 422 var symabis string // Only set if we actually create the file 423 p := a.Package 424 if len(sfiles) != 0 { 425 symabis = a.Objdir + "symabis" 426 if err := mkSymabis(p, sfiles, symabis); err != nil { 427 return "", err 428 } 429 } 430 431 return symabis, nil 432 } 433 434 // toolVerify checks that the command line args writes the same output file 435 // if run using newTool instead. 436 // Unused now but kept around for future use. 437 func toolVerify(a *Action, b *Builder, p *load.Package, newTool string, ofile string, args []any) error { 438 newArgs := make([]any, len(args)) 439 copy(newArgs, args) 440 newArgs[1] = base.Tool(newTool) 441 newArgs[3] = ofile + ".new" // x.6 becomes x.6.new 442 if err := b.Shell(a).run(p.Dir, p.ImportPath, nil, newArgs...); err != nil { 443 return err 444 } 445 data1, err := os.ReadFile(ofile) 446 if err != nil { 447 return err 448 } 449 data2, err := os.ReadFile(ofile + ".new") 450 if err != nil { 451 return err 452 } 453 if !bytes.Equal(data1, data2) { 454 return fmt.Errorf("%s and %s produced different output files:\n%s\n%s", filepath.Base(args[1].(string)), newTool, strings.Join(str.StringList(args...), " "), strings.Join(str.StringList(newArgs...), " ")) 455 } 456 os.Remove(ofile + ".new") 457 return nil 458 } 459 460 func (gcToolchain) pack(b *Builder, a *Action, afile string, ofiles []string) error { 461 var absOfiles []string 462 for _, f := range ofiles { 463 absOfiles = append(absOfiles, mkAbs(a.Objdir, f)) 464 } 465 absAfile := mkAbs(a.Objdir, afile) 466 467 // The archive file should have been created by the compiler. 468 // Since it used to not work that way, verify. 469 if !cfg.BuildN { 470 if _, err := os.Stat(absAfile); err != nil { 471 base.Fatalf("os.Stat of archive file failed: %v", err) 472 } 473 } 474 475 p := a.Package 476 sh := b.Shell(a) 477 if cfg.BuildN || cfg.BuildX { 478 cmdline := str.StringList(base.Tool("pack"), "r", absAfile, absOfiles) 479 sh.ShowCmd(p.Dir, "%s # internal", joinUnambiguously(cmdline)) 480 } 481 if cfg.BuildN { 482 return nil 483 } 484 if err := packInternal(absAfile, absOfiles); err != nil { 485 return sh.reportCmd("", "", nil, err) 486 } 487 return nil 488 } 489 490 func packInternal(afile string, ofiles []string) error { 491 dst, err := os.OpenFile(afile, os.O_WRONLY|os.O_APPEND, 0) 492 if err != nil { 493 return err 494 } 495 defer dst.Close() // only for error returns or panics 496 w := bufio.NewWriter(dst) 497 498 for _, ofile := range ofiles { 499 src, err := os.Open(ofile) 500 if err != nil { 501 return err 502 } 503 fi, err := src.Stat() 504 if err != nil { 505 src.Close() 506 return err 507 } 508 // Note: Not using %-16.16s format because we care 509 // about bytes, not runes. 510 name := fi.Name() 511 if len(name) > 16 { 512 name = name[:16] 513 } else { 514 name += strings.Repeat(" ", 16-len(name)) 515 } 516 size := fi.Size() 517 fmt.Fprintf(w, "%s%-12d%-6d%-6d%-8o%-10d`\n", 518 name, 0, 0, 0, 0644, size) 519 n, err := io.Copy(w, src) 520 src.Close() 521 if err == nil && n < size { 522 err = io.ErrUnexpectedEOF 523 } else if err == nil && n > size { 524 err = fmt.Errorf("file larger than size reported by stat") 525 } 526 if err != nil { 527 return fmt.Errorf("copying %s to %s: %v", ofile, afile, err) 528 } 529 if size&1 != 0 { 530 w.WriteByte(0) 531 } 532 } 533 534 if err := w.Flush(); err != nil { 535 return err 536 } 537 return dst.Close() 538 } 539 540 // setextld sets the appropriate linker flags for the specified compiler. 541 func setextld(ldflags []string, compiler []string) ([]string, error) { 542 for _, f := range ldflags { 543 if f == "-extld" || strings.HasPrefix(f, "-extld=") { 544 // don't override -extld if supplied 545 return ldflags, nil 546 } 547 } 548 joined, err := quoted.Join(compiler) 549 if err != nil { 550 return nil, err 551 } 552 return append(ldflags, "-extld="+joined), nil 553 } 554 555 // pluginPath computes the package path for a plugin main package. 556 // 557 // This is typically the import path of the main package p, unless the 558 // plugin is being built directly from source files. In that case we 559 // combine the package build ID with the contents of the main package 560 // source files. This allows us to identify two different plugins 561 // built from two source files with the same name. 562 func pluginPath(a *Action) string { 563 p := a.Package 564 if p.ImportPath != "command-line-arguments" { 565 return p.ImportPath 566 } 567 h := sha1.New() 568 buildID := a.buildID 569 if a.Mode == "link" { 570 // For linking, use the main package's build ID instead of 571 // the binary's build ID, so it is the same hash used in 572 // compiling and linking. 573 // When compiling, we use actionID/actionID (instead of 574 // actionID/contentID) as a temporary build ID to compute 575 // the hash. Do the same here. (See buildid.go:useCache) 576 // The build ID matters because it affects the overall hash 577 // in the plugin's pseudo-import path returned below. 578 // We need to use the same import path when compiling and linking. 579 id := strings.Split(buildID, buildIDSeparator) 580 buildID = id[1] + buildIDSeparator + id[1] 581 } 582 fmt.Fprintf(h, "build ID: %s\n", buildID) 583 for _, file := range str.StringList(p.GoFiles, p.CgoFiles, p.SFiles) { 584 data, err := os.ReadFile(filepath.Join(p.Dir, file)) 585 if err != nil { 586 base.Fatalf("go: %s", err) 587 } 588 h.Write(data) 589 } 590 return fmt.Sprintf("plugin/unnamed-%x", h.Sum(nil)) 591 } 592 593 func (gcToolchain) ld(b *Builder, root *Action, targetPath, importcfg, mainpkg string) error { 594 cxx := len(root.Package.CXXFiles) > 0 || len(root.Package.SwigCXXFiles) > 0 595 for _, a := range root.Deps { 596 if a.Package != nil && (len(a.Package.CXXFiles) > 0 || len(a.Package.SwigCXXFiles) > 0) { 597 cxx = true 598 } 599 } 600 var ldflags []string 601 if cfg.BuildContext.InstallSuffix != "" { 602 ldflags = append(ldflags, "-installsuffix", cfg.BuildContext.InstallSuffix) 603 } 604 if root.Package.Internal.OmitDebug { 605 ldflags = append(ldflags, "-s", "-w") 606 } 607 if cfg.BuildBuildmode == "plugin" { 608 ldflags = append(ldflags, "-pluginpath", pluginPath(root)) 609 } 610 611 // Store BuildID inside toolchain binaries as a unique identifier of the 612 // tool being run, for use by content-based staleness determination. 613 if root.Package.Goroot && strings.HasPrefix(root.Package.ImportPath, "cmd/") { 614 // External linking will include our build id in the external 615 // linker's build id, which will cause our build id to not 616 // match the next time the tool is built. 617 // Rely on the external build id instead. 618 if !platform.MustLinkExternal(cfg.Goos, cfg.Goarch, false) { 619 ldflags = append(ldflags, "-X=cmd/internal/objabi.buildID="+root.buildID) 620 } 621 } 622 623 // Store default GODEBUG in binaries. 624 if root.Package.DefaultGODEBUG != "" { 625 ldflags = append(ldflags, "-X=runtime.godebugDefault="+root.Package.DefaultGODEBUG) 626 } 627 628 // If the user has not specified the -extld option, then specify the 629 // appropriate linker. In case of C++ code, use the compiler named 630 // by the CXX environment variable or defaultCXX if CXX is not set. 631 // Else, use the CC environment variable and defaultCC as fallback. 632 var compiler []string 633 if cxx { 634 compiler = envList("CXX", cfg.DefaultCXX(cfg.Goos, cfg.Goarch)) 635 } else { 636 compiler = envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch)) 637 } 638 ldflags = append(ldflags, "-buildmode="+ldBuildmode) 639 if root.buildID != "" { 640 ldflags = append(ldflags, "-buildid="+root.buildID) 641 } 642 ldflags = append(ldflags, forcedLdflags...) 643 ldflags = append(ldflags, root.Package.Internal.Ldflags...) 644 ldflags, err := setextld(ldflags, compiler) 645 if err != nil { 646 return err 647 } 648 649 // On OS X when using external linking to build a shared library, 650 // the argument passed here to -o ends up recorded in the final 651 // shared library in the LC_ID_DYLIB load command. 652 // To avoid putting the temporary output directory name there 653 // (and making the resulting shared library useless), 654 // run the link in the output directory so that -o can name 655 // just the final path element. 656 // On Windows, DLL file name is recorded in PE file 657 // export section, so do like on OS X. 658 // On Linux, for a shared object, at least with the Gold linker, 659 // the output file path is recorded in the .gnu.version_d section. 660 dir := "." 661 if cfg.BuildBuildmode == "c-shared" || cfg.BuildBuildmode == "plugin" { 662 dir, targetPath = filepath.Split(targetPath) 663 } 664 665 env := []string{} 666 if cfg.BuildTrimpath { 667 env = append(env, "GOROOT_FINAL="+trimPathGoRootFinal) 668 } 669 return b.Shell(root).run(dir, root.Package.ImportPath, env, cfg.BuildToolexec, base.Tool("link"), "-o", targetPath, "-importcfg", importcfg, ldflags, mainpkg) 670 } 671 672 func (gcToolchain) ldShared(b *Builder, root *Action, toplevelactions []*Action, targetPath, importcfg string, allactions []*Action) error { 673 ldflags := []string{"-installsuffix", cfg.BuildContext.InstallSuffix} 674 ldflags = append(ldflags, "-buildmode=shared") 675 ldflags = append(ldflags, forcedLdflags...) 676 ldflags = append(ldflags, root.Package.Internal.Ldflags...) 677 cxx := false 678 for _, a := range allactions { 679 if a.Package != nil && (len(a.Package.CXXFiles) > 0 || len(a.Package.SwigCXXFiles) > 0) { 680 cxx = true 681 } 682 } 683 // If the user has not specified the -extld option, then specify the 684 // appropriate linker. In case of C++ code, use the compiler named 685 // by the CXX environment variable or defaultCXX if CXX is not set. 686 // Else, use the CC environment variable and defaultCC as fallback. 687 var compiler []string 688 if cxx { 689 compiler = envList("CXX", cfg.DefaultCXX(cfg.Goos, cfg.Goarch)) 690 } else { 691 compiler = envList("CC", cfg.DefaultCC(cfg.Goos, cfg.Goarch)) 692 } 693 ldflags, err := setextld(ldflags, compiler) 694 if err != nil { 695 return err 696 } 697 for _, d := range toplevelactions { 698 if !strings.HasSuffix(d.Target, ".a") { // omit unsafe etc and actions for other shared libraries 699 continue 700 } 701 ldflags = append(ldflags, d.Package.ImportPath+"="+d.Target) 702 } 703 return b.Shell(root).run(".", targetPath, nil, cfg.BuildToolexec, base.Tool("link"), "-o", targetPath, "-importcfg", importcfg, ldflags) 704 } 705 706 func (gcToolchain) cc(b *Builder, a *Action, ofile, cfile string) error { 707 return fmt.Errorf("%s: C source files not supported without cgo", mkAbs(a.Package.Dir, cfile)) 708 }