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