Toolstash provides a way to save, run, and restore a known good copy of the Go toolchain and to compare the object files generated by two toolchains.
toolstash [-n] [-v] save [tool...] toolstash [-n] [-v] restore [tool...] toolstash [-n] [-v] [-t] go run x.go toolstash [-n] [-v] [-t] [-cmp] compile x.go
The toolstash command manages a “stashed” copy of the Go toolchain kept in $GOROOT/pkg/toolstash. In this case, the toolchain means the tools available with the 'go tool' command as well as the go, godoc, and gofmt binaries.
The command “toolstash save”, typically run when the toolchain is known to be working, copies the toolchain from its installed location to the toolstash directory. Its inverse, “toolchain restore”, typically run when the toolchain is known to be broken, copies the toolchain from the toolstash directory back to the installed locations. If additional arguments are given, the save or restore applies only to the named tools. Otherwise, it applies to all tools.
Otherwise, toolstash's arguments should be a command line beginning with the name of a toolchain binary, which may be a short name like compile or a complete path to an installed binary. Toolstash runs the command line using the stashed copy of the binary instead of the installed one.
The -n flag causes toolstash to print the commands that would be executed but not execute them. The combination -n -cmp shows the two commands that would be compared and then exits successfully. A real -cmp run might run additional commands for diagnosis of an output mismatch.
The -v flag causes toolstash to print the commands being executed.
The -t flag causes toolstash to print the time elapsed during while the command ran.
The -cmp flag causes toolstash to run both the installed and the stashed copy of an assembler or compiler and check that they produce identical object files. If not, toolstash reports the mismatch and exits with a failure status. As part of reporting the mismatch, toolstash reinvokes the command with the -S flag and identifies the first divergence in the assembly output. If the command is a Go compiler, toolstash also determines whether the difference is triggered by optimization passes. On failure, toolstash leaves additional information in files named similarly to the default output file. If the compilation would normally produce a file x.6, the output from the stashed tool is left in x.6.stash and the debugging traces are left in x.6.log and x.6.stash.log.
The -cmp flag is a no-op when the command line is not invoking an assembler or compiler.
For example, when working on code cleanup that should not affect compiler output, toolstash can be used to compare the old and new compiler output:
toolstash save <edit compiler sources> go tool dist install cmd/compile # install compiler only toolstash -cmp compile x.go
Go Command Integration
The go command accepts a -toolexec flag that specifies a program to use to run the build tools.
To build with the stashed tools:
go build -toolexec toolstash x.go
To build with the stashed go command and the stashed tools:
toolstash go build -toolexec toolstash x.go
To verify that code cleanup in the compilers does not make any changes to the objects being generated for the entire tree:
# Build working tree and save tools. ./make.bash toolstash save <edit compiler sources> # Install new tools, but do not rebuild the rest of tree, # since the compilers might generate buggy code. go tool dist install cmd/compile # Check that new tools behave identically to saved tools. go build -toolexec 'toolstash -cmp' -a std # If not, restore, in order to keep working on Go code. toolstash restore
The Go tools write the current Go version to object files, and (outside release branches) that version includes the hash and time stamp of the most recent Git commit. Functionally equivalent compilers built at different Git versions may produce object files that differ only in the recorded version. Toolstash ignores version mismatches when comparing object files, but the standard tools will refuse to compile or link together packages with different object versions.
For the full build in the final example above to work, both the stashed and the installed tools must use the same version string. One way to ensure this is not to commit any of the changes being tested, so that the Git HEAD hash is the same for both builds. A more robust way to force the tools to have the same version string is to write a $GOROOT/VERSION file, which overrides the Git-based version computation:
echo devel >$GOROOT/VERSION
The version can be arbitrary text, but to pass all.bash's API check, it must contain the substring “devel”. The VERSION file must be created before building either version of the toolchain.