Posted at

wire に `-: cannot find package "pattern=." in any of:` って言われたら


TR;DR

golang.org/x/tools を 2018-10-13 以降のバージョン,具体的には 13ebad8 が含まれるバージョンまでアップデートしましょう.


発端

ある日,grapi で .go generate ./...` を実行したら突然死した.wire がすべて失敗しているようだった.

-: cannot find package "pattern=." in any of:

/usr/local/Cellar/go/1.11.1/libexec/src/pattern=. (from $GOROOT)
/Users/izumin/src/pattern=. (from $GOPATH)
wire: generate failed
cmd/grapi-gen-type/di/wire_gen.go:3: running "wire": exit status 1

pattern=. とは :thinking:


調査

当時の wire のバージョンは v0.2.0 を使っていた.

とりあえず pattern= で grep する.


func load(ctx context.Context, wd string, env []string, patterns []string) ([]*packages.Package, []error) {

cfg := &packages.Config{
Context: ctx,
Mode: packages.LoadAllSyntax,
Dir: wd,
Env: env,
BuildFlags: []string{"-tags=wireinject"},
// TODO(light): Use ParseFile to skip function bodies and comments in indirect packages.
}
escaped := make([]string, len(patterns))
for i := range patterns {
escaped[i] = "pattern=" + patterns[i]
}
pkgs, err := packages.Load(cfg, escaped...)
if err != nil {
return nil, []error{err}
}

internal/wire/parse.go#L332-L348


internal/wire.load はコード生成時の初っ端に呼ばれる関数.patterns には引数 or . が入っている.これの各要素に対して pattern= という prefix をつけている.

packagesgolang.org/x/tools/go/packages で, 静的解析まわりを使いやすくするためのラッパー.ここでもおもむろに "pattern" とかで grep する.


  // Extract file= and other [querytype]= patterns. Report an error if querytype

// doesn't exist.
extractQueries:
for _, pattern := range patterns {
eqidx := strings.Index(pattern, "=")
if eqidx < 0 {
restPatterns = append(restPatterns, pattern)
} else {
query, value := pattern[:eqidx], pattern[eqidx+len("="):]
switch query {
case "file":
containFiles = append(containFiles, value)
case "pattern":
restPatterns = append(restPatterns, value)
case "name":
packagesNamed = append(packagesNamed, value)
case "": // not a reserved query
restPatterns = append(restPatterns, pattern)
default:
for _, rune := range query {
if rune < 'a' || rune > 'z' { // not a reserved query
restPatterns = append(restPatterns, pattern)
continue extractQueries
}
}
// Reject all other patterns containing "="
return nil, fmt.Errorf("invalid query type %q in query pattern %q", query, pattern)
}
}
}

go/packages/golist.go#L55-L84 - github.com/golang/tools@3c39ce7


引数をパースしてる.おもむろに blame すると,10/13のコミット で追加されたことがわかる.

と,いうことで golang.org/x/tools のバージョンを上げましょう.


余談

packages は内部で go list 叩くんですね.

go list の内部実装が internal になってるのを export しないのはなんでなんだろう.


// golistDriverCurrent uses the "go list" command to expand the

// pattern words and return metadata for the specified packages.
// dir may be "" and env may be nil, as per os/exec.Command.
func golistDriverCurrent(cfg *Config, words ...string) (*driverResponse, error) {
// go list uses the following identifiers in ImportPath and Imports:
//
// "p" -- importable package or main (command)
// "q.test" -- q's test executable
// "p [q.test]" -- variant of p as built for q's test executable
// "q_test [q.test]" -- q's external test package
//
// The packages p that are built differently for a test q.test
// are q itself, plus any helpers used by the external test q_test,
// typically including "testing" and all its dependencies.

// Run "go list" for complete
// information on the specified packages.
buf, err := golist(cfg, golistargs(cfg, words))
if err != nil {
return nil, err
}

go/packages/golist.go#L185-L205@13ebad8 - github.com/golang/tools@13ebad8