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