Go version
go version go1.25.0 darwin/arm64
Output of go env
in your module/workspace:
AR='ar'
CC='cc'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='c++'
GCCGO='gccgo'
GO111MODULE=''
GOARCH='arm64'
GOARM64='v8.0'
GOAUTH='netrc'
GOBIN=''
GOCACHE='/Users/adamhamrick/Library/Caches/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='/Users/adamhamrick/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/2l/06jbymcj3mn7yp510wxm_jq40000gn/T/go-build4290062372=/tmp/go-build -gno-record-gcc-switches -fno-common'
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMOD='/Users/adamhamrick/Projects/branch-out/golang/example_project/go.mod'
GOMODCACHE='/Users/adamhamrick/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='darwin'
GOPATH='/Users/adamhamrick/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/opt/homebrew/Cellar/go/1.25.0/libexec'
GOSUMDB='sum.golang.org'
GOTELEMETRY='on'
GOTELEMETRYDIR='/Users/adamhamrick/Library/Application Support/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/opt/homebrew/Cellar/go/1.25.0/libexec/pkg/tool/darwin_arm64'
GOVCS=''
GOVERSION='go1.25.0'
GOWORK=''
PKG_CONFIG='pkg-config'
What did you do?
I'm writing a simple program to discover and load basic information about all packages in a Go project. This code normally works fine.
func loadPackagesFromModule(moduleDir string) {
config := &packages.Config{
Mode: packages.NeedName | packages.NeedModule | packages.NeedFiles,
Dir: moduleDir,
Tests: true,
}
pkgs, err := packages.Load(config, "./...")
if err != nil {
return nil, fmt.Errorf("failed to load packages from module %s: %w", moduleDir, err)
}
}
What did you see happen?
I'm dealing with a project that has build tags in many of their files. When I run the above code on this project:
.
├── go.mod
├── go.sum
├── main.go * Has build tag
├── package1
│ └── other1.go * Has build tag
│ └── other1_test.go * Has build tag
├── package2
│ └── other2.go * Has build tag
│ └── other2_test.go * Has build tag
├── base.go * Has build tag
├── base_test.go * Has build tag
the code returns only the main package and its files in IgnoredFiles
.
What did you expect to see?
I can get it to properly Load all packages if I provide BuildFlags: []string{"-tags", "build_flag"}
, but I thought the whole point of IgnoredFiles
was to find files and packages that might have been ignored thanks to build tags. I wouldn't expect only the main package to be loaded in IgnoredFiles
and all the rest to never be touched.
Is this a bug? Or am I misunderstanding how this is intended to work? Is there any way to fully Load all packages, agnostic of build tags?
Comment From: gabyhelp
Related Issues
- cmd/go: wildcard should consistently match package with invalid build tags #41410 (closed)
- x/tools/go/packages: Load in NeedName|NeedFiles mode can produce incorrect results for certain modules #31894 (closed)
- x/tools/go/packages: support for loading files/syntax irrespective of build tags #28121
- x/tools/go/packages: Load returns different results depending on build cache state #33687 (closed)
- cmd/go: confusing situation with 'go run' and '//go:build ignore' #73152
- x/tools/go/packages: relative "files=" query path are not relative to Config.Dir #65965
- x/tools/go/packages: missing TypesInfo when NeedTypesInfo was set while NeedSyntax \& NeedTypes were not #69931
- go fmt ./... does not add //go:build comments when subdirectory only contains tagged files #48771 (closed)
- x/tools/gopls: build tags not working #51111
- x/tools/go/packages: packages.Load does not internally filter out -modcacherw #56534 (closed)
(Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.)
Comment From: cherrymui
cc @adonovan @matloob
Comment From: IlyasYOY
Hey @kalverra,
I think the behavior you’re seeing is related to how go list
(and therefore packages.Load
) treats a package whose own files are excluded by build constraints. When the base package is considered empty, the tool stops recursing into its subdirectories, so those sub‑packages never get examined.
I reproduced a similar layout and observed the same pattern:
➜ testgolist go list .
package github.com/ilyasyoy/testgolist: build constraints exclude all Go files in /Users/ilyasyoy/Projects/IlyasYOY/testgolist
➜ testgolist go list ./...
go: warning: "./..." matched no packages
➜ testgolist go list -tags test .
github.com/ilyasyoy/testgolist
➜ testgolist go list -tags test ./...
github.com/ilyasyoy/testgolist
github.com/ilyasyoy/testgolist/package1
github.com/ilyasyoy/testgolist/package2
➜ testgolist tree
.
├── file1.go
├── go.mod
├── package1
│ └── file1.go
└── package2
└── file2.go
3 directories, 4 files
So, when the root package is filtered out by the default tags, the entire tree is effectively invisible to packages.Load
.
Does this line up with your observations?
UPD. I guess the answer is in this function: https://github.com/golang/go/blob/80038586ed2814a03dcb95cd6f130766f8d803c3/src/cmd/go/internal/modload/load.go#L253
Comment From: kalverra
@IlyasYOY Yes, this matches my observations. I suppose this is a bit of a niche case, but it strikes me as a bug.