gomのトレース

https://github.com/mattn/gom

パッケージマネージメントツール プロジェクトの依存するpackage(revisionを含む)を管理できる。 go標準のパッケージマネージメントツールがない時からあって、結構な利用者がいそう。

概要

データとしては、プロジェクト毎の依存パッケージ情報を記載するGomfileと、依存パッケージデータを格納するvendorディレクトリがある。

buildやtestなどのgoコマンドをラップしているgomコマンドが使えるようになり、gom経由で実行すると各プロジェクトのvendorディレクトリも認識されるようになる。

技術的要素

  • Gomfileの生成

    • プロジェクトをscanして、 build.Import で依存packageのパスを取得できる
    • build.Package.Goroot(bool)build.IsLocalImport でvendor packageかどうかを判別できる
    • 上記のパスを元に、GOPATH以下のpackage dirに移動して、 git rev-parse HEAD でrevisionを取得できる
  • Gomfileをparseするときの正規表現

    • 各最小単位の要素ごとに正しく定義して、それを組み合わせることで、一般的に読みにくい、理解しにくい正規表現をなるべくメンテナブルに。
    • gom ('repository name') や optionsの :(option name) => (option list or option name)
    • 行の種別ごとの正規表現を正しく定義すれば、最低限の正規表現の標準メソッドを使うだけで、シンプルにparserが実装できそう
    • スペースやカンマとかの区切りとか、正規表現で正しく表現するのはややこしそうだけど、いくつかのパターンのテストを用意しておけば、数回の試行錯誤で割と網羅的な表現が書けそうな気がする
  • install

    • GOPATHに ./vendor 設定した上で、 go get package することで、 ./vendor にpackageを持ってきている

memo

  • goの例外処理でひたらすerrorを返していくやり方は解り易くて良さそう
  • 各種変数や関数の定義が色々なファイルに散らばってしまっている感があるけど、これぐらいの規模なら問題ない?(普通にIDEを使えば実際読み解きずらいとは感じなかった)
  • コンソール出力の色付け github.com/daviddengcn/go-colortext 便利そう

main.go

  • flagArgsをsubArgsに切り出したり、flagArgs(0) あたりの処理、最初の引数がコマンドで、それ以降がコマンドごとの引数だということが解りやすい
  • コマンドのswitch文が読みやすい
  • genコマンドのswitchの入れ子、2階層ぐらいなら全然問題ない

exec_test.go

  • run経由で go envを実行して取得したgopathの中に、tmp pkgのvendor dirが含まれているかをチェック。
  • gomはgo ** 系のコマンドをラップしていて、PATHやGOPATHをvendorが読み込まれるようにしている。
    • それがちゃんと指定されているかをテストしている。
  • defer func(){...}() という書き方
  • ioutil.Temp**系の関数を使うと面倒そうなpkg dirや標準出力のparse系のテストが割と簡単に書ける?
  • cmdに渡すstdoutを、直接os.Stdoutを使うのではなく、変数のstdoutを経由させることでテストが可能になっている。

genGomfile

  • build.importsを再帰処理して、カジュアルに外部パッケージの一覧が取得できる
  • vcsのrevision番号の読み取りを正規表現のmaskとして実装しているのは直感的でわかりやすい
  • vcsのcommandをsliceで表現している。goはsliceの切り取りが直感的だからexec.Commandの受け渡しもシンプルに書ける
  • 簡易的な struct vcsで、vcs毎の差異を隠している