ちょっと話題の記事

Go言語の開発環境セットアップとサンプルプロジェクト作成

Javaのエンジニアだった私がGo言語を始める上で学んだ開発環境のセットアップ方法についてまとめていきたいと思います。 Go言語をインストール後サンプルプロジェクトを作成し、Goのプロブラムの実行/ビルド方法や各種周辺ツールをご紹介していきます。
2021.09.15

はじめに

Javaのエンジニアだった私がGo言語を始める上で学んだ開発環境のセットアップ方法についてまとめていきたいと思います。
Go言語をインストール後サンプルプロジェクトを作成し、Goのプロブラムの実行/ビルド方法や各種周辺ツールをご紹介していきます。
この記事がこれからGo言語で開発を始めたい方のとっかかりになれば幸いです。
※他言語で開発経験がある方を想定しております。

動作環境

今回使用した動作環境は以下のとおりです。

  • PC : Mac M1(Apple Silicon)チップ
  • OS : macOS Big Sir 11.5.2
  • Go : 1.17.1

Go言語のインストール

始めはローカルPCで使用するGo言語のバージョンを簡単に切り替えれるようにgoenvを使うつもりでしたが、以下の理由で思い止まりました。

  • この記事を書いている最中、以前のバージョンのGoにセキュリティイシューが見つかった。最新バージョンとして1.17.1がリリースされたが、goenvに反映されるまで少し時間がかかりそうだった。実際に商用環境で稼働中のサービスを開発/運用している場合、このタイムラグは問題になりそう。
  • Go言語は後方互換性についてのルールがあるため、「最新バージョンにアップデートしたら以前のバージョンで動いていたプログラムが動かなくなった。」というようなバージョンの違いによる問題が起こることはあまりなさそう。
  • もしプロジェクトにより複数バージョンのGo言語を使い分けなくてはならないケースがあったとしても、公式でも複数バージョンを管理する方法が提供されている。参考リンク : Installing multiple Go versions複数バージョンのGoをインストールする

セキュリティ的な事情等も考慮するといちはやく最新のバージョンに追随できる環境であることが望ましいと考え、今回は公式ページからInstaller版をダウンロードし最新バージョンのGo言語をインストールすることにしました。
Mac M1にGO言語をインストール方法の記事を参考にインストール後読み進めてください。

インストールが正常に完了したか下記コマンドで確認します。

% go version
go version go1.17.1 darwin/amd64
% where go
/usr/local/go/bin/go

Installer版でインストールした場合は、PATH等の設定も自動で行ってくれています。
下記コマンドで確認します。表示結果は私の環境の場合の設定です。

% echo $PATH | sed -e 's/:/\n/g'
~
/usr/local/go/bin
~

GOPATHについて

GOPATHとは、外部パッケージやGoでビルドしたコンパイル済の実行プログラムなどが置かれる場所を定義する環境変数で、デフォルトではユーザホームディレクトリ内のgoフォルダ($HOME/go)となっています。下記コマンドで確認できます。

% go env GOPATH

GOPATH配下には以下のフォルダが作成されます。(Go言語をインストールしたばかりだと未作成)

  • pkg : 外部パッケージなどが置かれます。 Javaでいうところの、ローカルのMavenリポジトリ(ユーザホームディレクトリの.m2フォルダ)のようなものと理解しています。
  • bin : Goでビルドしたコンパイル済の実行プログラムなどが置かれます。GOBINという環境変数でこの場所は変更可能です。

バージョン1.11以前は、GOPATH配下にさらにsrcというフォルダがあり作成するGoのプログラムは全てこのフォルダに置くというルールで、実質$HOME/go/src配下でしか開発できない状態だったようです。
バージョン1.11以降はGO111MODULEという環境変数をonに設定することで(1.16以降はデフォルトでon)この制約から開放され、$HOME/go/src以外でも開発可能になっています。

またGOPATHと似た概念でGOROOTという環境変数もあります。
これはGoのSDKの場所を定義する環境変数で、以下のコマンドで確認できます。

% go env GOROOT

複数のバージョンのGoを使用しない限り変更することはないと思われますが、IDEなどの設定時にこのGOROOTの場所を指定することがあります。

その他GOPATHについては以下の記事が勉強になりました。

サンプルプロジェクト

それではHello World的な簡単なサンプルプロジェクトを作成していこうと思います。

Go Modulesを使ったプロジェクト作成

Go Modulesは現在主流になっているパッケージ管理ツールです。
タスク自動化などの機能はありませんが、JavaのMaven/Gradle、Node.jsのnpmのような存在と理解しています。
任意のディレクトリで下記コマンドを実行すると新規プロジェクト(モジュール)を作成することができます。

% go mod init <モジュール名>

このコマンドを実行したフォルダがモジュールのルートディレクトリとなりgo.modというファイルが作成されます。
モジュール名は、Web上などに公開する場合は{Repository FQDN}/{Repository Path}/moduleAのような命名規則となっています。公開しない場合はmoduleAのみでも問題ありません。
この記事では任意の場所に作成したhello-worldフォルダをルートディレクトリとし、モジュール名はgithub.com/hoge/hello-worldgo mod initコマンドを実行した前提で進めていきます。

% cd {任意のディレクトリ}
% mkdir hello-world
% cd ./hello-world
% go mod init github.com/hoge/hello-world

その他Go Modulesに関する詳細は以下の記事が勉強になりました。

パッケージと構成

Goのソースコードに含まれる関数や変数は必ず1つのパッケージに属することになります。 また同じフォルダ内に属するファイルは全て同一のパッケージである必要があり、パッケージを1つにまとめたものがモジュールと呼ばれます。

このサンプルプロジェクトでは、下記の構成でファイルを配置してください。

hello-world/  ルートディレクトリ
  ┣ subpkg/  サブパッケージ = サブフォルダに置かれたパッケージ
  ┃ ┣ sub1.go
  ┃ ┗ sub2.go
  ┣ app.go
  ┣ main.go
  ┗ go.mod

サンプルコード

subpkgパッケージのファイル

sub1.go

package subpkg

import (
   "rsc.io/quote/v3"
)

func Hello() (str string) {
   return quote.HelloV3()
}

sub2.go

package subpkg

func Golang() (str string) {
   return "Welcome to the Golang."
}
  • sub1.go4行目のrsc.io/quote/v3はGo Modulesのサンプルによく使われている外部パッケージです。使用するには依存ファイルをダウンロードする必要があります。ダウンロード方法は後述します。

mainパッケージのファイル

app.go

package main

func Goodbye() (str string) {
   return "Goodbye."
}

main.go

package main

import (
   "fmt"
   "github.com/hoge/hello-world/subpkg"
)

func main() {
   fmt.Println(subpkg.Hello())
   fmt.Println(subpkg.Golang())
   fmt.Println(Goodbye())
}
  • mainパッケージのmain関数がエントリポイントとなります。  
  • main.go5行目のようにサブパッケージをインポートする場合、モジュール名をルートディレクトリとしたフルパスで記載する必要があります。(公開しない場合はsubpkgのみで大丈夫です)
  • main.go9,10行目のように別のパッケージに属する関数を呼び出す場合はパッケージ名.関数名()で呼び出す必要があリますが、main.go11行目のように同一パッケージの関数の場合は関数名()のみで呼べ出せます。実は同じパッケージに属するファイルであれば、分割しても同じファイルに記載しても動作的に全く影響はなく(単に読みやすいかや管理しやすいかという問題だけ)、app.goGoodbye関数をmain.goに定義しても何も動作は変わりません。

実行方法

実行前に外部パッケージのダウンロードを行います。ルートディレクトリで下記コマンドを実行してください。

% go get rsc.io/quote/v3

コマンド実行後、go.modファイルにrequireディレクティブが追記されルートディレクトリ直下にgo.sumというファイルが作成されます。go.sumはインポートする外部パッケージのSHA-256 チェックサム値が格納され、インポートする外部パッケージの完全性を担保する役割を果たします。
またダウンロードされた外部パッケージは$GOPATH/pkg配下に配置されます。

外部パッケージのダウンロード後、このサンプルは下記コマンドで実行できます。

% go run *.go
Hello, world.
Welcome to the Golang.
Goodbye.

補足ですが、go runコマンドは引数に指定したファイルのみを実行対象とする(ただしサブパッケージのファイルは含まれる)ので、今回のようにmainパッケージのソースコードを分割した場合全てのファイルを引数に指定する必要があリます。下記のようにmain.goのみを指定するとエラーとなります。

% go run main.go
./main.go:11:14: undefined: Goodbye

ビルド方法

下記コマンドで実行ファイルを作成することができます。

% go build

作成されたファイルを実行してみましょう。

% ls
app.go go.mod  go.sum hello-world main.go subpkg
% ./hello-world
Hello, world.
Welcome to the Golang.
Goodbye.

go install

go installは実行ファイルを作成しさらに$GOPATH/bin配下に配置するコマンドです。

% go install

コマンドを実行すると、$GOPATH/bin配下(GOBIN配下)に実行ファイルが配置されます。
Installer版でGoをインストールした場合、デフォルトでPATHが通っているので、$GOPATH/binフォルダ以外でも実行ファイルがコマンドとして実行できます。

% cd ~
% hello-world
Hello, world.
Welcome to the Golang.
Goodbye.

command not foundとなる場合はTerminalを再起動してみてください。
このgo installは、例えば開発チームで作った自作のコマンドラインツールを各チームメンバーに配布する際などに利用できそうです。

サンプルプロジェクトについては以上となります。

各種周辺ツールについて

gofmt/go fmt

gofmtはGoが標準で提供しているコードフォーマッターです。設定項目が存在しないので単一のスタイルが強制されますが、独自のスタイルが乱立しないというメリットもあります。下記のように対象ファイルを指定して実行しますが、デフォルトだと整形後のソースコードが表示されます。

% gofmt main.go

-wオプションを指定すると対象のファイルが直接書き換えられる挙動になり、さらに-lオプションを指定すると修正を行ったファイル名を表示します。

似たようなコマンドでgo fmtというコマンドがありますが、これはgofmt -l -wと同じ挙動をします。
下記のコマンドでディレクトリ配下の全てのファイルにフォーマットをかけることができます。

% go fmt ./...

go vet

go vetもGoが標準で提供している静的解析ツールで、バグの原因になりそうなコードを検出し警告してくれます。
go fmtも同様ですが、ファイル指定、ディレクトリ指定、階層指定ができます。

% go vet main.go
% go vet .
% go vet ./...

goimports

goimportsgofmt/go fmtの上位互換のツールでコードフォーマットに加えて、不要なimportを削除するなど、import文を整理してくれます。
使用するには下記コマンドでインストールが必要です。

% go install golang.org/x/tools/cmd/goimports@latest

下記コマンドのように対象ファイル指定し実行します。-wオプションを付けなかった場合、表示のみでファイルは修正されません。

% goimports -w main.go

goimportsコマンドはgo vetのように階層指定ができないので、ディレクトリ配下の全てのファイルを整形にするには下記コマンドのようにする必要があります。また-localオプションでimport文のソート順を指定することが可能です。下記コマンドは自作のサブパッケージが一番下に来るように指定しています。

% find . -print | grep --regex '.*\.go' | xargs goimports -w -local "github.com/hoge/hello-world"

staticcheck

staticcheckはGoogleが公式でスポンサーをしているlintツールです。golintというGoが標準で提供するlintツールもあるのですが、残念ながら開発が凍結されてしまいました。現在はstaticcheckgolintに代わるlintツールとして主流になっています。各チェック項目の有効/無効も設定可能です。
使用するには下記コマンドでインストールが必要です。

% go install honnef.co/go/tools/cmd/staticcheck@latest

ファイル指定、ディレクトリ指定、階層指定ができます。

% staticcheck main.go
% staticcheck .
% staticcheck ./...

Makefile

Goではタスク自動化ツールとして、古くからGo以外でも使われるビルドツールのmakeがよく利用されています。
こちらのGo言語開発を便利にするMakefileの書き方の記事がとても参考になりました。
サンプルとして下記のようなファイルを作成してみました。ルートディレクトリ直下に置いてください。

Makefile

format:
	@find . -print | grep --regex '.*\.go' | xargs goimports -w -local "github.com/hoge/hello-world"
verify:
	@staticcheck ./... && go vet ./...

インデントがタブになっていないと実行時に*** missing separator. Stop.というエラーが発生するので注意してください。

下記のようにMakefileに定義したコマンドを実行できます。

% make format
% make verify

最後に

所感ですが、GoはJavaと比べると開発環境の移り変わりが早いと感じました。
この記事の情報もすぐに鮮度が落ちると思うので、継続的に最新の開発トレンドを追いかけていく必要がありそうです。