2022-06-25
A limitation on what 'go install
' can install (as of Go 1.18)
As all people dealing with Go programs know or are learning, now that
Go is module-only, the way you install third
party Go programs from source is now 'go install <name>@latest
',
not the old way of 'go get <name>
'. However, this is not always
a completely smooth process that just works, because it's possible
to have Go programs in a state where they won't install this way.
Here's an illustration:
$ go install github.com/monsterxx03/gospy@latest go: downloading github.com/monsterxx03/gospy v0.5.0 go: github.com/monsterxx03/gospy@latest (in github.com/monsterxx03/gospy@v0.5.0): The go.mod file for the module providing named packages contains one or more replace directives. It must not contain directives that would cause it to be interpreted differently than if it were the main module.
What is happening here is that internally, gospy uses packages from its own repository (module) and one of them, github.com/monsterxx03/gospy/pkg/term, in turn uses github.com/gizak/termui/v3. However, the github.com/monsterxx03/gospy module has a replace directive for this termui module that changes it to github.com/monsterxx03/termui/v3.
If you clone the repository
and run 'go install
' inside it, everything works and you wind up
with a gospy
binary in your $HOME/go/bin. However, as we see here
'go install ...@latest
' works differently enough that the replace
directive causes this error. To fix the problem (ie, to build gospy
or any program like it), you must clone the repository and run 'go
install
' in the right place inside the repository.
(Alternately you can file bugs with the upstream to get them to fix
this, for example by dropping the replace
directive and directly using
the replacement in their code. But if the upstream is neglected, this
may not work very well.)
Unsurprisingly, there is a long standing but closed Go issue on
this 'go install
' behavior, cmd/go: go install cmd@version
errors out when module with main package has replace directive
#44840. This was
closed more than a year ago in 2021 with a 'working as designed',
and indeed the help for 'go install
' explicitly says about this
mode:
No module is considered the "main" module. If the module containing packages named on the command line has a go.mod file, it must not contain directives (replace and exclude) that would cause it to be interpreted differently than if it were the main module. The module must not require a higher version of itself.
(The apparent Go reference for why this exists is issue #40276, which I haven't tried to read through because I'm not that interested.)
Possible this will be changed someday, especially since it seems to keep coming up over and over again; issue #44840 contains quite the laundry list of projects that have hit this issue. Amusingly, one of the gopls releases hit this issue.
For now, if you're developing a Go program and you need to use
replace
directives in your go.mod
during development, you'll
have to do some extra work. One option is to strip the replace
directives out for releases (and you need to make releases, because
'go install ...@master' won't work because of your replace directives).
Another option is to switch to using Go workspaces
for local development and drop the go.mod replace directives entirely.
If you need to actually release a version of your program that uses
the replacement module, well, you're out of luck; you need to
actually change your code to explicitly use the replacement module
and drop the replace
directive.