コンテンツにスキップ

コマンド@Go

はじめに

本サイトにつきまして、以下をご認識のほど宜しくお願いいたします。


01. goコマンド

build

▼ buildとは

指定したパスをビルド対象として、ビルドのアーティファクトを作成する。

foo_test.goファイルはビルドから自動的に除外される。

# cmdディレクトリをビルド対象として、ルートディレクトリにcmdアーティファクトを作成する。
$ go build ./cmd

もし、ビルドのエラー時に終了ステータスのみが返却され、原因が不明の場合、panic関数が原因を握りつぶしている可能性を考える。

exit status 2. Docker build ran into internal error. Please retry. If this keeps happening, please open an issue..

-o

指定したパスにビルドのアーティファクトを作成する。

ビルド対象パスを指定しない場合、ルートディレクトリのgoファイルをビルドの対象とする。

# ルートディレクトリ内のgoファイルをビルド対象として
# $HOME/go/binディレクトリにルートディレクトリ名アーティファクトを作成する。
$ go build -o $HOME/go/bin

また、指定したパス内のgoファイルをビルド対象として、指定したパスにビルドのアーティファクトを作成もできる。

# cmdディレクトリ内のgoファイルをビルド対象として
# $HOME/go/binディレクトリにcmdアーティファクトを作成する。
$ go build -o $HOME/go/bin ./cmd

補足として、事前のインストールに失敗に、ビルド対象が存在していないと以下のようなエラーになってしまう。

package foo is not in GOROOT (/usr/local/go/src/foo)


clean

モジュールのキャッシュを削除する。

ローカル環境での開発中にgo installコマンドを実行しても、モジュールをアップグレードできない場合に使用する。

$ go clean --modcache

$ go mod tidy


env

▼ envとは

Goに関する環境変数を出力する。

*実装例*

$ go env

GO111MODULE="on"
GOARCH="amd64"

...

PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build887404645=/tmp/go-build -gno-record-gcc-switches"


fmt

▼ fmtとは

指定したパスのファイルのインデントを整形する。

パスとして『./...』を指定して、再帰的に実行するのがおすすめ。

$ go fmt ./...


install

▼ installとは

go getコマンドとは異なり、Goのバイナリをパッケージとしてでなく、ツールとしてグローバルにインストールする。

コードと外部パッケージに対してbuildコマンドを実行することにより、$GOPATH以下のbinディレクトリまたはpkgディレクトリにインストール (配置) する。

内部または外部のコードからビルドされたアーティファクト (バイナリファイル) であればbinディレクトリ配下に配置し、それ以外 (例:.aファイル) であればpkgディレクトリ配下に配置する。

$ go install

なお、インストールしたパッケージでreplaceを使用している場合、クローンしてから直接インストールする必要がある。

$ git clone <パッケージのリポジトリ>
$ cd <バイナリのディレクトリ>
$ go install

バイナリは、パスが通っていないため、直接パスを指定して実行する必要がある。

$ ${GOPATH}/bin/foo-package --version

▼ 削除

go installコマンドでインストールしたバイナリは、rmコマンドで直接削除する必要がある。

$ ls ${GOPATH}/bin

foo-package

$ rm ${GOPATH}/bin/foo-package


run

▼ runとは

go buildコマンドを実行しつつ、バイナリを実行する。

ソースコードを変更した後に動作を簡易的に確認する場合に便利である。

ただ、logパッケージなど処理中のメッセージは表示されず、main.goファイルの実行結果しかわからないため、エラーのデバッグには使いにくい。

$ go run ./...


test

▼ testとは

指定したパスのfoo_test.goファイルで『Test』から始まるテスト関数を実行する。

testディレクトリ内を再帰的に実行するのがおすすめ。

$ go test ./...

▼ -v

テスト時にテストの実施時間を出力する。

$ go test -v ./...

▼ -cover

テスト時に、foo_test.goファイルがあるパッケージ内ファイルの命令網羅の網羅率を解析する。

反対に、foo_test.goファイルがなければ、そのパッケージの網羅率は解析しない。

$ go test -cover ./...

▼ -coverpkg

パッケージを指定して、網羅率を解析する。

デフォルトではテストコードがあるパッケージしか解析しないため、全てのパッケージを含めて解析するために./...を指定する。

$ go test -coverpkg=./... -coverprofile=coverage.txt ./...

▼ -coverprofile

網羅率を解析し、結果からカバレッジレポートを作成する。

$ go test -coverprofile=coverage.txt ./...


tool

▼ cover

カバレッジレポートを使用して、特定のコンポーネントでのカバレッジを算出する。

パッケージ単位、関数単位 (-func) 、全体、のカバレッジを指定できる。

$ go tool cover -func coverage.txt


version

▼ -m

バイナリで使用しているGoのバージョンと、モジュールのバージョンを出力する。

$ go version -m <Goのバイナリのパス>

<Goのバイナリのパス>: go 1.19.5
...


vet

▼ vetとは

指定したパスのファイルに対して、静的解析を実施する。

パスとして『./...』を指定して、再帰的に実行するのがおすすめ。

$ go vet ./...


02. パッケージ管理系

大前提

Goでは、思想的にパッケージのバージョンを固定して運用せず、常に新しいバージョンを強制的に利用させるような仕組みがある。


get

▼ getとは

指定したパスからパッケージをダウンロードし、これに対してinstallコマンドを実行する。

また、go.modファイルも更新する。

これにより、内部または外部のコードからビルドされたアーティファクト (バイナリファイル) であればbinディレクトリ配下に配置し、それ以外 (例:.aファイル) であればpkgディレクトリ配下に配置する。

go getコマンドは不用意にgo.modファイル上の他のパッケージの定義も更新してしまうため、非推奨である。

# インストールの場合
$ go get <ドメインをルートとしたURL>@<バージョン>

go: downloading <ドメインをルートとしたURL> <バージョン>
go: added <ドメインをルートとしたURL> <バージョン>
# アップグレードの場合
$ go get <ドメインをルートとしたURL>@<バージョン>

go: downloading <ドメインをルートとしたURL> <バージョン>
go: upgraded <ドメインをルートとしたURL> <バージョン>

▼ go mod tidyとの使い分け

先にインストールしたパッケージのバージョンが優先になり、このバージョンを基準として他のパッケージのバージョンが決まる。

そのため、開発者によって結果が変わってしまう。

その反面、go mod tidyコマンドは同じ結果になる。

もし全てのパッケージのバージョンを開発者に限らず揃えたいなら、go mod tidyコマンドを使用する。


mod edit

▼ mod editとは

go.modファイルで指定しているバージョンを変更する。

$ go mod edit -go <バージョン>


mod tidy

▼ mod tidyとは

  • importで指定されているがgo getコマンドでインストールされていない場合は、これをインストールする。
  • importで指定のないパッケージは、go.modファイルとgo.sumファイルから削除する。
  • importで指定されているがgo.modファイルとgo.sumファイルに定義がない場合は、これを追加する。
  • アップグレード可能なパッケージはバージョンを変更する。
$ go mod tidy

もしgo.sumファイルにモジュールの指定があるのにも関わらず、以下のようなエラー (missing go.sum entry) が出る時は、go mod tidyコマンドを実行してgo.sumファイルを更新する必要がある。

cmd/main.go:4:5: missing go.sum entry for module providing package github.com/foo/foo-package (imported by github.com/hiroki-it/bar/cmd); to add:
        go get github.com/hiroki-hasegawa/bar/cmd

-go

Goのバージョンを指定して、go.modファイルとgo.sumファイルを更新する。

ただし、-goオプションは推奨バージョンを設定できるだけで、これを守らない場合もある。

$ go mod tidy -go <バージョン>

-v

importしていないためにgo.modファイルから削除したパッケージを、標準出力に出力する。

$ go mod tidy -v

unused <go.modファイルから削除したパッケージ>

▼ go getとの使い分け

go mod tidyコマンドは、たとえ-goオプションを使用しても、インストールするパッケージのバージョンを完全には制御できない。

(Goの思想的にも) できるだけ新しいバージョンを強制しようとするため、想定するバージョンよりも新しいパッケージをインストールしてしまう可能性がある。

そのため、特定のバージョン (特にコミットIDでの指定) はgo getコマンドでインストールする必要がある。

ただ推奨としては、Goの思想に則り、go mod tidyコマンドを実行して常に新しいバージョンを使う方が良いが...


mod verify

▼ mod verifyとは

go.sumファイルが正しいかどうかを検証する。

$ go mod verify

all modules verified


mod download

▼ mod downloadとは

  • importで指定されているがgo getコマンドでインストールされていない場合は、これをインストールする。
  • importで指定されているがgo.modファイルとgo.sumファイルに定義がない場合は、これを追加する。


go.modファイル

go.modファイルとは

アプリケーションで必要なパッケージのバージョンを設定する。

パッケージ内にgo.modファイルがある場合、そこに記載のあるバージョンは最低限必要なバージョンになる。

基本的には、パッケージのURLやディレクトリ構成と同じにする。

module github.com/hiroki-hasegawa/foo-repository

go 1.16

▼ パブリックリポジトリから (リリース済み)

パッケージ名とバージョンタグを使用して、パブリックリポジトリからリリース済みのパッケージをインポートする。

go mod tidyコマンドによって// indirectコメントのついたパッケージが実装される。

これは、使用しているパッケージではなく、インポートしているパッケージが依存しているパッケージである。

注意点として、パッケージ名は、使用したいパッケージのgo.modファイルを参照すること。

module github.com/hiroki-hasegawa/foo-repository

go 1.16

// 直接的に依存するパッケージ (アプリで使用するパッケージ)
require (
    <パッケージ名> <バージョンタグ>
    github.com/foo v1.3.0
    github.com/bar v1.0.0
)

// 間接的に依存するパッケージ (アプリで使用するパッケージが依存するパッケージ)
require (
    github.com/baz v1.0.0 // indirect
)
import "github.com/bar"

func main() {
    // 何らかの処理
}

▼ パブリックリポジトリから (開発中)

コミットIDやバージョンタグを使用して、パブリックリポジトリから開発中のパッケージをインポートする。

この場合、go getコマンドで特定のコミットIDやバージョンタグを指定し、モジュールをインストールする。

$ go get github.com/foo@<コミットID>

go: downloading github.com/foo v0.0.0-<コミット日時のタイムスタンプ>-<コミットID>
go: added github.com/foo v0.0.0-<コミット日時のタイムスタンプ>-<コミットID>

go getコマンドは、go.modファイルにインポート定義を追加する。

module github.com/hiroki-hasegawa/foo-repository

go 1.16

require (
    github.com/foo v0.0.0-<コミット日時のタイムスタンプ>-<コミットID> // indirect
)

▼ プライベートリポジトリから

デフォルトでは、プライベートリポジトリのパッケージをインポートできない。

$ go get github.com/foo@<コミットID/バージョンタグ>

github.com/foo@v1.0.0: verifying module: github.com/foo@v1.0.0: reading https://sum.golang.org/lookup/github.com/foo@v1.0.0: 410 Gone
    server response:
    not found: github.com/foo@v1.0.0: invalid version: git ls-remote -q origin in /tmp/gopath/pkg/mod/cache/vcs/*****: exit status 128:
        fatal: unable to look up github.com/foo.git (port 9418) (Name or service not known)

GOPRIVATE変数にプライベートリポジトリのURLを設定することで、インポートできるようになる。

$ go env -w GOPRIVATE=github.com/foo.git,github.com/bar.git,...

▼ ローカルマシンから

ローカルマシンのみで使用する自前共有パッケージがあるとする。

foo-repository/
├── cmd/
│   └── hello.go

├── go.mod
├── go.sum
└── local-pkg/
    ├── go.mod # 各パッケージにgo.modを配置する。
    └── module.go
// go.modファイル
module github.com/hiroki-hasegawa/foo-repository/local-pkg

go 1.16

この場合、パブリックリポジトリ上での自身のリポジトリからインポートせずに、replace関数を使用してインポートする必要がある。

自前共有の全パッケージでパッケージ名を置換する必要はなく、プロジェクトのルートパスについてのみ定義すれば良い。

パス実際、unknown revisionのエラーで、バージョンを見つけられない。

module github.com/hiroki-hasegawa/foo-repository

go 1.16

replace github.com/hiroki-hasegawa/foo-repository/local-pkg => /

これらにより、ローカルマシンのパッケージをインポートできるようになる。

package main

import "local.packages/local-pkg"

func main() {
    // 何らかの処理
}


go.sumファイル

go.sumファイルとは

PHPにおけるcomposer.lockファイルに相当する。

go.modファイルによって実際にインストールされたパッケージが自動的に実装される。

パッケージごとのチェックサムが記録されるため、前回のインストール時と比較して、パッケージに変更があるか否かを検知できる。