direnvで解決するGOPATHの3つの問題点

f:id:doloopwhile:20140618010525j:plain

GOPATH 問題

Gophers!

周知のようにgoはGOPATHで依存パッケージをどこにインストールするか決められます。

入門記事などでは、~/.bashrc~/.zshrcの中で

export GOPATH=$HOME/gocode

または

export GOPATH=$HOME

とすることが多いようです。

しかし、GOPATHを各プロジェクトのパッケージが混ざるので、

  1. 同じパッケージの異なるバージョンを同時にインストールできない
  2. 変なことをすると、全プロジェクトが巻き添えになる
  3. どれが不要なパッケージか分からなくなる

という欠点があります。

そこでdirenv

direnvを使うと、プロジェクトごと(ディレクトリごと)にGOPATHを分ける事ができます。

direnvのインスト―ルと基本的な設定

# HOMEにインストール
$ git clone http://github.com/zimbatm/direnv
$ cd direnv
$ make install DESTDIR=$HOME

# .bashrcに追加
$ echo 'eval "$(direnv hook bash)"' >> ~/.bashrc

例:

# fooプロジェクトの開発専用ディレクトリを作る
# src以下に、プロジェクトのレポジトリをインストール
$ git clone git://github.com/doloopwhile/foo ~/gocode-foo/src/github.com/doloopwhile/foo

# ディレクトリでdirenvの設定
$ cd ~/gocode-foo
$ echo 'layout go' > .envrc

.envrcがdirenv用の設定ファイルです。

cd すると、自動でGOPATHがセットされます!

$ cd ~ && cd ~/gocode-foo/
direnv: loading ~/.direnvrc
direnv: loading .envrc
direnv: export +GOPATH ~PATH

$ echo $GOPATH
/home/taro/gocode-foo

# ~/gocode-foo/src/ 以下に依存パッケージがインストールされる。
$ go get -t ./...

楽になりましたね!

もっと楽にする

ところで、.envrclayout goを書いただけでは、 ~/gocode-foo/src以下には、プロジェクト本体と依存パッケージが区別なく置かれてしまいます。

そこで direnv edit ..envrcに設定を追加します。

$ cd ~/gocode-foo

$ direnv edit .
(エディタが起動するので以下を記入)
layout go
path_add GOPATH .direnv/gocode

すると、依存パッケージは~/gocode-foo/.direnv/gocodeにインストールされるようになります。

.envrcには他にも専用コマンドがあり、普通のシェルコマンドも書けます。 たとえば、PATHをいじって、使うgoコマンドのバージョンを切り替えたりもできるでしょう。 https://github.com/zimbatm/direnv/blob/master/man/direnv-stdlib.1.md

ちなみに~/.bashrcなどでGOPATHを指定していても、ディレクトリ固有のパスが優先されます。 なので、~/.bashrcにはexport GOPATH=~/gocodeを書いておき、

  • プロジェクト用のパッケージは.envrcに指定した~/gocode-foo/.direnvにインストール
  • プロジェクトに無関係な日曜的なツール類は ~/gocode にインストール

と、使い分ければいいと思います。

ちなみに

direnvはシンボリックリンクの問題があり、リンク経由だと、.envrcが読み込まれません。

# 本来のパスを指定すると読み込まれる
$ cd ~/gocode-foo/src/github.com/doloopwhile/foo/
direnv: loading ~/.direnvrc
direnv: loading ../../../../.envrc
direnv: export ~GOPATH ~PATH

$ ln -s ~/gocode-foo/src/github.com/doloopwhile/foo/ ~/foo

# シンボリックリンクだと読み込まれない
$ cd ~/foo
direnv: loading ~/.direnvrc
direnv: loading ../../../../.envrc
/bin/bash: line 133: ./.envrc: No such file or directory

一応、forkしたレポジトリではシンボリックリンクに対応させており、本家にプルリクエスト中です。

ですので、forkを使うか(自己責任)、マージされるまで待ってください。

Gophers!