const buildIDSeparator … // actionID returns the action ID half of a build ID. func actionID(buildID string) string { … } // contentID returns the content ID half of a build ID. func contentID(buildID string) string { … } // toolID returns the unique ID to use for the current copy of the // named tool (asm, compile, cover, link). // // It is important that if the tool changes (for example a compiler bug is fixed // and the compiler reinstalled), toolID returns a different string, so that old // package archives look stale and are rebuilt (with the fixed compiler). // This suggests using a content hash of the tool binary, as stored in the build ID. // // Unfortunately, we can't just open the tool binary, because the tool might be // invoked via a wrapper program specified by -toolexec and we don't know // what the wrapper program does. In particular, we want "-toolexec toolstash" // to continue working: it does no good if "-toolexec toolstash" is executing a // stashed copy of the compiler but the go command is acting as if it will run // the standard copy of the compiler. The solution is to ask the tool binary to tell // us its own build ID using the "-V=full" flag now supported by all tools. // Then we know we're getting the build ID of the compiler that will actually run // during the build. (How does the compiler binary know its own content hash? // We store it there using updateBuildID after the standard link step.) // // A final twist is that we'd prefer to have reproducible builds for release toolchains. // It should be possible to cross-compile for Windows from either Linux or Mac // or Windows itself and produce the same binaries, bit for bit. If the tool ID, // which influences the action ID half of the build ID, is based on the content ID, // then the Linux compiler binary and Mac compiler binary will have different tool IDs // and therefore produce executables with different action IDs. // To avoid this problem, for releases we use the release version string instead // of the compiler binary's content hash. This assumes that all compilers built // on all different systems are semantically equivalent, which is of course only true // modulo bugs. (Producing the exact same executables also requires that the different // build setups agree on details like $GOROOT and file name paths, but at least the // tool IDs do not make it impossible.) func (b *Builder) toolID(name string) string { … } // gccToolID returns the unique ID to use for a tool that is invoked // by the GCC driver. This is used particularly for gccgo, but this can also // be used for gcc, g++, gfortran, etc.; those tools all use the GCC // driver under different names. The approach used here should also // work for sufficiently new versions of clang. Unlike toolID, the // name argument is the program to run. The language argument is the // type of input file as passed to the GCC driver's -x option. // // For these tools we have no -V=full option to dump the build ID, // but we can run the tool with -v -### to reliably get the compiler proper // and hash that. That will work in the presence of -toolexec. // // In order to get reproducible builds for released compilers, we // detect a released compiler by the absence of "experimental" in the // --version output, and in that case we just use the version string. // // gccToolID also returns the underlying executable for the compiler. // The caller assumes that stat of the exe can be used, combined with the id, // to detect changes in the underlying compiler. The returned exe can be empty, // which means to rely only on the id. func (b *Builder) gccToolID(name, language string) (id, exe string, err error) { … } // Check if assembler used by gccgo is GNU as. func assemblerIsGas() bool { … } // gccgoBuildIDFile creates an assembler file that records the // action's build ID in an SHF_EXCLUDE section for ELF files or // in a CSECT in XCOFF files. func (b *Builder) gccgoBuildIDFile(a *Action) (string, error) { … } // buildID returns the build ID found in the given file. // If no build ID is found, buildID returns the content hash of the file. func (b *Builder) buildID(file string) string { … } // fileHash returns the content hash of the named file. func (b *Builder) fileHash(file string) string { … } var counterCacheHit … var counterCacheMiss … var stdlibRecompiled … var stdlibRecompiledIncOnce … // useCache tries to satisfy the action a, which has action ID actionHash, // by using a cached result from an earlier build. At the moment, the only // cached result is the installed package or binary at target. // If useCache decides that the cache can be used, it sets a.buildID // and a.built for use by parent actions and then returns true. // Otherwise it sets a.buildID to a temporary build ID for use in the build // and returns false. When useCache returns false the expectation is that // the caller will build the target and then call updateBuildID to finish the // build ID computation. // When useCache returns false, it may have initiated buffering of output // during a's work. The caller should defer b.flushOutput(a), to make sure // that flushOutput is eventually called regardless of whether the action // succeeds. The flushOutput call must happen after updateBuildID. func (b *Builder) useCache(a *Action, actionHash cache.ActionID, target string, printOutput bool) (ok bool) { … } func showStdout(b *Builder, c cache.Cache, a *Action, key string) error { … } // flushOutput flushes the output being queued in a. func (b *Builder) flushOutput(a *Action) { … } // updateBuildID updates the build ID in the target written by action a. // It requires that useCache was called for action a and returned false, // and that the build was then carried out and given the temporary // a.buildID to record as the build ID in the resulting package or binary. // updateBuildID computes the final content ID and updates the build IDs // in the binary. // // Keep in sync with src/cmd/buildid/buildid.go func (b *Builder) updateBuildID(a *Action, target string, rewrite bool) error { … }