Go version
1.23.0 (Linux AMD64 and MacOS ARM64)
Output of go env in your module/workspace:
(rules_go does sanitize the env vars quite a bit, but here are the relevant variables)
CGO_ENABLED=1
GOARCH=amd64
GODEBUG=winsymlink=0
GOEXPERIMENT=nocoverageredesign
GOOS=linux
GOPATH=
GOROOT_FINAL=GOROOT
GOTOOLCHAIN=local
What did you do?
Context
https://github.com/bazelbuild/rules_go is an alternative way to build Go application using Bazel. It does so by manually instrumenting the go tools such as go tool compile or go tool link in hermetic Bazel sandboxes. The intermediate artifacts is then cached by Bazel for subsequent reuse.
What I did
For the Compile step, we are currently calling go tool compile with both -o <compile-output>.x and -linkobj <link-output>.a. The .x file is then used for subsequent downstream package compilations while the .a file will be used for downstream executable linkings.
Here is the relevant code: https://github.com/bazelbuild/rules_go/blob/8b8294b6359a9c0a89af968fb7092f6e7194f420/go/tools/builders/compilepkg.go#L494-L495
What did you see happen?
When I upgraded gorm.io/driver/mysql, a direct dependency of db.go, to a newer version, I noticed that the new db.x was changed.
The binary diff between 2 .x files are just a few bytes small
> diffoscope ~/Downloads/db*
--- /Users/sluongng/Downloads/db (1).x
+++ /Users/sluongng/Downloads/db.x
│┄ Command `'nm -s {}'` failed with exit code 1. Standard output:
│┄ /Library/Developer/CommandLineTools/usr/bin/nm: error: for the -s option: bad number of arguments (must be two arguments)
│┄ /Library/Developer/CommandLineTools/usr/bin/nm: error: a.out: No such file or directory
@@ -34357,15 +34357,15 @@
00086340: 0104 0203 0405 1000 8108 0082 0803 7b03 ..............{.
00086350: 4303 0103 0203 4103 8001 0324 034a 031b C.....A....$.J..
00086360: 03d4 0303 d503 03dc 0303 d803 0081 080f ................
00086370: 010d 0203 0405 0607 0809 0a0b 0c0d 0e0b ................
00086380: 0083 0800 8408 0343 034a 03d4 0303 d503 .......C.J......
00086390: 03d8 0303 da03 0341 0365 0083 080a 0108 .......A.e......
000863a0: 0203 0405 0607 0809 1200 8508 0086 0803 ................
-000863b0: bd01 03d8 0403 d403 037b 0341 0380 0103 .........{.A....
+000863b0: bd01 03d4 0303 d804 037b 0341 0380 0103 .........{.A....
000863c0: 4a03 ea04 03d5 0303 d504 03d8 0303 2403 J.............$.
000863d0: ef04 03d6 0403 dc03 0085 0811 010f 0203 ................
000863e0: 0405 0607 0809 0a0b 0c0d 0e0f 1026 0087 .............&..
000863f0: 0800 8808 0328 036f 0355 0358 035a 0302 .....(.o.U.X.Z..
00086400: 0341 0324 03d9 0403 dc04 03dd 0403 0103 .A.$............
00086410: 2303 5703 5903 2903 2503 5f03 6a03 4403 #.W.Y.).%._.j.D.
00086420: 6d03 4a03 1b03 e604 03e7 0403 d402 0380 m.J.............
@@ -113551,9 +113551,9 @@
001bb8e0: 0806 0103 0004 0000 0117 0004 0d00 0017 ................
001bb8f0: 0102 0100 0101 00d0 080f 0002 1700 0f01 ................
001bb900: 0100 1701 040b 0b01 00d0 0815 1701 0001 ................
001bb910: 0103 0000 0c3a 1700 0401 0105 0100 d008 .....:..........
001bb920: 2014 0100 d008 2300 0400 0100 d008 270a .....#.......'.
001bb930: 0100 d108 0400 0117 0004 0101 0500 0100 ................
001bb940: d208 0300 0a01 00d3 0803 0001 1401 00d3 ................
-001bb950: 080a 0004 0001 00d4 0802 8162 2d2d 5d3d ...........b--]=
-001bb960: fb45 0a24 240a .E.$$.
+001bb950: 080a 0004 0001 00d4 0802 907f c3c9 9764 ...............d
+001bb960: 77e3 0a24 240a w..$$.
exit 1
I tried using different tools I could find in x/tools to better understand the content of the file but it seems like the content is pretty consistent between 2 files
~/work/golang/pkg/mod/golang.org/x/tools@v0.25.0/go/gcexportdata> go run main.go -- ~/Downloads/db.x > /tmp/1.txt
~/work/golang/pkg/mod/golang.org/x/tools@v0.25.0/go/gcexportdata> go run main.go -- ~/Downloads/db\ \(1\).x > /tmp/2.txt
~/work/golang/pkg/mod/golang.org/x/tools@v0.25.0/go/gcexportdata> diff -u /tmp/{1,2}.txt | wc -l
0
I also used a modified version of x/tools/internal/gcimporter/main.go to analyze the file
func main() {
// Define the .x file input flag
filePath := flag.String("file", "", "Path to the .x file containing the export data")
flag.Parse()
if *filePath == "" {
log.Fatal("Please provide the path to a .x file using the -file flag")
}
// Create a new token.FileSet
fset := token.NewFileSet()
// Read the export data from the specified .x file
export, err := os.ReadFile(*filePath)
if err != nil {
log.Fatalf("can't read %q export data: %v", *filePath, err)
}
// Use gcexportdata to read the data and create a *types.Package
r, err := gcexportdata.NewReader(bytes.NewReader(export))
if err != nil {
log.Fatalf("reading export data %s: %v", *filePath, err)
}
tpkg1, err := gcexportdata.Read(r, fset, make(map[string]*types.Package), "")
if err != nil {
log.Fatalf("decoding export data: %v", err)
}
// Print the package information from the .x file
fmt.Println("# Read from export data:")
printPackage(tpkg1)
// Now reexport as indexed (deep) export data, and reimport.
var tpkg2 *types.Package
{
var out bytes.Buffer
if err := gcimporter.IExportData(&out, fset, tpkg1); err != nil {
log.Fatal(err)
}
var err error
_, tpkg2, err = gcimporter.IImportData(fset, make(map[string]*types.Package), out.Bytes(), tpkg1.Path())
if err != nil {
log.Fatal(err)
}
}
// Print the re-imported package information
fmt.Println("# After round-tripping through indexed export data:")
printPackage(tpkg2)
}
// printPackage prints out details of the package
func printPackage(pkg *types.Package) {
// ...taken from the original file
}
But still cannot see any diff between the 2 files.
Here are the 2 files attached.
db.tar.gz
What did you expect to see?
The expectation here is that if we were to compile package github.com/buildbuddy-io/buildbuddy/server/util/db/db.go, if there were no changes in exported data in db.go, then db.x will have reproducible content, and thus, downstream packages which depend on db.x will not need to recompile.
Go version
1.23.0 (Linux AMD64 and MacOS ARM64)
Output of
go envin your module/workspace:What did you do?
Context
https://github.com/bazelbuild/rules_go is an alternative way to build Go application using Bazel. It does so by manually instrumenting the go tools such as
go tool compileorgo tool linkin hermetic Bazel sandboxes. The intermediate artifacts is then cached by Bazel for subsequent reuse.What I did
For the Compile step, we are currently calling
go tool compilewith both-o <compile-output>.xand-linkobj <link-output>.a. The.xfile is then used for subsequent downstream package compilations while the.afile will be used for downstream executable linkings.Here is the relevant code: https://github.com/bazelbuild/rules_go/blob/8b8294b6359a9c0a89af968fb7092f6e7194f420/go/tools/builders/compilepkg.go#L494-L495
What did you see happen?
When I upgraded
gorm.io/driver/mysql, a direct dependency ofdb.go, to a newer version, I noticed that the newdb.xwas changed.The binary diff between 2
.xfiles are just a few bytes smallI tried using different tools I could find in
x/toolsto better understand the content of the file but it seems like the content is pretty consistent between 2 filesI also used a modified version of
x/tools/internal/gcimporter/main.goto analyze the fileBut still cannot see any diff between the 2 files.
Here are the 2 files attached.
db.tar.gz
What did you expect to see?
The expectation here is that if we were to compile package
github.com/buildbuddy-io/buildbuddy/server/util/db/db.go, if there were no changes in exported data indb.go, thendb.xwill have reproducible content, and thus, downstream packages which depend ondb.xwill not need to recompile.