Skip to content

x/tools/gopls: use cases for build tags #33389

Closed

Description

@stamblerre

What use cases do users have for build tags? Please post your own examples.

To start:

  • OS-specific
  • Certain package trees need specific build tags (e.g. WASM)
  • Some companies use build tags for things like integration testing, different environments etc.
  • +build ignore file main files

Activity

  1. added this to the Unreleased milestone on Jul 31, 2019
  2. added
    goplsIssues related to the Go language server, gopls.
    on Jul 31, 2019
  3. added
    WaitingForInfoIssue is not actionable because of missing required information, which needs to be provided.
    on Jul 31, 2019
  4. vcabbage commented on Jul 31, 2019

    @vcabbage
    Member

    I have used build tags in a library so that it only depends on stdlib by default but usage of 3rd party libraries like github.com/pkg/errors can be enabled via build tags.

  5. aykevl commented on Jul 31, 2019

    @aykevl

    In TinyGo (which should eventually support IDEs), we use build tags to select various things:

    • chip vendor (stm32, nrf), chip (stm32f103xx, nrf52840), and board names (bluepill, pca10056)
    • which GC style is being used (currently gc.none, gc.leaking, and gc.conservative)

    To manage all this, special JSON files are used that can be selected with a single compiler flag (similar to GCC spec files).

    More uses may be added in the future.

  6. niaow commented on Jul 31, 2019

    @niaow

    Build tags are commonly used for go generate - so that generators can be stored alongside the package in which they are used

  7. muirdm commented on Jul 31, 2019

    @muirdm

    We have used build tags mainly in two places:

    • OS specific system calls
    • New Go language or standard library features (e.g. // +build go1.13 in one file that implements something using features/code only available in 1.13, and // +build !go1.13 in the other file with the 1.12 implementation).

    That being said, we don't use build tags very much.

  8. seebs commented on Jul 31, 2019

    @seebs
    Contributor

    Two files, named something like paranoia.go and nop_paranoia.go, which both define the same const bool, one setting it to true and one to false. Then elsewhere in the code:

    if paranoia {
            // extra testing
    }
    

    This way, we can run with the extra testing sometimes, but omit expensive tests entirely at compile time. (Some of the tests are inside hot loops, so being able to get rid of even the cost of the branch is actually relevant.)

    Have also used them for what would typically be #defines in C; functionality changes which are innately program-wide and must be resolved at compile time because array sizes (for instance) depend on them.

    I've seen a package (hajimehoshi/ebiten) ship with a number of examples in subdirectories, marked with +build example so they don't get built by default but are available for users to experiment with.

  9. gdey commented on Jul 31, 2019

    @gdey

    I used build tags to add enhanced debugging that goes into tight loops. Using an if statement much like @seebs .

    One file id debug.go and the other debug_flg.go

    They each define a debug constant.

    then if I need to debug tight loops I'll eighter flip the const in debug.go from false to true (this is what I've mostly been doing this, because usually just want a single package) or build with the debug flag, which will cause debug_flg to compile and not debug.go to compile.

    This being said, this is probably not the best way to do this.

  10. neild commented on Jul 31, 2019

    @neild
    Contributor

    In google.golang.org/protobuf:

    • // +build ignore to exclude an expensive integration test from go test ./....
    • // +build purego to select a slower implementation of various operations that doesn't depend on the unsafe package.
    • // +build go1.12 to use (*reflect.Value).MapRange when available.
    • // +build protoreflect to disable fast-path implementations of various operations to allow testing the reflection-based fallbacks.
    • // +build proto1_legacy to enable support for some ancient features nobody should use. Used so we can run tests for that support.
  11. abhinav commented on Aug 1, 2019

    @abhinav
    Contributor

    We use build tags to separate the almost identical input and output files of
    our code generator.

    Specifically, we have an input file that is valid Go code, and has a build tag
    to exclude it from the build.

    $ cat foo.go
    // +build magic
    package foo
    
    func Bar() {
        ...
    }
    

    The code generator takes this file and spits out a nearly identical file with
    the build tag inverted and some changes to the function bodies.

    $ magic ./...
    $ cat foo_gen.go
    // +build !magic
    package foo
    
    func Bar() {
        ...
    }
    

    The code generator (magic) only considers files with the magic build tag.
    This excludes the generated foo_gen.go from consideration when the tool is
    run again. The go tool is invoked without the build tag so only the
    generated file is compiled in the final binary.

    magic go build // +build
    foo.go Use Ignore magic
    foo_gen.go Ignore Use !magic

    The desired experience here is for users to code in the input file like it's a
    regular Go file (with auto-completion and other editor features) without
    caring that it'll get substituted for the generated file at build time.

  12. AndersonQ commented on Aug 1, 2019

    @AndersonQ
    Contributor

    I use build tags to separate integration tests and examples (depending on external resources such as database) from unit tests (no external dependencies).

    • // +build integration
    • // +build example
      so go test won't run them by default
  13. gdey commented on Aug 1, 2019

    @gdey

    In the Tegola project we also use it select which features are enabled or not.

    • atlas/cache_s3.go:// +build !noS3Cache
    • atlas/provider_gpkg.go:// +build !noGpkgProvider
    • atlas/cache_redis.go:// +build !noRedisCache
    • atlas/cache_azblob.go:// +build !noAzblobCache
    • atlas/provider_postgis.go:// +build !noPostgisProvider
    • cmd/tegola/pprof.go:// +build pprof
    • provider/gpkg/gpkg.go:// +build cgo
    • server/viewer_disabled.go:// +build noViewer
    • server/viewer.go:// +build !noViewer

    This is important as some environments that Tegola is deployed into they do not want all the features, and turning them off or not configuring it is not good enough. So, having the ability to compile them out is important.

  14. JAicewizard commented on Aug 1, 2019

    @JAicewizard

    for building part of a program as a seperate binary. eg:part of a program parses some file, this would be used as an API by default, but with an alternate main file you can use the same codebase to build a parser(or any other modular thing) that would output to stdout or another file.

    I used something like this in a "bigger" project where the program also had to use some modules(not go modules) as external binary and communicate over std in/out.
    We could have a main wrapper that would usually not build, but with build tags we could build specific modules and use them in testing(or for example as seperate downloads)

  15. eliasnaur commented on Aug 3, 2019

    @eliasnaur
    Contributor

    The gioui.org/ui/app package is like package os, but for GUI programs: it uses build tags to distinguish between GOOS. Some examples:

    // +build !js
    // +build android
    // +build darwin,ios
    // +build linux windows
    

    I also use the standard GOOS build tags in file names: os_js.go, gl_android.go, egl_windows.go etc.

  16. 6 remaining items

  17. stamblerre commented on Aug 13, 2019

    @stamblerre
    ContributorAuthor

    Thanks all for your detailed replies! I will be working on compiling your answers into a document, and hopefully soon we'll be able to make some progress on supporting build tags in gopls.

  18. added and removed
    WaitingForInfoIssue is not actionable because of missing required information, which needs to be provided.
    on Aug 19, 2019
  19. meling commented on Sep 19, 2019

    @meling

    I teach and use Go for programming labs, and keep solutions and skeleton code for the students in the same folder to ensure consistency with common files and to remember to change both if necessary.

    I use // +build solution in solution files and // +build !solution in skeleton code, since they have overlapping type/func definitions. (This of course results in numerous compile errors when using gopls in VSCode.)

    I run tests using go test -tags solution.
    Running go test without the solution tag should fail the tests.

    I then copy the skeleton code to a separate repo to share with the students.

  20. jefferai commented on Nov 20, 2019

    @jefferai

    Build tags are crucial for projects that have open source and enterprise variants.

  21. rythmkraze commented on Dec 1, 2019

    @rythmkraze

    gobbi, a package that provides bindings to GTK GUI toolkit, uses Go build tags to allow targeting of specific library versions. See here for more details.

  22. modified the milestones: gopls v1.0, on Dec 4, 2019
  23. stamblerre commented on Jan 29, 2020

    @stamblerre
    ContributorAuthor

    Thanks all for the detailed replies! This information will be very valuable as we continue work on #29202. Closing this issue in favor of that one. Please feel free to comment here if you have a different use case for build tags.

  24. KBassford commented on Mar 17, 2020

    @KBassford

    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. The debug file just prints log messages to replace functions that make major changes to the running state of the system.

  25. locked and limited conversation to collaborators on Mar 17, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions