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