...

Source file src/pkg/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		"io/ioutil"
    11		"os"
    12		"os/exec"
    13		"strings"
    14	
    15		"cmd/go/internal/base"
    16		"cmd/go/internal/cache"
    17		"cmd/go/internal/cfg"
    18		"cmd/go/internal/load"
    19		"cmd/go/internal/str"
    20		"cmd/internal/buildid"
    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 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	// hashToString converts the hash h to a string to be recorded
   113	// in package archives and binaries as part of the build ID.
   114	// We use the first 96 bits of the hash and encode it in base64,
   115	// resulting in a 16-byte string. Because this is only used for
   116	// detecting the need to rebuild installed files (not for lookups
   117	// in the object file cache), 96 bits are sufficient to drive the
   118	// probability of a false "do not need to rebuild" decision to effectively zero.
   119	// We embed two different hashes in archives and four in binaries,
   120	// so cutting to 16 bytes is a significant savings when build IDs are displayed.
   121	// (16*4+3 = 67 bytes compared to 64*4+3 = 259 bytes for the
   122	// more straightforward option of printing the entire h in hex).
   123	func hashToString(h [cache.HashSize]byte) string {
   124		const b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
   125		const chunks = 5
   126		var dst [chunks * 4]byte
   127		for i := 0; i < chunks; i++ {
   128			v := uint32(h[3*i])<<16 | uint32(h[3*i+1])<<8 | uint32(h[3*i+2])
   129			dst[4*i+0] = b64[(v>>18)&0x3F]
   130			dst[4*i+1] = b64[(v>>12)&0x3F]
   131			dst[4*i+2] = b64[(v>>6)&0x3F]
   132			dst[4*i+3] = b64[v&0x3F]
   133		}
   134		return string(dst[:])
   135	}
   136	
   137	// toolID returns the unique ID to use for the current copy of the
   138	// named tool (asm, compile, cover, link).
   139	//
   140	// It is important that if the tool changes (for example a compiler bug is fixed
   141	// and the compiler reinstalled), toolID returns a different string, so that old
   142	// package archives look stale and are rebuilt (with the fixed compiler).
   143	// This suggests using a content hash of the tool binary, as stored in the build ID.
   144	//
   145	// Unfortunately, we can't just open the tool binary, because the tool might be
   146	// invoked via a wrapper program specified by -toolexec and we don't know
   147	// what the wrapper program does. In particular, we want "-toolexec toolstash"
   148	// to continue working: it does no good if "-toolexec toolstash" is executing a
   149	// stashed copy of the compiler but the go command is acting as if it will run
   150	// the standard copy of the compiler. The solution is to ask the tool binary to tell
   151	// us its own build ID using the "-V=full" flag now supported by all tools.
   152	// Then we know we're getting the build ID of the compiler that will actually run
   153	// during the build. (How does the compiler binary know its own content hash?
   154	// We store it there using updateBuildID after the standard link step.)
   155	//
   156	// A final twist is that we'd prefer to have reproducible builds for release toolchains.
   157	// It should be possible to cross-compile for Windows from either Linux or Mac
   158	// or Windows itself and produce the same binaries, bit for bit. If the tool ID,
   159	// which influences the action ID half of the build ID, is based on the content ID,
   160	// then the Linux compiler binary and Mac compiler binary will have different tool IDs
   161	// and therefore produce executables with different action IDs.
   162	// To avoid this problem, for releases we use the release version string instead
   163	// of the compiler binary's content hash. This assumes that all compilers built
   164	// on all different systems are semantically equivalent, which is of course only true
   165	// modulo bugs. (Producing the exact same executables also requires that the different
   166	// build setups agree on details like $GOROOT and file name paths, but at least the
   167	// tool IDs do not make it impossible.)
   168	func (b *Builder) toolID(name string) string {
   169		b.id.Lock()
   170		id := b.toolIDCache[name]
   171		b.id.Unlock()
   172	
   173		if id != "" {
   174			return id
   175		}
   176	
   177		path := base.Tool(name)
   178		desc := "go tool " + name
   179	
   180		// Special case: undocumented -vettool overrides usual vet,
   181		// for testing vet or supplying an alternative analysis tool.
   182		if name == "vet" && VetTool != "" {
   183			path = VetTool
   184			desc = VetTool
   185		}
   186	
   187		cmdline := str.StringList(cfg.BuildToolexec, path, "-V=full")
   188		cmd := exec.Command(cmdline[0], cmdline[1:]...)
   189		cmd.Env = base.EnvForDir(cmd.Dir, os.Environ())
   190		var stdout, stderr bytes.Buffer
   191		cmd.Stdout = &stdout
   192		cmd.Stderr = &stderr
   193		if err := cmd.Run(); err != nil {
   194			base.Fatalf("%s: %v\n%s%s", desc, err, stdout.Bytes(), stderr.Bytes())
   195		}
   196	
   197		line := stdout.String()
   198		f := strings.Fields(line)
   199		if len(f) < 3 || f[0] != name && path != VetTool || f[1] != "version" || f[2] == "devel" && !strings.HasPrefix(f[len(f)-1], "buildID=") {
   200			base.Fatalf("%s -V=full: unexpected output:\n\t%s", desc, line)
   201		}
   202		if f[2] == "devel" {
   203			// On the development branch, use the content ID part of the build ID.
   204			id = contentID(f[len(f)-1])
   205		} else {
   206			// For a release, the output is like: "compile version go1.9.1 X:framepointer".
   207			// Use the whole line.
   208			id = strings.TrimSpace(line)
   209		}
   210	
   211		b.id.Lock()
   212		b.toolIDCache[name] = id
   213		b.id.Unlock()
   214	
   215		return id
   216	}
   217	
   218	// gccToolID returns the unique ID to use for a tool that is invoked
   219	// by the GCC driver. This is used particularly for gccgo, but this can also
   220	// be used for gcc, g++, gfortran, etc.; those tools all use the GCC
   221	// driver under different names. The approach used here should also
   222	// work for sufficiently new versions of clang. Unlike toolID, the
   223	// name argument is the program to run. The language argument is the
   224	// type of input file as passed to the GCC driver's -x option.
   225	//
   226	// For these tools we have no -V=full option to dump the build ID,
   227	// but we can run the tool with -v -### to reliably get the compiler proper
   228	// and hash that. That will work in the presence of -toolexec.
   229	//
   230	// In order to get reproducible builds for released compilers, we
   231	// detect a released compiler by the absence of "experimental" in the
   232	// --version output, and in that case we just use the version string.
   233	func (b *Builder) gccgoToolID(name, language string) (string, error) {
   234		key := name + "." + language
   235		b.id.Lock()
   236		id := b.toolIDCache[key]
   237		b.id.Unlock()
   238	
   239		if id != "" {
   240			return id, nil
   241		}
   242	
   243		// Invoke the driver with -### to see the subcommands and the
   244		// version strings. Use -x to set the language. Pretend to
   245		// compile an empty file on standard input.
   246		cmdline := str.StringList(cfg.BuildToolexec, name, "-###", "-x", language, "-c", "-")
   247		cmd := exec.Command(cmdline[0], cmdline[1:]...)
   248		cmd.Env = base.EnvForDir(cmd.Dir, os.Environ())
   249		// Force untranslated output so that we see the string "version".
   250		cmd.Env = append(cmd.Env, "LC_ALL=C")
   251		out, err := cmd.CombinedOutput()
   252		if err != nil {
   253			return "", fmt.Errorf("%s: %v; output: %q", name, err, out)
   254		}
   255	
   256		version := ""
   257		lines := strings.Split(string(out), "\n")
   258		for _, line := range lines {
   259			if fields := strings.Fields(line); len(fields) > 1 && fields[1] == "version" {
   260				version = line
   261				break
   262			}
   263		}
   264		if version == "" {
   265			return "", fmt.Errorf("%s: can not find version number in %q", name, out)
   266		}
   267	
   268		if !strings.Contains(version, "experimental") {
   269			// This is a release. Use this line as the tool ID.
   270			id = version
   271		} else {
   272			// This is a development version. The first line with
   273			// a leading space is the compiler proper.
   274			compiler := ""
   275			for _, line := range lines {
   276				if len(line) > 1 && line[0] == ' ' {
   277					compiler = line
   278					break
   279				}
   280			}
   281			if compiler == "" {
   282				return "", fmt.Errorf("%s: can not find compilation command in %q", name, out)
   283			}
   284	
   285			fields := strings.Fields(compiler)
   286			if len(fields) == 0 {
   287				return "", fmt.Errorf("%s: compilation command confusion %q", name, out)
   288			}
   289			exe := fields[0]
   290			if !strings.ContainsAny(exe, `/\`) {
   291				if lp, err := exec.LookPath(exe); err == nil {
   292					exe = lp
   293				}
   294			}
   295			if _, err := os.Stat(exe); err != nil {
   296				return "", fmt.Errorf("%s: can not find compiler %q: %v; output %q", name, exe, err, out)
   297			}
   298			id = b.fileHash(exe)
   299		}
   300	
   301		b.id.Lock()
   302		b.toolIDCache[name] = id
   303		b.id.Unlock()
   304	
   305		return id, nil
   306	}
   307	
   308	// Check if assembler used by gccgo is GNU as.
   309	func assemblerIsGas() bool {
   310		cmd := exec.Command(BuildToolchain.compiler(), "-print-prog-name=as")
   311		assembler, err := cmd.Output()
   312		if err == nil {
   313			cmd := exec.Command(strings.TrimSpace(string(assembler)), "--version")
   314			out, err := cmd.Output()
   315			return err == nil && strings.Contains(string(out), "GNU")
   316		} else {
   317			return false
   318		}
   319	}
   320	
   321	// gccgoBuildIDFile creates an assembler file that records the
   322	// action's build ID in an SHF_EXCLUDE section for ELF files or
   323	// in a CSECT in XCOFF files.
   324	func (b *Builder) gccgoBuildIDFile(a *Action) (string, error) {
   325		sfile := a.Objdir + "_buildid.s"
   326	
   327		var buf bytes.Buffer
   328		if cfg.Goos == "aix" {
   329			fmt.Fprintf(&buf, "\t.csect .go.buildid[XO]\n")
   330		} else if (cfg.Goos != "solaris" && cfg.Goos != "illumos") || assemblerIsGas() {
   331			fmt.Fprintf(&buf, "\t"+`.section .go.buildid,"e"`+"\n")
   332		} else if cfg.Goarch == "sparc" || cfg.Goarch == "sparc64" {
   333			fmt.Fprintf(&buf, "\t"+`.section ".go.buildid",#exclude`+"\n")
   334		} else { // cfg.Goarch == "386" || cfg.Goarch == "amd64"
   335			fmt.Fprintf(&buf, "\t"+`.section .go.buildid,#exclude`+"\n")
   336		}
   337		fmt.Fprintf(&buf, "\t.byte ")
   338		for i := 0; i < len(a.buildID); i++ {
   339			if i > 0 {
   340				if i%8 == 0 {
   341					fmt.Fprintf(&buf, "\n\t.byte ")
   342				} else {
   343					fmt.Fprintf(&buf, ",")
   344				}
   345			}
   346			fmt.Fprintf(&buf, "%#02x", a.buildID[i])
   347		}
   348		fmt.Fprintf(&buf, "\n")
   349		if cfg.Goos != "solaris" && cfg.Goos != "illumos" && cfg.Goos != "aix" {
   350			secType := "@progbits"
   351			if cfg.Goarch == "arm" {
   352				secType = "%progbits"
   353			}
   354			fmt.Fprintf(&buf, "\t"+`.section .note.GNU-stack,"",%s`+"\n", secType)
   355			fmt.Fprintf(&buf, "\t"+`.section .note.GNU-split-stack,"",%s`+"\n", secType)
   356		}
   357	
   358		if cfg.BuildN || cfg.BuildX {
   359			for _, line := range bytes.Split(buf.Bytes(), []byte("\n")) {
   360				b.Showcmd("", "echo '%s' >> %s", line, sfile)
   361			}
   362			if cfg.BuildN {
   363				return sfile, nil
   364			}
   365		}
   366	
   367		if err := ioutil.WriteFile(sfile, buf.Bytes(), 0666); err != nil {
   368			return "", err
   369		}
   370	
   371		return sfile, nil
   372	}
   373	
   374	// buildID returns the build ID found in the given file.
   375	// If no build ID is found, buildID returns the content hash of the file.
   376	func (b *Builder) buildID(file string) string {
   377		b.id.Lock()
   378		id := b.buildIDCache[file]
   379		b.id.Unlock()
   380	
   381		if id != "" {
   382			return id
   383		}
   384	
   385		id, err := buildid.ReadFile(file)
   386		if err != nil {
   387			id = b.fileHash(file)
   388		}
   389	
   390		b.id.Lock()
   391		b.buildIDCache[file] = id
   392		b.id.Unlock()
   393	
   394		return id
   395	}
   396	
   397	// fileHash returns the content hash of the named file.
   398	func (b *Builder) fileHash(file string) string {
   399		sum, err := cache.FileHash(file)
   400		if err != nil {
   401			return ""
   402		}
   403		return 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, p *load.Package, actionHash cache.ActionID, target string) 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 96-bit partial SHA256 collision.
   425		a.actionID = actionHash
   426		actionID := 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		// Check to see if target exists and matches the expected action ID.
   441		// If so, it's up to date and we can reuse it instead of rebuilding it.
   442		var buildID string
   443		if target != "" && !cfg.BuildA {
   444			buildID, _ = buildid.ReadFile(target)
   445			if strings.HasPrefix(buildID, actionID+buildIDSeparator) {
   446				a.buildID = buildID
   447				if a.json != nil {
   448					a.json.BuildID = a.buildID
   449				}
   450				a.built = target
   451				// Poison a.Target to catch uses later in the build.
   452				a.Target = "DO NOT USE - " + a.Mode
   453				return true
   454			}
   455		}
   456	
   457		// Special case for building a main package: if the only thing we
   458		// want the package for is to link a binary, and the binary is
   459		// already up-to-date, then to avoid a rebuild, report the package
   460		// as up-to-date as well. See "Build IDs" comment above.
   461		// TODO(rsc): Rewrite this code to use a TryCache func on the link action.
   462		if target != "" && !cfg.BuildA && !b.NeedExport && a.Mode == "build" && len(a.triggers) == 1 && a.triggers[0].Mode == "link" {
   463			buildID, err := buildid.ReadFile(target)
   464			if err == nil {
   465				id := strings.Split(buildID, buildIDSeparator)
   466				if len(id) == 4 && id[1] == actionID {
   467					// Temporarily assume a.buildID is the package build ID
   468					// stored in the installed binary, and see if that makes
   469					// the upcoming link action ID a match. If so, report that
   470					// we built the package, safe in the knowledge that the
   471					// link step will not ask us for the actual package file.
   472					// Note that (*Builder).LinkAction arranged that all of
   473					// a.triggers[0]'s dependencies other than a are also
   474					// dependencies of a, so that we can be sure that,
   475					// other than a.buildID, b.linkActionID is only accessing
   476					// build IDs of completed actions.
   477					oldBuildID := a.buildID
   478					a.buildID = id[1] + buildIDSeparator + id[2]
   479					linkID := hashToString(b.linkActionID(a.triggers[0]))
   480					if id[0] == linkID {
   481						// Best effort attempt to display output from the compile and link steps.
   482						// If it doesn't work, it doesn't work: reusing the cached binary is more
   483						// important than reprinting diagnostic information.
   484						if c := cache.Default(); c != nil {
   485							showStdout(b, c, a.actionID, "stdout")      // compile output
   486							showStdout(b, c, a.actionID, "link-stdout") // link output
   487						}
   488	
   489						// Poison a.Target to catch uses later in the build.
   490						a.Target = "DO NOT USE - main build pseudo-cache Target"
   491						a.built = "DO NOT USE - main build pseudo-cache built"
   492						if a.json != nil {
   493							a.json.BuildID = a.buildID
   494						}
   495						return true
   496					}
   497					// Otherwise restore old build ID for main build.
   498					a.buildID = oldBuildID
   499				}
   500			}
   501		}
   502	
   503		// Special case for linking a test binary: if the only thing we
   504		// want the binary for is to run the test, and the test result is cached,
   505		// then to avoid the link step, report the link as up-to-date.
   506		// We avoid the nested build ID problem in the previous special case
   507		// by recording the test results in the cache under the action ID half.
   508		if !cfg.BuildA && len(a.triggers) == 1 && a.triggers[0].TryCache != nil && a.triggers[0].TryCache(b, a.triggers[0]) {
   509			// Best effort attempt to display output from the compile and link steps.
   510			// If it doesn't work, it doesn't work: reusing the test result is more
   511			// important than reprinting diagnostic information.
   512			if c := cache.Default(); c != nil {
   513				showStdout(b, c, a.Deps[0].actionID, "stdout")      // compile output
   514				showStdout(b, c, a.Deps[0].actionID, "link-stdout") // link output
   515			}
   516	
   517			// Poison a.Target to catch uses later in the build.
   518			a.Target = "DO NOT USE -  pseudo-cache Target"
   519			a.built = "DO NOT USE - pseudo-cache built"
   520			return true
   521		}
   522	
   523		if b.IsCmdList {
   524			// Invoked during go list to compute and record staleness.
   525			if p := a.Package; p != nil && !p.Stale {
   526				p.Stale = true
   527				if cfg.BuildA {
   528					p.StaleReason = "build -a flag in use"
   529				} else {
   530					p.StaleReason = "build ID mismatch"
   531					for _, p1 := range p.Internal.Imports {
   532						if p1.Stale && p1.StaleReason != "" {
   533							if strings.HasPrefix(p1.StaleReason, "stale dependency: ") {
   534								p.StaleReason = p1.StaleReason
   535								break
   536							}
   537							if strings.HasPrefix(p.StaleReason, "build ID mismatch") {
   538								p.StaleReason = "stale dependency: " + p1.ImportPath
   539							}
   540						}
   541					}
   542				}
   543			}
   544	
   545			// Fall through to update a.buildID from the build artifact cache,
   546			// which will affect the computation of buildIDs for targets
   547			// higher up in the dependency graph.
   548		}
   549	
   550		// Check the build artifact cache.
   551		// We treat hits in this cache as being "stale" for the purposes of go list
   552		// (in effect, "stale" means whether p.Target is up-to-date),
   553		// but we're still happy to use results from the build artifact cache.
   554		if c := cache.Default(); c != nil {
   555			if !cfg.BuildA {
   556				if file, _, err := c.GetFile(actionHash); err == nil {
   557					if buildID, err := buildid.ReadFile(file); err == nil {
   558						if err := showStdout(b, c, a.actionID, "stdout"); err == nil {
   559							a.built = file
   560							a.Target = "DO NOT USE - using cache"
   561							a.buildID = buildID
   562							if a.json != nil {
   563								a.json.BuildID = a.buildID
   564							}
   565							if p := a.Package; p != nil {
   566								// Clearer than explaining that something else is stale.
   567								p.StaleReason = "not installed but available in build cache"
   568							}
   569							return true
   570						}
   571					}
   572				}
   573			}
   574	
   575			// Begin saving output for later writing to cache.
   576			a.output = []byte{}
   577		}
   578	
   579		return false
   580	}
   581	
   582	func showStdout(b *Builder, c *cache.Cache, actionID cache.ActionID, key string) error {
   583		stdout, stdoutEntry, err := c.GetBytes(cache.Subkey(actionID, key))
   584		if err != nil {
   585			return err
   586		}
   587	
   588		if len(stdout) > 0 {
   589			if cfg.BuildX || cfg.BuildN {
   590				b.Showcmd("", "%s  # internal", joinUnambiguously(str.StringList("cat", c.OutputFile(stdoutEntry.OutputID))))
   591			}
   592			if !cfg.BuildN {
   593				b.Print(string(stdout))
   594			}
   595		}
   596		return nil
   597	}
   598	
   599	// flushOutput flushes the output being queued in a.
   600	func (b *Builder) flushOutput(a *Action) {
   601		b.Print(string(a.output))
   602		a.output = nil
   603	}
   604	
   605	// updateBuildID updates the build ID in the target written by action a.
   606	// It requires that useCache was called for action a and returned false,
   607	// and that the build was then carried out and given the temporary
   608	// a.buildID to record as the build ID in the resulting package or binary.
   609	// updateBuildID computes the final content ID and updates the build IDs
   610	// in the binary.
   611	//
   612	// Keep in sync with src/cmd/buildid/buildid.go
   613	func (b *Builder) updateBuildID(a *Action, target string, rewrite bool) error {
   614		if cfg.BuildX || cfg.BuildN {
   615			if rewrite {
   616				b.Showcmd("", "%s # internal", joinUnambiguously(str.StringList(base.Tool("buildid"), "-w", target)))
   617			}
   618			if cfg.BuildN {
   619				return nil
   620			}
   621		}
   622	
   623		// Cache output from compile/link, even if we don't do the rest.
   624		if c := cache.Default(); c != nil {
   625			switch a.Mode {
   626			case "build":
   627				c.PutBytes(cache.Subkey(a.actionID, "stdout"), a.output)
   628			case "link":
   629				// Even though we don't cache the binary, cache the linker text output.
   630				// We might notice that an installed binary is up-to-date but still
   631				// want to pretend to have run the linker.
   632				// Store it under the main package's action ID
   633				// to make it easier to find when that's all we have.
   634				for _, a1 := range a.Deps {
   635					if p1 := a1.Package; p1 != nil && p1.Name == "main" {
   636						c.PutBytes(cache.Subkey(a1.actionID, "link-stdout"), a.output)
   637						break
   638					}
   639				}
   640			}
   641		}
   642	
   643		// Find occurrences of old ID and compute new content-based ID.
   644		r, err := os.Open(target)
   645		if err != nil {
   646			return err
   647		}
   648		matches, hash, err := buildid.FindAndHash(r, a.buildID, 0)
   649		r.Close()
   650		if err != nil {
   651			return err
   652		}
   653		newID := a.buildID[:strings.LastIndex(a.buildID, buildIDSeparator)] + buildIDSeparator + hashToString(hash)
   654		if len(newID) != len(a.buildID) {
   655			return fmt.Errorf("internal error: build ID length mismatch %q vs %q", a.buildID, newID)
   656		}
   657	
   658		// Replace with new content-based ID.
   659		a.buildID = newID
   660		if a.json != nil {
   661			a.json.BuildID = a.buildID
   662		}
   663		if len(matches) == 0 {
   664			// Assume the user specified -buildid= to override what we were going to choose.
   665			return nil
   666		}
   667	
   668		if rewrite {
   669			w, err := os.OpenFile(target, os.O_WRONLY, 0)
   670			if err != nil {
   671				return err
   672			}
   673			err = buildid.Rewrite(w, matches, newID)
   674			if err != nil {
   675				w.Close()
   676				return err
   677			}
   678			if err := w.Close(); err != nil {
   679				return err
   680			}
   681		}
   682	
   683		// Cache package builds, but not binaries (link steps).
   684		// The expectation is that binaries are not reused
   685		// nearly as often as individual packages, and they're
   686		// much larger, so the cache-footprint-to-utility ratio
   687		// of binaries is much lower for binaries.
   688		// Not caching the link step also makes sure that repeated "go run" at least
   689		// always rerun the linker, so that they don't get too fast.
   690		// (We don't want people thinking go is a scripting language.)
   691		// Note also that if we start caching binaries, then we will
   692		// copy the binaries out of the cache to run them, and then
   693		// that will mean the go process is itself writing a binary
   694		// and then executing it, so we will need to defend against
   695		// ETXTBSY problems as discussed in exec.go and golang.org/issue/22220.
   696		if c := cache.Default(); c != nil && a.Mode == "build" {
   697			r, err := os.Open(target)
   698			if err == nil {
   699				if a.output == nil {
   700					panic("internal error: a.output not set")
   701				}
   702				outputID, _, err := c.Put(a.actionID, r)
   703				r.Close()
   704				if err == nil && cfg.BuildX {
   705					b.Showcmd("", "%s # internal", joinUnambiguously(str.StringList("cp", target, c.OutputFile(outputID))))
   706				}
   707				if b.NeedExport {
   708					if err != nil {
   709						return err
   710					}
   711					a.Package.Export = c.OutputFile(outputID)
   712				}
   713			}
   714		}
   715	
   716		return nil
   717	}
   718	

View as plain text