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