Skip to content

コマンドライン

Go のコマンドにはツールチェーン一式が含まれており、これらのコマンドはドキュメント、フォーマット、コードチェック、コンパイル、テスト、依存関係管理など多岐にわたり、Go 開発のあらゆる側面をカバーしています。

text
bug         バグ報告
build       パッケージと依存関係のコンパイル
clean       オブジェクトファイルのクリーンアップ
doc         ソースコードのドキュメント表示
env         Go 環境変数の表示
fix         Go バージョン変更による API 互換性問題の修正
fmt         ソースコードのフォーマット
generate    コード生成
get         依存関係の追加
install     パッケージのインストールとコンパイル
list        パッケージ/モジュール一覧
mod         モジュール管理
work        ワークスペース管理
run         コンパイルと実行
test        テスト
tool        指定された Go ツールの実行
version     Go バージョン情報の表示
vet         ソースコードの潜在的な問題のスキャンと報告

この記事では、それらの使用方法を簡単に紹介するだけです。すべての内容は公式ドキュメントを参考にしており、詳細については cmd/go をご覧ください。

help

最初に覚えるべきは help コマンドで、これを使用してコマンドの用法を読むことができます。2 つの用法があります。特定のコマンドの簡単な情報を取得したい場合は、コマンドの後に -h フラグを追加します。例えば

sh
$ go env -h
usage: go env [-json] [-u] [-w] [var ...]
Run 'go help env' for details.

go はコマンドの用法を簡潔に表示し、より詳細な情報を取得するには help コマンドを使用する必要があることを示しています

sh
$ go help env
usage: go env [-json] [-u] [-w] [var ...]

Env prints Go environment information.

By default env prints information as a shell script
(on Windows, a batch file). If one or more variable
names is given as arguments, env prints the value of
each named variable on its own line.

The -json flag prints the environment in JSON format
instead of as a shell script.

The -u flag requires one or more arguments and unsets
the default setting for the named environment variables,
if one has been set with 'go env -w'.

The -w flag requires one or more arguments of the
form NAME=VALUE and changes the default settings
of the named environment variables to the given values.

For more about environment variables, see 'go help environment'.

help コマンドをうまく活用することで、コマンドに関する多くの情報を取得できます。

doc

sh
$ go doc -h
Usage of [go] doc:
        go doc
        go doc <pkg>
        go doc <sym>[.<methodOrField>]
        go doc [<pkg>.]<sym>[.<methodOrField>]
        go doc [<pkg>.][<sym>.]<methodOrField>
        go doc <pkg> <sym>[.<methodOrField>]
For more information run
        go help doc

Flags:
  -C dir
        change to dir before running command
  -all
        show all documentation for package
  -c    symbol matching honors case (paths not affected)
  -cmd
        show symbols with package docs even if package is a command
  -short
        one-line representation for each symbol
  -src
        show source code for symbol
  -u    show unexported symbols as well as exported

doc コマンドは、指定されたパッケージ、定数、関数、型、変数、メソッド、さらには構造体フィールドのドキュメントコメントを出力します。パラメータなしで実行すると、現在のパッケージのコメントを出力します

sh
$ go doc

特定のパッケージを参照することもできます。例えば runtime パッケージのドキュメントコメントを参照する場合

sh
$ go doc runtime
package runtime // import "runtime"

Package runtime contains operations that interact with Go's runtime system,
such as functions to control goroutines. It also includes the low-level type
information used by the reflect package; see reflect's documentation for the
programmable interface to the run-time type system.
......

または特定の型

sh
$ go doc unsafe.Pointer
package unsafe // import "unsafe"

type Pointer *ArbitraryType
    Pointer represents a pointer to an arbitrary type. There are four special
    operations available for type Pointer that are not available for other
    types:
      - A pointer value of any type can be converted to a Pointer.
      - A Pointer can be converted to a pointer value of any type.
      - A uintptr can be converted to a Pointer.
      - A Pointer can be converted to a uintptr.
      ...

または特定の関数

sh
$ go doc runtime.GC
package runtime // import "runtime"

func GC()
    GC runs a garbage collection and blocks the caller until the garbage
    collection is complete. It may also block the entire program.

以下の一般的なフラグがあります

  • -u:プライベート型を表示
  • -all:指定されたパッケージのすべてのドキュメントを表示
  • -short:1 行の簡単な説明のみ表示
  • -src:ソースコードを出力
  • -cmd:go コマンドに属するパッケージのコードドキュメントも出力

例えば、runtime.inf 変数を参照する場合、これは外部に公開されていない変数です

sh
$ go doc -u runtime.inf
package runtime // import "runtime"

var inf = float64frombits(0x7FF0000000000000)

doc コマンドをうまく活用することで、ドキュメントをより便利に読むことができます。

コマンドドキュメントを読むもう一つの方法は、ソースコードを読むことです。一部のコマンドのドキュメントはそれほど詳細ではなく、ソースコードの方が詳しい説明がある場合があります。これらのコマンドはすべて Go で書かれているため、読むのも非常に便利です。これらのコマンドはすべて src/cmd パッケージ下にあり、各サブパッケージが個別のコマンドで、エントリーポイントは cmd/go/main.go ファイルにあります

go
func init() {
    base.Go.Commands = []*base.Command{
       bug.CmdBug,
       work.CmdBuild,
       clean.CmdClean,
       doc.CmdDoc,
       envcmd.CmdEnv,
       fix.CmdFix,
       fmtcmd.CmdFmt,
       generate.CmdGenerate,
       modget.CmdGet,
       work.CmdInstall,
       list.CmdList,
       modcmd.CmdMod,
       workcmd.CmdWork,
       run.CmdRun,
       test.CmdTest,
       tool.CmdTool,
       version.CmdVersion,
       vet.CmdVet,

       help.HelpBuildConstraint,
       help.HelpBuildmode,
       help.HelpC,
       help.HelpCache,
       help.HelpEnvironment,
       help.HelpFileType,
       modload.HelpGoMod,
       help.HelpGopath,
       get.HelpGopathGet,
       modfetch.HelpGoproxy,
       help.HelpImportPath,
       modload.HelpModules,
       modget.HelpModuleGet,
       modfetch.HelpModuleAuth,
       help.HelpPackages,
       modfetch.HelpPrivate,
       test.HelpTestflag,
       test.HelpTestfunc,
       modget.HelpVCS,
    }
}

ここで go のすべてのサブコマンドとそれらのヘルプドキュメント情報を見つけることができます。

bug

sh
$ go help bug
usage: go bug

Bug opens the default browser and starts a new bug report.
The report includes useful system information.

このコマンドにはパラメータやフラグはなく、デフォルトのブラウザで github.com/golang/go リポジトリの issue ページを開き、バグを報告しやすくするだけです。

version

version コマンドで現在の go のバージョン情報を確認できます。

go
$ go version -h
usage: go version [-m] [-v] [file ...]

パラメータなしで実行すると、現在の go 言語のバージョンを出力します

sh
$ go version
go version go1.21.0 windows/amd64

ファイルパスをパラメータとして受け取ることもでき、そのパス下のすべての識別可能なバイナリファイルのコンパイル時に使用された go バージョンを出力します。

sh
$ go version -v ./
buf.exe: go1.20.2
cobra-cli.exe: go1.21.0
dlv.exe: go1.20.2
goctl.exe: go1.20.2
goimports.exe: go1.20.2
golangci-lint.exe: go1.20.2
gopls.exe: go1.19.3
kratos.exe: go1.20.2
main.exe: go1.19.1
protoc-gen-go-grpc.exe: go1.20.2
protoc-gen-go-http.exe: go1.20.2
protoc-gen-go.exe: go1.20.2
protoc-gen-openapi.exe: go1.20.2
swag.exe: go1.21.0
wire.exe: go1.21.0

-v パラメータは version コマンドに識別できないファイルの go バージョンを出力させ、-m パラメータはバイナリファイルのモジュール情報とコンパイルパラメータを出力します。以下は簡単な例です。

sh
$ go version -v -m wire.exe
wire.exe: go1.21.0
        path    github.com/google/wire/cmd/wire
        mod     github.com/google/wire  v0.5.0  h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8=
        dep     github.com/google/subcommands   v1.0.1  h1:/eqq+otEXm5vhfBrbREPCSVQbvofip6kIz+mX5TUH7k=
        dep     github.com/pmezard/go-difflib   v1.0.0  h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
        dep     golang.org/x/tools      v0.0.0-20190422233926-fe54fb35175b      h1:NVD8gBK33xpdqCaZVVtd6OFJp+3dxkXuz7+U7KaVN6s=
        build   -buildmode=exe
        build   -compiler=gc
        build   DefaultGODEBUG=panicnil=1
        build   CGO_ENABLED=1
        build   CGO_CFLAGS=
        build   CGO_CPPFLAGS=
        build   CGO_CXXFLAGS=
        build   CGO_LDFLAGS=
        build   GOARCH=amd64
        build   GOOS=windows
        build   GOAMD64=v1

go 自体もバイナリファイルであり、実際にはパラメータなしで go version を実行すると、自身のバイナリファイルの go 言語バージョンを出力します。cmd/go のすべてのツールチェーンは go 言語自体によって実装されているためです。

env

env コマンドで go 環境変数のすべての情報を確認でき、これらの環境変数を変更すると go ツールチェーンの動作に影響します。

sh
$ go env -h
usage: go env [-json] [-u] [-w] [var ...]
Run 'go help env' for details.

パラメータなしで実行すると、go のすべての環境変数の値を出力します

sh
$ go env
set GO111MODULE=on
set GOARCH=amd64
...

環境変数名をパラメータとして指定すると、その変数の値のみを出力できます

sh
$ go env GO111MODULE
on

-json を追加すると JSON 形式で出力できます

sh
$ go env -json
{
        "AR": "ar",
        "CC": "gcc",
    ......
}

-w フラグを使用して、var=value 形式でパラメータを渡すと、特定の変数の値を永続的に変更できます

sh
$ go env -w GO111MODULE=on

-u フラグを使用して、特定の変数をデフォルト値に戻せます

sh
$ go env -u GO111MODULE

go help environment を実行すると、各環境変数の説明を確認できます

sh
$ go help environment
The go command and the tools it invokes consult environment variables
for configuration. If an environment variable is unset or empty, the go
command uses a sensible default setting. To see the effective setting of
the variable <NAME>, run 'go env <NAME>'. To change the default setting,
run 'go env -w <NAME>=<VALUE>'. Defaults changed using 'go env -w'
are recorded in a Go environment configuration file stored in the
per-user configuration directory, as reported by os.UserConfigDir.
The location of the configuration file can be changed by setting
the environment variable GOENV, and 'go env GOENV' prints the
effective location, but 'go env -w' cannot change the default location.
See 'go help env' for details.

General-purpose environment variables:

        GO111MODULE
                Controls whether the go command runs in module-aware mode or GOPATH mode.
                May be "off", "on", or "auto".
                See https://golang.org/ref/mod#mod-commands.
        GCCGO
                The gccgo command to run for 'go build -compiler=gccgo'.
        GOARCH
                The architecture, or processor, for which to compile code.
                Examples are amd64, 386, arm, ppc64.
        GOBIN
                The directory where 'go install' will install a command.
        GOCACHE
                The directory where the go command will store cached
                information for reuse in future builds.
    ......

以下はいくつかの一般的な環境変数の紹介です

GOVERSION

この環境変数の値は go 言語のバージョンに依存し、バージョン番号は $GOROOT/VERSION ファイルから取得されます。このファイルには現在の go のバージョンとビルド時間が記録されています。

sh
$ cat $GOROOT/VERSION
go1.21.3
time 2023-10-09T17:04:35Z

runtime.Version 変数の値は GOVERSION の値と同じで、この環境変数は変更できません。

GOENV

$GOROOT ディレクトリには go.env というデフォルトの構成ファイルがあります

sh
$ cat $GOROOT/go.env
# This file contains the initial defaults for go command configuration.
# Values set by 'go env -w' and written to the user's go/env file override these.
# The environment overrides everything else.

# Use the Go module mirror and checksum database by default.
# See https://proxy.golang.org for details.
GOPROXY=https://proxy.golang.org,direct
GOSUMDB=sum.golang.org

# Automatically download newer toolchains as directed by go.mod files.
# See https://go.dev/doc/toolchain for details.
GOTOOLCHAIN=auto

形式は単純な key=value で、go env -w key=value コマンドで変更された環境変数の値は構成ファイルに書き込まれます。ただし、デフォルトの構成ファイルを使用しないこともでき、GOENV 環境変数で env 構成ファイルのアドレスを手動で指定できます。GOENV 環境変数の値はオペレーティングシステムの環境変数でのみ上書きでき、go env -w コマンドでは変更できません。

GOHOSTARCH

マシンの CPU アーキテクチャを表します。これは表示用だけで、この環境変数の値は構成ファイルから読み取られるものではなく、変更もできません。

GOHOSTOS

マシンのオペレーティングシステムを表します。これは表示用だけで、この環境変数の値は構成ファイルから読み取られるものではなく、変更もできません。

GOOS

コンパイル時、GOOS の値はソースコードをどのターゲットシステムのバイナリファイルにコンパイルするかを決定します。デフォルト値は GOHOSTOS、つまりマシンのオペレーティングシステムです。以下のオプションがあります

  • linux
  • darwin
  • windows
  • netbsd
  • aix
  • android

実際にサポートされているオペレーティングシステムはこれらだけではありません。go tool dist list コマンドを使用して、サポートされているすべての値を確認できます

sh
$ go tool dist list | awk -F '/' '{print $1}' | awk '!seen[$0]++'
aix
android
darwin
dragonfly
freebsd
illumos
ios
js
linux
netbsd
openbsd
plan9
solaris
wasip1
windows

GOARCH

コンパイル時、GOARCH の値はコンパイル時にどの CPU アーキテクチャの命令を使用するかを決定します。デフォルト値は GOHOSTARCH、つまりマシンの CPU アーキテクチャです。以下のオプションがあります

  • amd64
  • 386
  • arm
  • ppc64

実際にサポートされているアーキテクチャはこれらだけではありません。go tool dist list コマンドを使用して、サポートされているすべての値を確認できます

sh
$ go tool dist list | awk -F '/' '{print $2}' | awk '!seen[$0]++'
ppc64
386
amd64
arm
arm64
riscv64
wasm
loong64
mips
mips64
mips64le
mipsle
ppc64le
s390x

GOOSGOARCH は任意に組み合わせることはできず、一部のオペレーティングシステムは特定の CPU アーキテクチャのみをサポートしています。

GOROOT

GOROOT は go 言語インストール場所のルートディレクトリを表し、GOROOT の値は直接変更できず、オペレーティングシステムの環境変数でのみ上書きできます。

sh
$ ls $GOROOT -1
api
bin
codereview.cfg
CONTRIBUTING.md
doc
go.env
lib
LICENSE
misc
PATENTS
pkg
README.md
SECURITY.md
src
test
VERSION

ルートディレクトリには以下の重要なフォルダやファイルがあります

  • lib:依存関係が格納されています。現時点では世界各国のタイムゾーン情報を含むライブラリのみで、$GOROOT/lib/time にあります。コンパイル後のバイナリファイルにはこれらのタイムゾーン情報は含まれません。
  • pkg:ツールライブラリとヘッダーファイルが格納されています。例えば go tool コマンドは $GOROOT/pkg/tool ディレクトリで go ツールチェーンのバイナリファイルを検索します
  • bin:バイナリファイルが格納されています。デフォルトでは gogofmt の 2 つの実行ファイルのみです。$GOROOT/bin はシステム変数に追加する必要があります。そうしないと go コマンドを使用できません。
  • src:go ソースコードが格納されています
  • VERSION:このファイルには go 言語のバージョン情報が格納されています
  • go.env:このファイルはデフォルトの env 構成ファイルです

GOPATH

GOPATH のデフォルト値は $HOME/go で、この環境変数の値は import 文の解析時にインポートされたファイルを検索する場所を指定します。gomod がない初期の頃、GOPATH はサードパーティライブラリを格納するために使用されていました。その構造は以下の通りです

sh
GOPATH=/home/user/go

/home/user/go/
    src/
        foo/
            bar/               (go code in package bar)
                x.go
            quux/              (go code in package main)
                y.go
    bin/
        quux                   (installed command)
    pkg/
        linux_amd64/
            foo/
                bar.a          (installed package object)

gomod が登場した後、GOPATHgo get でダウンロードされた依存関係を格納する場所、および go install でダウンロードおよびコンパイルされたバイナリファイルを格納する場所としてのみ使用されます。GOPATH の位置は GOROOT と同じであってはなりません。そうしないと機能しません。

sh
$ go env GOBIN
warning: GOPATH set to GOROOT (/home/user/go) has no effect

この記事を書いている時点で、go 言語のバージョンはすでに go1.21.3 になっており、非常に古いプロジェクトを除いて、gopath を使用して依存関係を管理する人はほとんどいません。

GOBIN

GOBINgo install でダウンロードおよびコンパイルされたサードパーティの実行バイナリファイルを格納するために使用され、デフォルト値は $GOPATH/bin です。$GOROOT/bin と同様に、このディレクトリもオペレーティングシステムの環境変数に追加する必要があります。そうしないと、GOBIN ディレクトリ下のバイナリファイルを使用できません。

GOMODCACHE

GOMODCACHEgo get でダウンロードされた依存関係が格納される場所を示し、デフォルト値は $GOPATH/pkg/mod です。格納形式は以下の通りです

$GOMODCACHE/domain/username/project@verion

同じレベルのディレクトリには sumdb というフォルダもあり、依存関係のチェックサムデータベースに関する情報が格納されています。

GOCACHE

コンパイル用のキャッシュ情報を格納し、デフォルト値は $HOME/.cache/go-build です。このディレクトリには README ファイルが生成されます。

sh
$ cat $(go env GOCACHE)/README
This directory holds cached build artifacts from the Go build system.
Run "go clean -cache" if the directory is getting too large.
Run "go clean -fuzzcache" to delete the fuzz cache.
See golang.org to learn more about Go.

build するたびに多くのファイルが生成され、go はこれらのファイルをキャッシュして、次のコンパイル時に再利用します。

GOTEMPDIR

コンパイル時に生成される一時ファイル用で、例えば go run で実行される一時バイナリファイルなどです。デフォルト値はオペレーティングシステムが指定する一時ディレクトリで、mac または linux では /tmp、windows では %TEMP% です。ユーザーが指定する場所に変更することもできます。

GO111MODULE

この環境変数は go プロジェクトの依存関係を管理する方法を示し、以下の 3 つの利用可能な値があります

  • off:gomod をオフにして gopath を使用し、すべての go.mod ファイルを無視します
  • on:gomod を使用し、gopath は使用しません(デフォルト)
  • auto:自動検知。プロジェクトファイルに go.mod が含まれている場合は gomod を使用して管理します

TIP

なぜ GO111MODULE という名前で、GOMODULE と呼ばないのでしょうか?それは gomod が go1.11 バージョンで初めて導入されたからです。

GOPROXY

go モジュールプロキシで、デフォルト値は https://proxy.golang.org,direct です。url はカンマで区切られ、direct はモジュールプロキシを直接使用せずに VCS を使用することを意味し、前者にアクセスできない場合にのみ後者が実行されます。もう一つの利用可能なオプションは off で、モジュールのダウンロードを禁止することを示します。さらに、GOPROXY はファイルアドレス也可以是、例えば

GOPROXY=file://$(go env GOMODCACHE)/cache/download

go get -x を使用すると、依存関係のダウンロードプロセスで実行されるコマンドを確認でき、プロキシを使用しているかどうかを知ることができます。

sh
$ go get -x github.com/spf13/cast
# get https://goproxy.cn/github.com/@v/list
# get https://goproxy.cn/github.com/spf13/cast/@v/list
# get https://goproxy.cn/github.com/spf13/@v/list
# get https://goproxy.cn/github.com/spf13/@v/list: 404 Not Found (0.118s)
# get https://goproxy.cn/github.com/@v/list: 404 Not Found (0.197s)
# get https://goproxy.cn/github.com/spf13/cast/@v/list: 200 OK (0.257s)
# get https://goproxy.cn/github.com/spf13/cast/@v/v1.5.1.info
# get https://goproxy.cn/github.com/spf13/cast/@v/v1.5.1.info: 200 OK (0.013s)
# get https://goproxy.cn/github.com/spf13/cast/@v/v1.5.1.mod
# get https://goproxy.cn/github.com/spf13/cast/@v/v1.5.1.mod: 200 OK (0.015s)
# get https://goproxy.cn/sumdb/sum.golang.org/supported
# get https://goproxy.cn/sumdb/sum.golang.org/supported: 200 OK (0.064s)
# get https://goproxy.cn/sumdb/sum.golang.org/lookup/github.com/spf13/cast@v1.5.1
# get https://goproxy.cn/sumdb/sum.golang.org/lookup/github.com/spf13/cast@v1.5.1: 200 OK (0.014s)
# get https://goproxy.cn/sumdb/sum.golang.org/tile/8/0/x079/736
# get https://goproxy.cn/sumdb/sum.golang.org/tile/8/0/x079/736: 200 OK (0.016s)
# get https://goproxy.cn/sumdb/sum.golang.org/tile/8/0/x068/334
# get https://goproxy.cn/sumdb/sum.golang.org/tile/8/1/266
# get https://goproxy.cn/sumdb/sum.golang.org/tile/8/0/x068/334: 200 OK (0.023s)
# get https://goproxy.cn/sumdb/sum.golang.org/tile/8/1/266: 200 OK (0.028s)
go: downloading github.com/spf13/cast v1.5.1
# get https://goproxy.cn/github.com/spf13/cast/@v/v1.5.1.zip
# get https://goproxy.cn/github.com/spf13/cast/@v/v1.5.1.zip: 200 OK (0.024s)
go: added github.com/spf13/cast v1.5.1

モジュールプロキシを使用すると、モジュールのダウンロード速度を効果的に向上させることができます。国内ユーザーは基本的にプロキシを使用しないとデフォルトの公式プロキシにアクセスできません。現在、公開されており信頼できるサードパーティモジュールプロキシは以下の通りです

  • https://proxy.golang.com.cn:オープンソースで、企業版サービスも提供
  • https://goproxy.cn:七牛雲が提供し、オープンソース

もちろん、オープンソースの自建モジュールプロキシソリューションもあります:goproxy

GOSUMDB

GOSUMDB は依存ライブラリのチェックサム検証データベースアドレスを設定するために使用され、デフォルトは sum.golang.org です。プロキシを設定すると、go はプロキシを介して検証データベースにアクセスします。

GOPRIVATE

GOPRIVATE 環境変数はプライベートライブラリを設定するために使用され、一致するライブラリは sumdb による検証もプロキシも経由しません。VCS を介して直接依存関係をダウンロードします。ワイルドカード設定をサポートし、カンマで区切ります。以下の例では、corp.example.com で終わるすべてのものと github.com/gohper/myproject という名前の依存関係はプロキシも sumdb も経由しません。

GOPRIVATE=*.corp.example.com,github.com/gohper/myproject

特定のユーザーまたは組織を直接設定することもできます

GOPRIVATE=github.com/gopher,github.com/myorganization

GONOPROXY

どの依存関係がプロキシを経由しない必要があるかを示し、ルールは GOPRIVATE と同じで、GOPRIVATE を上書きします。

GONOSUMDB

どの依存関係が検証データベースを経由しない必要があるかを示し、ルールは GOPRIVATE と同じで、GOPRIVATE を上書きします。

GOINSECURE

どの依存関係が VCS を直接使用してダウンロードされるかを示し、ルールは GOPRIVATE と同じで、GONOPROXYGONOSUMDB によって上書きされます。

GOVCS

モジュール管理のバージョン管理システムを設定し、デフォルトは public:git|hg,private:all です。特定のドメインの VCS を制限することもできます。例えば

GOVCS=github.com:git,evil.com:off,*:git|hg

上記の制限では、github は git のみ使用でき、evil.com は使用できません。| を使用して複数の VCS を表すことができます。制限を全く設定しない場合は、以下のように設定できます

GOVCS=*:all

VCS の使用を全く許可しない場合は、以下のように設定できます

GOVCS=*:off

GOWORK

ワークスペースを有効にするかどうかを設定し、デフォルトは空で有効です。off に設定すると無効になり、すべての go.work ファイルを無視します。

GOTOOLDIR

使用する go ツールチェーンの位置を設定し、デフォルトは $GOROOT/pkg/tool で、デフォルトのツールチェーンもこの位置に格納されています。

GODEBUG

デバッグオプションを設定し、キーバリュー形式で go プログラムの一部の実行動作を制御します。例えば

GODEBUG=http2client=0,http2server=0

これらの設定は、バージョン更新プロセス中に互換性のない変更が発生した場合に、go が以前の動作に戻すことができるようにするためです。例えば 1.21 では panic(nil) が許可されなくなりました。これについて、go 公式は GODEBUG History を記録しており、詳細については GODEBUG をご覧ください。

CGO_ENABLED

cgo を有効にするかどうかを示し、デフォルトは 1 で有効です。0 に設定すると無効になります。

上記の環境変数は一般的に使用されるもので、CGO、WASM などのあまり一般的でないものについてはここでは詳しく紹介しません。興味がある場合は自分で調べてみてください。

build

go がサポートするコンパイラには gccgo と gc の 2 種類があります。gcc は老舗の c/c++ コンパイラで、go を含む複数の言語をサポートしています。後者の gc はガベージコレクションを意味するのではなく、go compiler を意味します。go 言語は go1.5 でブートストラップを完了し、gc は完全に go 言語で書かれたコンパイラで、そのソースコードは cmd/compile パッケージ下にあり、完全に go 言語で実装されているため、内部メカニズムの学習と理解にも非常に便利です。デフォルトでは、コンパイラは gc を使用してコンパイルします。ついでに言うと、go 言語デバッガも gdb と dlv の 2 種類に分かれています。gdb は老舗の c/c++ デバッガで、go を含む複数の言語をサポートしています。後者の dlv は go 言語で書かれたデバッガで、go 言語のサポートがより親切です。これもオープンソースで、後者の使用をお勧めします。

build コマンドの役割は go ソースファイルを実行可能なバイナリファイルにコンパイルすることで、非常に迅速なコンパイル体験を味わうことができます。これも go 言語の特徴の一つです。

sh
$ go build -h
usage: go build [-o output] [build flags] [packages]
Run 'go help build' for details.

3 つのパラメータを受け取り、1 つ目は -o フラグで示されるファイル出力パス、2 つ目はコンパイル動作を定義するビルドフラグ build flags、3 つ目はコンパイルするパッケージで、このパラメータは最後に配置する必要があります。以下はビルドフラグを使用しない簡単な例です。

sh
# Windows
$ go build -o .\bin\golearn.exe golearn

# macOS / Linux
$ go build -o ./bin/golearn golearn

./bin/golearn.exe は出力パスを示し、golearn はコンパイルするモジュールを示します。エントリーファイル または フォルダ でもかまいません。以下は main.go エントリーファイルをコンパイルターゲットとする簡単な例です。

sh
# Windows
$ go build -o .\bin\golearn.exe main.go

# macOS / Linux
$ go build -o ./bin/golearn main.go

コンパイル時、_test.go で終わるすべてのファイルは無視されます。これらのファイルは慣例によりテストファイルであるためです。

さらに、build コマンドはコンパイル時の動作を制御するために相当数のビルドフラグをサポートしています。

  • -x:コンパイルプロセス中の詳細なコマンドを出力
  • -n-x と似ていますが、違いはこれらのコマンドを出力するだけで、実際には実行しないことです。
  • -v:コンパイルされたパッケージを出力
  • -p:コンパイルプロセス中の並行数
  • -a:最新であっても強制的に再構築します。
  • -compiler:使用するコンパイラを指定します。gccgo または gc で、後者は go で書かれたコンパイラです。
  • -race:競合検出を有効にします
  • -msan:メモリ分析を有効にします
  • -asan:アドレス分析を有効にします
  • -cover:コードカバレッジ検出を有効にします
  • -buildmode:コンパイルモードを指定します。archivec-archivec-shareddefaultsharedexepieplugin のオプションがあります。
  • -pgo:pgo ファイルを指定します
  • -trimpath:ソースファイルパスのプレフィックスを削除します。例えば相対パス /var/lib/go/src/main.go は、削除後に runtime を介して取得されるファイル名はモジュールパスへの相対パス /main.go のみになります。この項目を有効にすると、コンパイル時間は明らかに上昇し、ファイル数に応じて約 20-40% 上昇します。
  • -toolexec:コンパイル前に実行されるいくつかの go コマンドで、形式は -toolexec 'cmd args' です。
  • -gcflags:コンパイラ gc のいくつかのタグを指定します
  • -gccgoflags:コンパイラ gccgo のいくつかのタグを指定します
  • -ldflags:link ツールのいくつかのタグを指定します

ldflags などのパラメータ渡しの場合は、"-help" などのパラメータを渡して、その取り得る値を取得できます。例えば

sh
$ go build -ldflags -help
usage: link [options] main.o
  -B note
        add an ELF NT_GNU_BUILD_ID note when using ELF
  -E entry
        set entry symbol name
......

上記は一般的に使用されるもので、他のあまり一般的でないものは自分で調べてみてください。

gcflags

gcflags を使用して、コンパイラ gc にパラメータを渡し、特定の動作を制御できます。使用形式は -gcflags="pattern=args list" で、ages list はパラメータリスト、pattern は作用範囲で、以下の利用可能な値があります

  • main:エントリーファイルがあるトップレベルのパッケージパス
  • all:現在のモジュールおよび現在のモードのすべての依存関係
  • std:標準ライブラリ
  • cmdcmd パッケージ下のすべてのソースファイル
  • ワイルドカード:例えば ../...cmd/...

この pattern ルールは、この形式をサポートするすべてのフラグに適用されます。例えば ldflags です。以下のコマンドを使用して、パラメータの取り得る値を確認できます

sh
$ go build -gcflags -help
usage: compile [options] file.go...
  -%    debug non-static initializers
  -+    compiling runtime
  -B    disable bounds checking
  -C    disable printing of columns in error messages
  -D path
        set relative path for local imports
  -E    debug symbol export
  -I directory
        add directory to import search path
  -K    debug missing line numbers
  -L    also show actual source file names in error messages for positions affected by //line directives
  -N    disable optimizations
  -S    print assembly listing
  -V    print version and exit
  -W    debug parse tree after type checking
  ......

以下はいくつかの一般的なパラメータの紹介です

  • -S:コードのアセンブリ形式を出力
  • -N:コンパイル最適化を無効
  • -m:最適化決定を出力
  • -l:関数インラインを無効
  • -c:コンパイルの並行数
  • -dwarf:DWARF フラグを生成

例えば、コードのアセンブリ形式を確認したい場合は、-S パラメータを使用し、最適化とインラインを無効にして、本来の形式を復元する必要があります。以下はその例です

sh
$ go build -trimpath -gcflags="-N -l -S" main.go
main.main STEXT size=171 args=0x0 locals=0x58 funcid=0x0 align=0x0
        0x0000 00000 (./main.go:9)      TEXT    main.main(SB), ABIInternal, $88-0
        0x0000 00000 (./main.go:9)      CMPQ    SP, 16(R14)
        0x0004 00004 (./main.go:9)      PCDATA  $0, $-2
        0x0004 00004 (./main.go:9)      JLS     161
        0x000a 00010 (./main.go:9)      PCDATA  $0, $-1
        0x000a 00010 (./main.go:9)      PUSHQ   BP
        0x000b 00011 (./main.go:9)      MOVQ    SP, BP
        0x000e 00014 (./main.go:9)      SUBQ    $80, SP
        0x0012 00018 (./main.go:9)      FUNCDATA        $0, gclocals·J5F+7Qw7O7ve2QcWC7DpeQ==(SB)
        0x0012 00018 (./main.go:9)      FUNCDATA        $1, gclocals·bDfKCdmtOiGIuJz/x+yQyQ==(SB)
        0x0012 00018 (./main.go:9)      FUNCDATA        $2, main.main.stkobj(SB)
        0x0012 00018 (./main.go:10)     MOVUPS  X15, main..autotmp_0+40(SP)
        0x0018 00024 (./main.go:10)     LEAQ    main..autotmp_0+40(SP), CX
        0x001d 00029 (./main.go:10)     MOVQ    CX, main..autotmp_2+32(SP)

ldflags

ldflags を使用して、リンカーにパラメータを渡し、特定の動作を制御できます。以下のコマンドを使用して、ldflags のすべての利用可能な値を確認できます。ほぼ 20〜30 個あります。

sh
$ go build -ldflags -help
usage: link [options] main.o
  -B note
        add an ELF NT_GNU_BUILD_ID note when using ELF
  -E entry
        set entry symbol name
  -H type
        set header type
  -I linker
        use linker as ELF dynamic linker
  -L directory
        add specified directory to library path
  -R quantum
        set address rounding quantum (default -1)
  -T int
        set the start address of text symbols (default -1)
  -V    print version and exit
  -X definition
        add string value definition of the form importpath.name=value
  -a    no-op (deprecated)
  .....

ldflags-X パラメータは非常に実用的な機能で、リンク時に指定されたパッケージの文字列変数の値を定義できます。この機能を使用すると、コンパイル時にいくつかのメタ情報を簡単に注入できます。また、これは変数にすぎないため、実行時に取得するのも便利です。以下は簡単な例です。

go
package main

import "fmt"

var (
  Version string
)

func main() {
  fmt.Println(Version)
}

コマンドを実行

sh
go build -ldflags "-X main.Version=$(git describe --always)" main.go

実行後、git コミットの sha1 チェックサムが出力されます。

5e3fd7a

その他、実用的なパラメータには以下があります

  • -w:DWARF を生成しません。これはソースコードのデバッグに便利な情報です。
  • -s:シンボルテーブルを無効にします

これらは通常一緒に使用され、コンパイル後のバイナリファイルのサイズを大幅に削減できます。約 40%-50% 程度ですが、欠点も明らかで、デバッグができなくなります。以下はその例です。

sh
$ go build -ldflags="-w -s" main.go

クロスコンパイル

go 言語のコンパイルには 2 つの大きな特徴があります。1 つ目は高速であることで、もう 1 つはクロスコンパイルです。クロスコンパイルとは、ローカルで他のシステムのターゲットコードをコンパイルできることを指します。例えば windowslinuxdarwin 用のバイナリファイルをコンパイルでき、その逆も同様です。クロスコンパイルをサポートする言語は非常に多いですが、これは珍しいことではありません。しかし、go 言語のクロスコンパイルは非常に簡単で、以下の 2 ステップだけです

  1. GOOS 環境変数を設定し、ターゲットオペレーティングシステムを選択
  2. GOARCH 環境変数を設定し、ターゲット CPU アーキテクチャを選択
  3. 普段通り go build を使用してコンパイル

プロセス全体は非常に短く、追加のツールや設定は不要で、速度も普段と同じくらい速いです。以下に示します

makefile
build_linux:
  SET CGO_ENABLED=0
  SET GOOS="linux"
  SET GOARCH="amd64"
  go build -o golearn  main.go

build_mac:
  SET CGO_ENABLED=0
  SET GOOS="darwin"
  SET GOARCH="amd64"
  go build -o golearn main.go

build_win:
  SET CGO_ENABLED=0
  SET GOOS="win"
  SET GOARCH="amd64"
  go build -o golearn.exe main.go

.PHONY: build_linux \
    build_mac \
    build_win

最初のステップ SET CGO_ENABLED=0 は cgo を無効にします。コードが cgo を使用している場合、クロスコンパイルを正常に使用できません。2 番目のステップ SET GOOS はターゲットシステムを設定し、オプションは linuxdarwinwindwosnetbsd です。3 番目のステップは CPU アーキテクチャを設定し、SET GOARCH で、オプションは amd64386armppc64 です。最後のステップは普段通りコンパイルします。

コンパイル制御

build コマンドは tags を使用してコンパイルを制御できます。これはソースコード内にディレクティブとして存在します。例を見てみましょう。product.go ファイル

go
// +build product

package main

import "fmt"

func main() {
  fmt.Println("product")
}

debug.go ファイル

go
// +build debug

package main

import "fmt"

func main() {
  fmt.Println("debug")
}

これらには // +build ディレクティブがあり、どのような状況でコンパイルされるかを示します。基本形式は以下の通りです

go
// +build tag1 tag2

package pkg_name

遵守しなければならないルールがいくつかあります

  1. //+build の間に 1 つのスペースが必要です
  2. パッケージ宣言の上に配置する必要があります
  3. パッケージ宣言との間に 1 行の空行が必要です

さらに、単純なスペースを使用して論理制御を達成できます。スペースは OR、カンマは AND、!は NOT を表します。以下の例を見てください

go
// +build windows linux

package pkg_name

これは windows または linux プラットフォームで現在のファイルがコンパイルされることを示します。

go
// +build windows,amd64,!cgo linux,i386,cgo

package pkg_name

この例は、windows プラットフォーム amd64 アーキテクチャで cgo が無効になっている場合、または linux プラットフォーム i386 アーキテクチャで cgo が有効になっている場合にのみコンパイルされることを示します。特定のファイルをコンパイルに参加させたくないだけの場合は、ignore を使用できます。

go
// +build ignore

package pkg_name

複数の行のディレクティブも存在できます

go
// +build windows
// +build amd64

package pkg_name

複数の行のディレクティブは AND 方式で処理されます。プラットフォームやアーキテクチャなどのタグは、コンパイル時に go によって自動的に渡されます。カスタムタグを渡すこともできます。最初の 2 つのファイルを使用して例を示します

sh
$ go build -tags="debug" . && ./golearn.exe
debug

$ go build -tags="product" . && ./golearn.exe
product

異なるタグを渡すと異なる出力が得られることがわかります。コンパイル制御の目的はこれで達成されます。

run

run コマンドと build はどちらもソースコードをコンパイルしますが、run コマンドはコンパイル後に直接実行します。run コマンドはコンパイル速度を上げるために、コンパイルプロセス中にデバッグ情報を生成せず、デバッグもサポートしません。また、一時バイナリファイルのみを生成し、通常は GOTMEPDIR ディレクトリ(例:/temp/go-build2822241271/b001/exe/main.exe)に格納されます。

sh
$ go run -h
usage: go run [build flags] [-exec xprog] package [arguments...]
Run 'go help run' for details.

build コマンドのビルドフラグもサポートし、-exec パラメータを提供して、バイナリファイルを実行するプログラムを指定します。[arguments...] はプログラムの実行パラメータを指します。以下は例です

go
package main

import (
  "fmt"
  "os"
)

var (
  Version string
)

func main() {
  fmt.Println(Version)
  fmt.Println(os.Args[1:])
}

go run を使用して実行

sh
$ go run -ldflags="-X main.Version=$(git describe --always)" main.go hello
5e3fd7a
[hello]

全体的に go build と大きな違いはなく、これ以上の詳細は省略します。

tool

tool コマンド自体には機能はなく、cmd/ ディレクトリ下のツールを直接呼び出す役割を果たします。例えば cmd/compile は組み込みのコンパイラです。go tool を使用してこれらのツールを直接呼び出せ、これらのツールのバイナリファイルを手動で実行する必要はありません。

sh
$ go tool -h
usage: go tool [-n] command [args...]

-n パラメータを使用して、サポートされているすべてのコマンドパラメータを出力します

sh
$ go tool -n
addr2line
asm
buildid
cgo
compile
covdata
cover
doc
fix
link
nm
objdump
pack
pprof
test2json
trace
vet

これらのツールは GOROOT/pkg/tool ディレクトリ下に格納され、オペレーティングシステムと CPU アーキテクチャに基づいてグループ化されています。以下に示します。

sh
$ ls $GOROOT/pkg/tool/windows_amd64/ -1
addr2line.exe*
asm.exe*
buildid.exe*
cgo.exe*
compile.exe*
covdata.exe*
cover.exe*
doc.exe*
fix.exe*
link.exe*
nm.exe*
objdump.exe*
pack.exe*
pprof.exe*
test2json.exe*
trace.exe*
vet.exe*

go doc cmd/command 形式を使用して、各コマンドの用法を確認できます。例えば

sh
$ go doc cmd/compile
Usage:

    go tool compile [flags] file...

The specified files must be Go source files and all part of the same package.
The same compiler is used for all target operating systems and architectures.
The GOOS and GOARCH environment variables set the desired target.

Flags:

    -D path
        Set relative path for local imports.
    -I dir1 -I dir2
        Search for imported packages in dir1, dir2, etc,
        after consulting $GOROOT/pkg/$GOOS_$GOARCH.
    -L
        Show complete file path in error messages.
...

cmd/compile がサポートするフラグパラメータは、前述の gcflags がサポートするパラメータです。go tool compilego build の違いは、前者はコンパイルのみを担当し、ファイルのみをパラメータとして使用できることです。後者はフォルダ、パッケージ、ファイルをパラメータとして使用でき、ソースコードのコンパイルだけでなく、ファイルのリンク、不要なファイルのクリアなども担当します。前者は後者の一部です。build プロセス中に実行されるコマンドを出力できます

sh
$ go build -n main.go

#
# internal/goarch
#

mkdir -p $WORK\b004\
cat >$WORK\b004\importcfg << 'EOF' # internal
# import config
EOF
"/golang/pkg/tool/windows_amd64/compile.exe" -o "$WORK/b004/_pkg_.a" -trimpath "$WORK/b004=>" -p internal/goarch -std -+ -complete -buildid 3gunEkUExGdhOPa2rFsh/3gunEkUExGdhOPa2rFsh -goversion go1.21.0 -c=4 -nolocalimports -importcfg "$WORK/b004/importcfg" -pack "/golang/src/internal/goarch/goarch.go" "/golang/src/internal/goarch/goarch_amd64.go" "/golang/src/internal/goarch/zgoarch_amd64.go"
"/golang/pkg/tool/windows_amd64/buildid.exe" -w "$WORK/b004/_pkg_.a" # internal
...

プロセス中に /golang/pkg/tool/windows_amd64/compile.exe という部分があり、コンパイラを呼び出していることがわかります。compile の他にも多くのツールを呼び出せます。多くの go コマンドは実際にはそれらのエイリアスです。

clean

clean コマンドはコンパイルプロセス中に生成されたオブジェクトファイルをクリアするために使用されます

sh
$ go clean -h
usage: go clean [clean flags] [build flags] [packages]
Run 'go help clean' for details.

以下のフラグをサポートします

  • -i:対応するアーカイブファイルまたはバイナリファイルをクリア
  • -n:クリアプロセス中に実行されるコマンドを出力しますが、実際には実行しません
  • -x:クリアプロセス中に実行されるコマンドを出力し、実行します
  • -rimport path を介して再帰的にクリア
  • -cachego build によって生成されたすべてのキャッシュをクリア
  • -testcache:生成されたすべてのテストキャッシュをクリア
  • -modcache:ダウンロードされたすべてのモジュールキャッシュをクリア
  • -fuzzcachefuzz test によって生成されたキャッシュをクリア

go tool compile を使用する場合、コンパイラコマンドを直接呼び出し、go build のように多くの後処理を行わないため、オブジェクトファイルが生成されます。例えば以下のコマンドを実行すると

sh
go tool compile -N -S -l main.go

main.o というファイルが生成されます。go clean コマンドを使用してクリアします。または -n パラメータを使用して、実行されるコマンドを出力します。

sh
$ go clean -n
rm -f golearn golearn.exe golearn golearn.exe golearn.test golearn.test.exe golearn.test golearn.test.exe api api.exe main main.exe

コンパイルキャッシュをクリアすると、GOCACHE ディレクトリ下に生成されたコンパイルキャッシュが削除されます

sh
$ go clean -cache -n
rm -r /cache/00 /cache/01 /cache/02

fuzz test によって生成されたキャッシュをクリアします。これらのキャッシュはデフォルトで GOCACHE/fuzz/ ディレクトリ下に格納されます

sh
$ go clean -fuzzcache -n
rm -rf /cache/fuzz

fix

この記事を作成した時点で、go 言語はすでに 10 年になっています。言語が継続的に更新および修正されるプロセスで、API の変更による互換性の問題が発生することがあります。fix コマンドはこのために存在し、ソースファイル内で時代遅れになった API を検出し、新しい API に置き換えます。

sh
$ go fix -h
usage: go fix [-fix list] [packages]
Run 'go help fix' for details.

フォルダ、ファイル名、ディレクトリをパラメータとして受け取り、-fix フラグを受け取ってパラメータを渡し、どのような修正を行うかを示します。got tool fix -help コマンドを使用して、利用可能な値を確認できます

sh
$ go tool fix -help
usage: go tool fix [-diff] [-r fixname,...] [-force fixname,...] [path ...]
  -diff
        display diffs instead of rewriting files
  -force string
        force these fixes to run even if the code looks updated
  -go string
        go language version for files
  -r string
        restrict the rewrites to this comma-separated list

Available rewrites are:

buildtag
        Remove +build comments from modules using Go 1.18 or later

cftype
        Fixes initializers and casts of C.*Ref and JNI types

context
        Change imports of golang.org/x/net/context to context

egl
        Fixes initializers of EGLDisplay

eglconf
        Fixes initializers of EGLConfig

gotypes
        Change imports of golang.org/x/tools/go/{exact,themes} to go/{constant,types}

jni
        Fixes initializers of JNI's jobject and subtypes

netipv6zone
        Adapt element key to IPAddr, UDPAddr or TCPAddr composite literals.

        https://codereview.appspot.com/6849045/

printerconfig
        Add element keys to Config composite literals.

以下は例で、ソースコードで golang.org/x/net/context パッケージが使用されています

sh
package main

import (
  "fmt"
  "golang.org/x/net/context"
)

func main() {
  background := context.Background()
  fmt.Println(background.Err())
}

go fix を使用して修正し、標準ライブラリの context パッケージに置き換えます。以下のコマンドを使用して置き換えることができます

sh
$ go fix -fix context main.go

または、置き換えせずにファイルの前後の変更を確認することもできます。

sh
$ go tool fix -r context -diff  main.go
main.go: fixed context
diff main.go fixed/main.go
--- main.go
+++ fixed/main.go
@@ -1,8 +1,8 @@
 package main

 import (
+       "context"
        "fmt"
-       "golang.org/x/net/context"
 )

 func main() {

go 言語は 10 年以上の歴史がありますが、利用可能な置き換えパラメータは 9 つだけで、互換性が比較的よく保たれていることがわかります。

fmt

fmt コマンドは go 言語に組み込みのフォーマットツールで、go ソースコードファイルをフォーマットするために使用されます。

sh
$ go fmt -h
usage: go fmt [-n] [-x] [packages]
Run 'go help fmt' for details.

go doc gofmt コマンドを使用して、詳細ドキュメントを確認できます

sh
$ go doc cmd/gofmt
Gofmt formats Go programs. It uses tabs for indentation and blanks for
alignment. Alignment assumes that an editor is using a fixed-width font.

Usage:

    gofmt [flags] [path ...]

The flags are:

    -d
        Do not print reformatted sources to standard output.
        If a file's formatting is different than gofmt's, print diffs
        to standard output.
    -e
        Print all (including spurious) errors.
    -l
        Do not print reformatted sources to standard output.
        If a file's formatting is different from gofmt's, print its name
        to standard output.
    -r rule
        Apply the rewrite rule to the source before reformatting.
    -s
        Try to simplify code (after applying the rewrite rule, if any).
    -w
        Do not print reformatted sources to standard output.
        If a file's formatting is different from gofmt's, overwrite it
        with gofmt's version. If an error occurred during overwriting,
        the original file is restored from an automatic backup.

gofmt はインデントに tab を使用し、スペースで整列します。デフォルトでは、フォーマット後のコードは標準出力に出力され、元のファイルは上書きされません。go fmt コマンドは実際には gofmt コマンドを使用しています。これは独立したバイナリファイルで、GOROOT/bin ディレクトリ下に格納されています。

sh
$ ls $GOROOT/bin -1
go.exe*
gofmt.exe*

go fmt コマンドに -n フラグを追加すると、実行されるコマンドを確認できます。

sh
$ go fmt main.go
/golang/bin/gofmt.exe -l -w main.go

go fmt は実際には gofmt -l -w のエイリアスであることがわかります。gofmt コマンドには以下のパラメータがあります

  • -d:フォーマット前後のファイルの差分を出力
  • -e:すべてのエラーを出力
  • -l:変更されたファイル名を出力
  • -r:フォーマットルールを適用
  • -s:コードの簡素化を試みる
  • -w:ソースファイルを上書き。エラーが発生した場合はバックアップを復元

現在、以下のソースファイルがあると仮定します

go
$ cat main.go
package main

import "fmt"

func main() {
fmt.Println("hello world!")}

-d パラメータを使用して、変更をプレビューできます

sh
$ gofmt -d main.go
diff main.go.orig main.go
--- main.go.orig
+++ main.go
@@ -3,5 +3,5 @@
 import "fmt"

 func main() {
-fmt.Println("hello world!")}
-
+       fmt.Println("hello world!")
+}

-l パラメータは変更されるファイル名を出力します

$ gofmt -l .
main.go

構文エラーがある場合、-e パラメータはより詳細に出力できます

sh
$ gofmt -d -e main.go
main.go:6:27: missing ',' in argument list
main.go:6:28: expected operand, found newline
main.go:7:2: expected ')', found 'EOF'
main.go:7:2: expected ';', found 'EOF'
main.go:7:2: expected ';', found 'EOF'
main.go:7:2: expected '}', found 'EOF'
main.go:7:2: missing ',' in argument list

-w は変更をソースファイルに適用します

sh
$ gofmt -l -w .
main.go

$ cat main.go
package main

import "fmt"

func main() {
        fmt.Println("hello world!")
}

フォーマットツールとして、gofmt はカスタム構成を全く提供していないことがわかります。一方、js コードを美化するためのフォーマッター prettify はコードをフォーマットするために相当数の構成を提供しています。これは go 公式の態度を示しています。個性化を望まず、すべての人のコードスタイルをできるだけ一貫させることを望んでいます。少なくとも、コードを読むときに他人の習慣に適応する必要がないという利点があります。ただし、実際にはカスタム項目が 1 つ残されています。それはフォーマットコードの置換ルールで、ルールはカスタマイズできます。形式は以下の通りです

pattern -> replacement

例えば、冗長な括弧を削除します

(a) -> a

ファイルの変更を確認します

sh
$ gofmt -r "(a) -> a" -d -l .
main.go
diff main.go.orig main.go
--- main.go.orig
+++ main.go
@@ -3,5 +3,5 @@
 import "fmt"

 func main() {
-       fmt.Println(("hello world!"))
+       fmt.Println("hello world!")
 }

gofmt が冗長な括弧を削除することがわかります。

get

get コマンドは go 開発プロセスで最も一般的に使用されるコマンドの 1 つで、指定されたアドレスのパッケージのソースコードを GOMODCACHE が示すディレクトリにダウンロードする役割を果たします。

sh
$ go get -h
usage: go get [-t] [-u] [-v] [build flags] [packages]
Run 'go help get' for details.
  • -u:パッケージのマイナーバージョンおよびパッチバージョンの更新を試みます。メインバージョンの変更(例:v1->v2)が関与する場合は更新されません。
  • -t:テスト中の依存関係のバージョンを更新
  • -v:コンパイルされたパッケージを出力。実際には build flags のパラメータの 1 つです

昔、go get の役割は go install と似ており、これらのパッケージをダウンロードしてコンパイルしていました。しかし、go モジュールの登場と完成に伴い、この部分の役割は次第に廃止されました。現在、get コマンドの最も一般的な役割は go モジュールのダウンロードと依存関係の解析です。そのため、go get コマンドが build flags などのビルドフラグをサポートしていることがわかります。モジュール外で go install と同じように go get を使用しようとすると、この用法は廃止されたというプロンプトが表示されます。

sh
$ go get github.com/wire/wire
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'.

ドキュメントの説明でこれらが保持されている理由は不明ですが、get コマンドのソースコードを確認すると、以前のフラグが保持されていることがわかります。

go
var (
  getD        = CmdGet.Flag.Bool("d", true, "")
  getF        = CmdGet.Flag.Bool("f", false, "")
  getFix      = CmdGet.Flag.Bool("fix", false, "")
  getM        = CmdGet.Flag.Bool("m", false, "")
  getT        = CmdGet.Flag.Bool("t", false, "")
  getU        upgradeFlag
  getInsecure = CmdGet.Flag.Bool("insecure", false, "")
  // -v is cfg.BuildV
)

本題に戻ると、get コマンドは指定されたパッケージのソースコードをローカルのグローバル依存関係ディレクトリ(つまり GOCACHE が示すディレクトリ)にダウンロードし、情報を go.mod および go.sum ファイルに記録します。前者はバージョンを記録する責任があり、後者は sha1 チェックサムを記録してセキュリティを確保します。get コマンドは実際には VCS(ローカルのバージョン管理システム)に基づいており、以下のいくつかをサポートしています

  • git
  • hg (Mercurial)
  • bzr (Bazaar)
  • svn
  • fossil

その中で、デフォルトでは git と hg のみをサポートしています。GOVCS で構成でき、形式は以下の通りです

GOVCS=github.com:git,example.com:hg,*:git|hg,*:all

GOVCS は git と hg のみを VCS としてサポートしており、他の 3 つは GOPRIVATE で構成する必要があります。

go get コマンドには以下の使用方法があります。依存関係のアドレスを直接パラメータとして使用できます

sh
$ go get golang.org/x/net

バージョンを指定することもできます

sh
$ go get golang.org/x/net@0.17.0

最新バージョンを指定

sh
$ go get golang.org/x/net@latest

バージョンの更新を試みる

sh
$ go get -u golang.org/x/net

特定の依存関係を削除

sh
$ go get golang.org/x/net@none

上記は通常の依存関係を管理するためのものです。それほど一般的でない依存関係を管理するためにも使用できます。例えば go 言語のバージョンを更新

sh
$ go get go@latest
go: updating go.mod requires go >= 1.21.3; switching to go1.21.3
go: downloading go1.21.3 (windows/amd64)
go: upgraded go 1.21.0 => 1.21.3

go ツールチェーンのバージョンを更新するためにも使用できます

sh
$ go get toolchain@latest

go get を使用して go およびツールチェーンのバージョンを更新すると、GOMODCACHE/golang.org/ ディレクトリに新しいバージョンの go がインストールされます

sh
$ ls $(go env GOMODCACHE)/golang.org -1
'toolchain@v0.0.1-go1.21.3.windows-amd64'/
x/

この時点で GOROOT を手動で変更すると、指定されたバージョンに切り替えることができます。

install

install コマンドは get コマンドと似ており、どちらもサードパーティの依存関係をダウンロードするために使用されますが、違いは get コマンドがソースコードをダウンロードし、install コマンドがソースコードをマシンの実行可能なバイナリファイルにコンパイルすることです。バイナリファイルの格納パスはまず GOBIN ディレクトリで、次に GOPATH/bin です。このコマンドの主な機能は、サードパーティの公開されているいくつかのコマンドラインツールをダウンロードすることで、go 言語のコンパイル速度と移植性のおかげで、バイナリファイルをダウンロードする必要はなく、ソースコードをダウンロードしてローカルでコンパイルします。

sh
$ go install -h
usage: go install [build flags] [packages]
Run 'go help install' for details.

install コマンドはビルドフラグとパッケージ名をパラメータとして受け取り、gomod が有効な場合、パッケージ名にはバージョン番号が必要です。例えば delve デバッガをダウンロード

sh
$ go install -x github.com/go-delve/delve/cmd/dlv@latest
# get https://goproxy.cn/github.com/@v/list
# get https://goproxy.cn/github.com/go-delve/delve/cmd/@v/list
# get https://goproxy.cn/github.com/go-delve/delve/cmd/dlv/@v/list
# get https://goproxy.cn/github.com/go-delve/delve/@v/list
# get https://goproxy.cn/github.com/go-delve/@v/list
# get https://goproxy.cn/github.com/@v/list: 404 Not Found (0.014s)
# get https://goproxy.cn/github.com/go-delve/delve/cmd/@v/list: 404 Not Found (0.027s)
# get https://goproxy.cn/github.com/go-delve/delve/cmd/dlv/@v/list: 404 Not Found (0.027s)
# get https://goproxy.cn/github.com/go-delve/delve/@v/list: 200 OK (0.027s)
# get https://goproxy.cn/github.com/go-delve/@v/list: 404 Not Found (0.027s)
WORK=/home/user/tmp/go-build2033992495
mkdir -p $WORK/b001/
cat >/home/user/tmp/go-build2033992495/b001/importcfg.link << 'EOF' # internal
packagefile github.com/go-delve/delve/cmd/dlv=/home/user/.cache/go-build/f1/f11d552287458c0fce625abe50bf928c487064c36bbb1251ad8b1968772c3e4b-d
......
......
mkdir -p /home/wyh/gomod/bin/
mv $WORK/b001/exe/a.out /home/wyh/gomod/bin/dlv
rm -r $WORK/b001/

まずソースコードを GOMODCACHE が示すパスにダウンロードします。これは get コマンドと同じです。次に一時作業ディレクトリに切り替え、コンパイルを実行し、コンパイル完了後にバイナリファイルを GOPATH/bin ディレクトリに移動し、最後に一時フォルダを削除します。install コマンドにはもう 1 つ制限があり、ダウンロードするパッケージはプロジェクトのエントリーパッケージである必要があります。つまり main.go エントリーファイルを含む必要があります。そうしないと、インストールできないというプロンプトが表示されます。例えば、go install を使用して gin をダウンロード

sh
$ go install -x github.com/gin-gonic/gin@latest
# get https://goproxy.cn/github.com/@v/list
# get https://goproxy.cn/github.com/gin-gonic/gin/@v/list
# get https://goproxy.cn/github.com/gin-gonic/@v/list
# get https://goproxy.cn/github.com/@v/list: 404 Not Found (0.022s)
# get https://goproxy.cn/github.com/gin-gonic/gin/@v/list: 200 OK (0.027s)
# get https://goproxy.cn/github.com/gin-gonic/@v/list: 404 Not Found (0.028s)
package github.com/gin-gonic/gin is not a main package

gin は Web フレームワークの依存関係ライブラリであり、コマンドラインツールではないため、当然エントリーファイルがなく、インストールに失敗します。

list

list コマンドは指定された位置のパッケージを一覧表示し、1 行に 1 つずつ表示し、カスタムフォーマット出力をサポートし、多くのパラメータをサポートします。これを使用する前提は gomod をサポートするプロジェクト内であることです。

sh
$ go list -h
usage: go list [-f format] [-json] [-m] [list flags] [build flags] [packages]
Run 'go help list' for details.

サポートするパラメータは以下の通りです

  • -f:フォーマットパラメータ
  • -json:JSON 形式で出力
  • -compiled:コンパイラによってコンパイルされるすべてのパッケージを表示
  • -deps:各パッケージおよびその依存関係のすべてのパッケージの名前を表示
  • -test:各パッケージのテストパッケージを表示
  • -e:エラーが発生したパッケージでも正常に出力
  • -find:これらのパッケージの依存関係を解析しない
  • -export:このパラメータを使用すると、構造体 Package.Export フィールドの値を指定されたパッケージを含む最新の出力情報ファイルに設定し、Package.BuildID フィールドの値をパッケージの BuildID に設定します。主にフォーマット出力用です。

モジュール情報パラメータ:

  • -m:パッケージではなくモジュールを出力

  • -versions:モジュールのすべての利用可能な情報を表示

  • -retracted:モジュールの撤回バージョンを表示

[packages] パラメータは指定されたパッケージ名、フォルダ、または all にできます。all は任意の場所を意味し、-m パラメータを使用する場合、all は現在のモジュールによって参照されるすべての依存関係を意味します。

例えば、現在のファイルに main.go ファイルが 1 つだけで、"hello world" を出力するコードが 1 行だけある場合、go list -deps . を実行すると、現在のプロジェクトから fmt およびその参照されるすべての依存関係のパッケージが出力されます。

bash
$ ls
go.mod  go.sum  main.go

$ cat main.go
package main

import "fmt"

func main() {
        fmt.Println("hello world")
}

$ go list -deps .
internal/goarch
unsafe
internal/abi
internal/unsafeheader
internal/cpu
internal/bytealg
internal/coverage/rtcov
internal/godebugs
internal/goexperiment
internal/goos
runtime/internal/atomic
runtime/internal/math
runtime/internal/sys
runtime
......
......
path
io/fs
os
fmt
golearn

または、現在のプロジェクトのすべてのモジュール依存関係を出力

bash
$ go list -m all
golearn
cloud.google.com/go v0.26.0
github.com/246859/containers v0.0.1
github.com/246859/river v0.1.0 => D:\WorkSpace\Code\riverdb
github.com/BurntSushi/toml v0.3.1
github.com/Jleagle/steam-go v0.0.0-20230725082712-1053b441b1f2
github.com/Jleagle/unmarshal-go v0.0.0-20210227002040-694f544f9265
github.com/KyleBanks/depth v1.2.1
github.com/Microsoft/go-winio v0.6.1
github.com/PuerkitoBio/purell v1.1.1
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578
github.com/andeya/ameda v1.5.3
github.com/andeya/goutil v1.0.1
...

format

list コマンドの出力は行単位で、各行の出力はパッケージです。公式はカスタム行出力フォーマットのパラメータ -f を提供しており、受け取る値は template/text テンプレートエンジンパッケージによって定義されたテンプレート構文です。例えば以下の例

sh
-f "package {{ .Dir }} {{ .Name }}"

反復される各パッケージは以下の構造体として渡され、この構造体のすべてのフィールドはテンプレートパラメータとして使用できます。

go
type Package struct {
    Dir            string   // directory containing package sources
    ImportPath     string   // import path of package in dir
    ImportComment  string   // path in import comment on package statement
    Name           string   // package name
    Doc            string   // package documentation string
    Target         string   // install path
    Shlib          string   // the shared library that contains this package (only set when -linkshared)
    Goroot         bool     // is this package in the Go root?
    Standard       bool     // is this package part of the standard Go library?
    Stale          bool     // would 'go install' do anything for this package?
    StaleReason    string   // explanation for Stale==true
    Root           string   // Go root or Go path dir containing this package
    ConflictDir    string   // this directory shadows Dir in $GOPATH
    BinaryOnly     bool     // binary-only package (no longer supported)
    ForTest        string   // package is only for use in named test
    Export         string   // file containing export data (when using -export)
    BuildID        string   // build ID of the compiled package (when using -export)
    Module         *Module  // info about package's containing module, if any (can be nil)
    Match          []string // command-line patterns matching this package
    DepOnly        bool     // package is only a dependency, not explicitly listed
    DefaultGODEBUG string  // default GODEBUG setting, for main packages

    // Source files
    GoFiles           []string   // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
    CgoFiles          []string   // .go source files that import "C"
    CompiledGoFiles   []string   // .go files presented to compiler (when using -compiled)
    IgnoredGoFiles    []string   // .go source files ignored due to build constraints
    IgnoredOtherFiles []string // non-.go source files ignored due to build constraints
    CFiles            []string   // .c source files
    CXXFiles          []string   // .cc, .cxx and .cpp source files
    MFiles            []string   // .m source files
    HFiles            []string   // .h, .hh, .hpp and .hxx source files
    FFiles            []string   // .f, .F, .for and .f90 Fortran source files
    SFiles            []string   // .s source files
    SwigFiles         []string   // .swig files
    SwigCXXFiles      []string   // .swigcxx files
    SysoFiles         []string   // .syso object files to add to archive
    TestGoFiles       []string   // _test.go files in package
    XTestGoFiles      []string   // _test.go files outside package

    // Embedded files
    EmbedPatterns      []string // //go:embed patterns
    EmbedFiles         []string // files matched by EmbedPatterns
    TestEmbedPatterns  []string // //go:embed patterns in TestGoFiles
    TestEmbedFiles     []string // files matched by TestEmbedPatterns
    XTestEmbedPatterns []string // //go:embed patterns in XTestGoFiles
    XTestEmbedFiles    []string // files matched by XTestEmbedPatterns

    // Cgo directives
    CgoCFLAGS    []string // cgo: flags for C compiler
    CgoCPPFLAGS  []string // cgo: flags for C preprocessor
    CgoCXXFLAGS  []string // cgo: flags for C++ compiler
    CgoFFLAGS    []string // cgo: flags for Fortran compiler
    CgoLDFLAGS   []string // cgo: flags for linker
    CgoPkgConfig []string // cgo: pkg-config names

    // Dependency information
    Imports      []string          // import paths used by this package
    ImportMap    map[string]string // map from source import to ImportPath (identity entries omitted)
    Deps         []string          // all (recursively) imported dependencies
    TestImports  []string          // imports from TestGoFiles
    XTestImports []string          // imports from XTestGoFiles

    // Error information
    Incomplete bool            // this package or a dependency has an error
    Error      *PackageError   // error loading package
    DepsErrors []*PackageError // errors loading dependencies
}

type PackageError struct {
    ImportStack   []string // shortest path from package named on command line to this one
    Pos           string   // position of error (if present, file:line:col)
    Err           string   // the error itself
}

モジュールが反復される場合、以下の構造体として渡され、そのすべてのフィールドもテンプレートパラメータとして使用できます。

go
type Module struct {
    Path       string        // module path
    Query      string        // version query corresponding to this version
    Version    string        // module version
    Versions   []string      // available module versions
    Replace    *Module       // replaced by this module
    Time       *time.Time    // time version was created
    Update     *Module       // available update (with -u)
    Main       bool          // is this the main module?
    Indirect   bool          // module is only indirectly needed by main module
    Dir        string        // directory holding local copy of files, if any
    GoMod      string        // path to go.mod file describing module, if any
    GoVersion  string        // go version used in module
    Retracted  []string      // retraction information, if any (with -retracted or -u)
    Deprecated string        // deprecation message, if any (with -u)
    Error      *ModuleError  // error loading module
    Origin     any           // provenance of module
    Reuse      bool          // reuse of old module info is safe
}

type ModuleError struct {
    Err string // the error itself
}

すべてのパッケージを表示

bash
$ go list -f "package {{.Dir}} {{.Name}}" ./...
package /golearn main
package /golearn/app cmd
package /golearn/cmd cmd
package /golearn/docs docs
package /golearn/tool tool
package /golearn/tool_test tool

モジュールを表示

bash
$ go list -m -f "mod {{.Path}} {{.Version}} {{.GoVersion}} {{.GoMod}}"
mod golearn  1.21.3 /golearn/go.mod

mod

go mod は go モジュールを管理するための専用コマンドです。

sh
$ go mod help
Go mod provides access to operations on modules.

Note that support for modules is built into all the go commands,
not just 'go mod'. For example, day-to-day adding, removing, upgrading,
and downgrading of dependencies should be done using 'go get'.
See 'go help modules' for an overview of module functionality.

Usage:

        go mod <command> [arguments]

The commands are:

        download    download modules to local cache
        edit        edit go.mod from tools or scripts
        graph       print module requirement graph
        init        initialize new module in current directory
        tidy        add missing and remove unused modules
        vendor      make vendored copy of dependencies
        verify      verify dependencies have expected content
        why         explain why packages or modules are needed

Use "go help mod <command>" for more information about a command.

以下のサブコマンドがあります

  • downloadgo.mod ファイルに記載されているすべての依存関係をローカルキャッシュにダウンロード
  • editgo.mod ファイルを編集。コマンドラインインターフェースは主に他のツールやスクリプトに提供されます。
  • init:現在のディレクトリに gomod プロジェクトを初期化
  • tidy:不足している依存関係をダウンロードし、未使用の依存関係を削除
  • graph:依存関係グラフを出力
  • verify:ローカルの依存関係を検証
  • why:なぜこれらのモジュールに依存しているかを説明
  • vendor:プロジェクトの依存関係を vendor ディレクトリにエクスポート

init

sh
$ go help mod init
usage: go mod init [module-path]

init コマンドは gomod プロジェクトを初期化するために使用され、唯一のパラメータはモジュールパスです。将来的に他人が依存関係をダウンロードする必要がある場合、このモジュールパスを基準として使用する必要があります。命名ルールは一般的に以下の通りです

domain_name/user_name/repo_name

例えば、プロジェクトを github に配置するのが一般的なので、以下のようになります

github.com/jack/gotour

特殊な記号をモジュールパスとして使用することはお勧めしません。使用例を見てみましょう

sh
$ mkdir gotour

$ cd gotour

$ go mod init "github.com/jack/gotour"
go: creating new go.mod: module github.com/jack/gotour

tidy

$ go help mod tidy
usage: go mod tidy [-e] [-v] [-x] [-go=version] [-compat=version]

tidy コマンドは go.mod から未使用の依存関係(参照されていない依存関係)をクリアし、参照されているが存在しない依存関係をダウンロードします。以下のパラメータをサポートします

  • -v:削除されたモジュール依存関係を出力
  • -e:プロセス中にエラーが発生しても無視して続行
  • -x:実行プロセスを出力
  • -go=versiongo.mod ファイルの go バージョンを更新
  • -compact=version:指定された主要な Go バージョンから必要な追加チェックサムを保持してモジュールグラフを正常に読み込み、そのバージョンの go コマンドが異なるモジュールバージョンからインポートされたパッケージを読み込むと tidy がエラーを発生させます。このパラメータは一般的にほとんど使用されず、通常はバージョン変更時にのみエラーが発生します。stackoverflow のこの回答をご覧ください:go modules - go mod tidy error message: "but go 1.16 would select" - Stack Overflow

使用例を見てみましょう

sh
$ go mod tidy -v
unused github.com/246859/containers
unused github.com/246859/river
unused github.com/Jleagle/steam-go
unused github.com/Jleagle/unmarshal-go
unused github.com/KyleBanks/depth
unused github.com/Microsoft/go-winio
unused github.com/PuerkitoBio/purell
unused github.com/PuerkitoBio/urlesc
unused github.com/andeya/ameda
unused github.com/andeya/goutil
unused github.com/asaskevich/govalidator
unused github.com/buger/jsonparser
unused github.com/bwmarrin/snowflake
unused github.com/bytedance/go-tagexpr/v2
unused github.com/bytedance/sonic
unused github.com/cespare/xxhash/v2
unused github.com/chenzhuoyu/base64x
......

download

sh
$ go help mod download
usage: go mod download [-x] [-json] [-reuse=old.json] [modules]

download コマンドの名前は翻訳するとダウンロードですが、依存関係をローカルの依存関係キャッシュにダウンロードするだけで、go.mod ファイルは変更しません。その役割は依存関係をローカルのファイルキャッシュに事前にダウンロードすることで、特定の依存関係をダウンロードしたい場合は、go get または go mod tidy を使用することをお勧めします。

以下はいくつかの使用例です

sh
$ go mod download -x gorm.io/gorm
# get https://goproxy.cn/gorm.io/gorm/@v/list
# get https://goproxy.cn/gorm.io/gorm/@v/list: 200 OK (0.084s)

パラメータなしで実行すると、go.mod ファイルに存在するがローカルの依存関係キャッシュに存在しないすべての依存関係をダウンロードします。ダウンロードする必要がない場合は、以下を出力します

go: no module dependencies to download

edit

sh
$ go help mod edit
usage: go mod edit [editing flags] [-fmt|-print|-json] [go.mod]

editgo.mod ファイルを変更するためのコマンドラインインターフェースで、通常は他のプログラムに提供されます。一部のエディタ IDE は gomod サポートを提供するためにこれらのコマンドを使用します。以下のパラメータをサポートします

  • -module:モジュールパスを変更
  • -go=version:期待される go バージョンを変更
  • -require=path@version:依存関係を追加
  • -droprequire=path@version:依存関係を削除
  • -exclude=path@version:除外依存関係を追加
  • -dropexclude=path@version:除外依存関係を削除
  • -replace=old@version=new@version:置換依存関係を追加
  • -dropreplace=old@version:置換依存関係を削除
  • -retract=version:バージョンロールバック項目を追加
  • -dropretract=version:バージョンロールバック項目を削除

表示に使用される他のパラメータもあります

  • -print:ファイルの内容を出力
  • -json:JSON 形式で出力

以下の例を見てください

$ go mod edit -print
module golearn

go 1.21.3

require (
        github.com/dstgo/task v1.2.0
        github.com/spf13/cast v1.5.1
        github.com/swaggo/swag v1.16.2
        golang.org/x/net v0.19.0
        gorm.io/gorm v1.25.5
)

graph

sh
$ go help mod graph
usage: go mod graph [-go=version] [-x]

graph コマンドは現在のプロジェクトの依存関係グラフを出力します。可読性は非常に悪く、ほとんどの場合、人間が読むためのものではなく、結果は通常処理されて視覚的な形式で表示されます。各行は依存関係で、形式は以下の通りです

引用方 被引用方

例えば

golearn go@1.21.3

2 つのパラメータもサポートします

  • -go=version:指定された go バージョンを使用して依存関係グラフを読み込みます。その値は go.mod ファイルのバージョンより小さくすることはできません。
  • -x:プロセス中に実行されるコマンドを表示します。

簡単な使用例を見てみましょう

sh
$ go mod graph
golearn github.com/246859/containers@v0.0.1
golearn github.com/246859/river@v0.1.0
golearn github.com/Jleagle/steam-go@v0.0.0-20230725082712-1053b441b1f2
golearn github.com/Jleagle/unmarshal-go@v0.0.0-20210227002040-694f544f9265
golearn github.com/KyleBanks/depth@v1.2.1
golearn github.com/Microsoft/go-winio@v0.6.1
golearn github.com/PuerkitoBio/purell@v1.1.1
golearn github.com/PuerkitoBio/urlesc@v0.0.0-20170810143723-de5bf2ad4578
golearn github.com/andeya/ameda@v1.5.3
golearn github.com/andeya/goutil@v1.0.1
golearn github.com/asaskevich/govalidator@v0.0.0-20230301143203-a9d515a09cc2
golearn github.com/buger/jsonparser@v1.1.1
golearn github.com/bwmarrin/snowflake@v0.3.0
golearn github.com/bytedance/go-tagexpr/v2@v2.9.11
......

vendor

sh
$ go help mod vendor
usage: go mod vendor [-e] [-v] [-o outdir]

vendor は gomod が導入される前の gopath の代替案で、すべての go プロジェクトの下に vendor ディレクトリがあり、domain/user/project 形式で各プロジェクトの依存関係が個別に格納されています。隣接する nodeJs の膨大な node_module のように、各プロジェクトの依存関係が別々に格納されています。この依存関係管理方法は現在見ると確かに愚かですが、当時はより良い解決策がありませんでした。vendor が保持されているのは、go が下位互換性の約束を維持しているためで、いくつかの古いプロジェクトや go のソースコードでさえ vendor を使用している可能性があります。

本題に戻ると、vendorgo mod のサブコマンドで、現在のモジュールによって参照されるグローバル依存関係を vendor ディレクトリにエクスポートできます。

sh
$ go mod vendor -h
usage: go mod vendor [-e] [-v] [-o outdir]
Run 'go help mod vendor' for details.

以下のパラメータがあります

  • -o:出力パスフォルダを指定
  • -v:各依存関係を出力
  • -e:エラーが発生しても終了せずに続行

以下の例を見てみましょう。まず go list -m all を使用して、現在のプロジェクトによって参照される依存関係を確認します

sh
$ go list -m all
github.com/dstgo/task
github.com/davecgh/go-spew v1.1.1
github.com/pkg/errors v0.9.1
github.com/pmezard/go-difflib v1.0.0
github.com/stretchr/objx v0.5.0
github.com/stretchr/testify v1.8.4
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405
gopkg.in/yaml.v3 v3.0.1

現在の vendor ディレクトリにエクスポート

sh
$ go mod vendor -v -e -o vendor
# github.com/davecgh/go-spew v1.1.1
## explicit
github.com/davecgh/go-spew/spew
# github.com/pkg/errors v0.9.1
## explicit
github.com/pkg/errors
# github.com/pmezard/go-difflib v1.0.0
## explicit
github.com/pmezard/go-difflib/difflib
# github.com/stretchr/testify v1.8.4
## explicit; go 1.20
github.com/stretchr/testify/assert
# gopkg.in/yaml.v3 v3.0.1
## explicit
gopkg.in/yaml.v3

エクスポート後のディレクトリ構造は以下の通りです

└─vendor
    ├─github.com
    │  ├─davecgh
    │  │  └─go-spew
    │  │      └─spew
    │  ├─pkg
    │  │  └─errors
    │  ├─pmezard
    │  │  └─go-difflib
    │  │      └─difflib
    │  └─stretchr
    │      └─testify
    │          └─assert
    └─gopkg.in
    |    └─yaml.v3
    |
    |--modules.txt

その中の modules.txt はすべての依存関係項目を記述するファイルで、現在の go.mod に似ています。

verify

sh
$ go help mod verify
usage: go mod verify

このコマンドは、プロジェクトの依存関係がローカルにダウンロードされた後に変更されたかどうかをチェックします。例えば、問題がなければ all modules verified を出力します

sh
$ go mod verify
all modules verified

そうでない場合は、どこが変更されたかを報告し、異常な状態でコマンドを終了します。例えば

sh
$ go mod verify
gorm.io/gorm v1.25.5: dir has been modified (/go/mod/libs/gorm.io/gorm@v1.25.5)

why

$ go help mod why
usage: go mod why [-m] [-vendor] packages...

なぜこのパッケージに依存しているかを説明し、実際にはそれに関する依存関係グラフを出力します。例えば

sh
$ go mod why gorm.io/gorm
# gorm.io/gorm
golearn
gorm.io/gorm

デフォルトでは main からのインポートのみを解析し、-m パラメータを追加すると、各パッケージのインポート状況を分析できます。

work

work コマンドは go の複数モジュール管理のためのローカル開発ツールです

bash
$ go work help
Work provides access to operations on workspaces.

Note that support for workspaces is built into many other commands, not
just 'go work'.

The commands are:

        edit        edit go.work from tools or scripts
        init        initialize workspace file
        sync        sync workspace build list to modules
        use         add modules to workspace file
        vendor      make vendored copy of dependencies

Use "go help work <command>" for more information about a command.

init

init サブコマンドは workspace を初期化するために使用され、go.work というファイルを作成します

bash
$ go work init -h
usage: go work init [moddirs]
Run 'go help work init' for details.

パラメータ [moddirs] を受け取り、どのモジュールを管理に含めるかを指定します。例えば

bash
$ go work init ./service ./api

use

use サブコマンドは go.work に管理対象モジュールディレクトリを追加するために使用されます

bash
$ go help work use
usage: go work use [-r] [moddirs]

Use provides a command-line interface for adding
directories, optionally recursively, to a go.work file.

パラメータ [moddirs] を受け取り、-r[moddirs] パス下でモジュールを再帰的に検索することを示します。例えば

bash
$ go work use -r ./oss-api ./multi_modules

edit

edit サブコマンドの役割は go mod edit と同じで、他のツールやスクリプトに提供されるコマンドラインインターフェースです。

$ go help work edit
usage: go work edit [editing flags] [go.work]

Edit provides a command-line interface for editing go.work,
for use primarily by tools or scripts. It only reads go.work;
it does not look up information about the modules involved.
If no file is specified, Edit looks for a go.work file in the current
directory and its parent directories

パラメータは以下の通りです

  • -fmtgo.work ファイルをフォーマット

  • -use-dropuse:モジュールパスを追加および削除

  • -replace=old[@v]=new[@v]-dropreplace=old[@v]=new[@v]:置換するモジュールを追加および削除

  • -go-toolchain=name:go バージョンを指定、および使用するツールチェーンを指定

  • -print:最後の変更を出力し、ファイルに書き戻しません

  • -jsonjson 形式で出力。-print と同時に存在することはできません。対応する型構造は以下の通りです

    go
    type GoWork struct {
            Go        string
            Toolchain string
            Use       []Use
            Replace   []Replace
    }
    
    type Use struct {
            DiskPath   string
            ModulePath string
    }
    
    type Replace struct {
            Old Module
            New Module
    }
    
    type Module struct {
            Path    string
            Version string
    }

いくつかの使用例を以下に示します。フォーマット出力

bash
$ go work edit -fmt -print
go 1.22.0

use (
        ./ab/cd
        ./auth
        ./user
)

json 出力

bash
$ go work edit -fmt -json
{
        "Go": "1.22.0",
        "Use": [
                {
                        "DiskPath": "./ab/cd"
                },
                {
                        "DiskPath": "./auth"
                },
                {
                        "DiskPath": "./user"
                }
        ],
        "Replace": null
}

sync

sync サブコマンドは go.work のモジュールリストを workspace の各モジュールに戻すために使用されます。

bash
$ go help work sync
usage: go work sync

Sync syncs the workspace's build list back to the
workspace's modules

このプロセスは主にローカル開発完了後、各モジュールのリリース作業が完了した後に発生します。この時点で sync を使用すると、各モジュールの依存関係に基づいて worksapce 内のすべてのモジュールの go.mod の依存関係が更新され、手動で更新する必要がなくなります。

vendor

vendor コマンドは workspace 内のすべてのモジュールが依存するライブラリのコピーを vendor ディレクトリに作成します。

bash
$ go work help vendor
usage: go work vendor [-e] [-v] [-o outdir]

機能は go mod vendor と同じで、これ以上の詳細は省略します。

vet

vet コマンドは go 言語ソースコードの静的エラーチェックツールで、他の言語の lint ツール(例えば Eslint)に似ています。

sh
$ go vet -h
usage: go vet [build flags] [-vettool prog] [vet flags] [packages]
Run 'go help vet' for details.
Run 'go tool vet help' for a full list of flags and analyzers.
Run 'go tool vet -help' for an overview.

まず簡単な例を見てみましょう。以下のソースコードがあります

sh
$ cat main.go
package main

import "fmt"

func main(){
        fmt.Println("hello world!"
}

同じディレクトリでパラメータなしで go vet を実行

sh
$ go vet
vet: ./main.go:6:28: missing ',' before newline in argument list (and 1 more errors)

vet はどのファイルのどの行に什么问题があるかを報告します。ビルドフラグをパラメータとしてサポートし、例えば -n-x、パッケージ、フォルダ、ファイル名をパラメータとしてサポートします。

sh
$ go vet .
$ go vet main.go
$ go vet ./cmd
$ go vet runtime

以下のコマンドを使用して、より詳細なパラメータと説明を確認できます。

sh
$ go tool vet help
vet is a tool for static analysis of Go programs.

vet examines Go source code and reports suspicious constructs,
such as Printf calls whose arguments do not align with the format
string. It uses heuristics that do not guarantee all reports are
genuine problems, but it can find errors not caught by the compilers.

Registered analyzers:

    asmdecl      report mismatches between assembly files and Go declarations
    assign       check for useless assignments
    atomic       check for common mistakes using the sync/atomic package
    bools        check for common mistakes involving boolean operators
    buildtag     check //go:build and // +build directives
    ......

go tool vet コマンドはコードのチェックに直接使用できず、go vet を使用する必要があります。go vet パラメータの [vet flag] はコードアナライザーを設定でき、利用可能な値は以下の通りです

asmdecl      アセンブリファイルと go 宣言の不一致をチェック
assign       無用な代入をチェック
atomic       sync/atomic の使用時の一般的なミスをチェック
bools        論理演算子の誤った使用をチェック
buildtag     build tag をチェック
cgocall      cgo ポインタ渡しルールの違反をチェック
composites   初期化されていない複合構造(map、chan など)をチェック
copylocks     ロックの値コピーが発生したかチェック
directive    go ツールチェーンディレクティブをチェック
errorsas     errors.As に非ポインタ型または非 error 型が渡されているかチェック
framepointer コンパイル最適化後のアセンブリコードがフレームポインタを保存する前にクリアするかチェック
httpresponse httpresponse の誤った使用をチェック
ifaceassert  インターフェースからインターフェースへの型アサーションをチェック
loopclosure  ループ変数の参照問題
lostcancel   context.WithCancel が cancel 関数を使用していない
nilfunc      関数と nil の間の無用な比較をチェック
printf       printf のフォーマットパラメータが正しいかチェック
shift        整数幅と等しいか超えるシフトをチェック
sigchanyzer  バッファなしの chan os.Signal をチェック
slog         無効な構造化ログ呼び出しをチェック
stdmethods   既知のインターフェースメソッドの署名が正しいかチェック
stringintconv 文字列整数変換をチェック
structtag    構造体 tag が正しいかチェック
testinggoroutine テストで goroutine を使用して testing.Fatal を呼び出しているかチェック
tests        テストと例の一般的な誤った使用法をチェック
timeformat   (time.Time).Format または time.Parse の時間形式が正しいかチェック
unmarshal    unmarshal に非ポインタまたは非インターフェース型を渡しているかチェック
unreachable  到達できないコードをチェック
unsafeptr    uintptr から unsafe.Pointer への不正な変換をチェック
unusedresult 未使用の関数戻り値をチェック

これらはすべて特定のポイントを分析するアナライザーで、例えば timeformat アナライザーは time.Format の呼び出し形式が正しい構文に従っているかを分析します。デフォルトでは上記のすべてのアナライザーが有効になります。個別に有効にするには以下の形式を使用します

sh
$ go vet -timeformat main.go

個別に無効にする

sh
$ go vet -timeformat=false main.go

これらのアナライザーのソースコードは cmd/vendor/golang.org/x/tools/go/analysis/passes にあり、各アナライザーは go 言語でよくあるミスを対象としているため、vet コマンドを使用してコードをチェックすることを強くお勧めします。これら以外にも、いくつかの他のフラグパラメータをサポートしています

  • -V:バージョンのみを出力して終了
  • -json:JSON 形式で出力
  • -c=n:コンテキスト内の指定された数の競合行を表示(実際には何の役にも立たないようです)

いくつかの外部アナライザーもあります。例えば shadows は、短変数名の変数隠蔽問題をチェックします。外部にあるため、go install を使用してダウンロードする必要があります

sh
$ go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow@latest

使用形式は以下の通りです

sh
$ go vet -vettool=$(which shadow)

test

sh
$ go test -h
usage: go test [build/test flags] [packages] [build/test flags & test binary flags]
Run 'go help test' and 'go help testflag' for details.

test コマンドは go 言語ツールチェーンがテスト機能を提供するコマンドで、この機能は非常に重要です。ソフトウェアにとって、完善されたテストは不可欠です。ここでは test コマンドの使用方法を簡単に紹介するだけです。テストに関する詳細については、テスト をご覧ください。

build コマンドのコンパイルパラメータをサポートするほか、test は以下のパラメータもサポートします

  • -args:プログラムのエントリーパラメータ
  • -c:現在のパッケージのテストバイナリファイルを現在のディレクトリにコンパイルしますが、実行しません。pkg.test 方式で命名
  • -exec:テスト開始前に他のコマンドを実行
  • -json:テストの出力スタイルを json に変更
  • -o:テストバイナリファイルのパス名を指定

また、多くの testflag もサポートしています。help コマンドを使用してすべての testflag を確認できます

sh
$ go help testflag
The 'go test' command takes both flags that apply to 'go test' itself
and flags that apply to the resulting test binary.

The following flags are recognized by the 'go test' command and
control the execution of any test:
        -bench regexp
        -benchtime t
        -count n
    ......

いくつかの一般的なものを紹介します

  • -v:各ユースケースのテスト結果を出力
  • -timeout duration:テスト実行のタイムアウト時間
  • -skip regexp:指定されたテストユースケースをスキップ
  • -short:実行時間の長いテストユースケースの実行時間を短縮
  • -shuffle:すべてのテストユースケースの実行順序をシャッフル
  • -run regexp:指定されたテストユースケースを実行
  • -list regexp:各テストユースケースを一覧表示
  • -cpu 1,2,4:CPU 数を指定
  • -count n:各テストユースケースの実行回数を指定

最も簡単な使い方は、パラメータなしで、現在のパッケージ下のすべてのテストユースケースを実行し、結果を出力します。

sh
$ ls *_test.go
hello_test.go

$ go test
PASS
ok      golearn 0.522s

特定のテストファイルを指定

sh
$ go test hello_test.go
ok      command-line-arguments  0.041s

-v パラメータを追加すると、より詳細な出力を確認できます。非常に一般的に使用されます。

sh
$ go test -v hello_test.go
=== RUN   TestHello
    hello_test.go:6: hello world!
--- PASS: TestHello (0.00s)
PASS
ok      command-line-arguments  0.041s

特定のテストユースケースを指定

sh
$ go test -v -run TestHello
=== RUN   TestHello
    hello_test.go:6: hello world!
--- PASS: TestHello (0.00s)
PASS
ok      golearn 0.028s

テスト時、test コマンドには 2 つのモードがあります。まず 1 つ目のフォルダモードから説明します。package パラメータなしで test コマンドを実行すると、フォルダモードでテストが実行されます。以下のコマンドのようになります

sh
$ go test
$ go test -v

このモードでは、テストキャッシュが無効になります。もう 1 つのモードはリストモードで、package パラメータが空でない場合、リストモードでテストが実行されます。前者との違いはテストキャッシュが有効かどうかです。例えば以下のようになります

sh
$ go test -v .
$ go test -v ./...
$ go test .
$ go test -v net/http

リストモードでは、go は指定されたパッケージ下の各パッケージのテストファイルをバイナリファイルにコンパイルして実行します。テストの重複実行を避けるため、go はデフォルトで結果をキャッシュし、2 回目の実行時に再コンパイルしません。以下のパラメータを使用すると、デフォルトでキャッシュが有効になります

  • -benchtime
  • -cpu
  • -list
  • -parallel
  • -run
  • short
  • -timeout
  • -failfast
  • -v

これらのパラメータ以外の他のパラメータを使用すると、キャッシュを無効にできます。公式が推奨する方法は -count=1 を使用してキャッシュを無効にすることです。例えば

sh
$ go test -v -count=1 ./...

ディレクティブ

コマンドとは異なり、go のディレクティブはソースファイル内にハードコードされた形式で存在し、別の専門的な名前があります:コンパイルディレクティブ(progma directives)です。

コンパイラとリンカーはこれらの動作を変更することでコンパイルを制御し、c 言語のマクロに少し似ています。もちろん、すべてのディレクティブがコンパイルに影響を与えるわけではなく、一部は他の機能的な動作に使用されます。例えば generate ディレクティブは通常コード生成機能に使用されます。これらのディレクティブは通常コメントの形式で存在し、//go: をプレフィックスとし、間にスペースを含めることはできません。例えば //go:generate ディレクティブです。すべてのディレクティブタイプは 2 つに分類されます

  • 機能的ディレクティブ:これらは go が提供する機能的ディレクティブで、自由に使用できます。例えば generateembedbuild
  • コンパイラディレクティブ:これらは慎重に使用する必要があり、無闇に使用すると予測できない結果を招く可能性があります。

機能的ディレクティブを除き、ほとんどのディレクティブは関数署名にのみ作用できます。コンパイラディレクティブについては、go doc compile コマンドを実行してディレクティブを確認できます。すべてのディレクティブについては、cmd/compile/internal/ir/node.go: 440 で関連情報を見つけることができます。

generate

sh
$ go help generate
usage: go generate [-run regexp] [-n] [-v] [-x] [build flags] [file.go... | packages]

generate ディレクティブはその名の通り生成に関連するもので、通常はコード生成およびソースコード更新のコマンドを実行するために使用されますが、実際には任意のコマンドを実行できます。さらに、generate ディレクティブは他のディレクティブとは異なり、ソースファイル内のすべての generate ディレクティブを実行するための専用コマンドがあります。ファイル名またはパッケージ名を入力パラメータとして使用して、どのファイルの generate ディレクティブを実行するかを示せます。以下は他のパラメータです。

  • -run=regex:指定された generate ディレクティブを実行
  • -skip=regex:指定された generate ディレクティブをスキップ
  • -n:実行されるコマンドを出力
  • -x:プロセス中に実行されるコマンドを出力
  • -v:処理されたファイルを出力

さらに、generate ディレクティブで実行されるコマンドは以下の組み込みパラメータもサポートします

  • $GOARCH:CPU アーキテクチャ
  • $GOOS:オペレーティングシステム
  • $GOFILE:ファイル名
  • $GOLINE:行番号
  • $GOPACKAGE:パッケージ名
  • $GOROOT:go root
  • $DOLLAR:ドル記号
  • $PATH:path 環境変数

例を見てみましょう。コードは何もなく、コメントが 1 行だけです

go
package main

//go:generate echo "hello world!"

コマンドを実行

$ go generate .
hello world!

この例は go コマンドを実行します

go
package main

//go:generate go version

コマンドを実行

sh
$ go generate .
go version go1.21.3 windows/amd64

generate ディレクティブは任意のコマンドを実行するために使用でき、例えば swagger で API ドキュメントを生成したり、Wire で IOC コードを生成したりできます。ただし、このディレクティブは特に複雑なコマンドを実行するには適していません。短いコマンドの実行に適しており、複雑な要件がある場合はスクリプトまたは makefile を代わりに使用できます。

embed

embed ディレクティブは 1.16 で追加され、静的ファイルをバイナリファイルにパッケージ化する役割を果たします。例えば HTML テンプレートなどです。形式は以下の通りです

go
//go:embed pattern

pattern は glob 式、フォルダ、または特定のファイルにできます。例を見てみましょう

go
package main

import "embed"

//go:embed *
var static embed.FS

embed ディレクティブは embed.Fs 型のグローバル変数の上に配置する必要があります。グローバル変数である必要があり、これを使用するには embed パッケージをインポートする必要があります。この例では、* は現在のフォルダ下のすべてのファイルをバイナリファイルにパッケージ化することを表しますが、. で始まるフォルダの存在は許可されません。

以下の例は、埋め込まれたファイルから内容を読み取る方法を示しています

go
package main

import (
  "embed"
  "fmt"
)

//go:embed *.txt
var static embed.FS

func main() {
  bytes, err := static.ReadFile("hello.txt")
  if err != nil {
    panic(err)
  }
  fmt.Println(string(bytes))
}

3 つのメソッドしかなく、通常のファイルシステムとの使い方に違いはありません。io/Fs インターフェースを実装しているため、Fs オブジェクトとして渡すこともできます。

go
func (f FS) Open(name string) (fs.File, error)
func (f FS) ReadFile(name string) ([]byte, error)
func (f FS) ReadDir(name string) ([]fs.DirEntry, error)

以下の例は、embed ディレクティブを介して html ファイルを埋め込み、http サービスを介してアクセスする方法を示しています。

go
package main

import (
  "embed"
  "net/http"
)

//go:embed index.html
var htmlFs embed.FS

func main() {
  http.Handle("/", http.FileServer(http.FS(htmlFs)))
  http.ListenAndServe(":8080", http.DefaultServeMux)
}

アクセス結果は以下の通りです

sh
$ curl -s -GET 127.0.0.1:8080
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>hello world!</title>
</head>
<body>
<H1>Hello World!</H1>
<script>
    alert("hello world!");
</script>
</body>
</html>

embed ディレクティブは、グローバル変数の型を []byte にすることもサポートします。以下の例を見てください

go
package main

import (
  _ "embed"
  "net/http"
)

//go:embed index.html
var rawdata []byte

func main() {
  http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
    writer.Write(rawdata)
  })
  http.ListenAndServe(":8080", http.DefaultServeMux)
}

実装される効果は前の例とあまり変わりません。

sh
$ curl -s -GET 127.0.0.1:8080
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>hello world!</title>
</head>
<body>
<H1>Hello World!</H1>
<script>
    alert("hello world!");
</script>
</body>
</html>

build

build-コンパイル制御 セクションでは、// +build ディレクティブを使用してコンパイル動作を制御する方法について説明しました。//go:build ディレクティブは 1.17 で新しく導入され、以前のディレクティブに取って代わることを意図していましたが、現在 1.21 でもまだ置き換えられておらず、おそらく今後は共存する形で存在するでしょう。この新しいディレクティブについては、公式ドキュメントでも紹介されています:build constraints。機能は前者と変わりませんが、構文はより厳格で、ブール式をサポートします。例を見てみましょう

go
//go:build (linux && 386) || (darwin && !cgo)

package pkg_name

この方式は元の方式よりもはるかに可読性が高くなっています。

line

line ディレクティブは次の行の行番号、列番号、およびファイル名に影響します。その役割はこれだけで、ほとんどの場合、エラーのデバッグなどに使用される可能性があります。例えば、エラー発生時にコンパイラが出力する情報を変更します。

go
package main

var a undefinedType

func main() {

}

通常、コンパイラは以下を出力します

.\main.go:3:7: undefined: undefinedType

しかし、line ディレクティブを使用すると、状況が異なります

go
package main

//line abc.go:10:100
var a undefinedType

func main() {

}

出力は以下のようになります

abc.go:10:106: undefined: undefinedType

歴史的な理由により、line ディレクティブは他のディレクティブとは異なる唯一のディレクティブです。形式は以下の通りです

go
//line filename:line:column

他のディレクティブとは異なり、go: をプレフィックスとする必要がないことがわかります。

linkname

このディレクティブの操作は、他のパッケージの関数またはグローバル変数にリンクするために使用でき、それがプライベート型であってもかまいません。この操作は標準ライブラリ、特に runtime で頻繁に使用されます。関数本体がない一部の関数はこの方式を介して実装され、もう一方の関数本体が空の関数はアセンブリによって実装されます。使い方を確認してみましょう。使用形式は以下の通りです

go
//go:linkname リンク型名 リンクされる型

使用前に、例えば unsafe パッケージをインポートする必要があります。標準ライブラリのプライベート型にリンクする簡単な例を見てみましょう

go
import (
  "fmt"
  "unsafe"
)

//go:linkname memhash runtime.memhash
func memhash(p unsafe.Pointer, h, s uintptr) uintptr

func MemHash(data []byte) uint64 {
  ptr := unsafe.Pointer(unsafe.SliceData(data))
  return uint64(memhash(ptr, 0, uintptr(len(data))))
}

func main() {
  fmt.Println(MemHash([]byte("hello")))
}

出力

15395306441938000233

runtime.memhash というプライベート関数を自分で宣言した関数にリンクしました。この関数は関数本体ではなく署名だけで、キャリアの役割を果たします。memhash の役割は、ポインタ、ハッシュシード、およびメモリオフセットを指定して、メモリに基づいてハッシュ値を計算することです。このリンクプロセスはコンパイル時に完了します。

標準ライブラリでない場合、状況は少し異なります。例えば example パッケージに test 関数がある場合、リンクする前にまずこのパッケージを匿名インポートする必要があります。

go
package example

// プライベート型、外部からアクセスできません。
func test() string {
  return "a"
}
go
package main

import (
  "fmt"
  _ "golearn/example"
  _ "unsafe"
)

//go:linkname test golearn/example.test
func test() string

func main() {
  fmt.Println(test())
}

出力

a

リンクが成功したことがわかります。この方法は go のモジュールシステムを迂回して何でもできますが、大規模な使用はお勧めしません。自分が何をしているかを知っている場合を除きます。

noinline

noinline ディレクティブは、関数が非常にシンプルであっても、インライン最適化を禁止することを示します。簡単な例を見てみましょう

go
package main

func val() string {
  return "val"
}

func main() {
  var c = val()
  _ = c
}

val は非常にシンプルな関数で、文字列リテラルを返す役割を果たします。非常にシンプルで結果が常に予測可能であるため、コンパイル時に以下の形式に最適化されます

go
package main

func main() {
  var c = "val"
  _ = c
}

アセンブリ形式を確認してみましょう。val 関数の呼び出しが見つからないことがわかります。

TEXT    main.val(SB), NOSPLIT|NOFRAME|ABIInternal, $0-0
FUNCDATA        $0, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)
FUNCDATA        $1, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)
LEAQ    go:string."val"(SB), AX
MOVL    $3, BX

FUNCDATA        $0, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)
RET

次に noinline ディレクティブを追加します

go
package main

//go:noinline
func val() string {
  return "val"
}

func main() {
  var c = val()
  _ = c
}

アセンブリ形式を再度確認してみましょう

CMPQ    SP, 16(R14)
PCDATA  $0, $-2
JLS     17
PCDATA  $0, $-1
PUSHQ   BP
MOVQ    SP, BP
FUNCDATA        $0, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)
FUNCDATA        $1, gclocals·g2BeySu+wFnoycgXfElmcg==(SB)
PCDATA  $1, $0
CALL    main.val(SB)
POPQ    BP
RET

今回は main.val という呼び出しがはっきりと見え、これが noinline ディレクティブの機能で、コンパイラ最適化時の関数インラインを阻止します。

nosplit

nosplit ディレクティブの役割はスタックオーバーフロー検出をスキップすることです。go の並行スケジューリングモデルはプリエンプティブスケジューリングであるため、関数が非常に低レベルのコードを実行し、他の goroutine がこの関数を呼び出す際にプリエンプトに適さない場合、このディレクティブを使用して示すことができます。

go
//go:nosplit
func nospilitFn()

このディレクティブを使用すると、スタック成長も実行されなくなります。

noescape

noescape は名前からも推測できるようにエスケープに関連するもので、現在の関数でメモリリーク動作が発生しないことを示します。実行完了後、すべてのリソースが回収され、この関数は署名のみで関数本体を持たない必要があります。この場合、通常関数の実装はアセンブリによって実装されます。

例えば、以前に使用した memhash はこのディレクティブを使用します

go
//go:noescape
//go:linkname memhash runtime.memhash
func memhash(p unsafe.Pointer, h, s uintptr) uintptr

これにより、コンパイラはエスケープ分析を実行しなくなります。エスケープが発生しないことを保証する必要があります。エスケープが発生した場合、どのような結果になるかはわかりません。

uintptrescapes

uintptrescapes ディレクティブは、この関数内の uintptr 型パラメータがポインタ値に変換され、ヒープにエスケープし、存続を維持する必要があることを示します。このディレクティブは通常、いくつかの低レベルのシステム呼び出しに使用され、ほとんどの場合、理解する必要はありません。

go
//go:uintptrescapes
func nospilit(ptr uintptr) uintptr

以前は notinheaps というディレクティブがあり、型がヒープにメモリを割り当てることを許可しないことを示していましたが、どのバージョンで削除されたかはわかりません。

norace

norace ディレクティブは、関数のメモリアクセスが競合分析を必要としないことを示します。通常、低レベルのコードを実行する際に競合分析に適さない場合に使用されます。

go
//go:norace
func low_level_code(ptr uintptr) uintptr

TIP

runtime パッケージのみで使用が制限されているディレクティブもあり、外部では使用できません。それらはより深いものに関与し、詳細については Runtime-only compiler directives で確認できます。

Golang学习网由www.golangdev.cn整理维护