golang / go

The Go programming language
https://go.dev
BSD 3-Clause "New" or "Revised" License
122.98k stars 17.54k forks source link

cmd/go: `go clean` doesn't remove binaries installed by `go install module@version` #65687

Open Rican7 opened 7 months ago

Rican7 commented 7 months ago

Go version

go version go1.21.7 darwin/arm64

Output of go env in your module/workspace:

GO111MODULE=''
GOARCH='arm64'
GOBIN=''
GOCACHE='/Users/tnsuarez/Library/Caches/go-build'
GOENV='/Users/tnsuarez/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMODCACHE='/Users/tnsuarez/Development/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='darwin'
GOPATH='/Users/tnsuarez/Development/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/opt/homebrew/Cellar/go/1.21.7/libexec'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/opt/homebrew/Cellar/go/1.21.7/libexec/pkg/tool/darwin_arm64'
GOVCS=''
GOVERSION='go1.21.7'
GCCGO='gccgo'
AR='ar'
CC='cc'
CXX='c++'
CGO_ENABLED='1'
GOMOD='/dev/null'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/gd/n720k8816r7c0bnv44nb9crw0000gp/T/go-build4251940867=/tmp/go-build -gno-record-gcc-switches -fno-common'

What did you do?

I installed a binary via go install and then was unable to use the same tool to uninstall/clean the installed package binary.

I had to manually remove the binary via rm.

What did you see happen?

~ $ go install github.com/Rican7/define@latest
~ $ which define
/Users/tnsuarez/Development/go/bin/define
~ $ go clean github.com/Rican7/define@latest
package github.com/Rican7/define@latest: can only use path@version syntax with 'go get' and 'go install' in module-aware mode
~ $ go clean github.com/Rican7/define
no required module provides package github.com/Rican7/define: go.mod file not found in current directory or any parent directory; see 'go help modules'
~ $ go clean -i github.com/Rican7/define
no required module provides package github.com/Rican7/define: go.mod file not found in current directory or any parent directory; see 'go help modules'
~ $ go clean -i github.com/Rican7/define@latest
package github.com/Rican7/define@latest: can only use path@version syntax with 'go get' and 'go install' in module-aware mode

What did you expect to see?

I expected that a go clean github.com/Rican7/define would remove the package binary that it had installed.

It would likely make sense to have the command maybe be go clean -i github.com/Rican7/define@latest, but maybe the version suffix is awkward?

The version suffix is what changes the behavior of go install to no longer consider the current module (go.mod) defined version, so there would be symmetry there, but also having to specify the version to uninstall might be awkward.

I couldn't find anything related to this, beyond maybe #50261.

Also, a quick search shows that there seems to be a lot of confusion around this very (lack of?) behavior:

gophun commented 7 months ago

It's just a binary file. Why insist in invoking a special command when you can just rm it?

Rican7 commented 7 months ago

@gophun For the same reason go clean -i even exists in the first place.

The fact that it doesn't work seemed like a bug to me.

In a practical sense:

  1. Because the install location of the binary is dependent on numerous environmental factors and it's install location (which isn't reported at install time) is otherwise not known to the user. So manually performing the deletion could be non-obvious to a user.
  2. For completeness. Allowing the install, but not the deletion, is asymmetric.
  3. Having the same environment that determines that location to install and remove allows for easier scripting and automation via the same defined environmental controls.
  4. Again, because the command already exists, and just doesn't seem to be working for this case.
rittneje commented 7 months ago

The docs for go install say:

Executables (main packages) are installed to the directory named by the GOBIN environment variable, which defaults to $GOPATH/bin or $HOME/go/bin if the GOPATH environment variable is not set.

And the docs for go clean say:

Clean removes object files from package source directories. The go command builds most objects in a temporary directory, so go clean is mainly concerned with object files left by other tools or by manual invocations of go build.

If a package argument is given or the -i or -r flag is set, clean removes the following files from each of the source directories corresponding to the import paths:

_obj/ old object directory, left from Makefiles _test/ old test directory, left from Makefiles _testmain.go old gotest file, left from Makefiles test.out old test log, left from Makefiles build.out old test log, left from Makefiles *.[568ao] object files, left from Makefiles

DIR(.exe) from go build DIR.test(.exe) from go test -c MAINFILE(.exe) from go build MAINFILE.go *.so from SWIG

So it would seem that go clean is functioning properly, since the executable is not in the source directory.

Also, I don't think something like go clean github.com/Rican7/define@latest should work, given that go install doesn't seem to record the version it installed anywhere, and I think it would be confusing if go clean github.com/Rican7/define@v1.0.0 were to delete what go install github.com/Rican7/define@v1.0.1 installs.

I can see the argument for go clean github.com/Rican7/define working, but then the lack of symmetry could be confusing.

thanm commented 7 months ago

@bcmills @matloob per owners.

matloob commented 7 months ago

I think this might be a documentation issue. The words (what 'go install' would create) applied to what go install created before the new go install module@version functionality was added. We should maybe remove that phrase.

meling commented 7 months ago

I guess it would not hurt if go install reported the path in which the binary was installed. Users new to Go often don’t know about GOPATH/bin.

Rican7 commented 7 months ago

I guess it would not hurt if go install reported the path in which the binary was installed.

That could be helpful, yea.

Users new to Go often don’t know about GOPATH/bin.

Especially since setting GOPATH is no longer required with module mode.

thanm commented 7 months ago

I guess it would not hurt if go install reported the path in which the binary was installed.

One easy but slightly hacky way to do this: install it, then immediately run install -n:

$ go install golang.org/x/debug/cmd/viewcore@latest
$ go install -n golang.org/x/debug/cmd/viewcore@latest
touch /Users/thanm/go1/bin/viewcore
$ 
bcmills commented 7 months ago

I don't think something like go clean github.com/Rican7/define@latest should work, given that go install doesn't seem to record the version it installed anywhere, and I think it would be confusing if go clean github.com/Rican7/define@v1.0.0 were to delete what go install github.com/Rican7/define@v1.0.1 installs.

The installed version should be recorded in a metadata block in the binary (compare go version -m). So I think it would actually be feasible to make go clean -i github.com/Rican7/define@v1.0.1 only clean v1.0.1 — but I also think that would be a confusing UX.

That said, I do think it would be reasonable for go clean -i github.com/Rican7/define@v1.0.1 to unconditionally remove the binary at the location that would be installed by go install github.com/Rican7/define@v1.0.1, even if the version doesn't match. That's not fundamentally different from what should happen if, say, you were to run go install github.com/Rican7/define@v1.0.1 before that call to go clean -i.

bcmills commented 7 months ago

I guess it would not hurt if go install reported the path in which the binary was installed. Users new to Go often don’t know about GOPATH/bin.

See: