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

今川館

都内勤務の地味OLです

go buildを試しつつGoのパッケージの規則を覚えた

go buildでバイナリを作る

go buildコマンドを実行すると、カレントディレクトリからmainパッケージを探してバイナリを作ってくれる。

Githubでの管理を念頭に推奨されるプロジェクト構成

  • go getでgithub.comから依存ライブラリを取ってくる
  • importで自己ないし外部プロジェクトのモジュールを参照する

上記2点を考慮すると、以下の記事で紹介されている構成にしておくのが無難らしい。

Golangで自分自身で定義したパッケージをインポートする方法あれこれ - Qiita

$GOPATH/src/github.com/[username]/[appname]
この中で最初から作業をしてしまう.

実際に試してみた

$GOPATH/src/github.com/oyakata/tokyo
├── hello
│   ├── api.go  # helloパッケージ
│   └── main.go # helloパッケージ
└── main.go      # mainパッケージ

まず、tokyo/main.goの内容は以下。

package main

import (
	ikzo "github.com/oyakata/ikzo/hello"
	"github.com/oyakata/tokyo/good"
	tyo "github.com/oyakata/tokyo/hello"
)

func main() {
	ikzo.World() // ikzoのhello.World()を実行
	tyo.World() // tokyoのhello.World()を実行
	tyo.GotoTokyo() // tokyoのhello/api.goに定義した関数
	best.DoBest(1000) // tokyoのgood/main.goに定義した関数
}

わたしは /home/echizen/_go をGOPATHに指定したので、以下の手順でプロジェクトを配備した。

$ mkdir -p /home/echizen/_go/src
$ cd /home/echizen/_go/src
$ git clone git@github.com:oyakata/ikzo.git
$ git clone git@github.com:oyakata/tokyo.git
$ cd tokyo

$ export GOPATH=/home/echizen/_go
$ go build -o tyo  # => tyoという名前のバイナリができる

# あるいは
$ GOPATH=/home/echizen/_go go build -o tyo

$ ./tyo  # tokyo/main.goのmainに書いた処理が実行される

イレギュラーなことも試してみた

ルール通りにやれば何ということもなくビルド&実行できたのだが、いくつかルールを破ってみた場合にどうなるのか試してみた。

対象ディレクトリ配下に複数のmainパッケージのモジュールを置く

例えば、tokyo/の直下にmain_tyo.goとmain_ikzo.goという、mainパッケージのモジュールを2つ置いてgo buildしてみる。

これはコンパイルエラーが出てビルドが失敗する。

echizen@Seigaku:~/_go/src/github.com/oyakata/tokyo$ go build
# github.com/oyakata/tokyo
./main_tyo.go:5: main redeclared in this block
	previous declaration at ./main_ikzo.go:7

異なる名前のパッケージ宣言を持つモジュールを同じディレクトリの中に置く

例えば、tokyo/helloの中で、main.goはhelloパッケージ、api.goはapiパッケージを宣言してみる。

これもコンパイルエラーになる。

echizen@Seigaku:~/_go/src/github.com/oyakata/tokyo$ go build
main.go:6:2: found packages api (api.go) and hello (main.go) in /home/echizen/_go/src/github.com/oyakata/tokyo/hello

ディレクトリと異なる名前でパッケージ宣言してはいけないのか?

例えば、tokyo/goodディレクトリの中で、bestパッケージを宣言してみる。

これはコンパイルが通る。が、インポートすると best という名前で認識される。

package main

import "github.com/oyakata/tokyo/good"

func main() {
	// good.DoBest(1000)  // パッケージ宣言は best なので、goodという名前では参照できない
	// # command-line-arguments
	// ./main_tyo.go:6: imported and not used: "github.com/oyakata/tokyo/good" as best
	// ./main_tyo.go:13: undefined: good in good.DoBest
	best.DoBest(1000) // これは大丈夫
}

或いは

package main

import good "github.com/oyakata/tokyo/good"

func main() {
	good.DoBest(1000)
}

このように、(ディレクトリの名前はgoodにもかかわらず)名前付きインポートでわざわざgoodという名前を指定するといった煩雑なことをしなければならない。
それだったら最初からディレクトリの名前とパッケージ宣言は合わせておけよ、というわけで、やっぱり面倒なことをしないに限る。