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