YAMAGUCHI::weblog

土足で窓から失礼いたします。今日からあなたの息子になります。 当年とって92歳、下町の発明王、エジソンです。

Windows 10の開発環境を整えた

はじめに

こんにちは、大正デモクラシーです。年末年始に実家に帰るにあたって、Windows 10がインストールされているXPS 13を持って行ったんですが、実家で庭木の剪定以外にやることがなかったので、それ以外の時間はずっとコード書いてました。しかし、持って行ったマシンの開発環境がまったく整ってなかったのでいろいろ設定しなおしてとりあえずいい感じになったので、その作業メモを書いておきます。

TL;DR

これまでLinuxmacOSで育ててきた環境をWindows 10で使うことはあきらめて、これらのツールをとりあえず入れました。

MSYS2を入れて捨てた

はじめはMSYS2を使ってLinuxMacと似たような環境にしようかなと思ってごちゃごちゃやってたんですが、中に入ってるパッケージが古かったり、いろんなものとの相性が悪かったので捨てました。入れて作業して捨てるまでに1日半くらい使った気がします。まあMSYS2のいろいろな設定を見て回って一通り触ってみて良し悪しがわかったので、それは良かったとしましょう。

PowerShell on cmderで生きていくことにした

@ryushi と @aodag にどうしよっかねえ、と相談した結果、WindowsWindowsの世界で生きたほうが楽になれそうだったので、全部すっぱり捨ててPowerShellに移行することにしました。bash on Windowsなどを使う、という方法もあるかもしれませんが、どうもしっくりこないなと思ったのでやめました。今度暇なときに触ってみようと思う。

さて、Windowsを使っていくとはいえ、素のPowerShellはフォント回りをいじるのにレジストリいじったりしないといけない感じだったので、10年前とかに無駄にRegseekerで遊びまくって何度もWindows XPを再起不能にしていた身としては、極力レジストリはいじりたくない気持ちがあったので、ターミナルエミュレータのcmderを使っていくことにしました。

cmderを入れた理由はこんな感じです。

  • デフォルトでもろもろのコンソールを立ち上げられる
  • タブで各コンソールを管理できる
  • フォント回りを柔軟に設定できる
  • デフォルトで設定されている profile.ps1 がいい感じにGit for Windowsと連携してくれる
  • 上記の profile.ps1 が追加でカスタムの profile.ps1 を読み込む口を用意してくれている

Git Credential Manager for Windows

PowerShellで生きていく設定はできたので、次はコードを持ってくるために必要なGitの設定。ここで問題になるのは、自分はGitHubで2FAを使ってるので、そこをどうするか。LinuxmacOSではいい具合にcredential managerを使えるので、HTTPSであってもパスワード入力なしにpushもできるんですが、Windowsではデフォルトではそれをサポートしていません。で、いい方法ないかなと思って探したらやはりありました。

インストーラーがあって、Gitのインストールも一緒にしてくれる。これを入れたら2FAにしたGitHubアカウントでも快適にコードの管理ができるようになりました。

nvm for Windows

正月休みにVisual Studio Codeの拡張を更新するかと思っていたので、node.jsの環境をまず整えることにした。nvmを使って入れたいと思っていたら、ちゃんとWindowsに対応したものがあったのでこれを使ってインストール。

chocolatey

Windowsの環境をまじめに設定するのがWindows XP以来なので変化に驚いています。Visual Studio向けにはNuGetがあってなんかいい感じらしい、ということは聞いていたのですが、それ以外の普通のツール(パッケージ)管理用にchocolateyというツールがあるのをいまさらながら知りました。これでいちいち自分でパッケージを探してきて入れなくても済むんですね。本当に便利。

posh-git

PowerShell上でGitを扱うためにposh-gitをインストールしました。これはchocolateyにパッケージとして登録されているのでそちら経由でインストールしました。

> choco install poshgit

Python 3.6

Pythonをちょろっと書く必要があったのでPythonもchocolatey経由でインストール。

> choco install python3

Go 1.7.3

当然Goもインストールした。最新版がパッケージ登録されている。

> choco install golang

細かなこと

権限回り

Windowsは権限回りがかなり厳しく、PowerShellでいろいろなことを行うにはもろもろの設定が必要でした。ただしこれらもコマンド1つで設定を変えられるのでかなり楽な印象。

> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser

(追記: 2017/01/05 18:13:00)

Windows 10の開発環境を整えた - YAMAGUCHI::weblog

powershellはset-exectionpolicyでガバガバにするの止めて、powershell.exeの起動時のオプションに「-exectionpolicy remotesigned」付けて権限をプロセス単位に閉じ込める方が良いと思う。

2017/01/05 17:30
b.hatena.ne.jp

なるほどそういう方法があったのですね!早速cmderのPowerShellの起動オプションに追加します!

コマンドレット

PowerShellのコマンドレットを覚えるのは大変 *1 だけれども、Get-Help コマンドレットで対象のコマンドのオプションはすぐにわかるし *2 、オプションで -Online をつければ、ブラウザで詳細なドキュメントがすぐに読めるのでとても良い。

*1:Unixシェル的なエイリアスが用意されているけれど、なるべくWindowsの世界で生きてみるために使わないことにしている

*2: ところで Get-Help のエイリアスは man になっているあたりが優しい

YAMAGUCHI::weblogの2016年を振り返る

はじめに

こんにちは、Go界の低温調理器です。今日でまた1歳年齢を重ねました。例のやつです。

関連エントリ

なんだかんだで振り返りを10年やってるっぽい。

ymotongpooの2016年

昨年立てた目標

昨年立てた目標的なものを見てみます。この項目毎年年末に適当に書いてて、まったく翌年覚えてないことに気が付きました。

  • 動画関連技術で遊ぶ
  • アクションカムと360度カメラで遊ぶ
  • Androidで遊ぶ
  • 資産運用を真面目に考える

動画関連技術で遊ぶ

家の録画機で遊んでいたくらいで、仕事では積極的には動画で遊んでいませんでした。YouTubeにいたときと比べると動画に触れる機会がかなりすくない。Android関連でいうとExoPlayer2が地道に対応コーデック増やしているのを追いかけていたりするくらいなので、来年はその辺で仕事もっと作れないかなあと考えています。

アクションカムと360度カメラで遊ぶ

昨年の今頃は360度動画をカメラとかが来るかもと思っていろいろ調べていた記憶がよみがえってきました。個人的には旅行に行ったりするときはアクションカムや360度カメラを持って遊んでいるのですが、やはり加工に手間がかかる部分をどう解消するかというのが課題ですね。

Androidで遊ぶ

Androidアプリは下位互換を捨てると年々書きやすくなっている印象で、Android Studioの進化でより開発がしやすくなってきたなあと感じました。もちろん、業務で扱ってるから下手にはまることはないんだけれど、客観的に見てかなり直線的に開発できるようになったと思います。

資産運用をまじめに考える

まじめに考えた結果いろいろ動き始めました。また pyspa Slack の #kane チャンネルでいろいろと話してると資産運用やら税制の話などがあって面白いです。

今年特にやってたこと

まずそもそも「お前の仕事はなんなんだ」っていうところから始まるんですが、自分の職種は、自社で開発した製品やサービスを広く紹介すると同時に、フィードバックを集めて製品チームとともに問題を解決していく、というのが主な役割です。これに関連すればなんでもする、という役職なので、コミュニティの場でプレゼンをすることもあれば、サンプルコードを書くこともありますし、PRDのレビューをすることもあれば、PRDを書くこともあります。

仕事など

今年はAccelerated Mobile Pages(AMP)のGoogle検索対応が年初からあり、それも何段階かのリリースがあったため、この1年はだいぶAMP関連の仕事が多かったです。

いくつかご一緒させていただいた企業のブログエントリにもリンクを貼りましたが、ほかにもすでにリリース済みのものや現在進行形のもの合わせて多くの企業にご協力いただいています。AMPはGoogleがイニシアチブを取っているオープンソースプロダクトではあるけれども、実際にAMPページを生成してくれるサイトオーナーがいてこそのものなので、実際に取り組んでくださった企業にはご迷惑をおかけしたこともありつつ、AMPを良いものにするために多くのフィードバックをいただき、それらの多くがすでにレポジトリに反映されています。

またProgressive Web Apps(PWA)を全面に押し出したのもこの1年でした。AMPで素早くファーストビューをとり、PWAでは総合的なウェブ体験の向上を、という提案は個人的にはかなりおすすめではあったのですが、まだまだ様々な課題や懸念もあり、まだまだ実際の導入事例は少ないのが現状です。来年一年はもっと増えることに期待しつつ、コツコツと取り組みを進めていくしかないですね。

Chrome Custom TabsSmart Lock for Passwords はひきつづき一押し機能なので来年もプッシュしていきます。

変わり種としてはTangoの担当を始めたことが挙げられます。Tangoは個人的にはいま一番実用性が高い端末ではないかと思っていて、特にB2CではなくB2Bにおいて受ける端末だと思っています。Unityのプラグインもなかなかに使いやすくなっていると思うので、来年はもっと多くの事例の支援ができればなあとぼんやり考えています。

Anova

今年、と言っても1か月程度だけれど、Anovaの低温調理器が本当にすごいです。昨日のエントリにも書きましたが、いままでなんとなくでやっていた肉料理に対する見方が完全に覆されました。ステーキやローストビーフなど、塊肉に対して中心までどのように火を通すか、これは料理において大きな課題でしたが、完全に再現可能な形で容易に実施できるのは科学の勝利と言っていいと思います。

鶏むね肉のサラダチキン、ローストビーフなどはすでに何度か調理して完全にその質に満足しているので、来年はAnovaを活かしてより高度な低温調理の世界を堪能したいと思います。

海外出張

2016年は例年に比べて多くの出張がありました。面白いのはそれぞれの出張が全部違うプロジェクトだったことで、来年もおそらく同様のスケジュールになりそうです。

多くのプロダクトを抱えるのは日々の仕事においては大変なことも多いですが、多くの人と知り合えるという利点もあるので、これからも各地にいる製品チームに多くのフィードバックしていきます。

スペイン語

海外出張の合間に9月にスペインに行ってきました。

ymotongpoo.hatenablog.com

スペインは期待していた以上に素晴らしく、これなら毎年来たいと思うくらいでした。そこで現地の人との交流のために旅先で始めたスペイン語の勉強ですが、無理のないペースでちょこちょことやっていました。最近はさぼりがちになってしまったけれど、スペイン語は面白いので、長期的に勉強したいなあと思います。

ジム

今年の後半はずっとジムに行っていました。半年でだいぶ体が変わってきたので、引き続きこの調子でやっていって、来年はさらなるレベルアップをしたいと思います。懸垂20回をコンスタントに達成できるようにしなければ。

来年に向けて

毎年これ書いてるけど、毎年忘れてるんだよなあ。まあでも書かないよりはマシと言ことで今年も書きます。

  • スペイン語の勉強
    • Duolingoだけでは限界があるので、まずは文法書的なものがほしい。
  • Pythonやり直す
    • numpyやらscipyやらをいじり始めたので
  • 資産運用を引き続き
    • お金は大事
  • ジム
    • コツコツ頑張ります

2016年買ってよかったもの

はじめに

こんにちは、しうまちの成年後見人です。日付変わって今年も残すところあと2日ですね。さて、毎年大晦日に1年の振り返りを書いてるんですが、それを書こうかといろいろ考えてたら、今年1年は買ってよかったものがたくさんあったことを思い出したので、別途記事としてまとめておこうと思います。

2016年に買ってよかったもの

Anova Precision Cooker

まずなにを差し置いてもAnovaの低温調理器。

これは本当に素晴らしくて、たとえば100g 50円みたいな鶏むね肉を、塩コショウで簡単に下味をつけて、ジップロックに入れて、適当なハーブをぶちまけて、浸水式でぴったりと口を締めた後、60度超で2時間弱くらい放っておけば、あっという間にぷりぷりのサラダチキンができてしまう。あるいは輸入牛の内もも肉を1kgくらい塊で買って、適当に下味をつけて56度とかで一晩ほったらかしとけば、もうローストビーフの出来上がり。

気に入っている点を箇条書きで書くとこんな感じです。

  • 火を使わないのでほっといても安心
  • 温度を一定に保つことに一切気を遣わなくて良いので並行して別のことができる
  • 確実に温度を一定に保てるので調理の再現性がきわめて高い
  • コンロをふさがないのでほかの料理は普通にできる
  • 収納時はただの筒なので収納しやすい
  • Anovaでの下ごしらえのあとは香りづけに集中できる

安い肉が調理一つでここまでおいしくできるという事実を知って、料理がより楽しくなりました。

Anker SoundBudsシリーズ

Bluetoothイヤホンはいろんなオーディオ機器メーカーから出ているけれど、Ankerのものは2500円程度でありながら、その値段の割に良い音質で、接続も安定していて、電池の持ちもそこそこ長いので気に入っています。自分はSoundBuds Sportをはじめに買って、アムステルダム出張のときに無くしてしまい、いまはその代わりに買ったSoundBuds Sport IE20を使っています。どちらも接続が安定していて重宝しています。(いまAmazonでもSoundBuds Sportを売っているけど、かなりぼったくり価格になっているので注意)

ErgoDox EZ

これを買った経緯と買ってからの実感はこのエントリを読めば書いてあるので割愛。

ケンジントンのSlimBlade

ErgoDox EZの購入に合わせて、トラックボールを導入しました。初トラックボールではあったけれど、使ってみると案外すんなり導入できました。マウスはエルゴノミクス型のものもあるけれど、どうしてもマウスを操作することで肩に力が入ってしまい凝りにつながるので、鏡を見ながら肩の位置がなるべく変化しない位置を探して設置しました。

ケンジントントラックボールでよいのは4つボタンがあることで、トラックボールでやりづらい操作をしやすくしているという点。たとえば「左ボタンを押しっぱなしにする」みたいな操作をファイルのドラッグアンドドロップで行うけれども、クリックしただけで「左クリックをホールドする」というボタンを作れます。またボール周辺の板の角度が手首に無理のない角度で疲れません。またケンジントントラックボールの中でもSlimBladeのボールの滑りが一番良いと思います。

THERMOSの600mlのタンブラー

サーモス 真空断熱タンブラー 600ml ステンレス JDA-600 S

サーモス 真空断熱タンブラー 600ml ステンレス JDA-600 S

420mlのほうではなく600mlであることが重要。これにより500mlの缶ビールを一気に注げる。熱いものも冷たいものも、このタンブラー一つでストレスなく飲めるというのは気楽です。

α6000のズームレンズキット

海外出張や海外旅行にいままで頑張ってNikonのD610とレンズをいくつか持って行っていたけれど、場所がかさばるし重いので、歩き回ることが多いところだとかなり疲労していました。もちろん撮れる画はきれいなので満足ではあるけれど、多少写真の質が下がっても機動力を上げたいと思い、サブ機としてミラーレス一眼の購入を検討していましたが、悩んだ結果ソニーのα6000のズームレンズキットを購入しました。

実際に使ってみるとA-PSCではあるものの、十分優れた画が取れて、操作性もよく、またPCと携帯との接続もスムーズにできるため、ストレスがかなり減りました。またさすがソニーだけあって、動画の質が高いのもうれしい点。ボディとレンズ合わせたサイズが小さくなったことから、最低限として持ち歩く三脚もマンフロットのミニ三脚でかなりのシチュエーションをカバーできるようになりました。

Manfrotto ミニ三脚 PIXI ブラック MTPIXI-B

Manfrotto ミニ三脚 PIXI ブラック MTPIXI-B

骨盤職人&フォームローラー

ペルビス 骨盤職人 PV01

ペルビス 骨盤職人 PV01

「筋膜リリース」「トリガーポイント」などの検索ワードで調べると必ず出てくるもの。骨盤職人は腰や臀部の凝りが面白いように取れて、ほぐし終わると尻がつきたての餅みたいになります。ふわふわです。これをやってから寝ると本当に体が軽く、ぐっすり眠れます。

IncaseとAerのバックパック

これまでThe North Faceのフューズボックスを使っていたけれど、中に仕切りがないのが普段使いで使いづらいと思うことが何度かあったことと、旅行や出張で使いづらさを感じていたので、通勤用と出張・旅行用でバッグを分けることにしてからかなり探していきついたのがこの2つのバッグ。

自分が求めていたのは次のような点を満たすもの。

  • 仕切りやポケットがスリムに必要な分だけある
  • 出張・旅行用は1泊分の替えの下着と上着を入れられるだけのスペースを持たせる
  • PCのスリーブがある
  • アクセスしやすいポケットがある
  • 肩を疲れにくくするためハーネスがある
  • 外見がすっきりしている
  • 作りが丈夫

上の2つのバッグは実際に使い始めてみて、かなりしっくりきていて、QOLが向上しました。しかし、引き続きより良いバッグがあればと探しているので、良い情報を持っている人はぜひ教えてください。

おわりに

自分は普段はあまりものを買わないほうだけれども、なぜか今年は買い物を多くして、その中でも良いものが多かった年でした。安物買いの銭失いは嫌いだけれども、良いものは買うと生活を変えてくれるので、これからもそういう良品を見つけていきたい。

Visual Studio CodeでOSSライセンス用の拡張を書いた感想

はじめに

こんにちは、Visual Studio Code界のホエイプロテイン(WPI)です。これはVisual Studio Code Advent Calendar 2016の21日目の記事です。昨日はEbXpJ6bpさんでした。

Visual Studio Codeのリリース以後、ライトユーザーとして便利に使っています。今日は1ユーザーとして便利に使っていたものの、欲しい機能がなかったので拡張を作った話とその感想を書こうと思います。

作ったもの

Visual Studio CodeでOSSライセンス用のLICENSEファイルの作成、およびライセンスヘッダの挿入を簡単に行う拡張である "licenser" という拡張を作りました。

インストールは次のとおりです。

> ext install licenser

拡張を作った動機

Visual Studio Codeを使い始めるまでは、コードを書くときはEmacsVimAndroidのコード書くときはAndroid Studioを使って書いてたわけですが、いろいろ始めから設定されてるという理由でVisual Studio Codeを使い始めました。使いはじめてこれは良いなと思ったのはこんな感じ。

  • IDEとエディタの中間のような操作感
    • IntelliSense由来のポップアップ補完の優秀さはさすがMSですね
  • 設定の容易さ
    • JSONを1つ設定しておけば良い簡便さ。設定中もオプション項目は補完が効くのは便利。
  • 公式サイトでの拡張の管理。
    • 3rd partyではなく拡張が公式にホストされているのは安心感がある。バージョンが上がりエディタ内でも管理出来るようになったのが楽。
  • 充実したカラースキーム
    • なんかオシャレっぽくできてよい
  • 拡張がTypsScriptまたはJavaScriptで書ける
    • 見た目だけでいえばAtomに似てるわけですが、拡張を書くためだけにCoffeeScript覚えたくなかったので、他の用途でも使えるTypeScriptで書けるのは良かった。
  • デフォルトで結構戦える
    • 拡張をまったく入れなくてもメジャーどころの言語を使うときはシンタックスハイライトや補完も大体問題ないので良い

しかしながら、欲が出てくるもので、IDEっぽく使えるならもっとIDEっぽい機能がほしいなと思うようになり、その中でもダントツにほしく感じたのはOSSライセンス関係の機能でした。公式で提供しているかとも思ったのですが、ありませんでした。また3rd partyで公開されていた2つも、こんな感じで求めたものではありませんでした。

  • LicenseHeader - Visual Studio Marketplace
    • ライセンスヘッダの追加のみしか行ってくれない上に、MITライセンス決め打ちなので自分の用途に合っていない。またC言語スタイルのコメント以外は使えない。
  • License Injector - Visual Studio Marketplace
    • ライセンスヘッダの追加のみしか行ってくれない上に、MITライセンス決め打ちなので自分の用途に合っていない。

これらの拡張にContributeしてもよかったのですが、大元のコードが非常に短かかったので結局はじめから書き直すことになると思い、練習がてら自分で書いてみることにしました。

拡張を作った感想

豊富な公式ドキュメント

さすがはMSだなあと思ったのは、まず提供されているドキュメントの量と質。公式ドキュメントにチュートリアルもあり、手元で少し動かすだけで簡単に動かすことが出来ました。例えばこのあたりから読みはじめて手を動かすだけでした。

また実際に自分の拡張を書く際にもAPIリファレンスが整備されていたので、ドキュメントとエディタの補完だけですんなり書くことが出来ました。

code.visualstudio.com

Yeomanを使ったボイラープレートの提供

ドキュメントにも書いてありますが、Visual Studio Codeの拡張を作る際には決まった形のディレクトリ構成でコードを書き始めることになります。自分で用意するのは面倒ですが、Yeomanのボイラープレートが用意されてるのですぐに拡張を書き始めることが出来ます。楽ちん。

$ npm install -g yo generator-code
$ yo code

標準でのテスト環境の提供

作った拡張がうまく動作しているかを確認するのも簡単で、デバッグタブからデバッグ実行を行うことで、バニラ環境に開発中の拡張を入れただけのVisual Studio Codeが立ち上がり、そこで開発中の拡張の振る舞いを確認することができます。ログは開発を行っている側のウィンドウで確認できるので、特に複雑な設定をしなくても十分な検証ができます。便利。

拡張の公開用ツールの提供

作った拡張を一般公開したい場合には、パッケージングをして中央レポジトリに登録するわけですが、それも専用のNode.js製ツールを使ってコマンド一発で公開ができます。

$ npm install -g vsce
$ vsce publish

最初の1回目はもろもろと設定が必要ですが、それも公式ドキュメントに細かにスクリーンショット付きで記述してあるので、迷うこと無く設定ができました。

今後の予定

とりあえず日常用途で使う分には便利に使えるようになったので、細かなアップデートが出来ればなあと思っています。たとえば

  • メントスタイルを1行コメントと複数行コメントの両方を選択できるようにする
    • いまはライセンスヘッダは、1行コメントがある言語はすべて1行コメントで、複数行コメントしかない言語はそちらを使って書くようにしている。
  • shebangがある場合は2行目からライセンスヘッダを書くようにする
    • 対応したはずなんだけどうまく動いてないようなのであとでやる
  • プロジェクト内のファイル全体に一気にライセンスヘッダを挿入する
    • あんまり自分が必要になってないのでやってない

おわりに

Visual Studio Codeは安定して開発が進んでいる印象で、バージョンが上がる度にデフォルト機能が良くなっているのも好印象です。個人的には、現状の挙動だと、文字を入力する度にフックを入れるようにするとどんどん動作が重くなってしまうので、補完系のツールは気をつけないと折角の軽い動作が損なわれてしまう可能性があるということで、その辺の改善が行われていくと良いなあと思っています。

明日は @albatrosary さんです。

GoのASTを使ってパッケージのメンテナンスを考える

はじめに

こんにちは、Go界の骨盤職人です。buildersconにmattnさんがいらしていたということで生mattnさんに謁見したかったのですが、諸事情でこの日程はどうしても都合がつかず、参加できなかったことが非常に残念でいまでも悔やんでいます。

さて、Goは安定して開発が進んでおり、いまは安定バージョンが1.8にもなろうというところです。セルフホスティングも1.5で達成し、GCの高速化も順調に進んでいる中、いまだにGoの問題として挙げられるものとして「パッケージバージョンの管理」があります。今日はその辺の話をしようと思います。

TL;DR

go パッケージを使って、自分たちが書いたコードが依存してるパッケージを明らかにし、依存先パッケージの更新に追従していこう。

前置き

以下の話はGoをプロダクションで中規模〜大規模に利用している環境を想定しており、細かな閉じられたパッケージのみを開発している状況は想定していません。(そのような状況であれば既存のパッケージ管理ツールでも運用可能だと思っている)

背景

現在Goのバージョン管理には公式ではGo 1.5からvendoringがあり、3rd partyなものとしては gbgodepglide などがあります。すでに1.8にもなろうとしているのに、なぜ公式がいまだにパッケージのバージョン管理のサポートを暫定的な措置であるvendoringでしかサポートしていないのか、その理由は公式ドキュメントのFAQから垣間見ることが出来ます。

"Go get" does not have any explicit concept of package versions. Versioning is a source of significant complexity, especially in large code bases, and we are unaware of any approach that works well at scale in a large enough variety of situations to be appropriate to force on all Go users. What "go get" and the larger Go toolchain do provide is isolation of packages with different import paths. For example, the standard library's html/template and text/template coexist even though both are "package template". This observation leads to some advice for package authors and package users.

Packages intended for public use should try to maintain backwards compatibility as they evolve. The Go 1 compatibility guidelines are a good reference here: don't remove exported names, encourage tagged composite literals, and so on. If different functionality is required, add a new name instead of changing an old one. If a complete break is required, create a new package with a new import path.

If you're using an externally supplied package and worry that it might change in unexpected ways, the simplest solution is to copy it to your local repository. (This is the approach Google takes internally.) Store the copy under a new import path that identifies it as a local copy. For example, you might copy "original.com/pkg" to "you.com/external/original.com/pkg". The gomvpkg program is one tool to help automate this process.

The Go 1.5 release includes an experimental facility to the go command that makes it easier to manage external dependencies by "vendoring" them into a special directory near the package that depends upon them. See the design document for details.

ここにあるように、そもそもの思想として「パッケージ管理は一筋縄ではいかない」「パプリックで公開しているものは作者は依存パッケージのアップデートに追従すべき」というものがあり、Googleが実際に行っている方法として「破壊的変更が心配ならローカルのレポジトリにバージョン固定でコピー」をおすすめしています。(この思想がvendoringにも反映されていると見ていいでしょう)

ローカルにパッケージをコピーすればひとまずバージョンは固定できますから、オリジナルで破壊的変更があってもローカル(個人なら手元、会社で使っている場合は社内)ではビルドが壊れないので安心です。またローカルでバージョンを固定しておくことで他のプロジェクトがそのパッケージを利用する場合にも同一のバージョンを利用しているという安心感があります。実際に、1パッケージ1バージョンを維持することの重要性は今後のパッケージ管理のロードマップでも語られています。

とはいえ、オリジナルとローカルのインタフェースの差分が大きくなればなるほど追従が難しくなります。たとえば外部パッケージAをローカルに保存し、それ依存したローカルパッケージB、Cを開発している場合、Aから破壊的変更があったバージョンA'に更新すると、BとCの作者は壊れないように追従しなければいけません。さらにB、Cに依存したパッケージDの作者はさらに追従しなければいけません。またBとCがA1, A2とAの異なるバージョンに依存している場合にも、どちらを優先すべきかなどの問題が発生します。 このような連鎖的な依存関係はよくあることですが、製品規模が大きくなった場合にこのような変更にどのように追従していけばよいのでしょうか。

体制

一例としてGoogleの例を挙げます。ACMGoogleのソフトウェアエンジニアが社内での開発体制を紹介した記事でも語っていますが、Googleではサードパーティーライブラリ(社内で開発していないもの)は特定のディレクトリ以下にスナップショットを取り特定のバージョンを利用するようにしています。

そのスナップショットのバージョンを上げるとなると、それに依存して製品を作っている人々は対応をしなければいけないわけです。次の引用がまさにその状況を説明しています。

This model also requires teams to collaborate with one another when using open source code. An area of the repository is reserved for storing open source code (developed at Google or externally). To prevent dependency conflicts, as outlined earlier, it is important that only one version of an open source project be available at any given time. Teams that use open source software are expected to occasionally spend time upgrading their codebase to work with newer versions of open source libraries when library upgrades are performed.

サードパーティーライブラリのバージョンを上げるときは依存した製品の開発責任を持つ人々が同じタイミングで製品の動作保証をするように対応するわけです。

Goをメインに使って開発を行う場合に、このような体制をどのように構築したら良いのでしょうか。体制とまではいかずとも、このような依存関係を常に意識する簡単な方法はないでしょうか。

パッケージの依存関係をGoの抽象構文木(AST)から知る

長い前置きとなりましたが、ようやく本題です。Goでは標準パッケージ内に go パッケージがあり、Goのソースコードそのものをなるべく扱いやすくできるようになっています。Goの利点として語られるところにIDEに頼らずとも、標準ツールやサードパーティーツールと連携させることで、VimEmacsVisual Studio Code等々のエディタでも高い生産性を発揮できるというものがありますが、それらのツールもこの go パッケージを利用して作られています。go パッケージの操作の簡易性は日本でも多くの方々がすでに良い記事を書いてくださっているので説明はそちらに譲ります。(記事のリンク等は参考の節にまとめました)

さて、go パッケージの中にはサブパッケージとして go/parser があり、これを使うとカジュアルにGoのソースコードをASTにしてくれます。実際にどれくらいカジュアルか、あるディレクトリ配下のソースコードを全部パースするコードを書いてみます。

import (
    "go/parser"
    "go/token"
)

fset := token.NewFileSet()
pkgs, first := parser.ParseDir(fset, path, nil, parser.ImportsOnly)

以上です。驚くほど簡単です。さらに go/parser は用途によりパースの内容を定数で変更できます。

parser.Mode で定義してあるのですが、デフォルトの 0ソースコードすべてを解析します。それ以外の用途のためにいくつかのオプションが用意されています。ドキュメントを見ると解説付きでわかりやすく書いてあります。

const (
        PackageClauseOnly Mode             = 1 << iota // stop parsing after package clause
        ImportsOnly                                    // stop parsing after import declarations
        ParseComments                                  // parse comments and add them to AST
        Trace                                          // print a trace of parsed productions
        DeclarationErrors                              // report declaration errors
        SpuriousErrors                                 // same as AllErrors, for backward-compatibility
        AllErrors         = SpuriousErrors             // report all errors (not just the first 10 on different lines)
)

今回は依存しているパッケージを明らかにすることが目的なので、 ImportsOnlyimport 文までを読めば事足ります。先ほどの parser.ParseDir でパースすると、戻り値の pkgs にそのパッケージを構成するすべての情報が入っています。これを解析したASTのルートと考えていいでしょう。

ASTが手に入ったので、次はこれをいろいろと辿りながらいろいろと工作をしていきたくなるわけですが、「木構造を辿る」というような計算機科学でよく出てくる課題のようなプログラムを書かないといけないかというと、そんなことはありません。おあつらえ向きの関数が用意されています。

func Walk(v Visitor, node Node)

Walk という関数名は見たことがありますね。そうです、これは filepath.Walk のように、ASTのルートとWalkのための関数を渡してあげるとあとはよしなに深さ優先探索でASTを辿ってくれるという関数です。ASTの各ノードは次のように定義されています。

type Node interface {
        Pos() token.Pos // position of first character belonging to the node
        End() token.Pos // position of first character immediately after the node
}

このインターフェースを実装した構造体が各ノードになっているわけです。各構造体の定義は go/ast のドキュメントを参照してください。

さて ast.Walk に渡す ast.Visitor ですが、これは適当にそのインターフェースを満たす構造体を宣言するだけです。今回のサンプルでは次のように実装しています。

type packageVisitor struct {
    imports []string
}

func (p *packageVisitor) Visit(node ast.Node) ast.Visitor {
    if node != nil {
        switch n := node.(type) {
        case *ast.ImportSpec:
            is := (*ast.ImportSpec)(n)
            if is.Path != nil {
                p.imports = append(p.imports, (*is.Path).Value)
            }
            return p
        default:
            return p
        }
    }
    return p
}

これも実装が非常に素直です。Visit はノードに着くたびに呼ばれるので、その都度、ast.Node の実際の型によって処理を切り替えてあげるだけです。今回は import の中身だけみたいので、ImportSpecの場合だけ処理すればよいでしょう。さて、つらつらと説明してきましたが、この辺で簡単にサンプルを置いてみます。

もうちょっといろいろやろうと思ったんですが、とりあえずはここまで。この example ディレクトリ内の main.go をビルドしてそのディレクトリで実行すると repos.json 内に書いてある3rd partyパッケージが依存しているパッケージがすべて羅列されます。試しに実行してみると

% ./example
[Processing]: github.com/simeji/jid
[Done]: github.com/simeji/jid
/tmp/src/github.com/simeji/jid :
     "bytes"
     "github.com/bitly/go-simplejson"
     "github.com/nsf/termbox-go"
     "github.com/pkg/errors"
     "github.com/stretchr/testify/assert"
     "io"
     "io/ioutil"
     "os"
     "regexp"
     "sort"
     "strconv"
     "strings"
     "testing"
/tmp/src/github.com/simeji/jid/cmd/jid :
     "flag"
     "fmt"
     "github.com/simeji/jid"
     "github.com/stretchr/testify/assert"
     "os"
     "testing"
...

と、つらつらと雑な感じでつらつらと依存しているパッケージが羅列されます。このサンプルでは一段下までしかやりませんが、go getすると依存パッケージはすべて獲得するので、取得したパッケージを入力にして再帰してあげればすべての依存ツリーを作成することが可能です。(実際の go get の実装ではそのようになっています

先にあげたサンプルはちょろちょろと書きなぐっただけのものですが、ほんの少しの手間でパッケージの依存を明らかにすることが出来ます。開発中のパッケージのオーナー情報と依存パッケージの関係を紐付けておくことで、通知などを行うこともできるでしょう。

おわりに

ということでパッケージ管理の一例を紹介し、それに関連してASTを使った簡単なツールの作成を行ってみました。正規表現reflectgo generate 使っても強力なツールを作ることができますが、ASTを使うことでより強力なツールが作れるようになると思います。冬休みにぜひ試してみて下さい。

参考