-
Notifications
You must be signed in to change notification settings - Fork 18k
cmd/go: should Go version be part of the go.mod specification? #23969
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
A module may contain many packages, each of which may contain many files. Specifying the version in go.mod forces all packages and files within a module to use a consistent version. I'm not sure if that's an argument for or against. |
Suppose the go.mod file specifies "go1.10", does that only mean the language as specified in Go1.10 or does that include other stuff? A tangential question is whether the standard library will still be versioned along with the language specification? Clearly some packages like |
I don't understand what benefit this would add over build tags. What would this be used for? |
Like @dsnet said, it depends what you mean. From bug reports, I've noticed plenty of folks run older Go versions (> 1 version behind the current version) which causes issues when the package uses newer Go features. Build tags work, but things get hairy and you end up with little wrapper functions for the newer features—it all feels very un-Go-like. Personally, I've resorted to only supporting the most current Go version. But that isn't super user-friendly because sometimes they have legitimate reasons for staying on previous versions. It'd be nice to force things to be pinned to some minimum Go version, even if it's less than the current version. |
It should, but make it optional so that people are not forced to make a choice when they don't know better. Having Go version be part of go.mod would help give better error message and more importantly, save time for people who run "go get example.com/foo/bar" while running an older version of vgo-capable Go - "go get" can fail earlier rather than later. |
As a point of reference clojure leiningen specifies the language/stdlib version in the build tool. |
The |
@drewblas there are packages that cannot be built with early go stdlibs, the only distinction between the stdlib/compiler and other libs is one is harder to fetch dynamically, conceptually they seem the same and the stdlib/compiler is just another minimal version dependency. |
Ignoring Go2 for a moment, there are three cases that I can think of for specifying a minimum version of Go
(Aside: 1 and 2 could largely be determined mechanically, though the go version build tags would complicate that somewhat) I don't see any point in distinguishing between these cases because they all come bundled together regardless. In all of these cases, because of the compatibility guarantees of Go, it's not important which version of Go is used, as long as it's greater than or equal to the one specified. Unlike other requirements, if it's unspecified, it's implicitly Go 1.0.0. If vgo sees that module X requires Go 1.15 but it's being run on Go 1.14 it would just have to stop and say "requires Go 1.15". If you're trying to use a module, the build could just short circuit, let you know you need at least such and such version of Go, and save you some time. If you're developing a module and making sure it works with newer versions of dependencies it could skip any that require newer versions of Go, let you know you need at least such and such version of Go to continue testing, and save you some time. Having this could help with the transition to Go 2 because modules could explicitly declare that they're 2.0.0+ only. I think it should be included but it would mean something different than other requires (perhaps it should have its own syntax to reflect this). |
I am still unsure what this would solve that isn't solved with build tags. @jimmyfrasche, you listed three cases: language changes, new objects, and behavior changes. In the first two cases, the code will not compile. In the third case, build tags would allow a package to require a particular version. If lower versions need to be forbidden, that can be accomplished with tagged file that deliberately fails to compile: //+build !go1.10
package foo
// If you are seeing a compiler error here, please upgrade to Go 1.10 before
// using foo. It relies on the changes to Barbaz introduced in that version.
This package requires go1.10. To my eyes, the only benefit is that it would preempt a compiler error with a more descriptive error message if an imported package relied upon newer Go features. Is that all? |
Yes, but that's not nothing. The benefit of having it in one well defined place is good for humans and computers. If you're looking a module you can see at a glance what the requirements are (and vgo could compute the transitive requirements so that even if all your code only requires Go 1.5+ but something you import requires Go 1.15+ it would list Go 1.15). Also, imagine it's 10 years from now and for whatever reason you're stuck with Go 2.1.3 at work. You need a package for Oauth4 so you search for it on godoc.org. There are many results but you're going to have to download them one by one just to see if they compile. If the minimum transitive Go version is part of the machine-readable module specification, godoc.org could let you include that constraint in the search and filter out the results that definitely won't work. |
Also writing "require go 1.11" or what have you is just a lot easier than making a file like that so it's more likely people will document the constraint, especially if there's a tool that automatically derives it in cases 1 and 2 so they'd only ever need to think about it in case 3. |
@jimmyfrasche Okay, I'm glad we've clarified, then, that this is about ease of use, not about missing capabilities. Of course, you are right that its not nothing. I still lean against this proposal though, just on the generic grounds that it expands the spec for go.mod files without enough benefit. It requires special syntax and handling, and makes me wonder why the file doesn't also specify things GOOS and GOARCH constraints, like "this only works for linux" or "this only works for amd64." |
@spenczar There is a thing that this would enable, that build-tags don't, which is verifiable/reproducible builds. What I kind of dislike about this is how it interacts with MVS (which is kind of hypocritical, given that the same arguments also apply to anything else). If a module resolves its toolchain version to go1.4, but I have go1.10 installed, this would seem to imply that we should use go1.4. But I really don't want to use go1.4 to build anything anymore :) But if we drop/circumvent MVS for the Go version itself, we also loose almost any benefit of specifying it at all (except maybe better error messages). |
I don't see any reason why the Go standard library (and implicitly language version) should be treated any different than any other package when it comes to versioning. |
This would indeed help with reproducible builds, akin to what Bazel does. However, when does one stop? Will we have to also declare the gcc/clang version used to compile cgo packages? @Merovius raises a valid point. Will the user be required to have and use an older version of the stdlib/language, possibly with performance/security repercussion, due to the package management? |
Another thought is that if you admit the standard library (not necessarily the compiler), then it becomes possible to replace standard library packages with local variants. I often wish to temporarily modify code in the standard library for debugging purposes. Editing the code directly in $GOROOT feels a little unclean when you could potentially do something like:
The runtime implications for that might be a bit too far reaching though. Another possibility is to split the stdlib itself into multiple modules, allowing, for example, replacement of the "encoding" module without replacing runtime too. |
What @rogpeppe described would also help the dynamic linking story, especially when it comes to packaging Go binaries in distributions. (Not that I want or care about dynamic linking, just making an observation.) |
No. The Go version is a problem here:
For the first case build tags should be used, for the second case a fork should be made. Otherwise we don’t need to know the Go version. |
It could be argued that Obviously we can't remove |
The build tags functionality is a superset of module versioning. It allows you to provide different implementations of the same API depending on what Go version you are running on. I disagree that they're a mistake. |
Go version is mostly significant to a module in terms of the public types and identifiers of the standard library for a given release, so my reasoning lead to the conclusion most of the standard library should actually be a set of modules and modules should support GOOS/GOARCH metadata, perhaps in the form of build constraint blocks. Edit: I'm now at the conclusion modules need to support the full set of build constraints to express differences in dependencies that can be expressed currently with build constraints which I think implies go_*.mod |
The Go-on-App-Engine team would like to express support for including go version in Currently we use We prefer to use Go-idiomatic patterns where possible, and adding support for a minimum-go-version to We support the addition of a minimum version of Go in |
The addition of the go version seems compelling to me because:
If the version is added, I think the Go tool should insert the value automatically, but it's less clear to me what value it should insert. Should it use the current version of the Go tool? Or will that cause packages to be unnecessarily unavailable to users of older versions of Go? Could the tool make an intelligent decision about what version of Go the module requires? Or would that be more effort than it's worth? |
What level of the version would it include? Would it include: major, minor and patch (e.g. Major, minor, patch: Allows packages to declare they need a version that has a fix. |
It should, as the version of go compiler is part of the prerequisites for building a program successfully. This will somehow confuse users indeed, In case the user use the wrong version of go, |
Change https://golang.org/cl/125940 mentions this issue: |
Change https://golang.org/cl/164878 mentions this issue: |
The 'go version' statement was added during Go 1.11 development in CL 125940. That CL added the GoVersion field to modinfo.ModulePublic struct, but did not document it in cmd/go documentation. This was consistent with the CL description, which stated "We aren't planning to use this or advertise it much yet". CL 147281, applied during Go 1.12 development, was a change to start adding the 'go version' statement when initializing go.mod. The 'go version' statement is now being used, and it has been documented in the Go 1.12 release notes at https://golang.org/doc/go1.12#modules. It's now due time to documement the GoVersion field in cmd/go as well. Keep the Error field bottom-most, both because it makes sense not to place it in the middle of other fields, and for consistency with the field order in struct Package, where the Error information is located at the very bottom. Regenerate alldocs.go by running mkalldocs.sh. Updates #28221 Updates #23969 Change-Id: Iaf43a0da4f6a2489d861092a1d4e002a532952cb Reviewed-on: https://go-review.googlesource.com/c/go/+/164878 Run-TryBot: Dmitri Shuralyov <dmitshur@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Bryan C. Mills <bcmills@google.com>
If not, why not?
The text was updated successfully, but these errors were encountered: