go: cmd/go: varying GOROOT_FINAL invalidates precompiled C dependencies of "net"

What version of Go are you using (go version)?

$ ~/go/bin/go1.18beta1 version
go version go1.18beta1 linux/amd64

Does this issue reproduce with the latest release?

It reproduces with the latest beta of 1.18. However, it is fine with go 1.17.5!

What operating system and processor architecture are you using (go env)?

go env Output
$ ~/go/bin/go1.18beta1 version
go version go1.18beta1 linux/amd64
I [unix@go118 foo]$ ~/go/bin/go1.18beta1 env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/unix/.cache/go-build"
GOENV="/home/unix/.config/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/home/unix/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/unix/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/home/unix/sdk/go1.18beta1"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/home/unix/sdk/go1.18beta1/pkg/tool/linux_amd64"
GOVCS=""
GOVERSION="go1.18beta1"
GCCGO="gccgo"
GOAMD64="v1"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/home/unix/foo/go.mod"
GOWORK=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build2623523050=/tmp/go-build -gno-record-gcc-switches"

What did you do?

I tried to compile this program:

$ cat main.go 
package main

import (
        t "github.com/lyderic/tools"
)

func main() {
        t.Magentaln("FOO")
}

What did you expect to see?

I expected it to compile with go 1.18 as it compiles with go 1.17

What did you see instead?

I got the following error:

$ ~/go/bin/go1.18beta1 build
# runtime/cgo
cgo: C compiler "gcc" not found: exec: "gcc": executable file not found in $PATH

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 28 (20 by maintainers)

Commits related to this issue

Most upvoted comments

Is that all that’s needed here for 1.18, or is there anything more?

I’m going to run it by @rsc and @matloob later today, but my opinion at the moment is that CL 380503 is sufficient to address this issue for 1.18.

If I understand correctly, once the accepted proposal #47257 is implemented, this issue (and others like it) will stop being relevant. So that is the long-term fix for this problem, and we just need a short-term one here.

One possible resolution might be to build the release with -trimpath, so that users could themselves build with -trimpath to avoid staleness. (That change would be up to @golang/release to decide.)

It seems that it’s not tenable to release with -trimpath unless that becomes the default in cmd/go as well, since we want to avoid staleness with the most common configuration, which is using defaults. Either way, this isn’t relevant post-#47257, and there are better short-term alternatives below.

Another possible resolution is to declare that you need a C compiler in order to build with CGO_ENABLED=1. (That seems eminently reasonable to me, but it would be a change in policy.)

Needing a C compiler to build with CGO_ENABLED=1 seems reasonable to me too, and I don’t see how things can work otherwise. This seems inevitable post-#47257, and before then, there are better short-term alternatives below.

A third possible resolution is to declare that if you want to use a precompiled Go distribution without a C compiler, you must either set GOROOT_FINAL explicitly to the path used when building that distribution, or install that precompiled distribution to exactly its preordained GOROOT_FINAL location.

As a more immediate workaround (but IMO clearly a hack), I suppose we could change golang.org/dl/internal/version to explicitly set GOROOT_FINAL itself before invoking the go command. 😅

(@golang/release: how do y’all feel about that?)

This seems like the best option to improve things before #47257 is resolved. Its only downside is it increases complexity in golang.org/dl/internal/version, but oh well.

Now that os.Executable() exists and cmd/go finds itself and infers its GOROOT from that, I wonder if GOROOT_FINAL has outlived its usefulness and can be removed, which would help remove the complexity/issues it adds. If so, this workaround in golang.org/dl/internal/version can eventually go away.

When -trimpath is not set, the value of GOROOT_FINAL ends up in the compiled C dependencies. Because of that, we now include GOROOT_FINAL (which defaults to GOROOT) in the cache key for those dependencies.

Looking at #48319 the issues to be the DW_AT_comp_dir debugging entry which describes the compilation directory. But I don’t understand why we can’t use -fdebug-prefix-map to avoid this.

Ah. This is probably from https://go.dev/cl/353352 (which fixed #48319).

When -trimpath is not set, the value of GOROOT_FINAL ends up in the compiled C dependencies. Because of that, we now include GOROOT_FINAL (which defaults to GOROOT) in the cache key for those dependencies.

x/build/cmd/release explicitly sets GOROOT_FINAL to /usr/local/go, but go1.18beta1 doesn’t actually end up installing itself there: instead, it installs to $HOME/sdk/go1.18beta1. When you run go1.18beta1 env GOROOT it notices the mismatch and reports the real GOROOT instead, but the built C artifacts are stale because they have embedded the path /usr/local/go instead of $HOME/sdk/go1.18beta1.

So when you run go build, it tries to rebuild the C dependencies of the net package (see https://github.com/golang/go/issues/47257#issuecomment-882618111), and because CGO_ENABLED=1 it requires gcc to do so.

Indeed, setting GOROOT_FINAL to match the value set by x/build/cmd/release causes the precompiled .a file to no longer be stale and allows the build to succeed:

$ GOROOT_FINAL=/usr/local/go go1.18beta1 build -o /dev/null .

cc @bcmills as this is perhaps related to #47215

Also cc @golang/release for if there is anything related to building the release.

Marking as release blocker as it is a regression and was previously a release blocker.

Please note that we had the same regression issue when we moved from 1.16 to 1.17: https://github.com/golang/go/issues/47215

It had been fixed then.