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