読者です 読者をやめる 読者になる 読者になる

yunomuのブログ

酒とゲームと上から目線

glide getやglide upが動かない

 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は読むのが楽で助かる。