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  

View as plain text