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