I'd like to document my notes on go/packages. These are eitherthings that I need, or things that I have noticed.How are we supposed to use go/packages if we also want to support Go 1.10?go/packages depends on behavior in 'go list' that was added in Go1.11, and has no graceful fallback. This means we cannot usego/packages as long as Go 1.10 is still supported, which for mostpeople is until Go 1.12 is released. At the same time, supportfor Go modules only exists in go/packages, and I'd really want tosupport them once Go 1.11 is released.Are there any plans for a fallback to go/build in go/packages? Orare we expected to use both go/loader and go/packages and tolayer our own abstraction on top?
Mapping of generated files to source filesIn the context of 'go build', I need to know which Go files are theresult of cgo preprocessing. This is because of an interesting UI needin linters; quoting my own code comment:> // The //line compiler directive can be used to change the file> // name and line numbers associated with code. This can, for> // example, be used by code generation tools. The most prominent> // example is 'go tool cgo', which uses //line directives to refer> // back to the original source code.> //> // In the context of our linters, we need to treat these> // directives differently depending on context. For cgo files, we> // want to honour the directives, so that line numbers are> // adjusted correctly. For all other files, we want to ignore the> // directives, so that problems are reported at their actual> // position and not, for example, a yacc grammar file. This also> // affects the ignore mechanism, since it operates on the position> // information stored within problems. With this implementation, a> // user will ignore foo.go, not foo.yIn other words, it matters whether a file was generated during thebuild process, or if it was generated offline (e.g. with yacc)
The way flags and the environment are passed to go list, Bazel etcIt's been previously stated that there are no current plans for anabstraction of tags, GOOS, GOARCH etc across the different buildsystems. Instead, flags and the environment will be passed through.This focusses heavily on the interactive use case, but ignoresprogrammatic approaches. For example, I'd want to check differentGOOS/GOARCH/tag combinations for a specified package to intersect thereported problems.Even without a provided abstraction, I could synthesize the rightflags and environment myself, but that requires the option toexplicitly pass these in, versus being infered from os.Args and os.Env.
Additionally, relying on flags and the environment makes it necessarythat tool writers are aware of these flags, as not to accidentallyreuse them for something else. I reckon that the majority of us toolwriters are not familiar with all build systems, especially not Blaze,so I'd like the specific list of flags, and environment variables, ofeach build system to be documented as part of go/packages'sdocumentation.
x/tools APIs that currently assume go/loaderThere are some helper libraries, such as ssautil, that depend ongo/loader. What are the plans for making these work with go/packages?Timeframe, backwards compatibility, etc.Build tagsWhat's the story with build tags? Do Bazel and Blaze have thisconcept? Is something similar to build.Package.AllTags viable withgo/packages, or does this concept not span build systems?
I'd like to document my notes on go/packages. These are eitherthings that I need, or things that I have noticed.
How are we supposed to use go/packages if we also want to support Go 1.10?
go/packages depends on behavior in 'go list' that was added in Go
1.11, and has no graceful fallback. This means we cannot use
go/packages as long as Go 1.10 is still supported, which for most
people is until Go 1.12 is released. At the same time, support
for Go modules only exists in go/packages, and I'd really want to
support them once Go 1.11 is released.
Are there any plans for a fallback to go/build in go/packages? Or
are we expected to use both go/loader and go/packages and to
layer our own abstraction on top?
The first version of the CL introducing go/packages actually had aGeneratedBy map[string]string field. Why was it removed?
The way flags and the environment are passed to go list, Bazel etc
x/tools APIs that currently assume go/loaderThere are some helper libraries, such as ssautil, that depend ongo/loader. What are the plans for making these work with go/packages?Timeframe, backwards compatibility, etc.
What's the story with build tags? Do Bazel and Blaze have thisconcept? Is something similar to build.Package.AllTags viable withgo/packages, or does this concept not span build systems?
I'll follow up with more emails if I discover additional issues whileporting staticcheck to go/packages.
Hi Dominik,Thanks for this great feedback. We definitely want to work with you and other tool authors to make sure that go/packages supports what you need to do.On Thu, Jul 12, 2018 at 11:44 PM, Dominik Honnef <dominik...@gmail.com> wrote:I'd like to document my notes on go/packages. These are eitherthings that I need, or things that I have noticed.How are we supposed to use go/packages if we also want to support Go 1.10?go/packages depends on behavior in 'go list' that was added in Go1.11, and has no graceful fallback. This means we cannot usego/packages as long as Go 1.10 is still supported, which for mostpeople is until Go 1.12 is released. At the same time, supportfor Go modules only exists in go/packages, and I'd really want tosupport them once Go 1.11 is released.Are there any plans for a fallback to go/build in go/packages? Orare we expected to use both go/loader and go/packages and tolayer our own abstraction on top?Even go/build doesn't really work with Go 1.10 due to build caching. We should have had go/packages out for the Go 1.10 release, and we dropped the ball on that. My apologies. To correct the problem you identified, I think once we are sure that we understand what go/packages needs from cmd/go, we can backport those additional list flags to a Go 1.10 point release and then go/packages will work with it. Probably we won't reach back to Go 1.9 though.In the interim, I assume you've got something in golint already (probably a use of go/build) that kind of works, and I would say to keep that code around until go/packages handles all the Go versions you want to support. I know this is not even close to ideal.
Mapping of generated files to source filesIn the context of 'go build', I need to know which Go files are theresult of cgo preprocessing. This is because of an interesting UI needin linters; quoting my own code comment:> // The //line compiler directive can be used to change the file> // name and line numbers associated with code. This can, for> // example, be used by code generation tools. The most prominent> // example is 'go tool cgo', which uses //line directives to refer> // back to the original source code.> //> // In the context of our linters, we need to treat these> // directives differently depending on context. For cgo files, we> // want to honour the directives, so that line numbers are> // adjusted correctly. For all other files, we want to ignore the> // directives, so that problems are reported at their actual> // position and not, for example, a yacc grammar file. This also> // affects the ignore mechanism, since it operates on the position> // information stored within problems. With this implementation, a> // user will ignore foo.go, not foo.yIn other words, it matters whether a file was generated during thebuild process, or if it was generated offline (e.g. with yacc)I am not sure I agree with this premise. If I make a Go mistake in a yacc rule, I want golint to report the source line I can edit - the one in the yacc file - not some line in generated code that I have to manually back out to the corresponding line in an editable file. If the yacc //line annotations are somehow wrong, we should fix them. Some tools choose to show both, likex.y:123[y.tab.go:2345]: problem hereI believe we dropped GeneratedBy precisely because the //line information should be used instead.
The way flags and the environment are passed to go list, Bazel etcIt's been previously stated that there are no current plans for anabstraction of tags, GOOS, GOARCH etc across the different buildsystems. Instead, flags and the environment will be passed through.This focusses heavily on the interactive use case, but ignoresprogrammatic approaches. For example, I'd want to check differentGOOS/GOARCH/tag combinations for a specified package to intersect thereported problems.Even without a provided abstraction, I could synthesize the rightflags and environment myself, but that requires the option toexplicitly pass these in, versus being infered from os.Args and os.Env.Additionally, relying on flags and the environment makes it necessarythat tool writers are aware of these flags, as not to accidentallyreuse them for something else. I reckon that the majority of us toolwriters are not familiar with all build systems, especially not Blaze,so I'd like the specific list of flags, and environment variables, ofeach build system to be documented as part of go/packages'sdocumentation.The idea here was that tools shouldn't in general be trying to imagine all possible worlds. In general there are going to be environment variables and command-line flags that affect the way the underlying build system interprets source code, so there should be a way to pass through arbitrary additional environment variables and flags. I think it would suffice for tools using go/packages to have flags like -env X=Y and -buildflags '-whatever' and just pass those through to the go/packages. Note that this matters not just for GOOS/GOARCH/build tags but also for plain configuration about where to find the code or how to build it (necessary to get export data), like CC, CC_FOR_linux_amd64, CGO_CFLAGS, GOCACHE, GOPATH, -compiler, -ldflags, -getmode, -pkgdir (hopefully not anymore), -toolexec, and so on. Trying to abstract away GOOS/GOARCH/build tags does not do anything for all the other important adjustments users might need to make. That's why we said let's just have a simple pass-through. Users running golint on code in build system X are already familiar with the environment variables and command-line flags necessary for build system X. The tools just need to be able to let the users pass them through.
I do sympathize with the fact that go/packages is not helping to answer the question "what are all the possible build configurations for this code?" but honestly the underlying build system may not even know. Maybe in a future version we could add something to suggest guesses about interesting alternate configurations to try (different GOOS, GOARCH etc). But right now I'd just like to get back to being able to load packages at all (see note above about Go 1.10 breaking everything).
x/tools APIs that currently assume go/loaderThere are some helper libraries, such as ssautil, that depend ongo/loader. What are the plans for making these work with go/packages?Timeframe, backwards compatibility, etc.Build tagsWhat's the story with build tags? Do Bazel and Blaze have thisconcept? Is something similar to build.Package.AllTags viable withgo/packages, or does this concept not span build systems?Bazel/Blaze don't have a concept of build tags in general - they are unique to Go. But the Go support for these systems does apply build tags to winnow the file list before invoking the compiler, same as the go command.I think it would be reasonable to put AllTags into the go list output and expose it in go/packages. Want to file an issue for that?
Go 1.10 broke relatively few things, actually. Most third party tools use go/loader, loading code entirely from source. They aren't affected by changes to GOPATH/pkg or caching. Go 1.11, however, will have a lot more people trying out Go modules, and I'd rather not see the issues of the vendor experiment repeat themself (people using vendor, tools not understanding vendor.)
Backporting the cmd/go changes would be great. Supporting Go 1.10 and Go 1.11 ought to be enough.
Generated code and linting is a nuanced problem. Yes, if x.y includes inlined Go code, it'd be better to use the positions in x.y. However, code generation generates a lot of supporting code that isn't controlled by the user, which may, however, still contain issues. In the case of staticcheck, these would likely be bugs in the code generator itself. My users have found it easier to understand when these errors were reported in the .go file. The input to a code generator may not even include any Go code at all.
Of course, considering -buildflags, this may not actually need any form of abstraction. The user could just specify a list of values for -buildflags, and tools could be oblivious to what the different values mean.
As an aside, "what are all the possible build configurations for this code" isn't actually a useful question to ask. Once we consider transitive dependencies, we pull in the runtime, and the number of build configurations explodes.I should be clear and say that I don't expect go/packages to solve all my issues in Go 1.11. I'd be happy to see these solved in 1.12 or later. Some of these things, however, could be complicated by unfortunate API choices now, especially if go/packages makes its way into the standard library, subject to backwards compatibility rules. At the same time, not all of my expectations make sense, or can be solved differently, so I appreciate your input.
-j