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