Rのつく財団入り口

ITエンジニア関連の様々な話題を書いているはずのブログです。

【Go】VSCodeでGo言語の開発環境を整えてみよう【Golang】【主にWindows・初心者向け】

Visual Studio Code(VSCode)でGoによる開発しよう

 クラウドやコンテナ界隈でもよく聞くGo言語。最近入門したので備忘録を兼ねて、このエントリではVSCodeを使ったGo開発環境構築の手順を記してみます。
 以下、主にWindowsを想定していますが他のOSでもそれほど変わりませんので、適宜読み替えてください。

VSCodeでGo言語開発環境を整えてみよう

余談ですが上のアイキャッチ画像は、Go公式のブログにあるGo's New Brand記載のカラーとGo fonts記載の公式Goフォントを使ってみています。ロゴやマスコットのゴーファー君も透過PNGの画像が用意されていてありがたいです。

Go言語のインストール手順

 ネット上でも各所に載っていますが、この記事でもまとめてみます。

go.dev

  • 公式サイトのDownloadsページから、Windws/Mac/Linux用それぞれのバイナリファイルをダウンロード。
  • Windows: .msiインストーラー形式なのでこれを実行。
    • インストール先がデフォルトはC:\Go\になっているので適宜変更。
    • [Install]ボタンでインストール実施。
    • Windowsシステム環境変数Pathにインストール先の
      {Goのインストール先}¥bin
      を追加。これでコマンドプロンプトからGoのコマンドが実行できるようになる。
    • Windows環境変数GOPATHに任意のフォルダを設定する。ホームの%USERPROFILE%¥go がオススメ。実際の値は
      C:\Users\<username>\go
      のようになる。ここにGoのライブラリが入るので重要。
    • ここでフォルダ名は厳密にはgoでなくともよいのだが、他のアプリケーションや他の用途のファイルが混在しないように注意。基本goにしておく。
  • Mac: .pkgファイルをダウンロードしてインストール。
    • インストール先は /usr/local/go になる。
  • Linux: .tar.gzファイルを解凍して配置。
    • インストール先はMacと同様
    • 環境変数PATH{Goのインストール先}/binを設定。
    • 環境変数GOPATH$HOME/goを設定。

 コマンドプロンプトやターミナルから go version と打って結果が返ってくればインストール成功です。

CMD> go version
go version go1.18 windows/amd64

付属のGoツールでgo envと打つとGoビルドシステムに関する環境変数一覧をずらーっと表示してくれます。このうち、開発者に関係のあるGOROOTGOPATHが正しく設定されているのを確認します。

CMD> go env GOROOT
C:\go1.18 (のような値)
CMD> go env GOPATH
C:\Users\<username>\go (のような値)

VSCodeでの開発環境準備

 Go言語のプログラムの実体は拡張子.goのテキストファイル+αなので、基本は何を使っても書けます。vimemacsなどでガシガシ書いている強者の方も割とおられますね。往年のEclipseIntelliJ IDEAにもGo用のプラグインがあります。
 Go専用のIDEでは、JETBRAIN社の一連のIDEシリーズの中にGo専用のGoLandというIDEがあり、補完もかなり強力なようです。(ただし例によって有償...)

www.jetbrains.com

 OS間の差をほぼ気にせず開発できるGoとしては、クロスプラットフォームな開発ツールを使うのも良いでしょう。というわけで本エントリではモダンな開発の超定番、VSCodeを使うことにします。ネット上の情報を見てもだいたい皆さんVSCodeを使っている方が多いようです。

拡張機能『Go』を入れる

 拡張機能の検索窓で"go"と打つと出てくる、言語の名前と同じそのまんまの『Go』が公式の拡張機能です。これをインストールしてVSCodeを再起動します。

VSCode拡張機能の『Go』

この拡張機能『Go』は元はVSCode自体の開発チームがメンテしていましたが、2020年にGoチームに担当が移ったそうで力が入っているのが分かります。またこの記事によると、Go開発者の41%がVSCodeを使っているとあります。

atmarkit.itmedia.co.jp

 この拡張機能『Go』の実体はGitHub上にあり、リポジトリ名はvscode-goとなっています。

github.com

 インストール後、VSCodeで後WindowsならCtrl + Shift + p でコマンドパレットを開き、"gotools"などと入力すると以下のコマンドが候補に出ます。

> Go:Install/Update Tools

しばらく待つと一緒にインストールできる標準のツール類が一覧表示されます。Goのバージョンやインストールした時期によって違うようですが、実際にやったときは以下の8つが出ました。

  go-outline  (JSON表現を抽出するためのシンプルなユーティリティ)
  gotests  (特定のソースファイルまたはディレクトリ全体のテスト生成)
  gomodifytags  (?)
  impl  (インターフェースを実装するためのメソッドスタブ生成)
  goplay (Webインターフェース)
  dlv (Goのデバッガ)
  staticcheck (静的チェッカー)
  gopls (Go言語用のLanguage serverのデファクト)

表示されたものはすべてインストールすべき基本ツール類ですので、全てチェックしてOKで実行します。ネットワークアクセスが走ってインストールが行われます。

ネットワーク問題でツールのインストールが失敗する場合

 社内イントラネットからプロキシ経由でWebに繋いでいる場合などで、上記ツールのインストールが失敗することがあります。VSCodeのコンソール窓を調べていくと以下のように表示されます。

Installing github.com/ramya-rao-a/go-outline@latest FAILED
Installing github.com/cweill/gotests/gotests@latest FAILED
Installing github.com/fatih/gomodifytags@latest FAILED
Installing github.com/josharian/impl@latest FAILED
Installing github.com/haya14busa/goplay/cmd/goplay@latest FAILED
Installing github.com/go-delve/delve/cmd/dlv@latest FAILED
Installing honnef.co/go/tools/cmd/staticcheck@latest FAILED
Installing golang.org/x/tools/gopls@latest FAILED

 僕の場合もこれが起こり、GitHubや別のGitリポジトリには繋がるしVSCode拡張機能は繋がるしなんでだろう...としばらくハマりました。
 解決方法の一つは、コマンドプロンプトでプロキシの環境設定を正しく設定した後、Go言語付属のコマンド go install {リポジトリのFQDN}/{リポジトリのパス} で直接インストールすることでした。

CMD> SET http_proxy=http[s]://{プロキシサーバーのホスト}:{プロキシサーバーのポート}
CMD> SET https_proxy=%http_proxy%

上記はWindowsの場合で、Linuxなら export http_proxy=... のようになります。場合によっては{ユーザー}:{パスワード}@をホストの前に付けるケースもあります。
その後同じコマンドプロンプト上で go install {パッケージのURL} を順番に叩いていくことでインストールできました。

CMD> go install github.com/ramya-rao-a/go-outline@latest
CMD> go install github.com/cweill/gotests/gotests@latest
CMD> go install github.com/fatih/gomodifytags@latest
CMD> go install github.com/josharian/impl@latest
CMD> go install github.com/haya14busa/goplay/cmd/goplay@latest
CMD> go install github.com/go-delve/delve/cmd/dlv@latest
CMD> go install honnef.co/go/tools/cmd/staticcheck@latest
CMD> go install golang.org/x/tools/gopls@latest

 VSCode上でGo:Install/Update Toolsを実行すると内部で上のgo installコマンドを呼び出していたので、Goコマンドがプロキシを経由して実行できるようにしてあげる必要があったわけですね。

 現在のGo言語付属のコマンドでは、go installでこうしたパッケージ類をインストールすることになっています。ちょっと前まではgo getでやっていたので、ネット上の少し前の情報だとそう書かれていることもあります。ご注意ください。
 2021年2月Go1.16から、新規バイナリのインストールにはgo installを使うようになっています。
 このエントリを書いている時点でgo get -v -u {パッケージのURL} などを叩くと、以下のように今はgo install を使うのでござると英文で教えてくれます。

go: go.mod file not found in current directory or any parent directory.
        'go get' is no longer supported outside a module.
        To build and install a command, use 'go install' with a version,
        like 'go install example.com/cmd@latest'
        For more information, see https://golang.org/doc/go-get-install-deprecation
        or run 'go help get' or 'go help install'.
ツール類の確認方法

 VSCodeのコマンドパレット経由/コマンドプロンプト経由でツール類がインストールされると、環境変数GOPATHで指定されたフォルダ配下に実物がインストールされます。更新日が新しくなっていることなどで確認できます。

C:/Users/<username>/go
  bin/
    // 今回の例だと、合計8つのツールの実行ファイル.exeが新たに配置
    dlv.exe
    ...
  pkg/
    // ツールのパッケージの格納場所
  src/
    // ユーザが定義したプロジェクトのソース置き場と標準でなっている場所

 これでめでたくVSCode上で拡張子.goのファイルを編集するとGoのソースファイルと認識され、コード補完やフォーマッティングが動くようになります。

VSCodeの設定ファイルsettigs.jsonの設定

言語個別の設定

VSCodeの歯車マーク-[設定] の後の右上の小さなアイコン [設定(JSON)を開く] クリックで直接設定できるsettings.json。複数言語でVSCodeを活用し、フォーマッティングの拡張機能Prettierを使っている方は、このsettings.jsonファイルの第一階層に言語ごとの設定を書いているのではと思います。

{
    // 中略...
    "[javascript]": {
        "editor.tabSize": 2
        "editor.insertSpaces": true,
        "editor.formatOnSave": false, // 重いときはコマンド呼び出しでフォーマット
        "editor.defaultFormatter": "esbnb.prettier-vscode"
    },
    "[typescript]": {
        "editor.tabSize": 2
        "editor.insertSpaces": true,
        "editor.formatOnSave": true, // ファイル保存時にPrettierでフォーマット
        "editor.defaultFormatter": "esbnb.prettier-vscode"
    },
}

ここと同じ並びにGo言語用の設定も追加しておきましょう。

    "[go]": {
        "editor.tabSize": 2, // 公式のサイズは不明。2か4
        "editor.insertSpaces": false, // Go公式はスペースでなくタブ
        "editor.formatOnSave": true, // ファイル保存時にフォーマット
        "editor.defaultFormatter": "golang.go" // 変える
    },

 以下、それぞれの設定項目の解説です。

"editor.tabSize":
 Go言語公式のインデント桁数は明記されていないようです。手近な情報で確認してみました。

2桁:

  • 入門本の定番『スターティングGo言語』
  • Webアプリケーション開発を扱った『Goプログラミング実践入門』

4桁:

 なお商業本『改訂2版 みんなのGo言語』の中は章によって混在でした。

 サーバーサイドのプログラミング言語ではRubyScalaを除くとだいたい4桁が伝統でした。でもモダンな今どきの開発では2桁がもう主流だし2桁かな...?と思いきや、意外と4桁としているところも多いですね。まあここはお好みで。

"editor.insertSpaces":
 僕も最初に知ったとき割と驚いたのですが、Go公式のインデントの指定はスペースではなく「タブ」なんですね。なのでここはfalse固定です。

"editor.formatOnSave":
 trueにしておくと、ソースファイルを保存した瞬間にGo付属コマンドのgo fmtを打った時と同様に公式で決まっている形式にフォーマットしてくれます。書き方に悩まなくて済むので楽です。宗教戦争を合理的に回避しているGoらしい思想です。
 ソースファイルが長くなったり他のファイル数が多くなったりでだんだん動作が遅くなってきたら、falseにしておくこともできます。この場合はVSCodeのコマンドパレットで"format"などと打つと出てくるドキュメントのフォーマット(Format Document)コマンドで同じことが実行できます。このへんの動きは他の言語と同じです。

"editor.defaultFormatter":
 デフォルトのフォーマッターはフロントエンド開発だと拡張機能Prettierでおなじみの"esbnb.prettier-vscode"にしていることが多いかと思います。

marketplace.visualstudio.com

フロントエンド界隈の技術がターゲットなのでPrettierにはGoのフォーマットはないんですね。この状態だとVSCodeが警告を出してくるので、上の設定例のように固定値でGo公式の"golang.go"を指定します。

 このへんは仕事など本格的な開発でプロジェクトやプロダクトや開発チームの固有ルールがある場合は、そちらに従ってください。

indent-rainbowの設定

 またインデントを色分け表示してくれて楽しい拡張機能indent-rainbowをお使いの方は、editor.tabSizeと同じ値を設定しておくとGoコードのインデントも虹色になってくれます。

    "indentRainbow.indentSetter": {
        "[javascript]": 2,
        "[typescript]": 2,
        "[ruby]": 2,
        "[php]": 4,
        "[python]": 4,
        "[go]": 2, // ここにeditor.tabSizeと同じ値を追加
    },

その他のGo向けVSCode拡張機能

 他にGo開発でほぼ必須になるようなレベルの個別のVSCode拡張機能は、特には見当たりませんでした。Go言語自体が備えている充実した標準パッケージやコマンド類、上の拡張機能『Go』で一緒に入るものでだいたい間に合うということでしょうか。さすがに開発環境回りはコンパクトにまとめていますね。
 Go言語は開発環境をVSCodeに限定しているわけではないので、VSCode拡張機能ではなくGo自体のパッケージとして様々なツール類を展開しているというのもあります。コマンドで実行できる系のツールもいろいろありますね。

 Go言語に限定しない、VSCodeのおすすめ拡張機能はネットでも多数見つかります。
当ブログではVSCodePHP開発環境を整える記事で一覧を挙げていますので、こちらも併せてどうぞ。

iwasiman.hatenablog.com

VSCode自体を扱った商業本も何冊かありますが、2022年現在でもまた新しいのが出たようです。

定番のものがこちらの3冊。

徹底解説Visual Studio Code

徹底解説Visual Studio Code

Amazon

新規プロジェクトの作成方法

別パッケージ内が呼べない場合がある!?
udemy-gogo-golang
  sectionXXX/
    foo/
      bar.go // Bar関数が定義されている
    main.go
main.goの中身
package main //sectionXX
import (
    "fmt"
    "foo"
)

func main() {
    fmt.Println(foo.Bar())
}

 任意の場所に作ったプロジェクトで、上のようにmain関数から別パッケージ内を呼ぼうとするとエラーが起こりました。

"foo"
could not import foo (cannot find package "foo" in any of
C:\go1.18\src\foo (from $GOROOT)
C:\Users\<username>\go\src\foo (from $GOPATH))/
"./foo"
 main.go:7:2: "./foo" is relative, but relative import paths are not supported in module mode

 またパッケージ内に_test.goのようなテスト用ファイルを作って一括で実行しようとした場合も同様のエラーが起こりました。

>go test
go: cannot find main module, but found .git/config in D:\xxx\udemy-gogo-golang
        to create a module there, run:
        cd .. && go mod init

 調べて分かったのですが、パッケージを指定しても環境変数GOROOTGOPATHの下しか見てくれないんですね。元々はGo言語の開発はGOPATH/src/にプロジェクトごとのディレクトリを掘って作るのが普通だったそうです。
 これは他言語で開発してきた方にはけっこう受け入れがたいのではないでしょうか。僕も最初に知った時、例えばWindowsであれば
C:\Users\<username>\go\src\some_project\.... でしか開発できないとか正気か?と面食らいました。(笑)

 このへんも進化しており、外部パッケージの管理システムGo Modulesが途中からGo本体に採用されています。2018/8リリースのGo 1.11でGo Modulesが登場、このころはGOPATHモードとModuleモードが両方。
2019/3リリースのGo 1.13で正式にModuleモードがデフォルトになっています。また過渡期ではGoが使う環境変数GO111MODULEがあり、手動で"on"という値を入れる必要があった時期もありましたが、その後デフォルト値が"on"になり、もうこの環境変数自体を気にしなくてよくなりました。

 2019年3月~ということで、このGo Modulesの話はWeb上の情報、学習プラットフォーム、商業書籍でも触れられていないことがけっこうあります。ググる際は情報の発信日にご注意ください。
 Goに入門した方のブログ記事などを見ると、このGo Modulesの話はコードを書き始める前にもっと早く知りたかった...という声を割と見かけます。(僕もです!w)

Go Modulesを使ってプロジェクトを作る(GOPATH配下)

 Go言語では同じディレクトリ配下のファイルに置かれ、ソース先頭にpackage fooなどと記されたものが同一パッケージの扱い。そして複数のパッケージの集合で一緒にリリースされ、ひとつの実行ファイルに入るものを「モジュール」と呼びます。このエントリで「プロジェクト」と言っているものとイコールになります。

 安直ですが勉強用にudemy-gogo-golangというリポジトリがあり、これをマシン上にクローンしたとします。 まずは {環境変数GOPATHの場所}/src/udemy-gogo-golang に取ってきたとします。

CMD> cd {環境変数GOPATHの場所}/src/udemy-gogo-golang
CMD> go mod init udemy-gogo-golang (モジュール名を入力)
go: creating new go.mod: module udemy-gogo-golang
go: to add module requirements and sums:
        go mod tidy

 最初に> go mod init でモジュールが作られます。この時、GOPATH配下の場所で作った時に限り、モジュール名を省略しても自動でフォルダ名が使われました。
 このコマンド実行でそのフォルダ配下がGo Modules管理下に入り、プロジェクトのルートに go.mod というファイルが自動で作られます。go.mod の中身は以下のようなテキストファイルです。

module udemy-gogo-golang

go 1.18

これにより他のパッケージも参照でき、> go testでまとめてテスト実行した場合もフォルダの階層関係が正しく認識されて動くようになりました。

Go Modulesを使ってプロジェクトを作る(任意の場所)

今度は {任意の場所}/udemy-gogo-golang にクローンしてきたとします。

CMD> cd {任意の場所}/udemy-gogo-golang
CMD> go mod init udemy-gogo-golang //ここではモジュール名必須!
go: creating new go.mod: module udemy-gogo-golang
go: to add module requirements and sums:
        go mod tidy

ふつう入力するので問題ないと思いますが、> go mod init の後のモジュール名は入力しないと自動で認識はしてくれませんでした。GOPATH配下の時とまったく同じようにgo.mod ファイルが作られます。

udemy-gogo-golang
  sectionXXX/
    foo/
      bar.go // Bar関数が定義されている
    main.go
  go.mod // ルートに自動生成
main.goの中身
package main //sectionXX
import (
    //VSCodeだと自動でパスが挿入、手動でエイリアス定義が必要
    foo "udemy-gogo-golang/sectionXX/foo"
    "fmt"
)

func main() {
    fmt.Println("fooの中のBar関数を実行", foo.Bar())
}

上記のようにしてコンパイルエラーも起こらず実行できました。なおimport文のところは、GOPATH配下にプロジェクトを作成した場合は"foo"でも行けたのですが任意の場所にプロジェクトを作った場合は"udemy-golang-webgosql/sectionXX/foo"のようにルートからの絶対パスが自動で入りました。これは例がアレなのでこういうパスになっていますが、実際の開発で外部パッケージを使う場合は"github.com/accountname/go-xx-tool"のようなパスが入ることになります。
 Go側で階層構造を認識する際にこの情報を使っているようです。書籍『みんなのGo言語』でも、サブパッケージをインポートする際には ./fooのような相対パスでなく、上記のような絶対パスを使うよう推奨されています。

*_test.go のテスト用ファイルを作った場合も、Go Modulesで認識されていれば正常に動かすことができます。

> cd udemy-gogo-golang/sectionXX
> go test
PASS
ok      udemy-gogo-golang/sectionXX 5.580s
> go test ./foo //パッケージに限ったテストは、./の相対パス。
?       udemy-gogo-golang/sectionXX/foo    [no test files] //_test.goがない時
> go test ./... //全パッケージの全テストファイルを実行する場合

 他にもGo Modulesにはいくつかのコマンドがあり、生成されるgo.modgo.sumなどのファイルは基本的には手動で弄らなくてもコマンド実行時で自動で書き換わり、モジュールを管理してくれます。JavaScriptのnpmでルートに生成されるpackage.jsonと同じような動きですね。"go modules 使い方"などでググると情報があります。以下のページなどが参考になります。

おまけ:main関数が複数あるプロジェクトの場合

 業務だとそんなプロジェクトありませんが、学習用のコードだと短いファイルにその都度main関数を書いたり、同じような構造体を何回も定義したりすることがあるかもしれません。

udemy-gogo-golang/
  section1/
    main.go // mainパッケージ。中にmain関数があったり、構造体Userがあったり
    main2.go // ここにもmain関数、同じ構造体...
  section2/
    main1.go // 同じ
    main2.go // 同じ

 上記の構造で学習が進むたびに1ファイルごとに> go run したりするのは問題なかったのですが、Go Modules を使ってルートにgo.modファイルができたとたんにフォルダ内の構造が正しく認識され、チェックが走ってmainパッケージが複数ある! main関数が複数ある! 同名の関数が複数ある! 同じ構造体が何度も定義されてる!と厳格なことで知られるGoコンパイラ様が大量のエラーを吐くようになりました。
 まあそりゃそうだという話なんですが、Go Modulesで管理されているモジュール(プロジェクト)だと認識されるとこういう動きになるんですね。このケースでは下記のようにさらにフォルダを掘ることで、mainパッケージやmain関数がそれぞれのファイルごとにあっても問題なく動作するのが分かりました。

udemy-gogo-golang/
  section1/
    main1/
      main.go // mainパッケージ。中にmain関数があったり、構造体Userがあったり
    main2/ // フォルダ名は自由
      main2.go // ここにもmain関数、同じ構造体...
  section2/
    sec2-1/
      main21.go // 同じ
    sec22hoge/
      main22.go // 同じ
      foo/
        bar.go // mainの下のサブパッケージも認識できた
CMD> cd udemy-gogo-golanng/section2/sec22hoge/
CMD> go run main22.go // コンパイル&実行できる

 Goの言語仕様ではディレクトリ名(フォルダ名)とパッケージ名が等しくなくても動くので、こういう動作になるわけですね。いろいろ試すと面白いです。

 以上、VSCodeでGo言語開発環境を構築する話でした。

ほか、Go言語が学べるおすすめ本リストは以下のエントリもどうぞ。

iwasiman.hatenablog.com

VSCodeでGo言語開発環境を整えてみよう