メインコンテンツへスキップ
#81662accepted

cmd/cgo: add cgo without a C toolchain as a GOEXPERIMENT

ステータス変更: likely_accept accepted

要約

AIによる要約であり、誤りを含む場合があります。

概要

#81450「C ツールチェーンなしの cgo」は現在も議論中の proposal です。本 proposal は、その実装を本体リポジトリ(golang/go)で先行して進められるように、cgobindings という GOEXPERIMENT(実験的機能の有効/無効を切り替えるビルド時フラグ)を追加するものです。

ステータス変更

likely_accept → accepted
2026年9月24日に @aclements がレビューグループを代表して likely accept を宣言しました。その後1週間、反対意見は出ず、10月1日に「No change in consensus」として正式に承認されました。本 proposal は本体 #81450 の採否を決めるものではありません。実験用のフラグで機能を囲った状態でコードを追加し、試せるようにするための手続きです。範囲が限られており、失敗しても元に戻しやすいため、議論はほとんど起きませんでした。

技術的背景

現状の問題点

import "C" を含むパッケージをビルドするには、C コンパイラが必須です。理由は2つあります。cgo コマンドが C プリアンブル(import "C" 直前のコメントに書く C コード)を C コンパイラで解析していること、そして生成されるグルーコード(Go と C の呼び出しをつなぐ中継コード)や runtime/cgo 自体に C コードが含まれていることです。このため、ビルド済みの .so/.a を呼び出すだけのパッケージでも、利用者側に C ツールチェーンが要ります。クロスコンパイルではさらに、C のクロスコンパイラや sysroot の準備も必要になります。
親 proposal #81450 はこの問題を次の方法で解決しようとしています。

  • C のインターフェースを Go の構文で書いた「バインディングファイル」で表す(例: //cgo:binding C.compute という注釈を付けた関数宣言)
  • C の呼び出し規約(ABI)への変換は cgo コマンドが Go とアセンブリで生成する
  • runtime/cgo を Go とアセンブリで書き直す
    ただし、バインディングの書式(注釈方式か、別ファイル方式か)など未確定の点が多く残っています。

提案された解決策

  • internal/goexperiment に cgobindings フラグを追加する
  • デフォルトは無効。親 proposal の結論が出るまでは、関連する実装をこのフラグの内側に置く
  • #81450 が承認された場合はフラグをデフォルトで有効にしてから削除し、却下された場合は実験とその内側のコードをすべて削除する
    実装 CL 832864(internal/goexperiment: add cgobindings experiment)はすでにマージされています。この CL は flags.go と exp_cgobindings_on.go/exp_cgobindings_off.go を追加しています。

これによって何ができるようになるか

  • Go チームが段階的に実装できる: バインディング生成(go tool cgo -gen-binding)、cmd/go の対応、ABI トランポリン(呼び出し規約を変換する中継コード)の生成といった作業を、デフォルトの挙動を変えずに本体へ取り込めます。
  • 利用者が試してフィードバックできる: purego や mattn/go-sqlite3 のようなライブラリの作者が、実際のコードで設計を検証できます。#81450 では、union 型の扱い、可変長引数、関数ポインタ、静的ライブラリへの対応などが論点に挙がっています。
  • やり直しが簡単になる: 却下された場合や設計が大きく変わった場合でも、フラグの内側のコードを消すだけで済みます。

コード例

// Before: C ツールチェーン必須の従来の cgo
package mypkg
/*
struct point { float x; float y; };
float compute(struct point p0);
*/
import "C"
// After(実験中・書式は #81450 の現時点の案):
// GOEXPERIMENT=cgobindings で、C コンパイラなしでビルドすることを目指す
//go:build darwin
package mypkg
import "C"
//cgo:binding C.point
type Point struct {
	x, y float32
}
//cgo:binding C.compute
func Compute(p0 Point) float32
GOEXPERIMENT=cgobindings go build ./...

なお、バインディングの書式は議論中のため、最終的な仕様は変わる可能性があります。

議論のハイライト

  • 本 issue にはボットのコメントと承認手続き以外の議論がありません。新機能を GOEXPERIMENT で先行実装するのは Go で確立した進め方であり、異論は出ませんでした。
  • 親 #81450 では、@cherrymui が「この方式でも cgo は有効なので、CGO_ENABLED=0 を切り替えのきっかけにするのは不適切」と述べています。C ツールチェーンなしのビルドは、cgo を無効にすることとは別物として扱われています。
  • @ianlancetaylor は、C の呼び出し規約が複雑だと指摘しました(C で呼び出し規約を実装した libffi は約5万行あります)。@cherrymui は、だからこそ実行時ではなくビルド時に静的に処理する、と答えています。こうした設計上の論点は実装して確かめる必要があり、実験フラグを設ける理由の一つと考えられます。
  • 親 proposal では、バインディングの書式(//cgo:binding 注釈方式か、別ファイル方式か)、可変長引数(//cgo:variadic 案)、関数ポインタ(//cgo:funcptr 案)などがまだ議論中です。
  • gabyhelp のコメントからは、関連 CL の中に cgo2、cgogoshapedbindings といった別名の案があったことがうかがえます。最終的に cgobindings という名前に決まりました。

関連リンク