glide getで新しいライブラリをインストールしようとするとこんなエラーが出た。
[ERROR] Error scanning github.com/golang/protobuf/ptypes/duration: open /Users/yunomu/.glide/cache/src/https-github.com-golang-protobuf-ptypes-duration: no such file or directory [ERROR] This error means the referenced package was not found. [ERROR] Missing file or directory errors usually occur when multiple packages [ERROR] share a common dependency and the first reference encountered by the scanner [ERROR] sets the version to one that does not contain a subpackage needed required [ERROR] by another package that uses the shared dependency. Try setting a [ERROR] version in your glide.yaml that works for all packages that share this [ERROR] dependency.
私がメインで使っているMacだとこうなるけど、他の環境ではこのエラーは起きない。
そもそもエラーメッセージの中では https-github.com-golang-protobuf-ptypes-duration を探そうとしているが私が探しているのは https-github.com-golang-protobuf/ptypes/duration である。パスの切り方が違うので当然ながらファイルは見つからない。
バグかと思ったけどもそういうissueも登録されていないのでコードを見てみる。 https://github.com/Masterminds/glide/tree/v0.12.3
件のエラーメッセージが dependency/resolver.go の543行目あたりに書かれている。
https://github.com/Masterminds/glide/blob/v0.12.3/dependency/resolver.go
543 } else if strings.Contains(errStr, "no such file or directory") {
544 r.hadError[dep] = true
545 msg.Err("Error scanning %s: %s", dep, err)
546 msg.Err("This error means the referenced package was not found.")
547 msg.Err("Missing file or directory errors usually occur when multiple packages")
548 msg.Err("share a common dependency and the first reference encountered by the scanner")
549 msg.Err("sets the version to one that does not contain a subpackage needed required")
550 msg.Err("by another package that uses the shared dependency. Try setting a")
551 msg.Err("version in your glide.yaml that works for all packages that share this")
552 msg.Err("dependency.")
エラーメッセージに “no such file or directory” という文字列が入っているかどうかでエラーを判断しているあたり大胆だ。
そのエラーの原因がここ。デバッグメッセージ付きで実行してみると、同ファイルのこの部分の時点で 493行目の r.Handler.PkgPath(dep) が https-github.com-golang-protobuf-ptypes-duration を返しており、成功するはずがない。
492 // Here, we want to import the package and see what imports it has.
493 msg.Debug("Trying to open %s (%s)", dep, r.Handler.PkgPath(dep))
494 var imps []string
495 pkg, err := r.BuildContext.ImportDir(r.Handler.PkgPath(dep), 0)
次に PkgPath の定義を見る。interfaceなので定義は2つあるが repo/installer.go の方。
https://github.com/Masterminds/glide/blob/v0.12.3/repo/installer.go
600 // PkgPath resolves the location on the filesystem where the package should be.
601 // This handles making sure to use the cache location.
602 func (m *MissingPackageHandler) PkgPath(pkg string) string {
603 root, sub := util.NormalizeName(pkg)
604
605 // For the parent applications source skip the cache.
606 if root == m.Config.Name {
607 pth := gpath.Basepath()
608 return filepath.Join(pth, filepath.FromSlash(sub))
609 }
610
611 d := m.Config.Imports.Get(root)
612 if d == nil {
613 d = m.Config.DevImports.Get(root)
614 }
615
616 if d == nil {
617 d, _ = m.Use.Get(root)
618
619 if d == nil {
620 d = &cfg.Dependency{Name: root}
621 }
622 }
623
624 key, err := cache.Key(d.Remote())
625 if err != nil {
626 msg.Die("Error generating cache key for %s", d.Name)
627 }
628
629 return filepath.Join(cache.Location(), "src", key, filepath.FromSlash(sub))
630 }
本来、最後629行目で key=https-github.com-golang-protobuf, filePath.FromSlash(sub)=ptypes/dulation にならなければならないが key=https-github.com-golang-protobuf-ptypes-duration になっている。全然ダメだ。
そもそも603行目のutil.NormalizeName()が root=github.com/golang/protobuf/ptypes/duration, sub= を返している。
このライブラリのリポジトリは https://github.com/golang/protobuf で、リポジトリ内のサブディレクトリは ptypes/dulation なので、ここでは root=github.com/golang/protobuf, sub=ptypes/dulation となってほしい。
ということで util.NoralizeName() を見る。
https://github.com/Masterminds/glide/blob/v0.12.3/util/util.go
300 // NormalizeName takes a package name and normalizes it to the top level package.
301 //
302 // For example, golang.org/x/crypto/ssh becomes golang.org/x/crypto. 'ssh' is
303 // returned as extra data.
304 //
305 // FIXME: Is this deprecated?
306 func NormalizeName(name string) (string, string) {
307 // Fastpath check if a name in the GOROOT. There is an issue when a pkg
308 // is in the GOROOT and GetRootFromPackage tries to look it up because it
309 // expects remote names.
310 b, err := GetBuildContext()
311 if err == nil {
312 p := filepath.Join(b.GOROOT, "src", name)
313 if _, err := os.Stat(p); err == nil {
314 return toSlash(name), ""
315 }
316 }
317
318 name = toSlash(name)
319 root := GetRootFromPackage(name)
320 extra := strings.TrimPrefix(name, root)
321 if len(extra) > 0 && extra != "/" {
322 extra = strings.TrimPrefix(extra, "/")
323 } else {
324 // If extra is / (which is what it would be here) we want to return ""
325 extra = ""
326 }
327
328 return root, extra
329 }
嫌な感じのコメントが書いてある。
それはいいとして、312行目でGOROOTから該当のライブラリを探している。
私の場合、 GOROOT=/usr/local/go なので、 /usr/local/go/src/github.com/golang/protobuf/ptypes/duration が存在するかどうかを確認している。実際に見てみると、あった。なんであるんじゃい。
このファイル(ディレクトリ)が存在することで314行目でreturnしてしまって、最終的におかしなURLにライブラリの更新確認に行って死んでいた模様。この後にパスからリポジトリの種類を判別してURLを作るのだがそもそも/usr/local以下はgitはgitでもbrewの配下である。
% sudo rm -rf /usr/local/go/src/github.com
で事なきを得た。そもそもなんでこんなところにこんなものが入っていたのかよくわからない。
このあたりを探っているとGoogle内部のリポジトリやパッケージ管理法とGitとの相性の悪さで外の人たちが割を食ってる感じがしてちょっと面白い。自分が悩む方でなければ。
あとGoは読むのが楽で助かる。