以前から Golang (Go 言語) にはポスト C 言語として興味があった。 マシン語を出力できる低レイヤー性にもかかわらず、スクリプト言語に近い記述しやすさも併せ持つというのはなかなかに魅力的だ。 今回はその Golang に関する概念を調べつつ開発環境を整えていくことにする。
環境は表題の通り Mac OS X を使っている。
$ sw_vers ProductName: Mac OS X ProductVersion: 10.10.5 BuildVersion: 14F27
Golang をインストールする
まずは Golang は Homebrew を使ってインストールする。 Homebrew が入っていない場合には、あらかじめ入れておこう。
$ brew install go
これで Golang に関する様々な操作を行うことのできる go コマンドが使えるようになった。
$ go version go version go1.5.1 darwin/amd64
Hello, World!
やっぱり最初はあれだよね、ということで以下のソースコードを用意する。 実行可能ファイルにビルドするソースコードの場合、必ず main という名前で package を宣言する。 その上で main() 関数を用意すると、そこがエントリポイントになる。
$ cat << EOF > helloworld.go package main import ( "fmt" ) func main() { fmt.Println("Hello, World!") } EOF
go run サブコマンドを使うことでソースコードをビルドすると同時に実行できる。
$ go run helloworld.go Hello, World!
ばっちり。
作業スペース ($GOPATH) を用意する
先ほどのような単独のソースコードなら何も考える必要はないけど、ちょっと複雑なことをやろうとする場合には環境変数 $GOPATH を定義する必要がある。 これは Golang を使ったプロジェクトの作業スペースのように考えれば良いようだ。
以下のように適当なディレクトリを $GOPATH として定義した上で、その bin ディレクトリにパスを通しておく。 $GOPATH に指定するディレクトリは単なる作業スペースなので、本当に適当な場所を選んで構わない。
$ export GOPATH=$HOME/go $ export PATH=$PATH:$GOPATH/bin
必要に応じてシェルの起動時に読み込むよう設定ファイルに残しておく。
$ echo 'export GOPATH=$HOME/go' >> ~/.zshrc $ echo 'export PATH=$PATH:$GOPATH/bin' >> ~/.zshrc
次に、指定した $GOPATH のディレクトリを作成する。
$ mkdir -p ${GOPATH}
試しに先ほど作成したソースコードを $GOPATH を使ったバージョンで書いてみよう。 まず、ソースコードは全て src ディレクトリ以下に用意することになる。 src ディレクトリ以下はディレクトリを使って名前空間を分割する。 ソースコードを GitHub で管理する場合には、以下のように github.com の後にユーザ名、その後にプロジェクト名を入れる。
$ cd ${GOPATH} $ mkdir -p src/github.com/example/helloworld
作成したディレクトリ (パッケージ) 以下にソースコードを用意する。
$ cat << EOF > src/github.com/example/helloworld/greet.go package main import ( "fmt" ) func main() { fmt.Println("Hello, World!") } EOF
go install サブコマンドを使ってソースコードをビルドすると共にインストールする。
$ go install github.com/example/helloworld
これで bin ディレクトリができると共にビルド済みのバイナリができる。
$ ls bin src $ ls bin helloworld $ file bin/helloworld bin/helloworld: Mach-O 64-bit executable x86_64
実行してみよう。
$ helloworld Hello, World!
いいかんじ。
ライブラリを作ってみる
先ほどのパッケージは実行可能ファイルにビルドするものだったので、今度は別のパッケージから利用されるライブラリを作ってみる。
新しく calc というパッケージ用のディレクトリを作成しよう。
$ mkdir -p src/github.com/example/calc
ふたつの変数を加算する Add という関数を定義したソースコードを用意する。
$ cat << EOF > src/github.com/example/calc/add.go package calc func Add(x int64, y int64) int64 { return x + y } EOF
go build コマンドで calc パッケージをビルドする。 特にエラーメッセージが出なければ上手くいっている。
$ go build github.com/example/calc
今度は helloworld パッケージの方に calc パッケージを利用したソースコードを用意する。 calc パッケージを $GOPATH/src 以下のディレクトリ構造を表した形で import している点に注目。
$ cat << EOF > src/github.com/example/helloworld/greet.go package main import ( "fmt" "github.com/example/calc" ) func main() { fmt.Printf("Add(%v, %v) = %v\n", 1, 2, calc.Add(1, 2)) } EOF
できたら helloworld パッケージをインストールする。
$ go install github.com/example/helloworld
実行してみよう。
$ helloworld Add(1, 2) = 3
ばっちり。
ライブラリを毎回コンパイルしなくて済むようにする
先ほどの状態ではビルド時にライブラリを毎回コンパイルすることになるはず。 これをやめさせるにも実行可能ファイルを作ったのと同様に go install サブコマンドを使う。
$ go install github.com/example/calc
これで pkg ディレクトリ以下にコンパイル内容がキャッシュされる。
$ ls bin pkg src $ tree pkg pkg └── darwin_amd64 └── github.com └── example └── calc.a 3 directories, 1 file
蛇足だけど、上記の tree コマンドが入っていない場合は Homebrew でインストールしよう。
$ brew install tree
ユニットテストを書いてみる
最近のプログラミングにはテストがないとお話にならない。 Golang にはユニットテスト用の機能も同梱されている。
テスト用のソースコードは、テスト対象のソースコードの名前に _test をつけた形で用意する。 テストの関数には (t *testing.T) というシグネチャを持たせよう。 そして、t.Errorf() 関数が呼び出されるとテストが失敗したことを意味している。
$ cat << EOF > src/github.com/example/calc/add_test.go package calc import "testing" func TestAdd(t *testing.T) { const x, y, expected = 1, 2, 3 result := Add(x, y) if result != expected { t.Errorf("Add(%v, %v) = %v, want: %v", x, y, result, expected) } } EOF
go test サブコマンドでパッケージを指定すると、自動的にテストが実行される。
$ go test github.com/example/calc ok github.com/example/calc 0.006s
ちなみに、テストコードを失敗するようにいじって実行した場合の結果は次の通り。
$ go test github.com/example/calc --- FAIL: TestAdd (0.00s) add_test.go:9: Add(1, 2) = 3, want: -1 FAIL FAIL github.com/example/calc 0.006s
サードパーティ製のライブラリを利用する
標準ライブラリだけでは不足している機能を補う上で、サードパーティ製のライブラリは重要となる。
サードパーティ製のライブラリは go get サブコマンドを使うと簡単にインストールできる。 例えば GitHub で公開されている JSON を扱うためのライブラリ go-simplejson をインストールするには、以下のようにする。
$ go get github.com/bitly/go-simplejson
すると、src 以下に go-simplejson がダウンロードされる。 そう、つまり Golang の場合サードパーティ製ライブラリをインストールするというのは、結局のところソースコードを src 以下に展開するということに過ぎないらしい。
$ ls src/github.com/bitly go-simplejson
もちろんダウンロードされるだけでなくビルドも行われる。
$ tree pkg pkg └── darwin_amd64 └── github.com ├── bitly │ └── go-simplejson.a └── example └── calc.a 4 directories, 2 files
インストールした go-simplejson を使ってみよう。 使い方は自分で先ほど作った calc パッケージと変わらない。 src 以下のディレクトリ構造の表現をそのまま import するだけだ。
$ cat << EOF > src/github.com/example/helloworld/greet.go package main import ( "fmt" "github.com/bitly/go-simplejson" ) func main() { json := simplejson.New() json.Set("message", "Hello, World!") b, _ := json.EncodePretty() fmt.Printf("%s\n", b) } EOF
ビルドして実行してみよう。
$ go install github.com/example/helloworld $ helloworld { "message": "Hello, World!" }
いいかんじ。
自作のプロジェクトを GitHub で管理する
もうお分かりだろうけど、ようするに src 以下のディレクトリを GitHub に突っ込めばいいだけ。
$ cd ${GOPATH}/src/github.com/example/calc $ git init $ git add -A $ git commit -m "Initial commit" $ git remote add origin git@github.com:example/calc.git $ git push origin master
gdb でデバッグする
さすがに printf デバッグするのはつらすぎるのでデバッガを導入する。
gdb をインストールする
Homebrew で gdb をインストールしよう。
$ brew tap homebrew/dupes $ brew install gdb
ただ、ここからの道のりがちょっとばかし長い。 何故なら gdb は Gatekeeper によって実行が制限されている。 そこで、まずは gdb を自己署名証明書でサインする必要がある。
自己署名証明書を作る
まずはキーチェーンアクセスを開く。
$ open "/Applications/Utilities/Keychain Access.app"
次に、メニューバーから以下を選択して証明書の作成ウィンドウを開く。
キーチェーンアクセス > 証明書アシスタント > 証明書を作成
適当な名前をつけてコード署名の証明書を作ろう。 今回は以下のようにした。
- 名前: selfsigned.gdb
- 固有名のタイプ: 自己署名ルート
- 証明書のタイプ: コード署名
以下のような警告画面が出るが、そのまま「続ける」を選択する。
作成した証明書を、「ログイン」から「システム」の項目に移動させる
移動できたら、証明書をダブルクリックする。 開いたウィンドウの「信頼」を展開して、「コード署名」の項目を「常に信頼」にする。
何か聞かれるので「許可」する。
これでやっとターミナルに戻ってこられる。 長かった…。
証明書で gdb にサインをする
一連の作業が終わったら、念のため taskgated のプロセスを再起動するために kill する。
$ sudo kill $(pgrep taskgated)
作成した証明書で gdb にサインする。
$ sudo codesign -s selfsigned.gdb $(which gdb)
gdb で Golang のコードをデバッグする
まずはデバッグしたいパッケージをデバッグ用の情報を残す形でビルドし直す。
$ go install -gcflags "-N -l" github.com/example/helloworld
あとは普通に gdb を使うだけ。 ポイントはブレークポイントの打ち先を main() 関数にする場合は、パッケージ名も含めて main.main にする必要がある点かな。
$ gdb -q $(which helloworld) Reading symbols from /Users/amedama/go/bin/helloworld...done. Loading Go Runtime support. (gdb) b main.main Breakpoint 1 at 0x2040: file /Users/amedama/go/src/github.com/example/helloworld/greet.go, line 8. (gdb) run Starting program: /Users/amedama/go/bin/helloworld [New Thread 0x114b of process 2029] [New Thread 0x1203 of process 2029] [New Thread 0x1303 of process 2029] [New Thread 0x1403 of process 2029] Breakpoint 1, main.main () at /Users/amedama/go/src/github.com/example/helloworld/greet.go:8 8 func main() { (gdb) n 9 json := simplejson.New() (gdb) n 10 json.Set("message", "Hello, World!") (gdb) c Continuing. { "message": "Hello, World!" } [Inferior 1 (process 2029) exited normally] (gdb) quit
複数の作業スペース ($GOPATH) を持ちたい場合
作業スペース ($GOPATH) を何らかのポリシーで分けて用意した上で、それを切り替えて使いたい場合もありそう。 そんなときは direnv を使うのが楽っぽいかな。
direnv の使い方については、以下の記事で解説している。
まずは direnv をインストールする。
$ brew install direnv
次に、$GOPATH にしたいディレクトリを作成する。
$ mkdir -p /tmp/example
そこに、direnv の設定ファイルを作成する。 ${PWD} を $GOPATH に指定しているところがポイント。
$ cat << EOF > /tmp/example/.envrc export GOPATH=\${PWD} export PATH=\$GOPATH/bin:\$PATH EOF
direnv の管理対象に作成したディレクトリを追加する。
$ direnv allow /tmp/example
そのディレクトリに移動すると設定が読み込まれて $GOPATH が書き換わった上でパスが通る。
$ cd /tmp/example direnv: loading .envrc direnv: export ~GOPATH ~PATH
go get サブコマンドを使うと、先ほどまで使っていた $HOME/go の代わりに現在のディレクトリの src 以下にパッケージがインストールされることがわかる。
$ go get github.com/bitly/go-simplejson $ tree src src └── github.com └── bitly └── go-simplejson ├── LICENSE ├── README.md ├── simplejson.go ├── simplejson_go10.go ├── simplejson_go10_test.go ├── simplejson_go11.go ├── simplejson_go11_test.go └── simplejson_test.go 3 directories, 8 files
完璧。
まとめ
今回は Mac OS X で Golang を使った開発環境の下地を整えてみた。 パッケージやライブラリの作成からユニットテストの書き方、サードパーティ製ライブラリの利用、ソースコードのデバッグなどなど、ひとまず最初に気になるようなところはおさえることができたかな?