Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 1764] ファイルの概要

このコミットは、Goコンパイラ(gc)におけるsysimport.c(後にbuiltin.cに改名)の生成方法を根本的に変更するものです。以前は自動生成されたファイルをバージョン管理システム(Perforce)にもコミットしていましたが、この運用がユーザーにとって不便であったため、生成プロセスを改善し、ユーザーのビルド体験を向上させることを目的としています。

コミット

commit af678a593da3e2bc942aa3bf41e66bd50e0b3bab
Author: Russ Cox <rsc@golang.org>
Date:   Thu Mar 5 18:26:12 2009 -0800

    new approach for generating sysimport.c
    (renamed to avoid any conflict with old p4 copies).
    
    this approach doesn't require auto-generating
    files also kept in p4, so it should be easier on
    go users who don't sync very often.
    
    this approach will be more work for go developers:
    builtin.c needs to be copied to builtin.c.boot in p4
    as new functions are added.  mkbuiltin does this
    for certain $USERs to help us remember.
    
    R=r
    DELTA=343  (176 added, 162 deleted, 5 changed)
    OCL=25803
    CL=25805

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/af678a593da3e2bc942aa3bf41e66bd50e0b3bab

元コミット内容

sysimport.cを生成するための新しいアプローチ(古いP4コピーとの競合を避けるために名前を変更)。

このアプローチは、P4にも保持されている自動生成ファイルを不要にするため、頻繁に同期しないGoユーザーにとってはより簡単になるはずです。

このアプローチはGo開発者にとってはより多くの作業を伴います。新しい関数が追加されるたびに、builtin.cをP4内のbuiltin.c.bootにコピーする必要があります。mkbuiltinは、特定の$USERに対してこれを実行し、開発者が忘れないようにします。

変更の背景

Go言語の初期段階において、コンパイラが使用する組み込み関数や型に関する情報を含むC言語のソースファイル(sysimport.c)は、sys.gounsafe.goといったGoのソースファイルから自動生成されていました。しかし、この自動生成されたファイルが同時にバージョン管理システム(当時はPerforce、通称P4)にもコミットされていたため、以下のような問題が発生していました。

  1. 同期の問題: ユーザーがGoのソースコードをP4から取得する際、sysimport.cが古いバージョンである場合がありました。この古いファイルが原因でビルドエラーが発生したり、予期せぬ動作を引き起こしたりすることがありました。特に、Goのソースコードを頻繁に同期しないユーザーにとっては、この問題は顕著でした。
  2. 開発者の負担: 自動生成されたファイルが変更された場合、開発者は手動でP4上でファイルを「開いて編集可能にする」(p4 open)必要があり、その後、新しい内容で上書きしてコミットするという手間がありました。これは、自動生成されるべきファイルが手動のバージョン管理プロセスに組み込まれていることによる非効率性でした。
  3. ビルドの不安定性: Makefileのルールが複雑で、sysimport.cの生成ロジックがP4の操作と密接に結びついていたため、ビルド環境やP4の状況によってはビルドが不安定になる可能性がありました。

このコミットは、これらの問題を解決し、Goのビルドプロセスをより堅牢でユーザーフレンドリーにすることを目的としています。

前提知識の解説

このコミットの変更内容を理解するためには、以下の前提知識が必要です。

  • Goコンパイラ(gc: Go言語の公式コンパイラであり、Goのソースコードを機械語に変換します。src/cmd/gcディレクトリにそのソースコードがあります。
  • 組み込み関数と型(Built-ins): プログラミング言語において、特別な宣言なしに利用できる基本的な関数や型のことです。Go言語にもlencapmakenewなどの組み込み関数や、intstringなどの組み込み型が存在します。コンパイラはこれらの組み込み要素に関する情報を内部的に持っている必要があります。
  • sys.gounsafe.go: Goの標準ライブラリの一部であり、Goランタイムやコンパイラが内部的に利用する低レベルな機能や、型安全性をバイパスする機能を提供します。これらのファイルには、Goの組み込み関数や型に関する情報が含まれており、コンパイラがC言語のコードを生成する際の元データとなります。
  • 6gコンパイラ: コミット当時のGoコンパイラの名称の一つで、64ビットシステム向けのGoコンパイラを指します。Goのソースコード(.goファイル)をコンパイルして、中間ファイル(.6ファイル)を生成します。
  • .6ファイル: 6gコンパイラによってGoのソースコードから生成される中間ファイルです。Goの抽象構文木(AST)や型情報などが含まれており、リンカや他のツールが利用します。
  • Perforce (P4): コミット当時、Goプロジェクトが利用していた集中型バージョン管理システムです。P4は、ファイルを編集する前に明示的に「開く」(p4 open)必要があるという特徴があり、自動生成されるファイルをバージョン管理下に置く場合に、開発者に余分な手間を強いることがありました。
  • Makefile: ビルドプロセスを自動化するためのツールです。ソースファイルのコンパイル順序や依存関係を定義し、コマンドを実行します。

技術的詳細

このコミットの核心は、sysimport.c(新名称builtin.c)の生成とバージョン管理の分離です。

旧アプローチの問題点と解決策:

旧アプローチでは、src/cmd/gc/sysimport.cmksys.c(現在のmkbuiltin1.c)によってsys.gounsafe.goから生成され、その上でPerforceにコミットされていました。Makefileには、sysimport.csys.gounsafe.gomksys.cよりも古い場合に再生成を試みるルールがありましたが、このルールはp4 open sysimport.cを含んでおり、Perforceのワークフローと衝突していました。ユーザーが古いP4同期を持っている場合、sysimport.cが古いままでビルドが失敗する可能性がありました。

新アプローチでは、この問題を解決するために以下の変更が導入されました。

  1. ファイル名の変更: sysimport.cbuiltin.cに、mksys.cmkbuiltin1.cにそれぞれ改名されました。これは、古いPerforceのコピーとの競合を避け、新しいアプローチを明確にするためです。
  2. builtin.c.bootの導入: sysimport.cbuiltin.c.bootとしてPerforceにコミットされるようになりました。このファイルは「ブートストラップコピー」として機能し、builtin.cの生成が何らかの理由で失敗した場合のフォールバックとして使用されます。これにより、ユーザーは常にビルド可能な状態を保つことができます。
  3. mkbuiltinスクリプトの導入: builtin.cの生成は、新しく導入されたシェルスクリプトsrc/cmd/gc/mkbuiltinによって行われるようになりました。このスクリプトは以下の手順を実行します。
    • mkbuiltin1.cgccでコンパイルして実行可能ファイルmkbuiltin1を生成します。
    • sys.gounsafe.go6gコンパイラでコンパイルし、中間ファイル(.6ファイル)を生成します。
    • mkbuiltin1を実行し、生成された.6ファイルから組み込み関数の情報を抽出し、C言語のコードとして_builtin.cに出力します。
    • 開発者向けの自動更新: mkbuiltinスクリプトには、特定のユーザー(ken, r, rscなど、Goの主要開発者)が実行した場合にのみ有効になる特別なロジックが含まれています。このロジックは、新しく生成された_builtin.cbuiltin.c.bootと異なる場合、自動的にbuiltin.c.bootをPerforce上で「開いて編集可能にし」(p4 open)、_builtin.cの内容で上書きします。これにより、builtin.c.bootは常に最新のbuiltin.cの内容を反映するようになり、開発者の手動での更新作業を軽減します。
    • 最終的に、_builtin.cの内容を標準出力に出力し、_builtin.cを削除します。
  4. Makefileの簡素化: Makefilebuiltin.c生成ルールは大幅に簡素化されました。
    • builtin.c:の依存関係がsys.go unsafe.go mkbuiltin1.c mkbuiltinに変更されました。
    • 生成コマンドはmkbuiltin >builtin.c || (echo 'mkbuiltin failed; using bootstrap copy of builtin.c'; cp builtin.c.boot builtin.c)となりました。これは、mkbuiltinスクリプトを実行してbuiltin.cを生成し、もしmkbuiltinが失敗した場合は、builtin.c.bootをフォールバックとして使用するという、より堅牢なロジックです。

この新しいアプローチにより、一般のGoユーザーはbuiltin.cが常にビルド時に最新の状態で生成されるため、Perforceの同期状態に起因する問題を回避できます。一方、Goのコア開発者は、builtin.c.bootの更新がmkbuiltinスクリプトによって半自動化されるため、手動でのバージョン管理の負担が軽減されます。

コアとなるコードの変更箇所

このコミットにおける主要なコード変更は以下のファイルに集中しています。

  1. src/cmd/gc/Makefile:
    • OFILESリストからsysimport.$Oが削除され、builtin.$Oが追加されました。
    • sysimport.cを生成する複雑なルールが削除されました。
    • 新しくbuiltin.cを生成するための簡潔なルールが追加されました。このルールはmkbuiltinスクリプトの実行と、失敗時のbuiltin.c.bootへのフォールバックを含みます。
    • cleanターゲットが更新され、mkbuiltin1_builtin.cなどの新しい生成物も削除されるようになりました。
  2. src/cmd/gc/{sysimport.c => builtin.c.boot}:
    • sysimport.cbuiltin.c.bootにファイル名が変更されました。これは、旧来のsysimport.cが新しいブートストラップファイルとして機能することを示します。
  3. src/cmd/gc/mkbuiltin:
    • 新しく追加されたシェルスクリプトです。builtin.cを生成する主要なロジックを含みます。
    • mkbuiltin1.cのコンパイル、sys.gounsafe.goのコンパイル、mkbuiltin1の実行、そして特定のユーザーに対するbuiltin.c.bootの自動更新ロジックが含まれています。
  4. src/cmd/gc/{mksys.c => mkbuiltin1.c}:
    • mksys.cmkbuiltin1.cにファイル名が変更されました。
    • コメントと使用法メッセージが更新され、その役割がより明確になりました。

コアとなるコードの解説

src/cmd/gc/Makefileの変更

--- a/src/cmd/gc/Makefile
+++ b/src/cmd/gc/Makefile
@@ -26,7 +26,7 @@ OFILES=\
 	mparith1.$O\
 	mparith2.$O\
 	mparith3.$O\
-	sysimport.$O\
+	builtin.$O\
 	compat.$O\
 
 $(LIB): $(OFILES)
@@ -40,25 +40,11 @@ y.tab.h: $(YFILES)\
 y.tab.c: y.tab.h
 	test -f y.tab.c && touch y.tab.c
 
-# the test here checks whether we have 6g at all.
-# if so, use it.  if not, just use the sysimport.c we have.
-# this happens on fresh perforce checkouts where
-# sysimport.c ends up with an older time stamp
-# than sys.go (or unsafe.go or mksys.c).
-sysimport.c:	sys.go unsafe.go mksys.c
-	if test -x $(BIN)/6g; then \
-		p4 open sysimport.c; \
-		gcc -o mksys mksys.c; \
-		6g sys.go; \
-		6g unsafe.go; \
-		./mksys sys >_sysimport.c && \
-		./mksys unsafe >>_sysimport.c && \
-		mv _sysimport.c sysimport.c; \
-	elif test -f sysimport.c; then \
-		touch sysimport.c; \
-	fi
+builtin.c:	sys.go unsafe.go mkbuiltin1.c mkbuiltin
+	mkbuiltin >builtin.c || \
+	(echo 'mkbuiltin failed; using bootstrap copy of builtin.c'; cp builtin.c.boot builtin.c)
 
 clean:
-\trm -f $(OFILES) *.6 enam.c 6.out a.out y.tab.h y.tab.c $(LIB) _sysimport.c
+\trm -f $(OFILES) *.6 enam.c 6.out a.out y.tab.h y.tab.c $(LIB) mkbuiltin1 builtin.c _builtin.c
 
 install: $(LIB)
  • OFILESの変更は、コンパイル対象のオブジェクトファイルがsysimport.oからbuiltin.oに変わったことを示します。
  • 最も重要な変更はsysimport.cの生成ルールが削除され、builtin.cの新しいルールが追加された点です。
    • 旧ルールは、6gコンパイラの存在チェック、p4 openmksys.cのコンパイルと実行、sys.gounsafe.goのコンパイルなど、多くのステップを含んでいました。これは、sysimport.cがバージョン管理下にありながら自動生成されるという矛盾を解決しようとする試みでした。
    • 新ルールbuiltin.c: sys.go unsafe.go mkbuiltin1.c mkbuiltinは、builtin.csys.gounsafe.gomkbuiltin1.c、そして新しく追加されたmkbuiltinスクリプトに依存することを示します。
    • コマンドmkbuiltin >builtin.c || (echo 'mkbuiltin failed; using bootstrap copy of builtin.c'; cp builtin.c.boot builtin.c)は、mkbuiltinスクリプトの出力をbuiltin.cにリダイレクトします。もしmkbuiltinの実行が失敗した場合(||)、エラーメッセージを出力し、builtin.c.boot(ブートストラップコピー)をbuiltin.cにコピーしてフォールバックします。これにより、ビルドの堅牢性が向上します。

src/cmd/gc/mkbuiltinの追加

--- /dev/null
+++ b/src/cmd/gc/mkbuiltin
@@ -0,0 +1,31 @@
+#!/bin/sh
+# Copyright 2009 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+set -e
+gcc -o mkbuiltin1 mkbuiltin1.c
+6g sys.go
+6g unsafe.go
+rm -f _builtin.c
+./mkbuiltin1 sys >_builtin.c
+./mkbuiltin1 unsafe >>_builtin.c
+
+# If _builtin.c has changed vs builtin.c.boot,
+# check in the new change if being run by
+# one of the people who tends to work on
+# the compiler.  This makes sure that changes
+# don't get forgotten, without causing problems
+# in end user Go repositories.
+case "$USER" in
+ken | r | rsc)
+	if ! cmp _builtin.c builtin.c.boot
+	then
+		p4 open builtin.c.boot
+		cp _builtin.c builtin.c.boot
+	fi
+esac
+
+cat _builtin.c
+rm -f _builtin.c
+```
*   このシェルスクリプトは、`builtin.c`を生成するための中心的なロジックをカプセル化しています。
*   `gcc -o mkbuiltin1 mkbuiltin1.c`: `mkbuiltin1.c`(旧`mksys.c`)をコンパイルして実行可能ファイル`mkbuiltin1`を生成します。
*   `6g sys.go`と`6g unsafe.go`: `sys.go`と`unsafe.go`をGoコンパイラでコンパイルし、それぞれ`sys.6`と`unsafe.6`という中間ファイルを生成します。
*   `./mkbuiltin1 sys >_builtin.c`と`./mkbuiltin1 unsafe >>_builtin.c`: `mkbuiltin1`を実行し、`sys.6`と`unsafe.6`から組み込み関数の情報を抽出し、C言語のコードとして`_builtin.c`に書き込みます。
*   `case "$USER" in ... esac`ブロック: これが開発者向けの自動更新ロジックです。
    *   `ken | r | rsc)`: 環境変数`USER`が`ken`、`r`、`rsc`のいずれかである場合にのみ、このブロック内の処理が実行されます。これらはGoの主要開発者のユーザー名です。
    *   `if ! cmp _builtin.c builtin.c.boot`: 新しく生成された`_builtin.c`と、バージョン管理されているブートストラップファイル`builtin.c.boot`を比較します。両者が異なる場合、つまり`builtin.c`の内容が更新された場合にのみ、以下の処理が実行されます。
    *   `p4 open builtin.c.boot`: Perforce上で`builtin.c.boot`を編集可能状態にします。
    *   `cp _builtin.c builtin.c.boot`: 新しい`_builtin.c`の内容で`builtin.c.boot`を上書きします。これにより、`builtin.c.boot`が最新の状態に保たれます。
*   `cat _builtin.c`: 生成された`_builtin.c`の内容を標準出力に出力します。`Makefile`のルールによって、この出力が`builtin.c`ファイルにリダイレクトされます。
*   `rm -f _builtin.c`: 一時ファイルである`_builtin.c`を削除します。

### `src/cmd/gc/{mksys.c => mkbuiltin1.c}`の変更

```diff
--- a/src/cmd/gc/mksys.c
+++ b/src/cmd/gc/mkbuiltin1.c
@@ -1,8 +1,8 @@
-// Copyright 2009 The Go Authors.  All rights reserved.\
+// Copyright 2009 The Go Authors. All rights reserved.\
 // Use of this source code is governed by a BSD-style\
 // license that can be found in the LICENSE file.\
 \
-// Extract import data from sys.6 and generate C string version.\
+// Compile .go file, import data from .6 file, and generate C string version.\
 \
 #include <stdio.h>\
 #include <stdlib.h>\
@@ -18,7 +18,7 @@ main(int argc, char **argv)\
 	char buf[1024], initfunc[1024], *p, *q;\
 \
 	if(argc != 2) {\
-\t\tfprintf(stderr, "usage: sys sys\\n");\
+\t\tfprintf(stderr, "usage: mkbuiltin1 sys\\n");\
 \t\tfprintf(stderr, "in file $1.6 s/PACKAGE/$1/\\n");\
 \t\texit(1);\
 \t}\
  • ファイル名がmksys.cからmkbuiltin1.cに変更されました。
  • コメントがExtract import data from sys.6 and generate C string version.からCompile .go file, import data from .6 file, and generate C string version.に変更され、このCプログラムの役割がより正確に記述されました。これは、Goの.goファイルをコンパイルして生成された.6ファイルからインポートデータを抽出し、C言語の文字列形式で出力する役割を担っていることを示しています。
  • エラーメッセージのusage: sys sysusage: mkbuiltin1 sysに更新されました。

これらの変更により、builtin.cの生成プロセスは、バージョン管理システムから独立し、ビルド時に常に最新の状態で生成されるようになりました。これにより、Goユーザーのビルド体験が向上し、開発者にとってもbuiltin.c.bootの更新が半自動化されるというメリットがもたらされました。

関連リンク

参考にした情報源リンク

  • Go言語のソースコード(特にsrc/cmd/gcディレクトリ)
  • コミットメッセージと差分情報
  • Go言語の初期のビルドシステムに関する一般的な知識
  • Perforceバージョン管理システムの基本的な概念