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