Slide 1

Slide 1 text

Go1.21から導入された Go Toolchainの仕組みをまるっと解説 株式会社メルコイン 取締役VP of Engineering @pooh

Slide 2

Slide 2 text

go.modのgo directiveの意味知っていますか? go1.21より前で
 go.modのgo directiveで「go 1.20 」と書かれていた場合、
 AとBどちらの挙動だったでしょうか?
 A
 go 1.20でbuildする
 B
 インストールされているgoで buildを試みる


Slide 3

Slide 3 text

2023/8/9にリリースされたGo1.21 
 新たに導入されたツールチェーン(Go Toolchains )
 次の内容を知ってもらい、Toolchainを使いこなせるようになること
 ● 導入が必要となった背景
 ● 導入で期待されること
 ● 具体的にはどう使うのか
 ● どんな挙動なのか
 
 セッションの目的

Slide 4

Slide 4 text

追加されたもの
 gotoolchain:コンパイラ、アセンブラ、その他のツールが含まれる
 環境変数:GOTOOLCHAIN
 go.mod / go.work:toolchain directive
 
 変更されたもの
 go.mod / go.work:go directive
 Toolchainに関係するもの

Slide 5

Slide 5 text

toolchainによって誤解を現実にする
 go.modファイルのgo directiveの仕様が直感的に期待する
 挙動ではなかった
 
 前方互換性を向上させる
 古いGo Toolchainが新しいGoのビルドを試み無いようにする
 何故、Go Toolchainが 導入されることになったのか? 参考:
 disucussion : extending Go forward compatibility #55092 
 issue : extended forwards compatibility for Go #57001 
 Proposal: Extended forwards compatibility in Go 
 Doc: Go Toolchains


Slide 6

Slide 6 text

Go1.21より前の仕様

Slide 7

Slide 7 text

go.modのgo directiveの意味知っていますか? go1.21より前で
 go.modのgo directiveで「go 1.20 」と書かれていた場合、
 AとBどちらの挙動だったでしょうか?
 A
 go 1.20でbuildする
 B
 インストールされているgoで buildを試みる


Slide 8

Slide 8 text

よくある勘違い ① コードをビルドするために使用できるGoの最小バージョンを設定するもの (ローカルにインストールされた)任意のバージョンのGoが コードのビルドを試みる。 古いバージョンのGoの場合はエラーを出すことがある。 02:00


Slide 9

Slide 9 text

よくある勘違い② 使用するGoの正確なバージョンを設定するもの インストールされているバージョンのGoを利用する

Slide 10

Slide 10 text

仕様の背景 誤ってgo directiveを新しすぎるものに設定しても、 できるだけ多くのコードがビルドされるようにする。 依存関係が新しいバージョンのGoを要求してても、 ローカルにインストールされているGoでコンパイルを試み、 成功するかどうかを試す

Slide 11

Slide 11 text

Go 1.21より前のgo.modのgo directiveの意味 Go 1.12がインストールされている場合 go.modファイル: 「go 1.14」と記載されている go 1.12でコンパイルを試す go 1.12までの機能しか使用していない場合は、 コンパイルが成功し正常終了する func main() { print("Hello, World!") }

Slide 12

Slide 12 text

Go 1.21より前のgo.modのgo directiveの意味 Go 1.13で搭載された 8進数リテラルのための 0o777構文を例にみていく dst, err := os.OpenFile( testGo, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0o777) if err != nil { log.Fatal(err) Go 1.13で実装されたので、 Go 1.12は解釈できない

Slide 13

Slide 13 text

Go 1.21より前のgo.modのgo directiveの意味 Go 1.12がインストールされている場合 go.modファイル: 「go 1.14」と記載されている go 1.12でコンパイルを試す エラーだったら、「go. 1.14」が必要というエラーが表示される % go version go version go1.12.17 darwin/amd64 % go build . ./main.go:25:71: syntax error: unexpected o777, expecting comma or ) note: module requires Go 1.14

Slide 14

Slide 14 text

Go 1.21より前のgo.modのgo directiveの意味 Go 1.12がインストールされている場合 go.modファイル: 「go 1.16」と記載されている go 1.12でコンパイルを試す エラーだったら、「go. 1.16」が必要というエラーが表示される % go version go version go1.12.17 darwin/amd64 % go build . ./main.go:25:71: syntax error: unexpected o777, expecting comma or ) note: module requires Go 1.16

Slide 15

Slide 15 text

Go 1.21より前のgo.modのgo directiveの意味 Go 1.12がインストールされている場合 go 1.14とgo.modに書いている場合は「note: module requires Go 1.14」 というエラーが表示される。 go 1.16とgo.modに書いている場合は「note: module requires Go 1.16」 というエラーが表示される。 このエラーが出ると、go.modみますよね? そして「go1.14」や[go1.16」をみて、何故てなりますよね? このエラーの理由は、 ローカルにインストールされている Goのバージョンが低い から。 go.modの設定がおかしいからではないが理解しにくいエラー表記となる。 本当に必要なのは、0o777構文に対応したGo 1.13以上のGo。

Slide 16

Slide 16 text

Go 1.21より前のgo.modのgo directiveの意味 Go 1.16がインストールされている場合 go.modファイル: 「go 1.12」と記載されている go 1.12でコンパイルを試す エラーだったら、「go. 1.13以降」が必要というエラーが表示される % go version  go version go1.16.15 darwin/arm64 % go build .  ./main.go:25:70: 0o/0O-style octal literals requires go1.13 or later (-lang was set to go1.12; check go.mod) Go 1.16は、 go.modで記載されている Go 1.12の言語セマンティクスで解析 してBuildをするので エラーとなる

Slide 17

Slide 17 text

Go 1.19.4にバグが入り込んだ。
 atomic.Pointer[T]を使用しているとコンパイルエラーになる問題
 https://github.com/golang/go/issues/57124
 Go 1.19.3とGo1.19.5であれば問題が発生しない。
 ※ BackPortされているので、現在ダウンロードできるGo1.19.4では問題は再現しない 
 go 1.21より前のバージョンでは、
 特定のバージョンのGoでビルドを強制できない。
 Go 1.19.4でBuildするとエラーになる。
 
 Go 1.19.3かGo 1.19.5でBuildをして欲しいのだけど、
 これをgo.modで表現することができなかった 。
 
 Go 1.21より前のgo.modのgo directiveの意味 辛いケース事例の紹介 06:00


Slide 18

Slide 18 text

Go 1.21より前のgo.modのgo directiveの意味 まとめ Buildするときに依拠する言語セマンティクスの助言 で、 実際にBuildするのはローカルにインストールされているGoがBuildをする。 ローカルのGoでBuildできるならBuildできる。 できない場合は、エラーとなる。

Slide 19

Slide 19 text

Go1.21のGo Toolchains

Slide 20

Slide 20 text

期待その1 インストールしているGoツールチェーンを明示的にアップデートが必要なくなる Go1.21からは インストールされているGoバージョンを意識する必要が薄れた。 プロジェクトを行き来すれば、 そのプロジェクトで使用すべきGoバージョンが使われる

Slide 21

Slide 21 text

期待その2 多くの人々が混乱し勘違いしている go.modが直感に沿った挙動をするようになる

Slide 22

Slide 22 text

期待その3 ループのスコープを修正する方法を提供すること。 redefining for loop variable semantics #56010 https://github.com/golang/go/discussions/56010 この変更により、古いGoはコンパイラーエラーがないからと言って、 新しいGoを正常にコンパイルできるとは想定できなくなる。 そのため、Go Toolchainはループの変更をする前提条件となってた。

Slide 23

Slide 23 text

実現されていること1 go.modのgo / toolchain directiveを編集するだけでは、 インストールされているGoがバージョンアップされない OSバンドルのGoなど デフォルトの GOのバージョンが勝手に変わることを防ぐ

Slide 24

Slide 24 text

実現されていること2 go.modのgo / toolchain directiveを編集すると、 そのプロジェクトでの最低Goバージョンが指定できる。 ビルドに使用する最低バージョンの Goを確実に制御できる 必要なバージョンの Goをダウンロードしキャッシュする 異なるバージョンを使用するプロジェクト間を行き来するだけで、 自動的に正しいバージョンを取得できる

Slide 25

Slide 25 text

GOTOOLCHAIN環境設定は、特定のGoバージョンを強制する
 go directiveやtoolchain directiveを上書きできる
 ローカルにgo1.21.2が存在しないため、
 go1.21.2をダウンロードしてキャッシュする
 ダウンロードしたgo1.21.2で実行する
 使用例その1 % GOTOOLCHAIN=go1.21.2 go version go: downloading go1.21.2 (darwin/amd64) go version go1.21.2 darwin/amd64 10:00


Slide 26

Slide 26 text

使用例 % GOTOOLCHAIN=go1.21.2 go version go version go1.21.2 darwin/amd64 すでにキャッシュされているバージョンの場合は再利用して実行する
 再度ダウンロードはしない


Slide 27

Slide 27 text

go1.21.2の場合のキャッシュ場所
 $HOME/go/pkg/mod/golang.org/
 [email protected]
 ダウンロードした Goの保存場所

Slide 28

Slide 28 text

必要に応じて新しいツールチェーンをダウンロードする時には、
 特別なモジュールとして対応する。
 ● モジュールパス:golang.org/toolchain
 バージョン:v0.0.1-goVersion,GOOS.GOARCH
 ツールチェーンのダウンロードは、
 GOPROXYを設定してプロキシ化され、チェックサムが検証される。
 toolchainのダウンロード

Slide 29

Slide 29 text

goコマンドは、GOTOOLCHAIN設定に基づいて使用するGoツールチェインを選択する。 
 任意のGo環境設定に対する標準ルールを使用する。 
 プロセス環境(os.Getenvで参照)
  GOTOOLCHAINが空でない値に設定されている場合、その値を使用する。 
 ユーザー環境のデフォルトファイル
  go env -w / go env -u で管理される 
  GOTOOLCHAINが空でない値に設定されている場合、その値を使用する。 
 バンドルされたGoツールチェインの環境デフォルトファイル 
  バンドルされた$GOROOT/go.env でGOTOOLCHAINが設定されている場合、その値を使用する。 
  標準では、GOOTOLCAHIN=autoを設定します。 
 
 
 GOTOOLCHAIN設定場所 % go env GOTOOLCHAIN auto GOTOOLCHAIN設定の確認 12:00


Slide 30

Slide 30 text

local : 常にバンドルされたGoツールチェーンを使用する
  :特定のGoツールチェーン名で指定されたバージョンを使用する
       例:go1.21.0
       PATHを探して無ければダウンロードして実行する
 +auto :必要に応じて新しいGoバージョンを選択して実行する。
         go directive/toolchain directiveの設定を参照する。無ければダウンロードする。
 +path :必要に応じて新しいGoバージョンを選択して実行する。
         go directive/toolchain directiveの設定を参照する。PATHに無ければ停止する。
 auto :local+autoの略記
 path :path+autoの略記
 GOTOOLCHAINで設定できる値

Slide 31

Slide 31 text

バンドルされているGoツールチェーンのバージョンを確認する
 
 
 ローカルにインストールされているバージョンを確認できます。
 ローカルのバージョンを確認するには、明示的に
 「GOTOOLCHAIN=local」を指定して実行します。
 2つの意味の go version % GOTOOLCHAIN=local go version go version go1.21.3 darwin/amd64

Slide 32

Slide 32 text

ワークスペースやメインモジュールで選択された
 Goツールチェーンのバージョン
 go.modの定義
 go 1.21.2
 toolchain go1.21.5
 
 2つの意味の go version % go version go version go1.21.5 darwin/amd64

Slide 33

Slide 33 text

No content

Slide 34

Slide 34 text


 
 
 
 
 
 
 
 
 GOTOOLCHAIN環境変数に最新のバージョンとautoを指定できる 
 常に最新の Goを使いたい場合 % export GOTOOLCHAIN=`curl -s -L "https://go.dev/VERSION?m=text" | head -n 1`+auto % print $GOTOOLCHAIN go1.22.4+auto % go version go: downloading go1.22.4 (darwin/amd64) go version go1.22.4 darwin/amd64 14:00


Slide 35

Slide 35 text

toolchain directive
 go directiveよりも優先される好ましい推奨ツールチェーンを設定する
 省略されるとgo directiveのバージョンをとって、
 toolchain directiveを暗黙的に記載されているように振る舞う
 go getで更新可能
 go directiveとtoolchain directiveが同じになった場合は、
 toolchain directiveが不要なので削除される
 go.mod/go.workでの定義変更と追加

Slide 36

Slide 36 text

go directive
 必要最小限のGoバージョンを宣言する
 省略した時
 go.modではgo1.16
 go.workではgo1.18
 モジュールはrequire/ワークスペースはuse文 で
 リストされたgoバージョン以上が必要
 モジュール内のパッケージをコンパイルするときに
 強制言語バージョンになりエラーとなる
 go getで更新可能
 go.mod/go.workでの定義変更と追加

Slide 37

Slide 37 text

go.modのtoolchain directiveの意味 Go 1.21.3がインストールされている場合 go.modファイル: go 1.21.1 toolchain go1.21.2  ローカルのgo 1.21.3 を使う % go version go version go1.21.3 darwin/amd64

Slide 38

Slide 38 text

go.modのtoolchain directiveの意味 Go 1.21.3がインストールされている場合 go.modファイル: go 1.21.1 toolchain go1.22.1  ローカルのgo 1.21.3 を使わずにgo 1.22.1を使用する  PATHを探して、なければダウンロードしてキャッシュして使用する % go build . go: downloading go1.22.1 (darwin/amd64)

Slide 39

Slide 39 text

Go 1.21以降のgo.modのgo directiveの意味 Go 1.21.3がインストールされている場合 go.modファイル: 「go 1.21.0」と記載されている go 1.21.3 を使ってBuildする

Slide 40

Slide 40 text

Go 1.21以降のgo.modのgo directiveの意味 Go 1.21.3がインストールされている場合 go.modファイル: 「go 1.21.7」と記載されている  go 1.21.3 を使わずにgo 1.21.7を使用する  PATHを探して、なければダウンロードしてキャッシュして使用する % go build . go: downloading go1.21.7 (darwin/amd64) // go.mod にgo1.21.9を記載(資料作成時には未リリースのバージョン % go build . go: downloading go1.21.9 (darwin/amd64) go: download go1.21.9 for darwin/amd64: toolchain not available

Slide 41

Slide 41 text

Go1.21のGo Get

Slide 42

Slide 42 text

go get toolchain@version % go get [email protected] go: upgraded toolchain go1.21.2 => go1.22.1 go.modファイル: go 1.21.1 toolchain go1.21.2 go.1.22.1をダウンロードしキャッシュし、 go.modのtoolchain directiveを更新する。 17:00


Slide 43

Slide 43 text

go getでGoライブラリのインポートの時の挙動 % go get github.com/yamatoya/test-go1.21.7 go: github.com/yamatoya/[email protected] requires go >= 1.21.7; switching to go1.21.11 go: downloading go1.21.11 (darwin/amd64) go: upgraded go 1.21.4 => 1.21.7 go: added toolchain go1.21.11 go: added github.com/yamatoya/test-go1.21.7 v0.0.1 go.modファイル: go 1.21.4 go.modでgo1.21.7を宣言しているライブラリをgo getする

Slide 44

Slide 44 text

go getでGoライブラリのインポートの時の挙動 go: github.com/yamatoya/[email protected] requires go >= 1.21.7; switching to go1.21.11 go: downloading go1.21.11 (darwin/amd64) go: upgraded go 1.21.4 => 1.21.7 go: added toolchain go1.21.11 go 1.21.7以降を必要とする 3つの利用候補から最小のバージョンを選択 する go directiveが1.21.7に更新される

Slide 45

Slide 45 text

goコマンドは実行ツールチェーンを決定するために
 利用可能なリストを取得する
 https://golang.org/dl/?mode=json&include=all 
 リストから最大で3つの候補を抽出します
 ● リリースされていないバージョンの最新のリリース候補 1.N₃rcR₃
 ● 最新のリリースされたバージョンの最新パッチリリース 1.N₂.P₂
 ● 直前のバージョンの最新のパッチリリース 1.N₁.P₁
 要件を満たす最小バージョンの候補を保守的に選択する
 go1.21.7を必要としていて、go1.21.11がリリースされている場合に
 go1.21.11を選択する
 3つの利用候補

Slide 46

Slide 46 text

まとめ

Slide 47

Slide 47 text

まとめ インストールしているGoツールチェーンを明示的にアップデートが必要なくなる プロジェクトを行き来すれば、 そのプロジェクトで使用すべきGoバージョンが使われる go directive toolchain directive

Slide 48

Slide 48 text

参考

Slide 49

Slide 49 text

1.21より前のバージョン遷移 
  1.20rc1 < 1.20rc2 < 1.20rc3 < 1.20 < 1.20.1
  以前のバージョンには、1.18beta2のようばベータリリースがあった。 
  1.18beta1 < 1.18beta2 < 1.18rc1 < 1.18 < 1.18.1 
 1.21以降のバージョン遷移 
  1.21 < 1.21rc1 < 1.21rc2 < 1.21.0 < 1.21.1
  Go 1.N のリリース候補は、1.N.0 の前に発行され1.NrcN(1.21rc1 / 1.21rc2….)を使用します。 
  最初のリリース候補は、1.Nrc1 
  初回リリースは1.Nだったが、 Go 1.21から1.N.0 となった。
 Goのバージョン定義まとめ

Slide 50

Slide 50 text

Go1.19.13 / Go1.20.8
 Go1.22でloopの意味が変わる可能性があります。 
 Go 1.22向けに書かれたコードをビルドしようとすると、 
 本来発生すべきでないエイリアシングのバグが発生する可能性があります。 
 go.dev/issue/60078を参照してください。 
 サポート終了後の古いGoツールチェーンの誤用や間違いをいくつか捕捉するためにチェックが追 加された
 
 過去のバージョンでの強制的要件

Slide 51

Slide 51 text

Go1.19.13でのバージョンブロッカー if p.Module != nil && !(allowedVersion(p.Module.GoVersion) || p.Module.GoVersion == "1.20" || p.Module.GoVersion == "1.21") { return errors.New("cannot compile Go " + p.Module.GoVersion + " code") }

Slide 52

Slide 52 text

Go1.20.8でのバージョンブロッカー if p.Module != nil && !(allowedVersion(p.Module.GoVersion) || p.Module.GoVersion == "1.20" || p.Module.GoVersion == "1.21") { return errors.New("cannot compile Go " + p.Module.GoVersion + " code") }

Slide 53

Slide 53 text

Thank you!