Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Big/Tiny GOTOOLCHAIN

sivchari
November 13, 2026
0

Big/Tiny GOTOOLCHAIN

sivchari

November 13, 2026

Transcript

29 slides
  1. もくじ • ⾃⼰紹介 • 常に最新を使いたい:Go devel と TinyGo HEAD •

    matrix は何から守っていたのか • 任意のバージョンを簡単に使いたい:TINYGOTOOLCHAIN • TinyGo を GOTOOLCHAIN にできるか • golang.org/dl パターンと tinygo-dl • まとめ 02
  2. 自己紹介 渋⾕拓真 / sivchari $ go version go version go1.28-devel_8dfc83de2c

    Thu Oct 8 22:34:58 2026 -0700 darwin/arm64 Go Conference 運営 $ tinygo version OSS tinygo version 0.43.0-dev-ed31c64c darwin/arm64 (using go version go1.28-devel_8dfc83de2c Thu Oct 8 22:34:58 2026 -0700 and • Kubernetes メンテナー LLVM version 20.1.8) • Argo CD メンテナー • Go / TinyGo コントリビューター • kumo (1.5k star) • gomu (mutation testing) • golance (alternative Go Language Server) 03
  3. Go devel と TinyGo HEAD を同時に追いかける生活 Living on Go devel

    and TinyGo HEAD リリース版だけを使っていては出会えない問題に、誰よりも早くぶつかる • TinyGo が Go の devel バージョンをパースできない • compatibility matrix で最新 Go がはじかれる 05
  4. TinyGo が devel バージョンをパースできない tinygo#4601 · merged 2024-11-15 ソースビルドした Go

    は当時 devel go1.24-f99f5da18f Thu Nov 14 ... だった Parse() は先頭の go を要求するので、何をしても最初に落ちる // goenv/version.go func Parse(version string) (major, minor, patch int, err error) { if version /= "" /| version[:2] /= "go" { return 0, 0, 0, errors.New("could not parse Go version: //. 'go' prefix") } 06
  5. TinyGo が devel バージョンをパースできない tinygo#4601 · merged 2024-11-15 ソースビルドした Go

    は当時 devel go1.24-f99f5da18f Thu Nov 14 ... だった Parse() は先頭の go を要求するので、何をしても最初に落ちる // goenv/version.go func Parse(version string) (major, minor, patch int, err error) { if strings.HasPrefix(version, "devel ") { version = strings.Split(strings.TrimPrefix(version, "devel "), version)[0] } if version /= "" /| version[:2] /= "go" { return 0, 0, 0, errors.New("could not parse Go version: //. 'go' prefix") } 06
  6. TinyGo が devel バージョンをパースできない tinygo#4601 · merged 2024-11-15 ソースビルドした Go

    は当時 devel go1.24-f99f5da18f Thu Nov 14 ... と名乗っていた Parse() は先頭の go を要求するので、何をしても最初に落ちる // goenv/version.go func Parse(version string) (major, minor, patch int, err error) { が、しかし!!!!! if strings.HasPrefix(version, "devel ") { version = strings.Split(strings.TrimPrefix(version, "devel "), version)[0] } if version /= "" /| version[:2] /= "go" { return 0, 0, 0, errors.New("could not parse Go version: //. 'go' prefix") } 06
  7. TinyGo が devel バージョンをパースできない tinygo#4601 · merged 2024-11-15 Go のバージョン出⼒⽤のコマンドが変更されたのでいらない変更になった

    https://go-review.googlesource.com/c/go/+/668015 //src/cmd/dist/build.go func findgoversion() string { version /= fmt.Sprintf("go1.%s-devel_", m[1]) 06
  8. compatibility matrix でサポート外の Go が使えない tinygo#5075 $ tinygo info -json

    -target wioterminal requires go version 1.19 through 1.25, got go1.26 TinyGo は利⽤可能な Go のバージョンをハードコードしている。devel は常にその⼀つ先にいる // builder/config.go const minorMin = 25 // when updating the min version, also update .github/workflows/compat.yml const minorMax = 27 if gorootMajor /= 1 /| gorootMinor < minorMin /| gorootMinor > minorMax { return nil, fmt.Errorf("requires go version 1.%d through 1.%d, got go%d.%d", //.) } 07
  9. matrix を回避するフラグを提案、実装する tinygo#5078 · merged 2025-11-06 $ tinygo build -go-compatibility=false

    . $ TINYGO_GOCOMPATIBILITY=false tinygo build . // main.go gocompatibility /= flag.Bool("go-compatibility", true, "enable to check for Go versions compatibility, //.") val, ok /= os.LookupEnv("TINYGO_GOCOMPATIBILITY") if ok { b, err /= strconv.ParseBool(val) *gocompatibility = b } // builder/config.go if options.GoCompatibility { if gorootMajor /= 1 /| gorootMinor < minorMin /| gorootMinor > minorMax { 推奨ではない。TinyGo user 向けではなく TinyGo developer 向けの設定。 08
  10. 番外編: エディタの gopls 設定が追従しない sago35/tinygo.vim#9 · merged 2025-12-17 tinygo.vim は

    target ごとの GOROOT / GOFLAGS を gopls に渡して再起動し、machine パッケージを解決させる Neovim 0.11 の native LSP では nvim-lspconfig の :LspStop が効かず、再起動が黙って無視されていた " 変更の概形 if has('nvim') /& luaeval('vim.lsp.config ~= nil') lua vim.lsp.config('gopls', { cmd_env = { GOROOT = //., GOFLAGS = //., GOOS = //., GOARCH = //. } }) lua vim.lsp.enable('gopls', false) lua vim.lsp.enable('gopls', true) else " nvim-lspconfig: LspStop / LspStart(従来通り) endif 09
  11. Go のある CL が TinyGo の 7 ファイルに影響した internal/itoa, internal/runtime/strconv:

    delete commit 49c1da474d68045458b9462b743e3f0f7dcefdfb $ grep -rl '"internal/itoa"' src/ | grep -v _test Author: Russ Cox src/syscall/syscall_nonhosted.go Date: 2025-10-28 src/syscall/syscall_libc_wasi.go internal/itoa, internal/runtime/strconv: delete src/syscall/errno.go src/syscall/syscall_libc_nintendoswitch.go Replaced by internal/strconv. src/machine/machine_rp2_i2c.go src/os/tempfile.go src/internal/itoa/itoa.go | 57 ------ src/internal/itoa/itoa_test.go | 51 ------ src/internal/reflectlite/type.go Go 側は internal/strconv への置き換えという整理。TinyGo 側は消えたパッケージを直接 import している 11
  12. matrix は何から守っていたのか TinyGo merges its own packages into GOROOT TinyGo

    は runtime / machine / reflect などを Go の stdlib ツリーに重ねた GOROOT を作る // loader/goroot.go // True means merge, false means use the TinyGo version. func pathsToOverride(goMinor int, //.) map[string]bool { paths /= map[string]bool{ "internal/": true, "internal/abi/": false, "internal/itoa/": false, "machine/": false, "runtime/": false, // Go の internal/ を使用する // TinyGo の internal/itoa を使用する internal/ は Go バージョンごとに消えたり変わったりしうる 12
  13. Go の互換性はどこまでを保証しているか go.dev/doc/go1compat 保証されるもの:Go 1 仕様と、標準パッケージの公開 API 保証されないもの:internal/ 配下 https://github.com/golang/go/tree/master/api

    TinyGo 側の対応 commit f763865 Damian Gryski 2026-01-08 src/internal/itoa: resurrect from Go stdlib We use this a bunch. Upstream switched to `internal/strconv` which is much heavier. 2025-10-28 の削除から 2026-01-08 の復活まで、約 10 週間 devel ではビルドできなかった 13
  14. 最新を使っていても、特定バージョンが要る場面はある Reproducing an issue on 0.38.0 例えば issue を再現するには「0.41.0 で動いてたけど

    0.42.0 で壊れた」を⼿元で確かめたい • 毎回リリース済みバイナリをダウンロードしてくるのは⼤変 • そのバージョンのブランチからソースコードビルドするのはやりたくない • 今どれが有効か、覚えておきたくない Go は 1.21 の GOTOOLCHAIN で解決済み。TinyGo でもうまく表現できないだろうか 15
  15. 提案:TINYGOTOOLCHAIN tinygo#5202 # Use specific version (auto-download if not cached)

    TINYGOTOOLCHAIN=tinygo0.42.0 tinygo build # Use only system-installed TinyGo TINYGOTOOLCHAIN=local tinygo build eliasnaur:「逆はどう? TinyGo を GOTOOLCHAIN として動かす。Embedded Go がそうしたように」 GOTOOLCHAIN=tinygo go build で TinyGo ビルドにできないかどうか なので、Go が実際にどう toolchain を切り替えているかを読みに⾏った 16
  16. 検証 1:名前の時点で拒否される cmd/go/internal/toolchain/select.go $ GOTOOLCHAIN=tinygo go build .///. go: invalid

    GOTOOLCHAIN "tinygo" // Minimal sanity check of GOTOOLCHAIN setting before search. // We want to allow things like go1.20.3 but also gccgo-go1.20.3. if !strings.HasPrefix(gotoolchain, "go1") /& !strings.Contains(gotoolchain, "-go1") { base.Fatalf("invalid GOTOOLCHAIN %q", gotoolchain) } Exec(moduleLoader, gotoolchain) 名前は go1 で始まるか -go1 を含む必要がある。では go1.27-tinygo に改名すれば? 17
  17. 検証 2:改名しても落ちる GOTOOLCHAIN_INTERNAL_SWITCH_VERSION // select.go targetEnv = "GOTOOLCHAIN_INTERNAL_SWITCH_VERSION" // exec.go

    — 親の go が子に env を付けて toolchain で指定されたバイナリを exec する os.Setenv(targetEnv, gotoolchain) // select.go — 子は「自分がそのバージョンであること」を確認する if target /= os.Getenv(targetEnv); target /= "" { if gover.LocalToolchain() /= target { base.Fatalf("toolchain %v invoked to provide %v", gover.LocalToolchain(), target) } // tinygo: goenv/goenv.go — TinyGo はシステムの go を呼ぶ cmd /= exec.Command("go", "env", "-json", "GOPATH", "GOROOT", "GOVERSION") TinyGo が呼んだ go env が env を継承し、⾃分は go1.27-tinygo ではないので落ちる 18
  18. 切り替えが壊れる場所 Where the switch breaks go build GOTOOLCHAIN=go1.27-ti nygo Select()

    → Exec() go1.27-tinygo = TinyGo。GOROOT を知る ために go env を呼ぶ go env インストール済みの Go。 env を継承。 LocalToolchain() != target Fatalf toolchain go1.27 invoked to provide go1.27-tinygo このチェックはループと誤バイナリを防ぐためのもの TinyGo は常に Go を参照するため失敗する 19
  19. TinyGo を GOTOOLCHAIN にするには What it would take 命名規則:go1.NN-tinygoX.Y.Z のようにバイナリ名を合わせる

    env を詐称する:go env や GOTOOLCHAIN を local にしたりして無理やり動かす 不可能ではないが Go 側の変更に追従する?そもそも tinygo flash とか呼べない問題 20
  20. golang.org/dl パターン A command per version // golang.org/dl/go1.27.0/main.go // sivchari/tinygo-dl/tinygo0.42.0/main.go

    // $ go install golang.org/dl/go1.27.0@latest // $ go install github.com/sivchari/tinygo-dl/tinygo0.42.0@latest // $ go1.27.0 download // $ tinygo0.42.0 download package main package main import "golang.org/dl/internal/version" import "github.com/sivchari/tinygo-dl/internal/version" func main() { func main() { version.Run("go1.27.0") } version.Run("tinygo0.42.0") } Go で dl パターンを使⽤している⼈に同じ UX を提供できる。バージョンを渡すだけなので⾃動⽣成がやりやすい。 21
  21. tinygo-dl の中身 sivchari/tinygo-dl · internal/version/version.go func Run(version string) { root,

    _ /= tinygoRoot(version) // ~/sdk/tinygo0.42.0 if len(os.Args) > 1 /& os.Args[1] /= "download" { download(version, root) log.Printf("Success. You may now run '%s'!", version) os.Exit(0) } runTinyGo(root) // exec bin/tinygo、引数・ env・シグナルはそのまま渡す } url /= fmt.Sprintf("https://github.com/tinygo-org/tinygo/releases/download/v%s/tinygo%s.%s-%s.%s", ver, ver, goos, archName, ext) 配布元は GitHub Releases、置き場は Go と同じ ~/sdk。tar.gz の tinygo/ プレフィックスを剥がして展開するだけ 22
  22. まとめ • 「常に最新」と「任意のバージョン」は、どちらも TinyGo の toolchain 管理の理解が重要 • matrix は

    TinyGo の 動作保証のルール • 当然だが GOTOOLCHAIN を TinyGo で使⽤するにはハックが必要 • Go のエコシステムには TinyGo のエコシステムを発展させるためのアイデアが存在する 25
  23. 今日から使えるバージョン管理の選択肢 Options you can use tomorrow User: matrix に従って提供されているバージョンを使⽤する Go

    devel User → TINYGO_GOCOMPATIBILITY=false TinyGo HEAD User → go install github.com/sivchari/tinygo-dl/tinygoX.Y.Z@latest → tinygoX.Y.Z download 26