github.com/bir3/gocompiler@v0.3.205/src/cmd/gocmd/internal/work/buildid.go (about) 1 // Copyright 2017 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 "bytes" 9 "fmt" 10 "os" 11 "os/exec" 12 "strings" 13 14 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/base" 15 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/cache" 16 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/cfg" 17 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/fsys" 18 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/str" 19 "github.com/bir3/gocompiler/src/cmd/internal/buildid" 20 "github.com/bir3/gocompiler/src/cmd/internal/quoted" 21 ) 22 23 // Build IDs 24 // 25 // Go packages and binaries are stamped with build IDs that record both 26 // the action ID, which is a hash of the inputs to the action that produced 27 // the packages or binary, and the content ID, which is a hash of the action 28 // output, namely the archive or binary itself. The hash is the same one 29 // used by the build artifact cache (see cmd/go/internal/cache), but 30 // truncated when stored in packages and binaries, as the full length is not 31 // needed and is a bit unwieldy. The precise form is 32 // 33 // actionID/[.../]contentID 34 // 35 // where the actionID and contentID are prepared by buildid.HashToString below. 36 // and are found by looking for the first or last slash. 37 // Usually the buildID is simply actionID/contentID, but see below for an 38 // exception. 39 // 40 // The build ID serves two primary purposes. 41 // 42 // 1. The action ID half allows installed packages and binaries to serve as 43 // one-element cache entries. If we intend to build math.a with a given 44 // set of inputs summarized in the action ID, and the installed math.a already 45 // has that action ID, we can reuse the installed math.a instead of rebuilding it. 46 // 47 // 2. The content ID half allows the easy preparation of action IDs for steps 48 // that consume a particular package or binary. The content hash of every 49 // input file for a given action must be included in the action ID hash. 50 // Storing the content ID in the build ID lets us read it from the file with 51 // minimal I/O, instead of reading and hashing the entire file. 52 // This is especially effective since packages and binaries are typically 53 // the largest inputs to an action. 54 // 55 // Separating action ID from content ID is important for reproducible builds. 56 // The compiler is compiled with itself. If an output were represented by its 57 // own action ID (instead of content ID) when computing the action ID of 58 // the next step in the build process, then the compiler could never have its 59 // own input action ID as its output action ID (short of a miraculous hash collision). 60 // Instead we use the content IDs to compute the next action ID, and because 61 // the content IDs converge, so too do the action IDs and therefore the 62 // build IDs and the overall compiler binary. See cmd/dist's cmdbootstrap 63 // for the actual convergence sequence. 64 // 65 // The “one-element cache” purpose is a bit more complex for installed 66 // binaries. For a binary, like cmd/gofmt, there are two steps: compile 67 // cmd/gofmt/*.go into main.a, and then link main.a into the gofmt binary. 68 // We do not install gofmt's main.a, only the gofmt binary. Being able to 69 // decide that the gofmt binary is up-to-date means computing the action ID 70 // for the final link of the gofmt binary and comparing it against the 71 // already-installed gofmt binary. But computing the action ID for the link 72 // means knowing the content ID of main.a, which we did not keep. 73 // To sidestep this problem, each binary actually stores an expanded build ID: 74 // 75 // actionID(binary)/actionID(main.a)/contentID(main.a)/contentID(binary) 76 // 77 // (Note that this can be viewed equivalently as: 78 // 79 // actionID(binary)/buildID(main.a)/contentID(binary) 80 // 81 // Storing the buildID(main.a) in the middle lets the computations that care 82 // about the prefix or suffix halves ignore the middle and preserves the 83 // original build ID as a contiguous string.) 84 // 85 // During the build, when it's time to build main.a, the gofmt binary has the 86 // information needed to decide whether the eventual link would produce 87 // the same binary: if the action ID for main.a's inputs matches and then 88 // the action ID for the link step matches when assuming the given main.a 89 // content ID, then the binary as a whole is up-to-date and need not be rebuilt. 90 // 91 // This is all a bit complex and may be simplified once we can rely on the 92 // main cache, but at least at the start we will be using the content-based 93 // staleness determination without a cache beyond the usual installed 94 // package and binary locations. 95 96 const buildIDSeparator = "/" 97 98 // actionID returns the action ID half of a build ID. 99 func actionID(buildID string) string { 100 i := strings.Index(buildID, buildIDSeparator) 101 if i < 0 { 102 return buildID 103 } 104 return buildID[:i] 105 } 106 107 // contentID returns the content ID half of a build ID. 108 func contentID(buildID string) string { 109 return buildID[strings.LastIndex(buildID, buildIDSeparator)+1:] 110 } 111 112 // toolID returns the unique ID to use for the current copy of the 113 // named tool (asm, compile, cover, link). 114 // 115 // It is important that if the tool changes (for example a compiler bug is fixed 116 // and the compiler reinstalled), toolID returns a different string, so that old 117 // package archives look stale and are rebuilt (with the fixed compiler). 118 // This suggests using a content hash of the tool binary, as stored in the build ID. 119 // 120 // Unfortunately, we can't just open the tool binary, because the tool might be 121 // invoked via a wrapper program specified by -toolexec and we don't know 122 // what the wrapper program does. In particular, we want "-toolexec toolstash" 123 // to continue working: it does no good if "-toolexec toolstash" is executing a 124 // stashed copy of the compiler but the go command is acting as if it will run 125 // the standard copy of the compiler. The solution is to ask the tool binary to tell 126 // us its own build ID using the "-V=full" flag now supported by all tools. 127 // Then we know we're getting the build ID of the compiler that will actually run 128 // during the build. (How does the compiler binary know its own content hash? 129 // We store it there using updateBuildID after the standard link step.) 130 // 131 // A final twist is that we'd prefer to have reproducible builds for release toolchains. 132 // It should be possible to cross-compile for Windows from either Linux or Mac 133 // or Windows itself and produce the same binaries, bit for bit. If the tool ID, 134 // which influences the action ID half of the build ID, is based on the content ID, 135 // then the Linux compiler binary and Mac compiler binary will have different tool IDs 136 // and therefore produce executables with different action IDs. 137 // To avoid this problem, for releases we use the release version string instead 138 // of the compiler binary's content hash. This assumes that all compilers built 139 // on all different systems are semantically equivalent, which is of course only true 140 // modulo bugs. (Producing the exact same executables also requires that the different 141 // build setups agree on details like $GOROOT and file name paths, but at least the 142 // tool IDs do not make it impossible.) 143 func (b *Builder) toolID(name string) string { 144 b.id.Lock() 145 id := b.toolIDCache[name] 146 b.id.Unlock() 147 148 if id != "" { 149 return id 150 } 151 152 path := base.Tool(name) 153 desc := "go tool " + name 154 155 // Special case: undocumented -vettool overrides usual vet, 156 // for testing vet or supplying an alternative analysis tool. 157 if name == "vet" && VetTool != "" { 158 path = VetTool 159 desc = VetTool 160 } 161 162 cmdline := str.StringList(cfg.BuildToolexec, path, "-V=full") 163 cmd := base.ToolCommand(cmdline[0], cmdline[1:]...) 164 var stdout, stderr strings.Builder 165 cmd.Stdout = &stdout 166 cmd.Stderr = &stderr 167 if err := cmd.Run(); err != nil { 168 if stderr.Len() > 0 { 169 os.Stderr.WriteString(stderr.String()) 170 } 171 base.Fatalf("go: error obtaining buildID for %s: %v", desc, err) 172 } 173 174 line := stdout.String() 175 f := strings.Fields(line) 176 if len(f) < 3 || f[0] != name && path != VetTool || f[1] != "version" || f[2] == "devel" && !strings.HasPrefix(f[len(f)-1], "buildID=") { 177 base.Fatalf("go: parsing buildID from %s -V=full: unexpected output:\n\t%s", desc, line) 178 } 179 if f[2] == "devel" { 180 // On the development branch, use the content ID part of the build ID. 181 id = contentID(f[len(f)-1]) 182 } else { 183 // For a release, the output is like: "compile version go1.9.1 X:framepointer". 184 // Use the whole line. 185 id = strings.TrimSpace(line) 186 } 187 188 b.id.Lock() 189 b.toolIDCache[name] = id 190 b.id.Unlock() 191 192 return id 193 } 194 195 // gccToolID returns the unique ID to use for a tool that is invoked 196 // by the GCC driver. This is used particularly for gccgo, but this can also 197 // be used for gcc, g++, gfortran, etc.; those tools all use the GCC 198 // driver under different names. The approach used here should also 199 // work for sufficiently new versions of clang. Unlike toolID, the 200 // name argument is the program to run. The language argument is the 201 // type of input file as passed to the GCC driver's -x option. 202 // 203 // For these tools we have no -V=full option to dump the build ID, 204 // but we can run the tool with -v -### to reliably get the compiler proper 205 // and hash that. That will work in the presence of -toolexec. 206 // 207 // In order to get reproducible builds for released compilers, we 208 // detect a released compiler by the absence of "experimental" in the 209 // --version output, and in that case we just use the version string. 210 // 211 // gccToolID also returns the underlying executable for the compiler. 212 // The caller assumes that stat of the exe can be used, combined with the id, 213 // to detect changes in the underlying compiler. The returned exe can be empty, 214 // which means to rely only on the id. 215 func (b *Builder) gccToolID(name, language string) (id, exe string, err error) { 216 key := name + "." + language 217 b.id.Lock() 218 id = b.toolIDCache[key] 219 exe = b.toolIDCache[key+".exe"] 220 b.id.Unlock() 221 222 if id != "" { 223 return id, exe, nil 224 } 225 226 // Invoke the driver with -### to see the subcommands and the 227 // version strings. Use -x to set the language. Pretend to 228 // compile an empty file on standard input. 229 cmdline := str.StringList(cfg.BuildToolexec, name, "-###", "-x", language, "-c", "-") 230 cmd := base.ToolCommand(cmdline[0], cmdline[1:]...) 231 // Force untranslated output so that we see the string "version". 232 cmd.Env = append(os.Environ(), "LC_ALL=C") 233 out, err := cmd.CombinedOutput() 234 if err != nil { 235 return "", "", fmt.Errorf("%s: %v; output: %q", name, err, out) 236 } 237 238 version := "" 239 lines := strings.Split(string(out), "\n") 240 for _, line := range lines { 241 if fields := strings.Fields(line); len(fields) > 1 && fields[1] == "version" || len(fields) > 2 && fields[2] == "version" { 242 version = line 243 break 244 } 245 } 246 if version == "" { 247 return "", "", fmt.Errorf("%s: can not find version number in %q", name, out) 248 } 249 250 if !strings.Contains(version, "experimental") { 251 // This is a release. Use this line as the tool ID. 252 id = version 253 } else { 254 // This is a development version. The first line with 255 // a leading space is the compiler proper. 256 compiler := "" 257 for _, line := range lines { 258 if strings.HasPrefix(line, " ") && !strings.HasPrefix(line, " (in-process)") { 259 compiler = line 260 break 261 } 262 } 263 if compiler == "" { 264 return "", "", fmt.Errorf("%s: can not find compilation command in %q", name, out) 265 } 266 267 fields, _ := quoted.Split(compiler) 268 if len(fields) == 0 { 269 return "", "", fmt.Errorf("%s: compilation command confusion %q", name, out) 270 } 271 exe = fields[0] 272 if !strings.ContainsAny(exe, `/\`) { 273 if lp, err := exec.LookPath(exe); err == nil { 274 exe = lp 275 } 276 } 277 id, err = buildid.ReadFile(exe) 278 if err != nil { 279 return "", "", err 280 } 281 282 // If we can't find a build ID, use a hash. 283 if id == "" { 284 id = b.fileHash(exe) 285 } 286 } 287 288 b.id.Lock() 289 b.toolIDCache[key] = id 290 b.toolIDCache[key+".exe"] = exe 291 b.id.Unlock() 292 293 return id, exe, nil 294 } 295 296 // Check if assembler used by gccgo is GNU as. 297 func assemblerIsGas() bool { 298 cmd := base.ToolCommand(BuildToolchain.compiler(), "-print-prog-name=as") 299 assembler, err := cmd.Output() 300 if err == nil { 301 cmd := base.ToolCommand(strings.TrimSpace(string(assembler)), "--version") 302 out, err := cmd.Output() 303 return err == nil && strings.Contains(string(out), "GNU") 304 } else { 305 return false 306 } 307 } 308 309 // gccgoBuildIDFile creates an assembler file that records the 310 // action's build ID in an SHF_EXCLUDE section for ELF files or 311 // in a CSECT in XCOFF files. 312 func (b *Builder) gccgoBuildIDFile(a *Action) (string, error) { 313 sfile := a.Objdir + "_buildid.s" 314 315 var buf bytes.Buffer 316 if cfg.Goos == "aix" { 317 fmt.Fprintf(&buf, "\t.csect .go.buildid[XO]\n") 318 } else if (cfg.Goos != "solaris" && cfg.Goos != "illumos") || assemblerIsGas() { 319 fmt.Fprintf(&buf, "\t"+`.section .go.buildid,"e"`+"\n") 320 } else if cfg.Goarch == "sparc" || cfg.Goarch == "sparc64" { 321 fmt.Fprintf(&buf, "\t"+`.section ".go.buildid",#exclude`+"\n") 322 } else { // cfg.Goarch == "386" || cfg.Goarch == "amd64" 323 fmt.Fprintf(&buf, "\t"+`.section .go.buildid,#exclude`+"\n") 324 } 325 fmt.Fprintf(&buf, "\t.byte ") 326 for i := 0; i < len(a.buildID); i++ { 327 if i > 0 { 328 if i%8 == 0 { 329 fmt.Fprintf(&buf, "\n\t.byte ") 330 } else { 331 fmt.Fprintf(&buf, ",") 332 } 333 } 334 fmt.Fprintf(&buf, "%#02x", a.buildID[i]) 335 } 336 fmt.Fprintf(&buf, "\n") 337 if cfg.Goos != "solaris" && cfg.Goos != "illumos" && cfg.Goos != "aix" { 338 secType := "@progbits" 339 if cfg.Goarch == "arm" { 340 secType = "%progbits" 341 } 342 fmt.Fprintf(&buf, "\t"+`.section .note.GNU-stack,"",%s`+"\n", secType) 343 fmt.Fprintf(&buf, "\t"+`.section .note.GNU-split-stack,"",%s`+"\n", secType) 344 } 345 346 if cfg.BuildN || cfg.BuildX { 347 for _, line := range bytes.Split(buf.Bytes(), []byte("\n")) { 348 b.Showcmd("", "echo '%s' >> %s", line, sfile) 349 } 350 if cfg.BuildN { 351 return sfile, nil 352 } 353 } 354 355 if err := os.WriteFile(sfile, buf.Bytes(), 0666); err != nil { 356 return "", err 357 } 358 359 return sfile, nil 360 } 361 362 // buildID returns the build ID found in the given file. 363 // If no build ID is found, buildID returns the content hash of the file. 364 func (b *Builder) buildID(file string) string { 365 b.id.Lock() 366 id := b.buildIDCache[file] 367 b.id.Unlock() 368 369 if id != "" { 370 return id 371 } 372 373 id, err := buildid.ReadFile(file) 374 if err != nil { 375 id = b.fileHash(file) 376 } 377 378 b.id.Lock() 379 b.buildIDCache[file] = id 380 b.id.Unlock() 381 382 return id 383 } 384 385 // fileHash returns the content hash of the named file. 386 func (b *Builder) fileHash(file string) string { 387 file, _ = fsys.OverlayPath(file) 388 sum, err := cache.FileHash(file) 389 if err != nil { 390 return "" 391 } 392 return buildid.HashToString(sum) 393 } 394 395 // useCache tries to satisfy the action a, which has action ID actionHash, 396 // by using a cached result from an earlier build. At the moment, the only 397 // cached result is the installed package or binary at target. 398 // If useCache decides that the cache can be used, it sets a.buildID 399 // and a.built for use by parent actions and then returns true. 400 // Otherwise it sets a.buildID to a temporary build ID for use in the build 401 // and returns false. When useCache returns false the expectation is that 402 // the caller will build the target and then call updateBuildID to finish the 403 // build ID computation. 404 // When useCache returns false, it may have initiated buffering of output 405 // during a's work. The caller should defer b.flushOutput(a), to make sure 406 // that flushOutput is eventually called regardless of whether the action 407 // succeeds. The flushOutput call must happen after updateBuildID. 408 func (b *Builder) useCache(a *Action, actionHash cache.ActionID, target string, printOutput bool) bool { 409 // The second half of the build ID here is a placeholder for the content hash. 410 // It's important that the overall buildID be unlikely verging on impossible 411 // to appear in the output by chance, but that should be taken care of by 412 // the actionID half; if it also appeared in the input that would be like an 413 // engineered 120-bit partial SHA256 collision. 414 a.actionID = actionHash 415 actionID := buildid.HashToString(actionHash) 416 if a.json != nil { 417 a.json.ActionID = actionID 418 } 419 contentID := actionID // temporary placeholder, likely unique 420 a.buildID = actionID + buildIDSeparator + contentID 421 422 // Executable binaries also record the main build ID in the middle. 423 // See "Build IDs" comment above. 424 if a.Mode == "link" { 425 mainpkg := a.Deps[0] 426 a.buildID = actionID + buildIDSeparator + mainpkg.buildID + buildIDSeparator + contentID 427 } 428 429 // If user requested -a, we force a rebuild, so don't use the cache. 430 if cfg.BuildA { 431 if p := a.Package; p != nil && !p.Stale { 432 p.Stale = true 433 p.StaleReason = "build -a flag in use" 434 } 435 // Begin saving output for later writing to cache. 436 a.output = []byte{} 437 return false 438 } 439 440 if target != "" { 441 buildID, _ := buildid.ReadFile(target) 442 if strings.HasPrefix(buildID, actionID+buildIDSeparator) { 443 a.buildID = buildID 444 if a.json != nil { 445 a.json.BuildID = a.buildID 446 } 447 a.built = target 448 // Poison a.Target to catch uses later in the build. 449 a.Target = "DO NOT USE - " + a.Mode 450 return true 451 } 452 // Special case for building a main package: if the only thing we 453 // want the package for is to link a binary, and the binary is 454 // already up-to-date, then to avoid a rebuild, report the package 455 // as up-to-date as well. See "Build IDs" comment above. 456 // TODO(rsc): Rewrite this code to use a TryCache func on the link action. 457 if !b.NeedExport && a.Mode == "build" && len(a.triggers) == 1 && a.triggers[0].Mode == "link" { 458 if id := strings.Split(buildID, buildIDSeparator); len(id) == 4 && id[1] == actionID { 459 // Temporarily assume a.buildID is the package build ID 460 // stored in the installed binary, and see if that makes 461 // the upcoming link action ID a match. If so, report that 462 // we built the package, safe in the knowledge that the 463 // link step will not ask us for the actual package file. 464 // Note that (*Builder).LinkAction arranged that all of 465 // a.triggers[0]'s dependencies other than a are also 466 // dependencies of a, so that we can be sure that, 467 // other than a.buildID, b.linkActionID is only accessing 468 // build IDs of completed actions. 469 oldBuildID := a.buildID 470 a.buildID = id[1] + buildIDSeparator + id[2] 471 linkID := buildid.HashToString(b.linkActionID(a.triggers[0])) 472 if id[0] == linkID { 473 // Best effort attempt to display output from the compile and link steps. 474 // If it doesn't work, it doesn't work: reusing the cached binary is more 475 // important than reprinting diagnostic information. 476 if printOutput { 477 if c := cache.Default(); c != nil { 478 showStdout(b, c, a.actionID, "stdout") // compile output 479 showStdout(b, c, a.actionID, "link-stdout") // link output 480 } 481 } 482 483 // Poison a.Target to catch uses later in the build. 484 a.Target = "DO NOT USE - main build pseudo-cache Target" 485 a.built = "DO NOT USE - main build pseudo-cache built" 486 if a.json != nil { 487 a.json.BuildID = a.buildID 488 } 489 return true 490 } 491 // Otherwise restore old build ID for main build. 492 a.buildID = oldBuildID 493 } 494 } 495 } 496 497 // Special case for linking a test binary: if the only thing we 498 // want the binary for is to run the test, and the test result is cached, 499 // then to avoid the link step, report the link as up-to-date. 500 // We avoid the nested build ID problem in the previous special case 501 // by recording the test results in the cache under the action ID half. 502 if len(a.triggers) == 1 && a.triggers[0].TryCache != nil && a.triggers[0].TryCache(b, a.triggers[0]) { 503 // Best effort attempt to display output from the compile and link steps. 504 // If it doesn't work, it doesn't work: reusing the test result is more 505 // important than reprinting diagnostic information. 506 if printOutput { 507 if c := cache.Default(); c != nil { 508 showStdout(b, c, a.Deps[0].actionID, "stdout") // compile output 509 showStdout(b, c, a.Deps[0].actionID, "link-stdout") // link output 510 } 511 } 512 513 // Poison a.Target to catch uses later in the build. 514 a.Target = "DO NOT USE - pseudo-cache Target" 515 a.built = "DO NOT USE - pseudo-cache built" 516 return true 517 } 518 519 // Check to see if the action output is cached. 520 if c := cache.Default(); c != nil { 521 if file, _, err := c.GetFile(actionHash); err == nil { 522 if buildID, err := buildid.ReadFile(file); err == nil { 523 if printOutput { 524 showStdout(b, c, a.actionID, "stdout") 525 } 526 a.built = file 527 a.Target = "DO NOT USE - using cache" 528 a.buildID = buildID 529 if a.json != nil { 530 a.json.BuildID = a.buildID 531 } 532 if p := a.Package; p != nil && target != "" { 533 p.Stale = true 534 // Clearer than explaining that something else is stale. 535 p.StaleReason = "not installed but available in build cache" 536 } 537 return true 538 } 539 } 540 } 541 542 // If we've reached this point, we can't use the cache for the action. 543 if p := a.Package; p != nil && !p.Stale { 544 p.Stale = true 545 p.StaleReason = "build ID mismatch" 546 if b.IsCmdList { 547 // Since we may end up printing StaleReason, include more detail. 548 for _, p1 := range p.Internal.Imports { 549 if p1.Stale && p1.StaleReason != "" { 550 if strings.HasPrefix(p1.StaleReason, "stale dependency: ") { 551 p.StaleReason = p1.StaleReason 552 break 553 } 554 if strings.HasPrefix(p.StaleReason, "build ID mismatch") { 555 p.StaleReason = "stale dependency: " + p1.ImportPath 556 } 557 } 558 } 559 } 560 } 561 562 // Begin saving output for later writing to cache. 563 a.output = []byte{} 564 return false 565 } 566 567 func showStdout(b *Builder, c *cache.Cache, actionID cache.ActionID, key string) error { 568 stdout, stdoutEntry, err := c.GetBytes(cache.Subkey(actionID, key)) 569 if err != nil { 570 return err 571 } 572 573 if len(stdout) > 0 { 574 if cfg.BuildX || cfg.BuildN { 575 b.Showcmd("", "%s # internal", joinUnambiguously(str.StringList("cat", c.OutputFile(stdoutEntry.OutputID)))) 576 } 577 if !cfg.BuildN { 578 b.output.Lock() 579 defer b.output.Unlock() 580 b.Print(string(stdout)) 581 } 582 } 583 return nil 584 } 585 586 // flushOutput flushes the output being queued in a. 587 func (b *Builder) flushOutput(a *Action) { 588 b.output.Lock() 589 defer b.output.Unlock() 590 b.Print(string(a.output)) 591 a.output = nil 592 } 593 594 // updateBuildID updates the build ID in the target written by action a. 595 // It requires that useCache was called for action a and returned false, 596 // and that the build was then carried out and given the temporary 597 // a.buildID to record as the build ID in the resulting package or binary. 598 // updateBuildID computes the final content ID and updates the build IDs 599 // in the binary. 600 // 601 // Keep in sync with src/cmd/buildid/buildid.go 602 func (b *Builder) updateBuildID(a *Action, target string, rewrite bool) error { 603 if cfg.BuildX || cfg.BuildN { 604 if rewrite { 605 b.Showcmd("", "%s # internal", joinUnambiguously(str.StringList(base.Tool("buildid"), "-w", target))) 606 } 607 if cfg.BuildN { 608 return nil 609 } 610 } 611 612 // Cache output from compile/link, even if we don't do the rest. 613 if c := cache.Default(); c != nil { 614 switch a.Mode { 615 case "build": 616 c.PutBytes(cache.Subkey(a.actionID, "stdout"), a.output) 617 case "link": 618 // Even though we don't cache the binary, cache the linker text output. 619 // We might notice that an installed binary is up-to-date but still 620 // want to pretend to have run the linker. 621 // Store it under the main package's action ID 622 // to make it easier to find when that's all we have. 623 for _, a1 := range a.Deps { 624 if p1 := a1.Package; p1 != nil && p1.Name == "main" { 625 c.PutBytes(cache.Subkey(a1.actionID, "link-stdout"), a.output) 626 break 627 } 628 } 629 } 630 } 631 632 // Find occurrences of old ID and compute new content-based ID. 633 r, err := os.Open(target) 634 if err != nil { 635 return err 636 } 637 matches, hash, err := buildid.FindAndHash(r, a.buildID, 0) 638 r.Close() 639 if err != nil { 640 return err 641 } 642 newID := a.buildID[:strings.LastIndex(a.buildID, buildIDSeparator)] + buildIDSeparator + buildid.HashToString(hash) 643 if len(newID) != len(a.buildID) { 644 return fmt.Errorf("internal error: build ID length mismatch %q vs %q", a.buildID, newID) 645 } 646 647 // Replace with new content-based ID. 648 a.buildID = newID 649 if a.json != nil { 650 a.json.BuildID = a.buildID 651 } 652 if len(matches) == 0 { 653 // Assume the user specified -buildid= to override what we were going to choose. 654 return nil 655 } 656 657 if rewrite { 658 w, err := os.OpenFile(target, os.O_RDWR, 0) 659 if err != nil { 660 return err 661 } 662 err = buildid.Rewrite(w, matches, newID) 663 if err != nil { 664 w.Close() 665 return err 666 } 667 if err := w.Close(); err != nil { 668 return err 669 } 670 } 671 672 // Cache package builds, but not binaries (link steps). 673 // The expectation is that binaries are not reused 674 // nearly as often as individual packages, and they're 675 // much larger, so the cache-footprint-to-utility ratio 676 // of binaries is much lower for binaries. 677 // Not caching the link step also makes sure that repeated "go run" at least 678 // always rerun the linker, so that they don't get too fast. 679 // (We don't want people thinking go is a scripting language.) 680 // Note also that if we start caching binaries, then we will 681 // copy the binaries out of the cache to run them, and then 682 // that will mean the go process is itself writing a binary 683 // and then executing it, so we will need to defend against 684 // ETXTBSY problems as discussed in exec.go and golang.org/issue/22220. 685 if c := cache.Default(); c != nil && a.Mode == "build" { 686 r, err := os.Open(target) 687 if err == nil { 688 if a.output == nil { 689 panic("internal error: a.output not set") 690 } 691 outputID, _, err := c.Put(a.actionID, r) 692 r.Close() 693 if err == nil && cfg.BuildX { 694 b.Showcmd("", "%s # internal", joinUnambiguously(str.StringList("cp", target, c.OutputFile(outputID)))) 695 } 696 if b.NeedExport { 697 if err != nil { 698 return err 699 } 700 a.Package.Export = c.OutputFile(outputID) 701 a.Package.BuildID = a.buildID 702 } 703 } 704 } 705 706 return nil 707 }