golang / go

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

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

Closed stamblerre closed 8 months ago

stamblerre commented 5 years ago

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.

glerchundi commented 5 years ago

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 commented 5 years ago

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

sjdweb commented 5 years ago

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

christiankiely commented 5 years ago

@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 commented 5 years ago

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:

We also need:

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 commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago

@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. https://github.com/golang/go/issues/29202#issuecomment-488469829

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 commented 5 years ago

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

zchee commented 5 years ago

@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 commented 5 years ago

@evanphx FYI, ref for your issue root cause: https://github.com/golang/go/issues/31705

zchee commented 5 years ago

@stamblerre Can we close this issue by below CL?

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

stamblerre commented 5 years ago

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.

zchee commented 5 years ago

@stamblerre I see, totally agreed.

davidhsingyuchen commented 5 years ago

@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"
    },

This incurs an Unknown configuration setting error in VS Code 1.36.1 with gopls v0.1.3, and instead I have to use the configuration below to make it work.

"go.toolsEnvVars": {
    "GOFLAGS": "-tags=test"
},
stamblerre commented 5 years ago

I recently opened https://github.com/golang/go/issues/33389 to get a better sense of the use cases for build tags. Please add your own use cases if they are not yet listed in this issue - it will help us better understand how to solve this issue.

stanleynguyen commented 4 years ago

+1 I'm having issues with VSCode flagging my different function implementations for different versions of go as duplications

KBassford commented 4 years ago

RS: You probably already know this, gopls does not handle mutually exclusive build tags at all. I have one file with '// +build debug', and an almost identical file in the same folder with '// +build !debug'. gopls flagged every constant, variable and function as being redefined between the two files. Good luck with the fix.

mvdan commented 4 years ago

https://github.com/golang/go/issues/39005 might be of interest to the people following this thread. It's an attempt at defining a build-system-agnostic list of build configurations for a project, in a way that can be consumed by many tools like staticcheck and gopls.

lucaswxp commented 4 years ago

For anyone having issues with wire + VSCode flagging auto generated function implementations as duplications, edit the project settings json and put this:

"go.buildTags": "wireinject"

This solved for me. Have a downside that now your wire.go file will not have any code completion and validation, but you can just uncomment the build flag when developing to get those back.

jackielii commented 4 years ago

in coc.nvim+coc-go, for wire inject, add:

  "go.goplsOptions": {
    "buildFlags": ["-tags=wireinject"]
  },
cyberbeast commented 4 years ago

Is there any timeline on releasing support for build tags in gopls, and consequently in vscode?

stamblerre commented 4 years ago

We will not add any new behavior to improve build tag handling before gopls/v1.0. For now, the best way to work with gopls and build tags is by using the configurations suggested above.

cyberbeast commented 4 years ago

@stamblerre What's the exact property that needs to be set in settings.json (vscode) to switch build tags?

stamblerre commented 4 years ago

I would suggest the work-around described in this comment: https://github.com/golang/go/issues/29202#issuecomment-488469829.

ericwq commented 4 years ago

for a variable being overwritten based on build constraints, I tried every suggested config in this thread. it doesn't work.

here is the overwritten case:

the propose is to return the internal/lsp/lsprpc/autostart_posix.go , the behavior of 'definition' query should consider the 'build tags', in this case, it is "// +build darwin dragonfly freebsd linux netbsd openbsd solaris".

any suggestion on this case? @stamblerre

clouedoc commented 4 years ago

To be able to edit windows-specific golang code ("xxx_windows.go") requiring windows-only golang packages on my OSX machine, I had to add this to my VSCode settings:

"go.toolsEnvVars": {
    "GOOS": "windows"
 }
Merrit commented 3 years ago
"gopls.env": {
        "GOFLAGS": "-tags=test"
    },

Where does this go? I tried settings.json and I get:

Unknown Configuration Setting

stamblerre commented 3 years ago

@Merrit: That's correct--the unknown configuration setting message is a warning, but it will still work.

Merrit commented 3 years ago

I see. Am I misunderstanding the purpose then?

For example in one of my _linux.go files I am still getting No packages found for open file, unable to access functions from this file, etc.

If I add // +build windows everything works as expected.

stamblerre commented 3 years ago

Are you working on Windows / adding the Windows tag to the Linux file? OS-specific tags are detected based on your OS, so unless you set GOFLAGS to -tags=linux, you won't get editor features working for the _linux.go files.

Merrit commented 3 years ago

Yes, currently working on Windows & having issues working with *_linux.go files. Not sure why I would want to be unable to reference or work on Linux files just because I am on Windows, or vice versa.

Setting this in settings.json seems to have it working, so far:

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

Thank you!

Merrit commented 3 years ago

Spoke too soon.

Editor features work fine, but it then fails to run / debug.

undefined: funcFromLinuxFile

I tried adding "buildFlags": "-tags=linux" to launch.json, but that makes it try to compile the linux version which fails quite spectacularly. :)

Also tried "buildFlags": "-tags=test", "buildFlags": "-tags=!linux" and "buildFlags": "-tags '!linux test'".

Only solution so far is to just stop naming things with the _os.go scheme, which I was given to understand was the preference for conditional compilation. FYI, naming the files featureLinux.go and leaving it be works just fine. Adding // +build linux to that same file breaks things the same as using the _linux.go naming scheme.

stamblerre commented 3 years ago

Not sure why I would want to be unable to reference or work on Linux files just because I am on Windows, or vice versa.

This is a current limitation of gopls, and this issue is tracking improving that behavior.

I tried adding "buildFlags": "-tags=linux" to launch.json, but that makes it try to compile the linux version which fails quite spectacularly. :)

You won't actually be able to run code meant for Linux on Windows--my suggestion was just to get editor features for development purposes.

Merrit commented 3 years ago

Right, I don't want to run or compile anything meant for a different OS. Just mentioning what I had tried to resolve my issue.

Ideally what I would like is:

  1. Name files by OS to take advantage of conditional compilation, ie feature_windows.go & feature_linux.go
  2. Editor features still work, eg it recognizes the packages, doesn't complain about undeclared variables or functions, etc just because those variables/functions are in a feature_linux.go when I am on Windows, or vice versa.
  3. Still be able to use VSCode's run/debug features (for the code related to the OS I am on).

Currently I can only get 2 & 3 on my list by not naming files by OS nor using the // +build _____ -- which I understand means no conditional compilation.

FrankReh commented 3 years ago

Wanted to be able to use go generate in a directory to get a go run gen.go called and found gopls doesn't seem to work on the gen.go file because of a mix of build flags in other local files. Can I work around this by invoking vim on gen.go with an environment variable set to something for the benefit of gopls or is moving the gen.go into its own directory my best option?

gopls definition does work from a file that has an ignore build tag, but not if a plain go file in the same directory exists.

$ cat -n gen.go 
     1  // +build ignore
     2  
     3  package main
     4  
     5  import (
     6      "fmt"
     7  )
     8  
     9  func main() {
    10      fmt.Println("Hello world!")
    11  }
$ gopls definition gen.go:10:10
gopls: /<snip>/gen.go:10:10: no packages returned: packages.Load error
$ rm config.go
$ gopls definition gen.go:10:10
/<snip>/go1.15.5/src/fmt/print.go:273:6-13: defined here as func fmt.Println<snip>
stamblerre commented 3 years ago

The command line doesn't really give you much configurability, but you can set the GOFLAGS environment variable before running the gopls command. Otherwise, you can configure gopls.buildFlags or GOFLAGS as described in the examples above.

VikingPingvin commented 3 years ago

I am facing this issue as well, appending settings.json with "gopls.env": { "GOFLAGS": "-tags=debug" },does not resolve the issue for me.

In my case I have two files thermal.go and thermal_mock.go, with build tags // +build !debug and // +build debug respectively, so I can mock target HW specific functions.

My biggest issue is that Intellisense completely stopped working in the _mock version (as in, worked the other day, next day poof) , and the gopls.env in settings.json didn't help either.

Go 1.15.5, gopls v0.6.0

Any follow up on the issue?

Edit: Loss of intellisense can be temporarily fixed by removing the build tag instruction, saving and readding it.

stamblerre commented 3 years ago

In my case I have two files thermal.go and thermal_mock.go, with build tags // +build !debug and // +build debug respectively, so I can mock target HW specific functions.

My biggest issue is that Intellisense completely stopped working in the _mock version (as in, worked the other day, next day poof) , and the gopls.env in settings.json didn't help either.

Would be happy to help investigate this, but please file a new issue with the following information: https://github.com/golang/tools/blob/master/gopls/doc/troubleshooting.md.

dylankb commented 3 years ago

Note for VSCode settings.json in case it's helpful. A bit after upgrading gopls to 0.6.2 I noticed some No packages found for open file... errors. I was using GOFLAGS to list build tags.

"gopls": {
  "env": {
    "GOFLAGS": "-tags=<tag1>,<tag2>"
  },
},

Switching to the go.buildFlags configuration below seemed to solve the problem. I no longer use GOFLAGS.

go.buildFlags: "<tag1>,<tag2>",

There might have also been a vscode-go release around the time I bumped my gopls version, but not sure.

kevlar700 commented 3 years ago

The workarounds here didn't work for me.

I found env GOOS="openbsd" code

Worked for me at the editor instance level

P.s. Irrelevant tip: if you also set HISTFILE and open a module folder after "code" then you can have dedicated terminal history and a project folder opened together.

serezlan commented 3 years ago

Hello,

I'm facing this issue on linux using emacs. Have tried suggestions above and the one that is working is to delete and re-adding build tag line. I also tried using VS Code with the same result.

jalseth commented 3 years ago

I was finally able to get this working in VS Code by using the following and restarting VS Code.

{
    "go.buildTags": "tag1,tag2"
}
laeo commented 3 years ago

I think gopls shouldn't care about build constraints, or it only cares about the file I'm editing, which I call "constraint aware".

calebstewart commented 2 years ago

This and #49657 were the only relevant results for my internet searches, so, for historical reasons, my solution for emacs is to use setenv at the global scope and modify GOFLAGS. This isn't perfect but has worked for me:

(setenv "GOFLAGS" "-tags=linux,windows")

Is there a plan to make something like this a permanent in gopls? Is there some problem with just specifying multiple platform tags by default? It hasn't seemed to cause any problems for me specifically. It doesn't make much sense to me that gopls would care about the platform, and simply failing for platform-tagged files breaks the entire workflow of developing cross-platform applications. This is almost certainly an undocumented bug in gopls, unless I missed some documentation some where (which I'd be happy if someone shared :smile:).

hyangah commented 2 years ago

@calebstewart That's insteresting. gopls heavily depends on go list and other go commands to parse go code, and by your setting -tags=linux,windows, the flag is passed to all the go commands gopls uses. If you see gopls and so go commands work with -tags=linux,windows, that's by accident, not intentional.

And, more puzzling thing is that AFAIK platform (os/arch) are special and go commands select them based on GOOS/GOARCH environment variables, not based on the -tags flag in most cases. So, I am not sure what's working and what's not working with specifying OSes using -tags.

hsiaosiyuan0 commented 2 years ago

for vscode users, update your PROJECT_ROOT/.vscode/settings.json like below may resolve this problem:

{
  "go.toolsEnvVars": {
    "GOOS": "js",
    "GOARCH": "wasm"
  }
}