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  	"os"
    11  	"os/exec"
    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  	"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 := exec.Command(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 := exec.Command(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  		fields := strings.Fields(line)
   242  		for i, field := range fields {
   243  			if strings.HasSuffix(field, ":") {
   244  				// Avoid parsing fields of lines like "Configured with: …", which may
   245  				// contain arbitrary substrings.
   246  				break
   247  			}
   248  			if field == "version" && i < len(fields)-1 {
   249  				// Check that the next field is plausibly a version number.
   250  				// We require only that it begins with an ASCII digit,
   251  				// since we don't know what version numbering schemes a given
   252  				// C compiler may use. (Clang and GCC mostly seem to follow the scheme X.Y.Z,
   253  				// but in https://go.dev/issue/64619 we saw "8.3 [DragonFly]", and who knows
   254  				// what other C compilers like "zig cc" might report?)
   255  				next := fields[i+1]
   256  				if len(next) > 0 && next[0] >= '0' && next[0] <= '9' {
   257  					version = line
   258  					break
   259  				}
   260  			}
   261  		}
   262  		if version != "" {
   263  			break
   264  		}
   265  	}
   266  	if version == "" {
   267  		return "", "", fmt.Errorf("%s: can not find version number in %q", name, out)
   268  	}
   269  
   270  	if !strings.Contains(version, "experimental") {
   271  		// This is a release. Use this line as the tool ID.
   272  		id = version
   273  	} else {
   274  		// This is a development version. The first line with
   275  		// a leading space is the compiler proper.
   276  		compiler := ""
   277  		for _, line := range lines {
   278  			if strings.HasPrefix(line, " ") && !strings.HasPrefix(line, " (in-process)") {
   279  				compiler = line
   280  				break
   281  			}
   282  		}
   283  		if compiler == "" {
   284  			return "", "", fmt.Errorf("%s: can not find compilation command in %q", name, out)
   285  		}
   286  
   287  		fields, _ := quoted.Split(compiler)
   288  		if len(fields) == 0 {
   289  			return "", "", fmt.Errorf("%s: compilation command confusion %q", name, out)
   290  		}
   291  		exe = fields[0]
   292  		if !strings.ContainsAny(exe, `/\`) {
   293  			if lp, err := cfg.LookPath(exe); err == nil {
   294  				exe = lp
   295  			}
   296  		}
   297  		id, err = buildid.ReadFile(exe)
   298  		if err != nil {
   299  			return "", "", err
   300  		}
   301  
   302  		// If we can't find a build ID, use a hash.
   303  		if id == "" {
   304  			id = b.fileHash(exe)
   305  		}
   306  	}
   307  
   308  	b.id.Lock()
   309  	b.toolIDCache[key] = id
   310  	b.toolIDCache[key+".exe"] = exe
   311  	b.id.Unlock()
   312  
   313  	return id, exe, nil
   314  }
   315  
   316  // Check if assembler used by gccgo is GNU as.
   317  func assemblerIsGas() bool {
   318  	cmd := exec.Command(BuildToolchain.compiler(), "-print-prog-name=as")
   319  	assembler, err := cmd.Output()
   320  	if err == nil {
   321  		cmd := exec.Command(strings.TrimSpace(string(assembler)), "--version")
   322  		out, err := cmd.Output()
   323  		return err == nil && strings.Contains(string(out), "GNU")
   324  	} else {
   325  		return false
   326  	}
   327  }
   328  
   329  // gccgoBuildIDFile creates an assembler file that records the
   330  // action's build ID in an SHF_EXCLUDE section for ELF files or
   331  // in a CSECT in XCOFF files.
   332  func (b *Builder) gccgoBuildIDFile(a *Action) (string, error) {
   333  	sfile := a.Objdir + "_buildid.s"
   334  
   335  	var buf bytes.Buffer
   336  	if cfg.Goos == "aix" {
   337  		fmt.Fprintf(&buf, "\t.csect .go.buildid[XO]\n")
   338  	} else if (cfg.Goos != "solaris" && cfg.Goos != "illumos") || assemblerIsGas() {
   339  		fmt.Fprintf(&buf, "\t"+`.section .go.buildid,"e"`+"\n")
   340  	} else if cfg.Goarch == "sparc" || cfg.Goarch == "sparc64" {
   341  		fmt.Fprintf(&buf, "\t"+`.section ".go.buildid",#exclude`+"\n")
   342  	} else { // cfg.Goarch == "386" || cfg.Goarch == "amd64"
   343  		fmt.Fprintf(&buf, "\t"+`.section .go.buildid,#exclude`+"\n")
   344  	}
   345  	fmt.Fprintf(&buf, "\t.byte ")
   346  	for i := 0; i < len(a.buildID); i++ {
   347  		if i > 0 {
   348  			if i%8 == 0 {
   349  				fmt.Fprintf(&buf, "\n\t.byte ")
   350  			} else {
   351  				fmt.Fprintf(&buf, ",")
   352  			}
   353  		}
   354  		fmt.Fprintf(&buf, "%#02x", a.buildID[i])
   355  	}
   356  	fmt.Fprintf(&buf, "\n")
   357  	if cfg.Goos != "solaris" && cfg.Goos != "illumos" && cfg.Goos != "aix" {
   358  		secType := "@progbits"
   359  		if cfg.Goarch == "arm" {
   360  			secType = "%progbits"
   361  		}
   362  		fmt.Fprintf(&buf, "\t"+`.section .note.GNU-stack,"",%s`+"\n", secType)
   363  		fmt.Fprintf(&buf, "\t"+`.section .note.GNU-split-stack,"",%s`+"\n", secType)
   364  	}
   365  
   366  	if err := b.Shell(a).writeFile(sfile, buf.Bytes()); err != nil {
   367  		return "", err
   368  	}
   369  
   370  	return sfile, nil
   371  }
   372  
   373  // buildID returns the build ID found in the given file.
   374  // If no build ID is found, buildID returns the content hash of the file.
   375  func (b *Builder) buildID(file string) string {
   376  	b.id.Lock()
   377  	id := b.buildIDCache[file]
   378  	b.id.Unlock()
   379  
   380  	if id != "" {
   381  		return id
   382  	}
   383  
   384  	id, err := buildid.ReadFile(file)
   385  	if err != nil {
   386  		id = b.fileHash(file)
   387  	}
   388  
   389  	b.id.Lock()
   390  	b.buildIDCache[file] = id
   391  	b.id.Unlock()
   392  
   393  	return id
   394  }
   395  
   396  // fileHash returns the content hash of the named file.
   397  func (b *Builder) fileHash(file string) string {
   398  	file, _ = fsys.OverlayPath(file)
   399  	sum, err := cache.FileHash(file)
   400  	if err != nil {
   401  		return ""
   402  	}
   403  	return buildid.HashToString(sum)
   404  }
   405  
   406  // useCache tries to satisfy the action a, which has action ID actionHash,
   407  // by using a cached result from an earlier build. At the moment, the only
   408  // cached result is the installed package or binary at target.
   409  // If useCache decides that the cache can be used, it sets a.buildID
   410  // and a.built for use by parent actions and then returns true.
   411  // Otherwise it sets a.buildID to a temporary build ID for use in the build
   412  // and returns false. When useCache returns false the expectation is that
   413  // the caller will build the target and then call updateBuildID to finish the
   414  // build ID computation.
   415  // When useCache returns false, it may have initiated buffering of output
   416  // during a's work. The caller should defer b.flushOutput(a), to make sure
   417  // that flushOutput is eventually called regardless of whether the action
   418  // succeeds. The flushOutput call must happen after updateBuildID.
   419  func (b *Builder) useCache(a *Action, actionHash cache.ActionID, target string, printOutput bool) bool {
   420  	// The second half of the build ID here is a placeholder for the content hash.
   421  	// It's important that the overall buildID be unlikely verging on impossible
   422  	// to appear in the output by chance, but that should be taken care of by
   423  	// the actionID half; if it also appeared in the input that would be like an
   424  	// engineered 120-bit partial SHA256 collision.
   425  	a.actionID = actionHash
   426  	actionID := buildid.HashToString(actionHash)
   427  	if a.json != nil {
   428  		a.json.ActionID = actionID
   429  	}
   430  	contentID := actionID // temporary placeholder, likely unique
   431  	a.buildID = actionID + buildIDSeparator + contentID
   432  
   433  	// Executable binaries also record the main build ID in the middle.
   434  	// See "Build IDs" comment above.
   435  	if a.Mode == "link" {
   436  		mainpkg := a.Deps[0]
   437  		a.buildID = actionID + buildIDSeparator + mainpkg.buildID + buildIDSeparator + contentID
   438  	}
   439  
   440  	// If user requested -a, we force a rebuild, so don't use the cache.
   441  	if cfg.BuildA {
   442  		if p := a.Package; p != nil && !p.Stale {
   443  			p.Stale = true
   444  			p.StaleReason = "build -a flag in use"
   445  		}
   446  		// Begin saving output for later writing to cache.
   447  		a.output = []byte{}
   448  		return false
   449  	}
   450  
   451  	c := cache.Default()
   452  
   453  	if target != "" {
   454  		buildID, _ := buildid.ReadFile(target)
   455  		if strings.HasPrefix(buildID, actionID+buildIDSeparator) {
   456  			a.buildID = buildID
   457  			if a.json != nil {
   458  				a.json.BuildID = a.buildID
   459  			}
   460  			a.built = target
   461  			// Poison a.Target to catch uses later in the build.
   462  			a.Target = "DO NOT USE - " + a.Mode
   463  			return true
   464  		}
   465  		// Special case for building a main package: if the only thing we
   466  		// want the package for is to link a binary, and the binary is
   467  		// already up-to-date, then to avoid a rebuild, report the package
   468  		// as up-to-date as well. See "Build IDs" comment above.
   469  		// TODO(rsc): Rewrite this code to use a TryCache func on the link action.
   470  		if !b.NeedExport && a.Mode == "build" && len(a.triggers) == 1 && a.triggers[0].Mode == "link" {
   471  			if id := strings.Split(buildID, buildIDSeparator); len(id) == 4 && id[1] == actionID {
   472  				// Temporarily assume a.buildID is the package build ID
   473  				// stored in the installed binary, and see if that makes
   474  				// the upcoming link action ID a match. If so, report that
   475  				// we built the package, safe in the knowledge that the
   476  				// link step will not ask us for the actual package file.
   477  				// Note that (*Builder).LinkAction arranged that all of
   478  				// a.triggers[0]'s dependencies other than a are also
   479  				// dependencies of a, so that we can be sure that,
   480  				// other than a.buildID, b.linkActionID is only accessing
   481  				// build IDs of completed actions.
   482  				oldBuildID := a.buildID
   483  				a.buildID = id[1] + buildIDSeparator + id[2]
   484  				linkID := buildid.HashToString(b.linkActionID(a.triggers[0]))
   485  				if id[0] == linkID {
   486  					// Best effort attempt to display output from the compile and link steps.
   487  					// If it doesn't work, it doesn't work: reusing the cached binary is more
   488  					// important than reprinting diagnostic information.
   489  					if printOutput {
   490  						showStdout(b, c, a, "stdout")      // compile output
   491  						showStdout(b, c, a, "link-stdout") // link output
   492  					}
   493  
   494  					// Poison a.Target to catch uses later in the build.
   495  					a.Target = "DO NOT USE - main build pseudo-cache Target"
   496  					a.built = "DO NOT USE - main build pseudo-cache built"
   497  					if a.json != nil {
   498  						a.json.BuildID = a.buildID
   499  					}
   500  					return true
   501  				}
   502  				// Otherwise restore old build ID for main build.
   503  				a.buildID = oldBuildID
   504  			}
   505  		}
   506  	}
   507  
   508  	// Special case for linking a test binary: if the only thing we
   509  	// want the binary for is to run the test, and the test result is cached,
   510  	// then to avoid the link step, report the link as up-to-date.
   511  	// We avoid the nested build ID problem in the previous special case
   512  	// by recording the test results in the cache under the action ID half.
   513  	if len(a.triggers) == 1 && a.triggers[0].TryCache != nil && a.triggers[0].TryCache(b, a.triggers[0]) {
   514  		// Best effort attempt to display output from the compile and link steps.
   515  		// If it doesn't work, it doesn't work: reusing the test result is more
   516  		// important than reprinting diagnostic information.
   517  		if printOutput {
   518  			showStdout(b, c, a.Deps[0], "stdout")      // compile output
   519  			showStdout(b, c, a.Deps[0], "link-stdout") // link output
   520  		}
   521  
   522  		// Poison a.Target to catch uses later in the build.
   523  		a.Target = "DO NOT USE -  pseudo-cache Target"
   524  		a.built = "DO NOT USE - pseudo-cache built"
   525  		return true
   526  	}
   527  
   528  	// Check to see if the action output is cached.
   529  	if file, _, err := cache.GetFile(c, actionHash); err == nil {
   530  		if buildID, err := buildid.ReadFile(file); err == nil {
   531  			if printOutput {
   532  				showStdout(b, c, a, "stdout")
   533  			}
   534  			a.built = file
   535  			a.Target = "DO NOT USE - using cache"
   536  			a.buildID = buildID
   537  			if a.json != nil {
   538  				a.json.BuildID = a.buildID
   539  			}
   540  			if p := a.Package; p != nil && target != "" {
   541  				p.Stale = true
   542  				// Clearer than explaining that something else is stale.
   543  				p.StaleReason = "not installed but available in build cache"
   544  			}
   545  			return true
   546  		}
   547  	}
   548  
   549  	// If we've reached this point, we can't use the cache for the action.
   550  	if p := a.Package; p != nil && !p.Stale {
   551  		p.Stale = true
   552  		p.StaleReason = "build ID mismatch"
   553  		if b.IsCmdList {
   554  			// Since we may end up printing StaleReason, include more detail.
   555  			for _, p1 := range p.Internal.Imports {
   556  				if p1.Stale && p1.StaleReason != "" {
   557  					if strings.HasPrefix(p1.StaleReason, "stale dependency: ") {
   558  						p.StaleReason = p1.StaleReason
   559  						break
   560  					}
   561  					if strings.HasPrefix(p.StaleReason, "build ID mismatch") {
   562  						p.StaleReason = "stale dependency: " + p1.ImportPath
   563  					}
   564  				}
   565  			}
   566  		}
   567  	}
   568  
   569  	// Begin saving output for later writing to cache.
   570  	a.output = []byte{}
   571  	return false
   572  }
   573  
   574  func showStdout(b *Builder, c cache.Cache, a *Action, key string) error {
   575  	actionID := a.actionID
   576  
   577  	stdout, stdoutEntry, err := cache.GetBytes(c, cache.Subkey(actionID, key))
   578  	if err != nil {
   579  		return err
   580  	}
   581  
   582  	if len(stdout) > 0 {
   583  		sh := b.Shell(a)
   584  		if cfg.BuildX || cfg.BuildN {
   585  			sh.ShowCmd("", "%s  # internal", joinUnambiguously(str.StringList("cat", c.OutputFile(stdoutEntry.OutputID))))
   586  		}
   587  		if !cfg.BuildN {
   588  			sh.Print(string(stdout))
   589  		}
   590  	}
   591  	return nil
   592  }
   593  
   594  // flushOutput flushes the output being queued in a.
   595  func (b *Builder) flushOutput(a *Action) {
   596  	b.Shell(a).Print(string(a.output))
   597  	a.output = nil
   598  }
   599  
   600  // updateBuildID updates the build ID in the target written by action a.
   601  // It requires that useCache was called for action a and returned false,
   602  // and that the build was then carried out and given the temporary
   603  // a.buildID to record as the build ID in the resulting package or binary.
   604  // updateBuildID computes the final content ID and updates the build IDs
   605  // in the binary.
   606  //
   607  // Keep in sync with src/cmd/buildid/buildid.go
   608  func (b *Builder) updateBuildID(a *Action, target string, rewrite bool) error {
   609  	sh := b.Shell(a)
   610  
   611  	if cfg.BuildX || cfg.BuildN {
   612  		if rewrite {
   613  			sh.ShowCmd("", "%s # internal", joinUnambiguously(str.StringList(base.Tool("buildid"), "-w", target)))
   614  		}
   615  		if cfg.BuildN {
   616  			return nil
   617  		}
   618  	}
   619  
   620  	c := cache.Default()
   621  
   622  	// Cache output from compile/link, even if we don't do the rest.
   623  	switch a.Mode {
   624  	case "build":
   625  		cache.PutBytes(c, cache.Subkey(a.actionID, "stdout"), a.output)
   626  	case "link":
   627  		// Even though we don't cache the binary, cache the linker text output.
   628  		// We might notice that an installed binary is up-to-date but still
   629  		// want to pretend to have run the linker.
   630  		// Store it under the main package's action ID
   631  		// to make it easier to find when that's all we have.
   632  		for _, a1 := range a.Deps {
   633  			if p1 := a1.Package; p1 != nil && p1.Name == "main" {
   634  				cache.PutBytes(c, cache.Subkey(a1.actionID, "link-stdout"), a.output)
   635  				break
   636  			}
   637  		}
   638  	}
   639  
   640  	// Find occurrences of old ID and compute new content-based ID.
   641  	r, err := os.Open(target)
   642  	if err != nil {
   643  		return err
   644  	}
   645  	matches, hash, err := buildid.FindAndHash(r, a.buildID, 0)
   646  	r.Close()
   647  	if err != nil {
   648  		return err
   649  	}
   650  	newID := a.buildID[:strings.LastIndex(a.buildID, buildIDSeparator)] + buildIDSeparator + buildid.HashToString(hash)
   651  	if len(newID) != len(a.buildID) {
   652  		return fmt.Errorf("internal error: build ID length mismatch %q vs %q", a.buildID, newID)
   653  	}
   654  
   655  	// Replace with new content-based ID.
   656  	a.buildID = newID
   657  	if a.json != nil {
   658  		a.json.BuildID = a.buildID
   659  	}
   660  	if len(matches) == 0 {
   661  		// Assume the user specified -buildid= to override what we were going to choose.
   662  		return nil
   663  	}
   664  
   665  	if rewrite {
   666  		w, err := os.OpenFile(target, os.O_RDWR, 0)
   667  		if err != nil {
   668  			return err
   669  		}
   670  		err = buildid.Rewrite(w, matches, newID)
   671  		if err != nil {
   672  			w.Close()
   673  			return err
   674  		}
   675  		if err := w.Close(); err != nil {
   676  			return err
   677  		}
   678  	}
   679  
   680  	// Cache package builds, but not binaries (link steps).
   681  	// The expectation is that binaries are not reused
   682  	// nearly as often as individual packages, and they're
   683  	// much larger, so the cache-footprint-to-utility ratio
   684  	// of binaries is much lower for binaries.
   685  	// Not caching the link step also makes sure that repeated "go run" at least
   686  	// always rerun the linker, so that they don't get too fast.
   687  	// (We don't want people thinking go is a scripting language.)
   688  	// Note also that if we start caching binaries, then we will
   689  	// copy the binaries out of the cache to run them, and then
   690  	// that will mean the go process is itself writing a binary
   691  	// and then executing it, so we will need to defend against
   692  	// ETXTBSY problems as discussed in exec.go and golang.org/issue/22220.
   693  	if a.Mode == "build" {
   694  		r, err := os.Open(target)
   695  		if err == nil {
   696  			if a.output == nil {
   697  				panic("internal error: a.output not set")
   698  			}
   699  			outputID, _, err := c.Put(a.actionID, r)
   700  			r.Close()
   701  			if err == nil && cfg.BuildX {
   702  				sh.ShowCmd("", "%s # internal", joinUnambiguously(str.StringList("cp", target, c.OutputFile(outputID))))
   703  			}
   704  			if b.NeedExport {
   705  				if err != nil {
   706  					return err
   707  				}
   708  				a.Package.Export = c.OutputFile(outputID)
   709  				a.Package.BuildID = a.buildID
   710  			}
   711  		}
   712  	}
   713  
   714  	return nil
   715  }
   716  

View as plain text