Is GOFLAGS respected when building via build.go?

Trying to make the syncthing package on Arch Linux reproducible.

I’m trying to figure why the build id is still random even with -buildid= in the ldflags.

Here is the relevant snippet:

export GOFLAGS="-buildmode=pie -trimpath '-ldflags=-linkmode=external -buildid=' -mod=readonly -modcacherw"
go run build.go -no-upgrade -version v${pkgver} build
go run build.go -no-upgrade -version v${pkgver} build strelaysrv
go run build.go -no-upgrade -version v${pkgver} build stdiscosrv

I don’t see why this wouldn’t work, except maybe if GOFLAGS or the ldflags within aren’t respected.

So, is the GOFLAGS environment variable respected when building (through build.go)?

Might be related

Only seems to be related in the way that both are trying to get reproducible builds.

I know why it’s not reproducible. It’s because of the build ID that the Go linker adds. I figured that out from diffoscope.

So I attempted to set -buildid= in ldflags in GOFLAGS to set the build ID to a blank string to improve reproducibility, but the thing is, it’s still using a random build ID that’s different on every build.

Using EXTRA_LDFLAGS instead of GOFLAGS seems to have gotten rid of the Go build ID or at least have made it reproducible.

1 Like

Did you also have to set SOURCE_DATE_EPOCH?

I wonder if I had this added for nothing as already found a fallback mechanism to determine from git state in build.go?

@marbens Thanks for the tip! :slight_smile: . I’ve found that also made the “libsyncthingnative.so” builds reproducible so F-Droid is happy.

export "EXTRA_LDFLAGS=-buildid="

ref: build-syncthing.py: Remove GO BUILDID (fixes #1383) · Catfriend1/syncthing-android@64986fe · GitHub

Not manually, because makerepropkg sets it for me. But the timestamps would be different if it wasn’t set, so yes.

1 Like

My impression is that buildid should be entirely deterministic and hence reproducible by default…

I don’t see why blanking the build ID helped out Catfriend1, but in my case it was just a red herring/domino effect. It was the unique temporary file paths created by Go that were stored in syncthing.debug that were the problem.

You might think what I’m saying conflicts with this:

But “it” here means the build ID, not the entire build.

Here is the relevant diffoscope output of comparing the build in extra-debug to a build I made myself:

│ │ │  String dump of section '.debug_line_str':
│ │ │ -  [     0]  ../../../../../../../tmp/go-build2698637364/b060
│ │ │ -  [    31]  ../../../../../../../tmp/go-build2698637364/b105
│ │ │ +  [     0]  ../../../../../../../tmp/go-build3466362441/b060
│ │ │ +  [    31]  ../../../../../../../tmp/go-build3466362441/b105

The go-build2698637364 folder name part is not reproducible.

I saw a change in the .gnu_debuglink in the non-debug package, and at first I thought it was another copy of the build ID was in there, but no, it was a CRC of the syncthing.debug file. See also loqs’ comment.

A workaround to make the entire build reproducible is to add to -s -buildid= to the ldflags, but that has the disadvantage of rendering the debug (syncthing-debug) package useless.

I was playing around with this and fail to accomplish reproducible builds even when it should be possible. That is to say, I get repeatable builds, but not the same binary from machine A and machine B with the same inputs.

For example, I create a linux/amd64 binary on my mac and on another linux box,

EXTRA_LDFLAGS="-s -buildid=" BUILDDEBUG=1 SOURCE_DATE_EPOCH=123456789 \
BUILD_USER=jb BUILD_HOST=reproducible GOTOOLCHAIN=go1.24.4 CGO_ENABLED=0 \
go run build.go -goos linux -goarch amd64 build ; sha256sum syncthing

imho they should be identical but they differ in lots of places – strings, and then offsets all over in the code. I’m not sure why. If anyone has looked into this previously and has any tips, I’m all ears. It’s not something prioritised, just something I got nerd sniped into due to the discussions going on…

(Deliberately leaving the new C-based SQLite stuff out here with CGO_ENABLED=0 to keep it simple in the first case…)

(Also staying away from the macos and windows builds as those have signatures and syso extensions and whatnot that complicate the picture)

Interesting. When looking at Syncthing-Fork Reproducibility Status I’ve finally gotten to a reproducible build via F-Droid’s build servers. That included setting the SOURCE_DATE_EPOCH & BUILD_ID=“” & BUILD_HOST=static_string & BUILD_USER=static_string