Skip to content
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

x/tools/gopls: improve handling for build tags #29202

Closed
stamblerre opened this issue Dec 12, 2018 · 83 comments
Closed

x/tools/gopls: improve handling for build tags #29202

stamblerre opened this issue Dec 12, 2018 · 83 comments
Assignees
Labels
FeatureRequest gopls Issues related to the Go language server, gopls. NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. Tools This label describes issues relating to any tools in the x/tools repository.
Milestone

Comments

@stamblerre
Copy link
Contributor

stamblerre commented Dec 12, 2018

We need to handle build tags correctly in the LSP. If a user is on Linux, they should still see completions, diagnostics, etc. for files tagged with any other OS.

@gopherbot gopherbot added this to the Unreleased milestone Dec 12, 2018
@stamblerre stamblerre self-assigned this Dec 19, 2018
@stamblerre stamblerre added gopls Issues related to the Go language server, gopls. and removed gopls Issues related to the Go language server, gopls. labels Mar 12, 2019
@glerchundi
Copy link

Bitten by this also, lost an hour trying to figure out why after migrating to gopls basic functionalities didn't work...no import-organize, no format, no nothing. Afterwards I found that it's was working properly for everything except for files with build tags as mentioned in #31286.

@myitcv
Copy link
Member

myitcv commented Apr 28, 2019

I'm wondering whether this is the right issue to become the umbrella issue for our long-running discussion about context?

@sjdweb
Copy link

sjdweb commented May 1, 2019

This is killing me with integration tests that have build tags. Any workarounds known?

@christiankiely
Copy link

@sjdweb current workaround I'm using is to set the GOFLAGS environment variable. This is the configuration I'm using in VS Code:

"gopls.env": {
        "GOFLAGS": "-tags=test"
    },

@myitcv
Copy link
Member

myitcv commented May 2, 2019

This was brought up on Tuesday's golang-tools call.

@christiankiely - the "editor level" GOFLAGS approach your describe works in some cases, but not all. Because setting the flag in that way sets the flag for all modules and packages, i.e. for the gopls instance used by the editor.

In discussions we've had thus far it seems for a complete solution we need to be able to define the sets of valid build flags at various levels:

  • Editor level (i.e. throughout editor)
  • Module level
  • Package (tree) level
  • File level

We also need:

  • the ability to specify, from within the editor, which build flags are in operation, because for a given file/package/module there might be multiple valid options
  • the ability to then pass this choice on a per request basis to gopls

The conclusion on Tuesday's call was broadly that there are other higher priority issues right now; add to that, a solution that doesn't involve LSP spec changes isn't exactly jumping out at us.

cc @ianthehat @dominikh

@breml
Copy link
Contributor

breml commented May 11, 2019

Bitten by this as well. I use build tags to distinguish between normal builds and debug builds. Now VSCode reports logError redeclared in this block error and the file is constantly marked as having errors.

@gracenoah
Copy link

Another populate use case: when using wire, there are two copies of the initializers:

  1. The template in a file with a build tag that never gets compiled
  2. The real one generated by wire with no build tags

gopls highlights them as duplicate definitions because it ignores build tags.

Is there a short term workaround in this simpler use case? Some way to make gopls ignore files with the wireinject build tag?

@stamblerre
Copy link
Contributor Author

Thank you for sharing this info. We realize that this is an issue that affects a lot of people, so we're trying to come up with a viable solution. It's a complicated problem, but to start, I think we can try to disable diagnostics on files that would be excluded given your GOOS/GOARCH.

@evanphx
Copy link
Contributor

evanphx commented May 25, 2019

As a data point, I'm experiencing this today in any package that picks up os/user because it uses build tags to decide how to wire in OS user lookup. In my case, it's that analysis fails because cgoLookupHost isn't defined as files aren't being evaluated.

I understand that you'd like for gopls to allow for reading and analysis of files intended for a different GOOS that the one that the editor is on, but given how packages are structured, you'd having to perform analysis on the matrix of build tags. That seems.... time consuming at best. Plumbing through a default tag set seems like an easy way to at least get folks moving again.

I'm happy to look into how to do that if need be! Please just let me know.

@evanphx
Copy link
Contributor

evanphx commented May 25, 2019

Looks like it's also firing from net looking like:

/usr/local/Cellar/go/1.12/libexec/src/net/lookup_unix.go:81:24: undeclared name: cgoLookupHost

@zchee
Copy link
Contributor

zchee commented May 26, 2019

@evanphx I also found this issue.
The workaround is launch gopls use CGO_ENABLED=0 or something. Note that might be vscode can't that workaround AFAIK. Also not solved if set CGO_ENABLED=0 to env. Because it is late to place.
#29202 (comment)

Now I develop the client side for IDE written in Go, it's implemented set some env to launch gopls binary using set to exec.Command.Env. I did solve the use it.
Anyway, We should including CGO_ENABLED=0 to gopls send Build info.

@evanphx
Copy link
Contributor

evanphx commented May 26, 2019

@zchee Ah! That makes a lot of sense, I'll start it with CGO_ENABLED=0 and see how it works.

@zchee
Copy link
Contributor

zchee commented May 27, 2019

@evanphx
Also, I was send CL (but I think still not user friendly) for support build tags.
https://go-review.googlesource.com/c/tools/+/178782
It solved if your code needs any build tags such as purego, static.

@zchee
Copy link
Contributor

zchee commented May 27, 2019

@evanphx
FYI, ref for your issue root cause:
#31705

@zchee
Copy link
Contributor

zchee commented Jun 4, 2019

@stamblerre
Can we close this issue by below CL?

https://go-review.googlesource.com/c/tools/+/178782

@stamblerre
Copy link
Contributor Author

I think we will still need special handling for build tags so that users don't need special configurations to get gopls features in files tagged for different build systems.

gopherbot pushed a commit to golang/tools that referenced this issue Jan 2, 2024
This CL leverages the new zero-config implementation to add support for
dynamic build tags (golang/go#29202). While this CL is large, the gist
of the change is relatively simple:
 - in bestView, check that the view GOOS/GOARCH actually matches the
   file
 - in defineView, loop through supported ports to find one that matches
   the file, and apply the necessary GOOS= GOARCH= env overlay
 - detect that views must be re-selected whenever a build constraint
   changes

Everything else in the CL is supporting / refactoring around this minor
adjustment to view selection. Notably, the logic to check whether a file
matches a port (using go/build) required some care, because the go/build
API is cumbersome and not particularly efficient. We therefore check
ports as little as possible, and trim the file content that is passed
into build.Context.MatchFile. Earlier attempts at this change were
simpler, because they simply matched all available ports all the time,
but this had significant cost (around a millisecond overhead added to
every operation, including change processing).

However, the good news is that with the logic as it is, I believe it is
safe to support all available ports, because we only loop through this
list when checking views, an infrequent operation.

For golang/go#57979
For golang/go#29202

Change-Id: Ib654e18038dda74164b57d51b2d5274f91a1306d
Reviewed-on: https://go-review.googlesource.com/c/tools/+/551897
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Alan Donovan <adonovan@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Run-TryBot: Robert Findley <rfindley@google.com>
@findleyr
Copy link
Contributor

Hi folks! I'm working on landing improvements to build tag support for gopls@v0.15.0. Specifically, https://go.dev/cl/551897 added logic so that when you open a file constrained by GOOS or GOARCH, gopls will try to find a GOOS/GOARCH combination that includes that file, and will start tracking a build for the new configuration. This means that most gopls functionality will Just Work when, for example, you open up a *_darwin.go file while working on linux.

If you're interested in being an early tester, you can install the first prerelease:

go install golang.org/x/tools/gopls@v0.15.0-pre.1

In the workaround suggested above (#29202 (comment)), gopls loads only one build configuration, modified to include files for both linux and windows, but this will often lead to spurious build errors for duplicate declarations. Now that it supports multiplexing across multiple builds (#57979), gopls can finally support multiple GOOS/GOARCH combinations simultaneously. If you are testing out the prerelease, please remove any buildFlags configuration from your settings.

If you encounter problems, please let me know and revert to gopls@latest. This is a rather large change to the way gopls operates, and so will require some time to polish before it can be released. Among the potential new bugs, we may encounter new performance edge cases, or possibly conflicting diagnostics from different builds (though I have tried to mitigate both). Additionally, we may require additional UX improvements to make it clearer which build configuration is being used for a given file.

One other area where we could improve build tag support is by automatically handling go version constraints (e.g. //go:build !go1.20). After consulting with the team and Go command experts such as @bcmills and @matloob, we think it is too complex and potentially problematic to support dynamic builds at different Go versions. We also expect that this is an order of magnitude less important than GOOS/GOARCH support -- let us know if you disagree.

@meling
Copy link

meling commented Jan 11, 2024

@findleyr re:

@mxygem can you say more about how you're using build tags in test files? Which build tags, and how are you using them?

I may have a similar scenario to what @mxygem described here.

I have a number of exercises for students, where I provide a template to ensure that our tests are compatible with a student submitted solution. Here is an example:

Template provided to students (rot13.go):

//go:build !solution   // This line is actually removed before we provide it to students.

package cipher

type rot13Reader struct{ r io.Reader }

func (r rot13Reader) Read(p []byte) (n int, err error) {
	return 0, nil
}

Test provided to students (rot13_test.go):

func TestRot13(t *testing.T) { ... }

We also have a solution file (rot13_sol.go) that we don't provide to students:

//go:build solution

package cipher

These files are in the same folder like this:

cipher/
├── rot13.go
├── rot13_sol.go
└── rot13_test.go

From the command line I can test that the template version fails because the solution tag is not set:

% go test
--- FAIL: TestRot13 (0.00s)
    rot13_test.go:33: rot13.Read("Go programming is fun."): (-want +got):
...

And similarly, that the rot13_sol.go version passes the tests because the solution tag is set:

% go test -tags solution
PASS
ok  	dat520/lab1/gointro/cipher	0.280s

This approach is convenient because I can keep the template, test, and the solution in same folder.

With VSCode, I can do:

    "gopls": {
        "buildFlags": [
            "-tags=solution"
        ]
    },

To enable compilation of the rot13_sol.go file, and switch to -tags=!solution to enable compilation of the rot13.go file.

However, I'm unaware of any solution to avoid the error:

No packages found for open file ... /lab1/gointro/cipher/rot13.go.
This file may be excluded due to its build tags; try adding "-tags=<build tag>" to your gopls "buildFlags" configuration
See the documentation for more information on working with build tags:
https://github.com/golang/tools/blob/master/gopls/doc/settings.md#buildflags-string.

Apologies for the long message, and the fact that I didn't take the time to read through the entire issue history. Anyway, I hope this can serve as another example of how tags are being used; I suspect others use it similarly.

@findleyr
Copy link
Contributor

@meling thanks for explaining your use-case. Indeed, there's no way to avoid the error you report in whichever file is excluded by the current configured build tags. In general, there's nothing we can do about this automatically, since it's not possible to guess the set(s) of build tags to use.

Theoretically, since we now support multiple builds per folder, we could accept something like

    "gopls": {
        "buildFlags": [
            [],
            ["-tags=solution"]
        ]
    },

I.e., overload the "buildFlags" setting to accept multiple sets of flags. I'd really like to avoid this if possible, because it complicates the configuration significantly. I wonder how many others would be interested in configurations like this.

@inliquid
Copy link

@findleyr is there any point in using "gopls.BuildFlags" instead of "go.buildTags"?

@findleyr
Copy link
Contributor

@inliquid I believe "gopls.buildFlags" affects only gopls, whereas "build.buildFlags" affects all functionality (gopls, test, linter, debugger, etc.), which may or may not be desirable. "go.buildTags" is an older configuration mechanism that was generalized to "buildFlags".

@meling
Copy link

meling commented Jan 11, 2024

@findleyr thanks for your reply.

For my "simple" use-case where there is a binary choice, would it be possible to recognize the tag of the current editor window and just remove the error from source files whose tag is the negation? And then switch if the active editor window changes to the negated tag.

It is not clear to me if dynamically "guessing" based on active editor window is possible.

@findleyr
Copy link
Contributor

@meling you're right, we could do that if there is only one build tag, though probably not anything more complicated. For example, we do have support for tags that are used to isolate scripts, such as //go:build ignore, via the "standaloneTags" configuration, and that looks for files with a build constraint consisting of a single tag.

@meling
Copy link

meling commented Jan 12, 2024

@findleyr thanks for the pointer. Would it be possible to lift the restriction: Gopls considers a file to be a standalone main file if and only if it has package name "main" ?? Maybe then my use case would work. Or would it be preferable to support my use case via a separate configuration option?

Either way I appreciate the work you are doing to improve the plugin and gopls. Thank you so much!

@arnevm123
Copy link

@findleyr I am trying out the new gopls and it seems to work perfectly.
The only thing that I'm wondering is if I'm using go to definition on a function that is both declared in a file with a Windows and Linux build flag it goes to the Linux implementation (which makes sense, I'm on Linux).
Is it meant to show both definitions (similar to go to implementation) or is this expected behaviour?

@findleyr
Copy link
Contributor

findleyr commented Jan 12, 2024

@arnevm123 thanks for testing, and regarding your question this is expected behavior. For each open file there is one "default" build, which is first and foremost the default build on your system, followed by the first match from here. This ordering is somewhat arbitrary, though first class ports are preferred. We do need a fixed order for determinism.

I'd prefer if we could return all definitions, but unfortunately for technical reasons this is really intractable. Note that a file without any build constraints matches all ports. We can't feasibly return every single definition across any port, because this would be much more expensive for little value in the common case. The toolchain (go list and the type checker) only operates on one build context at a time, so to answer questions about multiple build contexts we need to do additional work for each. It is infeasible to do 50x the work (there are around 50 supported ports), so we'd have to try to be smarter by e.g. looking at ignored files in the same directory as the definition to see if there are other declarations of the same symbol. At that point our heuristic is liable to be flaky and/or buggy, and is tightly coupled to the go command (taking us even farther from supporting other build systems). Still, it may be worth pursuing at some point.

For now, I think we should keep it simple. We're going from gopls completely not working to mostly working, albeit with some surprises. Of course we'd greatly appreciate any alternative suggestions, or ways that we could make the current behavior more transparent.

@findleyr
Copy link
Contributor

@meling

Gopls considers a file to be a standalone main file if and only if it has package name "main" ?? Maybe then my use case would work.

I think there's a fundamental difference between scripts, for which there may be many in the same directory, each consisting of a single file, and your use case, for which you want to have at most two versions of your package in the directory. In your use case, I assume you want gopls to resolve packages declarations contained in other unconstrained files in the same directory (for example a common.go file that is shared by both the solution and !solution builds).

@meling
Copy link

meling commented Jan 12, 2024

@meling

Gopls considers a file to be a standalone main file if and only if it has package name "main" ?? Maybe then my use case would work.

I think there's a fundamental difference between scripts, for which there may be many in the same directory, each consisting of a single file, and your use case, for which you want to have at most two versions of your package in the directory. In your use case, I assume you want gopls to resolve packages declarations contained in other unconstrained files in the same directory (for example a common.go file that is shared by both the solution and !solution builds).

Yes, we do have common unconstrained file!

@findleyr
Copy link
Contributor

@meling I think the problem is that we could perhaps make //go:build solution work automatically by creating a new build with -tags=solution overlaid, but then users may be confused when opening a file tagged //go:build (solution || experiment) doesn't work automatically, and we don't want to get into the business of solving constraint equations. The special handling for //go:build ignore, is justifiable because it's an extremely common pattern for scripts.

If enough users want this, we can reconsider. But for now, for the same reasons as #29202 (comment), let's keep it simple.

(and thanks again for the feedback!)

@findleyr
Copy link
Contributor

gopls@v0.15.0 will be released soon (likely next week) containing the improvements discussed above. If you're interested to try it out, please install the latest prerelease, which is the release candidate:

go install golang.org/x/tools/gopls@v0.15.0-pre.4

(this differs from -pre.3 by just one minor CL)

With this release, I think working on multiple GOOS and GOARCH combinations is much better, though still not perfect. To follow up, I filed:

This is in addition to @meling's:

I think we're ready to close this issue in favor of the more specific follow-up items above. Please try out the release and let us know if you have feedback, either by commenting on or 👍'ing one of the issues above, or filing a new issue describing the improvement you'd like to see (or bug you observe). Thanks!

@findleyr
Copy link
Contributor

To clarify one point: the issue title "improve handling for build tags" could certainly be interpreted as also referring to better support for custom build tags, in addition to multiple GOOS/GOARCH. However, the issue description specifically mentions "completions, diagnostics, etc. for files tagged with any other OS", and I think we've achieved that in the v0.15.0 release.

I think better support for user-defined tags is covered by these two issues specifically:

Those issues both address ways that gopls could support working on multiple sets of user-defined tags. In #65089, it is proposed that gopls could automatically detect when there is one tag that separates files in a package. In #65757, it is proposed that users could explicitly configure multiple sets of tags.

stephenfin added a commit to stephenfin/gophercloud that referenced this issue Mar 19, 2024
We have a number of tests that we do not appear to be running in CI. In
addition, we are not running go vet with all suitable build tags
resulting in some tests being skipped [1]. Combined, this has resulted
in a number of tests that have not been fully updated to use context.
While we will address the root cause elsewhere, we can fix the missed
updates here.

You can validate this fix by overriding GOFLAGS, as outlined in [1]. For
example, from the root directory:

  GOFLAGS="-tags=acceptance" go vet ./...

[1] golang/go#29202

Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
stephenfin added a commit to stephenfin/gophercloud that referenced this issue Mar 19, 2024
We have a number of tests that we do not appear to be running in CI. In
addition, we are not running go vet with all suitable build tags
resulting in some tests being skipped [1]. Combined, this has resulted
in a number of tests that have not been fully updated to use context.
While we will address the root cause elsewhere, we can fix the missed
updates here.

You can validate this fix by overriding GOFLAGS, as outlined in [1]. For
example, from the root directory:

  GOFLAGS="-tags=acceptance" go vet ./...

[1] golang/go#29202

Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
FeatureRequest gopls Issues related to the Go language server, gopls. NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. Tools This label describes issues relating to any tools in the x/tools repository.
Projects
None yet
Development

No branches or pull requests