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

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

このコミットは、Go言語のコマンドラインツールcmd/goに関連するもので、ビルドプロセスで不足していたファイルを追加することで、ビルドエラーを修正することを目的としています。具体的には、bootstrap.gohttp.goという2つの新しいファイルが追加されています。

コミット

このコミットは、Go言語の主要な開発者の一人であるRuss Coxによって行われました。コミットの目的は、cmd/goのビルドを修正するために、不足していたファイルを追加することです。

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

https://github.com/golang/go/commit/1cfae8bcbf283b3c6837ca5b8db9ddae05f311c0

元コミット内容

cmd/go: add missing files (fix build)

TBR=r
CC=golang-dev
https://golang.org/cl/5571050

変更の背景

このコミットの背景には、Go言語のビルドシステム、特にcmd/goコマンドのブートストラッププロセスがあります。Goのビルドは、Go自身を使ってGoをビルドするという、いわゆる「セルフホスト」な性質を持っています。このプロセスでは、まず非常に基本的なGoコンパイラとツールチェイン(ブートストラップ版)がビルドされ、それを使って完全なGoツールチェインがビルドされます。

このコミットが行われた2012年1月時点では、Goのビルドシステムはまだ進化の途中にありました。cmd/goは、バージョン管理システム(VCS)からのコード取得など、ネットワークアクセスを必要とする機能を持っています。これらの機能は通常、net/httpパッケージを使用します。しかし、ブートストラップビルドの段階では、net/httpのような依存関係の多いパッケージをビルドに含めることは、ビルド時間や複雑性を増大させるため、避けたいという意図がありました。

このコミットは、ブートストラップビルド時にnet/httpパッケージが利用できないことによるビルドエラーを解決するために導入されました。具体的には、ブートストラップビルド用のスタブ(ダミー)実装と、通常のビルド用の実際のHTTPクライアント実装を、ビルドタグを使って切り替えることで、この問題を解決しています。これにより、ブートストラップビルドは軽量に保たれつつ、最終的なgoコマンドは完全な機能を持つことができるようになります。

前提知識の解説

  1. Go言語のビルドプロセスとブートストラップ: Go言語は「セルフホスト」な言語であり、Goコンパイラやツールチェイン自体がGoで書かれています。そのため、GoのソースコードからGoツールチェインをビルドする際には、まず既存のGoコンパイラ(またはC言語で書かれた初期のコンパイラ)を使って、Goツールチェインの「ブートストラップ版」をビルドします。このブートストラップ版は、必要最小限の機能しか持たず、主に完全なGoツールチェインをビルドするために使用されます。その後、このブートストラップ版のツールチェインを使って、最終的なGoツールチェインがビルドされます。この多段階ビルドプロセスにより、Goは自身の進化をGo言語自身で支えることができます。

  2. Goのビルドタグ (+build ディレクティブ): Goのソースファイルには、ファイルの先頭に+buildディレクティブを記述することで、そのファイルを特定の条件下でのみコンパイルするように指定できます。これは「ビルドタグ」と呼ばれます。例えば、// +build linuxと書かれたファイルはLinux環境でのみコンパイルされ、// +build debugと書かれたファイルはgo build -tags debugのようにdebugタグが指定された場合にのみコンパイルされます。 このコミットでは、+build cmd_go_bootstrap+build !cmd_go_bootstrapという2つのタグが使用されています。

    • +build cmd_go_bootstrap: このタグを持つファイルは、cmd_go_bootstrapタグが有効な場合にのみコンパイルされます。これはブートストラップビルド時に使用されます。
    • +build !cmd_go_bootstrap: 先頭の!は否定を意味します。このタグを持つファイルは、cmd_go_bootstrapタグが有効でない場合にのみコンパイルされます。これは通常のビルド時に使用されます。
  3. net/httpパッケージ: Go言語の標準ライブラリに含まれるnet/httpパッケージは、HTTPクライアントおよびサーバー機能を提供します。Webアプリケーションやネットワーク通信を行うGoプログラムでは頻繁に利用されます。このパッケージは、ネットワークスタックやTLS/SSLなど、比較的多くの依存関係を持つため、ブートストラップビルドのような軽量な環境では、その依存関係が問題となることがあります。

技術的詳細

このコミットは、Goのビルドシステムにおける条件付きコンパイルの典型的な例を示しています。src/cmd/go/bootstrap.gosrc/cmd/go/http.goの2つのファイルが追加され、それぞれ異なるビルドタグが適用されています。

  • src/cmd/go/bootstrap.go: このファイルは// +build cmd_go_bootstrapというビルドタグを持っています。これは、Goツールチェインのブートストラップビルド時にのみコンパイルされることを意味します。 このファイルには、httpGETという関数が定義されていますが、その実装は非常にシンプルで、常にエラーを返すスタブ(ダミー)となっています。

    func httpGET(url string) ([]byte, error) {
        return nil, errors.New("no http in bootstrap go command")
    }
    

    このスタブ実装の目的は、ブートストラップビルド時にnet/httpパッケージのような重い依存関係を導入することなく、httpGETという関数シグネチャが存在するようにすることです。これにより、cmd/goの他の部分がhttpGETを呼び出しても、コンパイルエラーにならず、ブートストラップビルドが成功するようになります。ただし、ブートストラップ版のgoコマンドでは、HTTP通信を伴う機能(例: go get)は利用できません。

  • src/cmd/go/http.go: このファイルは// +build !cmd_go_bootstrapというビルドタグを持っています。これは、cmd_go_bootstrapタグが有効でない場合、つまり通常のGoツールチェインのビルド時にのみコンパイルされることを意味します。 このファイルには、実際のHTTP GETリクエストを実行するhttpGET関数の実装が含まれています。

    package main
    
    import (
        "fmt"
        "io/ioutil"
        "net/http"
    )
    
    // httpGET returns the data from an HTTP GET request for the given URL.
    func httpGET(url string) ([]byte, error) {
        resp, err := http.Get(url)
        if err != nil {
            return nil, err
        }
        defer resp.Body.Close()
        if resp.StatusCode != 200 {
            return nil, fmt.Errorf("%s: %s", url, resp.Status)
        }
        b, err := ioutil.ReadAll(resp.Body)
        if err != nil {
            return nil, fmt.Errorf("%s: %v", url, err)
        }
        return b, nil
    }
    

    この実装は、net/httpパッケージを利用して実際のHTTP通信を行い、指定されたURLからデータを取得します。ステータスコードのチェックやエラーハンドリングも含まれています。このファイルがコンパイルされることで、最終的にユーザーが利用するgoコマンドは、ネットワーク通信を伴うすべての機能(例: go getによるリモートリポジトリからのパッケージ取得)を完全に利用できるようになります。

この二重の実装とビルドタグによる切り替えは、Goのビルドシステムが、異なるビルドフェーズ(ブートストラップと最終ビルド)で異なる要件(軽量性 vs. 完全な機能)を満たすための巧妙な設計パターンを示しています。

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

このコミットで追加された主要なコードは以下の2つのファイルです。

  1. src/cmd/go/bootstrap.go

    // Copyright 2012 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.
    
    // +build cmd_go_bootstrap
    
    // This code is compiled only into the bootstrap 'go' binary.
    // These stubs avoid importing packages with large dependency
    // trees, like the use of "net/http" in vcs.go.
    
    package main
    
    import "errors"
    
    func httpGET(url string) ([]byte, error) {
        return nil, errors.New("no http in bootstrap go command")
    }
    
  2. src/cmd/go/http.go

    // Copyright 2012 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.
    
    // +build !cmd_go_bootstrap
    
    // This code is compiled into the real 'go' binary, but it is not
    // compiled into the binary that is built during all.bash, so as
    // to avoid needing to build net (and thus use cgo) during the
    // bootstrap process.
    
    package main
    
    import (
        "fmt"
        "io/ioutil"
        "net/http"
    )
    
    // httpGET returns the data from an HTTP GET request for the given URL.
    func httpGET(url string) ([]byte, error) {
        resp, err := http.Get(url)
        if err != nil {
            return nil, err
        }
        defer resp.Body.Close()
        if resp.StatusCode != 200 {
            return nil, fmt.Errorf("%s: %s", url, resp.Status)
        }
        b, err := ioutil.ReadAll(resp.Body)
        if err != nil {
            return nil, fmt.Errorf("%s: %v", url, err)
        }
        return b, nil
    }
    

コアとなるコードの解説

  • src/cmd/go/bootstrap.go:

    • // +build cmd_go_bootstrap: この行が最も重要で、このファイルがブートストラップビルド時にのみコンパイルされることを示します。
    • func httpGET(url string) ([]byte, error): httpGETという関数が定義されていますが、その実装は常にエラーを返すダミーです。これは、ブートストラップビルド時にnet/httpパッケージの複雑な依存関係を避けるためのプレースホルダーとして機能します。ブートストラップ版のgoコマンドでは、HTTP通信を必要とする機能は意図的に無効化されます。
  • src/cmd/go/http.go:

    • // +build !cmd_go_bootstrap: この行は、このファイルが通常のビルド時にのみコンパイルされることを示します。ブートストラップビルド時にはコンパイルされません。
    • import ("fmt", "io/ioutil", "net/http"): 実際のHTTP通信に必要な標準ライブラリパッケージがインポートされています。特にnet/httpが重要です。
    • func httpGET(url string) ([]byte, error): こちらのhttpGET関数は、net/http.Getを使用して実際にHTTP GETリクエストを実行し、レスポンスボディを読み取ってバイトスライスとして返します。HTTPステータスコードが200以外の場合や、ネットワークエラーが発生した場合には、適切なエラーを返します。この実装により、最終的なgoコマンドは完全なHTTP通信機能を持つことができます。

この2つのファイルは、同じ関数名httpGETを定義していますが、ビルドタグによってどちらか一方のみがコンパイルされるため、名前の衝突は発生しません。これはGoのビルドシステムにおける強力な機能の一つです。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント (ビルドタグに関する情報): https://pkg.go.dev/cmd/go#hdr-Build_constraints
  • Go言語のブートストラッププロセスに関する一般的な情報 (Goのソースコードや関連する設計ドキュメント)