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

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

このコミットは、Go言語のビルドシステムに大きな変更を導入するもので、新しいGoディストリビューションツールである cmd/dist を追加します。このツールは、既存のMakefileベースのビルドプロセスを置き換え、GoのビルドにおけるBashやその他のUnixツール、特にWindows環境でのCygwinへの依存を排除することを目的としています。

コミット

commit 3dd1e5be54417819c3c5e3024c9efbc8f8344195
Author: Russ Cox <rsc@golang.org>
Date:   Thu Feb 2 19:41:39 2012 -0500

    cmd/dist: new command
    
    dist is short for distribution.  This is the new Go distribution tool.
    
    The plan is to replace the Makefiles with what amounts to
    'go tool dist bootstrap', although it cannot be invoked like
    that since it is in charge of getting us to the point where we
    can build the go command.
    
    It will also add additional commands to replace bash scripts
    like test/run (go tool dist testrun), eventually eliminating our
    dependence on not just bash but all the Unix tools and all
    of cygwin.
    
    This is strong enough to build (cc *.c) and run (a.out bootstrap)
    to build not just the C libraries and tools but also the basic
    Go packages up to the bootstrap form of the go command
    (go_bootstrap).  I've run it successfully on both Linux and Windows.
    This means that once we've switched to this tool in the build,
    we can delete the buildscripts.
    
    This tool is not nearly as nice as the go tool.  There are many
    special cases that turn into simple if statements or tables in
    the code.  Please forgive that.  C does not enjoy the benefits
    that we designed into Go.
    
    I was planning to wait to do this until after Go 1, but the
    Windows builders are both broken due to a bug in either
    make or bash or both involving the parsing of quoted command
    arguments.  Make thinks it is invoking
    
            quietgcc -fno-common -I"c:/go/include" -ggdb -O2 -c foo.c
    
    but bash (quietgcc is a bash script) thinks it is being invoked as
    
            quietgcc -fno-common '-Ic:/go/include -ggdb' -O2 -c foo.c
    
    which obviously does not have the desired effect.  Rather than fight
    these clumsy ports, I accelerated the schedule for the new tool.
    We should be completely off cygwin (using just the mingw gcc port,
    which is much more standalone) before Go 1.
    
    It is big for a single CL, and for that I apologize.  I can cut it into
    separate CLs along file boundaries if people would prefer that.
    
    R=golang-dev, adg, gri, bradfitz, alex.brainman, dsymonds, iant, ality, hcwfrichter
    CC=golang-dev
    https://golang.org/cl/5620045

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

https://github.com/golang/go/commit/3dd1e5be54417819c3c5e3024c9efbc8f8344195

元コミット内容

このコミットは、Go言語の新しいディストリビューションツールである cmd/dist を導入します。このツールは、Goのビルドプロセスを管理し、既存のMakefileベースのシステムを置き換えることを目的としています。将来的には、test/run のようなBashスクリプトも go tool dist testrun のようなコマンドに置き換え、GoのビルドにおけるBash、Unixツール、およびCygwinへの依存を完全に排除することを目指しています。

cmd/dist は、C言語で書かれたライブラリやツール、そしてGoの基本的なパッケージをブートストラップ形式の go コマンド(go_bootstrap)までビルドする能力を持っています。このツールは既にLinuxとWindowsの両方で動作することが確認されており、導入後は既存のビルドスクリプトを削除できるようになります。

開発者は、このツールが go tool ほど洗練されていないことを認めつつも、C言語の制約の中で多くの特殊ケースをシンプルに処理していると説明しています。

この変更は、当初Go 1リリース後に予定されていましたが、Windows環境での make または bash の引用符付きコマンド引数のパースに関するバグにより、Windowsビルドが機能しなくなったため、前倒しで導入されました。この問題は、quietgcc のようなBashスクリプトが make から誤った形式の引数を受け取ることに起因していました。この dist ツールの導入により、Go 1リリース前にCygwinへの依存を完全に解消し、よりスタンドアロンなMinGW GCCポートのみを使用できるようになることが期待されています。

このコミットは単一の変更セットとしては大規模ですが、その必要性が強調されています。

変更の背景

Go言語の初期のビルドシステムは、Unix系の環境で広く使われている make とBashスクリプトに大きく依存していました。これは、GoがUnixライクなシステムで開発されたため自然な選択でしたが、Windowsのような異なるOS環境でのビルドにおいて問題を引き起こしていました。

特に、Windows環境では make やBashのポート(Cygwinなど)が、引用符付きのコマンド引数のパースに関して予期せぬバグを抱えていました。コミットメッセージに具体例として挙げられているように、makequietgcc -fno-common -I"c:/go/include" -ggdb -O2 -c foo.c のようにコマンドを呼び出そうとしても、Bashスクリプトはこれを quietgcc -fno-common '-Ic:/go/include -ggdb' -O2 -c foo.c のように誤って解釈し、ビルドが失敗するという問題が発生していました。

このようなクロスプラットフォームでのビルドの不安定性は、Go言語の普及と開発効率にとって大きな障害となっていました。Go 1のリリースを控える中で、この問題を根本的に解決し、より堅牢でポータブルなビルドシステムを確立することが急務でした。

このため、当初Go 1リリース後に計画されていた、Cygwinやその他のUnixツールへの依存を排除する新しいディストリビューションツールの開発が前倒しで実施されることになりました。これが cmd/dist の導入の直接的な背景です。

前提知識の解説

このコミットを理解するためには、以下の概念について基本的な知識があると役立ちます。

  1. Go言語のブートストラップ (Bootstrapping):

    • Go言語のコンパイラやツールチェイン自体もGo言語で書かれています。しかし、Go言語のコンパイラをビルドするためには、まず動作するGoコンパイラが必要です。この循環的な依存関係を解決するために、「ブートストラップ」というプロセスが用いられます。
    • 初期のGoコンパイラはC言語で書かれたり、以前のバージョンのGoコンパイラ(または別の言語のコンパイラ)を使ってビルドされたりします。このコミットで導入される cmd/dist は、このブートストラッププロセスを管理し、C言語で書かれたツールやライブラリ、そしてGo言語で書かれた初期の go コマンド(go_bootstrap)をビルドする役割を担います。
    • ブートストラップが完了すると、その go_bootstrap コマンドを使って、より完全なGoツールチェインをビルドできるようになります。
  2. Makefile:

    • make は、プログラムのソースコードから実行可能ファイルやライブラリをビルドするプロセスを自動化するためのツールです。Makefilemake が実行するタスク(ターゲット)とその依存関係、および実行するコマンドを記述したファイルです。
    • Go言語の初期のビルドシステムは、この Makefile を広範に利用していました。しかし、make はUnix系の環境で非常に強力ですが、Windows環境での互換性や挙動の差異が問題となることがあります。
  3. Bash (Bourne Again SHell):

    • Unix系OSで広く使われているコマンドラインシェルであり、スクリプト言語でもあります。Goのビルドプロセスでは、複雑なタスクを自動化するためにBashスクリプトが使用されていました。
    • Windows環境では、Cygwinのようなツールを通じてBashが利用できますが、これがWindowsネイティブのコマンドプロンプトやPowerShellとは異なる挙動を示すことがあり、互換性の問題を引き起こすことがあります。
  4. Cygwin:

    • Windows上でUnixライクな環境を提供するソフトウェアスイートです。これにはBashシェルや、makegcc などの多くのUnixツールが含まれています。
    • GoのWindowsビルドでは、これらのUnixツールを利用するためにCygwinに依存していましたが、これがWindows固有のビルド問題(特にパスの扱いや引用符の解釈)の原因となることがありました。
  5. MinGW (Minimalist GNU for Windows):

    • Windowsネイティブアプリケーションを開発するためのGNUツールチェイン(GCCなど)のポートです。Cygwinとは異なり、Unixエミュレーションレイヤーを必要とせず、よりスタンドアロンな形でWindows上で動作する実行ファイルを生成できます。
    • このコミットの目的の一つは、Cygwinへの依存を排除し、MinGWのようなよりネイティブに近いツールチェインに移行することです。
  6. C言語のメモリ管理 (Buf, Vec):

    • この cmd/dist ツールはC言語で書かれています。C言語では、メモリ管理(確保と解放)をプログラマが手動で行う必要があります。
    • コミットで導入される Buf (バイトバッファ) と Vec (文字列ベクトル) は、Go言語の []byte[]string のような動的なデータ構造をC言語で安全かつ効率的に扱うためのカスタム実装です。これらは、メモリリークを防ぎ、コードの可読性を向上させるための工夫です。

これらの知識があると、cmd/dist がなぜ必要とされ、どのような問題を解決しようとしているのか、そしてどのように実装されているのかをより深く理解できます。

技術的詳細

cmd/dist は、Go言語のビルドプロセスを制御するための新しいC言語製ツールです。その設計と実装には、ポータビリティ、依存関係の削減、および堅牢性の向上が強く意識されています。

1. C言語での実装とポータビリティ層

  • cmd/dist は、Go言語のツールチェインをブートストラップするために、Go言語自体に依存しないC言語で書かれています。
  • ポータビリティを最大化するため、C標準ライブラリを含むCライブラリとのすべての相互作用は、システム固有のファイル(plan9.c, unix.c, windows.c)に限定されています。これにより、異なるOSへの移植が容易になります。
  • ポータビリティ層の関数は、既存の関数名との衝突や混同を避けるために x プレフィックス(例: xprintf はポータブルな printf)を使用しています。

2. BufVec データ構造

  • C言語での文字列や動的配列の扱いの複雑さを軽減するため、Buf (バイトバッファ) と Vec (文字列ベクトル) というカスタムデータ構造が導入されています。
  • Buf: Goの []byte に相当し、動的にサイズが変更可能なバイトバッファです。binit (初期化), bfree (解放), bgrow (容量拡張), bwrite (書き込み), bstr (NUL終端文字列取得) などの操作が提供されます。
  • Vec: Goの []string に相当し、動的にサイズが変更可能な文字列の配列です。vinit (初期化), vfree (解放), vgrow (容量拡張), vadd (文字列追加), vuniq (ソートと重複排除) などの操作が提供されます。
  • これらの構造体は、自身が指すデータの所有権を持ち、binit/vinit で初期化し、bfree/vfree でメモリを解放するという慣用句(idiom)を強制することで、メモリリークのリスクを低減し、レキシカルスコープでのメモリ管理を可能にしています。

3. ビルドプロセスの管理 (build.c)

  • build.c は、Goのビルドプロセスの中核を担います。
  • 環境変数の初期化: GOROOT, GOBIN, GOOS, GOARCH などの重要な環境変数を初期化し、検証します。
  • ディレクトリ構造のセットアップ: GOROOT/bin, GOROOT/bin/go-tool, GOROOT/pkg などの必要なディレクトリを作成し、古いツールバイナリを削除します。
  • 依存関係の解決: deptab というテーブルを使用して、特定のターゲット(例: lib9, cmd/cc)に対するカスタム依存関係(インクルードファイル、除外ファイル、ディレクトリ全体のスキャンなど)を定義します。
  • ファイルフィルタリング (shouldbuild): go/build パッケージのコンテキストタグと同様のルールを適用し、ファイル名やファイル内容の // +build 行に基づいて、どのソースファイルをビルドに含めるべきかを決定します。これにより、特定のOSやアーキテクチャに特化したファイルが適切に選択されます。
  • コンパイルとリンク: C言語のソースファイル (.c, .s) は gcc を使用してコンパイルされ、Go言語のソースファイル (.go) はブートストラップコンパイラ (%sg など) を使用してコンパイルされます。最終的なバイナリやライブラリは、ar (Cライブラリ), pack (Goパッケージ), ld (Cコマンド), またはブートストラップリンカ (%sl) を使用してリンクされます。
  • ビルド順序 (buildorder): go bootstrap コマンドのビルド順序を定義する静的な配列 buildorder が存在し、これに従ってライブラリ、パッケージ、コマンドが順次ビルドされます。

4. 特殊なファイル生成 (buildgc.c)

  • buildgc.c は、cmd/gc (Goコンパイラ) のビルドに必要な特殊なファイルを生成するヘルパー関数を提供します。
  • gcopnames: go.h からGoのオペコード(OXXX enum)を抽出し、それらの名前を文字列としてマッピングする opnames.h を生成します。
  • mkenam: [568].out.h (アセンブラの出力ヘッダ) を読み込み、アセンブラの命令名を文字列としてマッピングする enam.c を生成します。

5. コマンドラインインターフェース (main.c)

  • main.ccmd/dist のエントリポイントであり、コマンドライン引数をパースして適切なサブコマンド(bootstrap, env, install)を呼び出します。

6. OS固有の実装 (unix.c, windows.c)

  • これらのファイルは、ファイルシステム操作(mkdir, readfile, writefile, removeall)、環境変数アクセス(getenv, setenv)、プロセス実行(run, runv)、パス操作(isabs, isdir, isfile, mtime)など、OSに依存する低レベルの機能を実装しています。
  • unix.c はUnix系システム(Linux, macOSなど)向け、windows.c はWindows向けの実装を提供します。これにより、コアロジックはOS非依存に保たれ、ポータビリティが実現されます。

これらの技術的詳細は、cmd/dist がいかにしてGoのビルドプロセスをより制御可能で、クロスプラットフォーム対応にし、外部ツールへの依存を減らすように設計されているかを示しています。

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

このコミットでは、src/cmd/dist ディレクトリが新規に作成され、以下の8つのファイルが追加されています。

  • src/cmd/dist/README
  • src/cmd/dist/a.h
  • src/cmd/dist/buf.c
  • src/cmd/dist/build.c
  • src/cmd/dist/buildgc.c
  • src/cmd/dist/main.c
  • src/cmd/dist/unix.c
  • src/cmd/dist/windows.c

これらのファイルはすべて新規追加であり、既存のコードの変更は含まれていません。これは、cmd/dist が既存のビルドシステムとは独立した新しいツールとして導入されたことを意味します。

コアとなるコードの解説

追加された各ファイルの役割は以下の通りです。

  • src/cmd/dist/README:

    • dist ツールの目的と基本的な設計原則を説明するドキュメントです。
    • Goディストリビューションのブートストラップツールであり、Cプログラム(Goコンパイラなど)と初期の go ツールをビルドする役割を担うと説明されています。
    • C言語で書かれており、ポータビリティのためにシステム固有のコードが分離されていること、BufVec というカスタムデータ構造がメモリ管理を容易にすることなどが記述されています。
  • src/cmd/dist/a.h:

    • cmd/dist 全体で共有される共通のヘッダファイルです。
    • bool 型の定義、nil (NULL) マクロ、nelem (配列要素数) マクロなどが含まれます。
    • Buf (バイトバッファ) と Vec (文字列ベクトル) の構造体定義と、それらに関連する関数のプロトタイプ宣言が含まれます。
    • build.c, buildgc.c, main.c, およびOS固有のファイル (unix.c, windows.c) で定義される主要な関数のプロトタイプ宣言も含まれており、ツール全体のインターフェースを定義しています。
  • src/cmd/dist/buf.c:

    • Buf (バイトバッファ) と Vec (文字列ベクトル) の実装を提供します。
    • Buf の操作(binit, breset, bfree, bgrow, bwrite, bwritestr, bstr, btake, bwriteb, bequal)と、Vec の操作(vinit, vreset, vfree, vgrow, vcopy, vadd, vaddn, vuniq, splitlines, splitfields)が含まれます。
    • これらの関数は、動的な文字列やバイト列の効率的かつ安全な操作をC言語で実現するための基盤となります。
  • src/cmd/dist/build.c:

    • cmd/dist のビルドロジックの大部分を実装しています。
    • 環境変数の初期化、ビルドディレクトリのセットアップ、古いツールのクリーンアップを行います。
    • deptab を使用して、各ターゲット(ライブラリ、パッケージ、コマンド)の依存関係を定義し、ソースファイルのフィルタリング (shouldbuild) を行います。
    • C言語のソースファイルやGo言語のソースファイルをコンパイルし、最終的なライブラリや実行ファイルをリンクするプロセスを管理します。
    • cmdbootstrap (ブートストラップビルドの実行), cmdenv (環境変数の表示), cmdinstall (指定されたパッケージのインストール) といったサブコマンドの具体的な実装が含まれます。
    • buildorder という配列で、ブートストラップビルドにおける各コンポーネントのビルド順序が定義されています。
  • src/cmd/dist/buildgc.c:

    • Goコンパイラ (cmd/gc) のビルドに必要な特殊なファイルを生成するヘルパー関数を実装しています。
    • gcopnames 関数は、Goのオペコード名を定義する opnames.h を生成します。
    • mkenam 関数は、アセンブラの命令名を定義する enam.c を生成します。
  • src/cmd/dist/main.c:

    • cmd/dist のメインエントリポイントです。
    • コマンドライン引数を解析し、cmdtab に定義されたサブコマンド(bootstrap, env, install)に対応する関数を呼び出します。
    • 引数が不足している場合や不明なコマンドが指定された場合には、使用方法のメッセージを表示して終了します。
  • src/cmd/dist/unix.c:

    • Unix系オペレーティングシステム(Linux, macOSなど)向けのOS固有の機能を実装しています。
    • ファイルシステム操作(open, read, write, stat, mkdir, unlink, rmdir, opendir, readdir)、プロセス実行(fork, execvp, waitpid, pipe)、環境変数アクセス(getenv, setenv)、メモリ管理(malloc, realloc, free, strdup)などのシステムコールをラップした関数が含まれます。
    • エラー処理 (fatal) や一時ディレクトリの作成 (xworkdir) なども含まれます。
  • src/cmd/dist/windows.c:

    • Windowsオペレーティングシステム向けのOS固有の機能を実装しています。
    • unix.c と同様の機能を提供しますが、Windows API (CreateFile, ReadFile, WriteFile, CreateDirectory, DeleteFile, RemoveDirectory, FindFirstFile, FindNextFile, GetEnvironmentVariable, SetEnvironmentVariable, _spawnvpe, _pipe, _getdcwd, _chdir, _stat, _mktemp) を使用して実装されています。
    • これにより、cmd/dist がWindows環境でもネイティブに動作し、Cygwinのようなエミュレーションレイヤーへの依存を排除できるようになります。

これらのファイルが連携することで、cmd/dist はGo言語のブートストラップビルドをクロスプラットフォームで実行できる、自己完結型のツールとして機能します。

関連リンク

  • Go言語の公式リポジトリ: https://github.com/golang/go
  • Go言語のブートストラップに関する議論(一般的な情報源):
    • Goのビルドプロセスに関する公式ドキュメントやブログ記事(当時のものを見つけるのは難しいかもしれませんが、現在のGoのビルドシステムに関する情報が参考になる可能性があります)。

参考にした情報源リンク

  • コミットメッセージ: https://golang.org/cl/5620045 (これはコミットメッセージ内に記載されているGo Code Reviewのリンクであり、コミットの元情報です。)
  • Go言語のソースコード: https://github.com/golang/go (コミットされたコード自体が主要な情報源です。)
  • Cygwin 公式サイト: https://www.cygwin.com/
  • MinGW 公式サイト: https://www.mingw-w64.org/
  • make コマンドに関する一般的な情報源 (例: GNU Make マニュアル)
  • Bashに関する一般的な情報源 (例: GNU Bash リファレンスマニュアル)
  • C言語の標準ライブラリ関数に関する情報源 (例: C言語リファレンス)
  • Go言語のブートストラップに関する一般的な概念を説明する記事やドキュメント (特定のURLは挙げませんが、Goのビルドシステムに関する一般的な解説が参考になります。)