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

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

このコミットは、Go言語のビルドシステムにAndroidプラットフォームのサポートを追加するものです。特に、AndroidがLinuxカーネルをベースとしているという特性を活かし、GOOS=android の場合に linux 向けのビルドタグやファイルがマッチするように変更が加えられています。これにより、既存のLinux向けコード資産をAndroidビルドで再利用しやすくなり、Androidサポートの導入が効率化されています。

コミット

commit a36348008c4acb493be8e4faf209a3818a11f0af
Author: David Crawshaw <david.crawshaw@zentus.com>
Date:   Tue Jul 1 17:21:50 2014 -0400

    all: add GOOS=android
    
    As android and linux have significant overlap, and
    because build tags are a poor way to represent an
    OS target, this CL introduces an exception into
    go/build: linux is treated as a synonym for android
    when matching files.
    
    http://golang.org/s/go14android
    https://groups.google.com/forum/#!topic/golang-dev/P1ATVp1mun0
    
    LGTM=rsc, minux
    R=golang-codereviews, mikioh.mikioh, dave, aram, minux, gobot, rsc, aram.h, elias.naur, iant
    CC=golang-codereviews, rsc
    https://golang.org/cl/105270043

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

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

元コミット内容

all: add GOOS=android

As android and linux have significant overlap, and
because build tags are a poor way to represent an
OS target, this CL introduces an exception into
go/build: linux is treated as a synonym for android
when matching files.

http://golang.org/s/go14android
https://groups.google.com/forum/#!topic/golang-dev/P1ATVp1mun0

LGTM=rsc, minux
R=golang-codereviews, mikioh.mikioh, dave, aram, minux, gobot, rsc, aram.h, elias.naur, iant
CC=golang-codereviews, rsc
https://golang.org/cl/105270043

変更の背景

このコミットの主な背景は、Go言語がAndroidプラットフォームを公式にサポートするための基盤を構築することにあります。コミットメッセージに明記されているように、AndroidはLinuxカーネルをベースにしているため、両者には「significant overlap(大きな重複)」があります。

従来のGoのビルドシステムでは、特定のOSをターゲットにするためにビルドタグ(// +build linux のようなディレクティブや、_linux.go のようなファイル名サフィックス)を使用していました。しかし、AndroidとLinuxのように多くの共通点を持つプラットフォームに対して、完全に独立したビルドタグを設けることは、コードの重複や管理の複雑さを招く可能性があります。コミットメッセージは、このようなビルドタグの利用方法が「poor way to represent an OS target(OSターゲットを表現するのに不適切な方法)」であると指摘しています。

この問題を解決するため、この変更では go/build パッケージに特別な例外を導入しています。具体的には、GOOS=android の場合に linuxandroid の「同義語(synonym)」として扱われるようにすることで、既存のLinux向けコード資産をAndroidビルドで自動的に利用できるようにし、Androidサポートの導入をより効率的かつ柔軟に行うことを目指しています。これにより、GoアプリケーションをAndroidデバイスで実行するための道が開かれました。

前提知識の解説

Goのビルドシステム

Go言語は、その強力なクロスコンパイル機能と、プラットフォーム固有のコードを扱うための洗練されたビルドシステムで知られています。

  • GOOSGOARCH: Goのビルドプロセスにおいて、GOOS (Operating System) と GOARCH (Architecture) は非常に重要な環境変数です。これらは、コンパイル対象のオペレーティングシステムとCPUアーキテクチャを指定します。例えば、GOOS=linux GOARCH=amd64 と設定すると、Linux x86-64ビット環境向けのバイナリが生成されます。
  • ビルドタグ (Build Tags): Goのソースファイルは、特定のビルド条件に基づいて含めたり除外したりすることができます。これは主に以下の2つの方法で行われます。
    • ファイル名サフィックス: filename_GOOS.gofilename_GOOS_GOARCH.go のような命名規則を使用します。例えば、network_linux.go はLinuxビルドでのみコンパイルされ、syscall_windows_amd64.go はWindows x86-64ビルドでのみコンパイルされます。
    • // +build ディレクティブ: ソースファイルの先頭に // +build tag1 tag2 のようなコメントを記述することで、特定のビルドタグが有効な場合にのみそのファイルをコンパイル対象に含めることができます。複数のタグを指定した場合、スペースで区切るとOR条件、カンマで区切るとAND条件になります。例えば、// +build linux,amd64 はLinuxかつAMD64の場合にのみコンパイルされます。
  • 条件付きコンパイルの決定: go build コマンドは、GOOSGOARCH、およびその他のビルド環境(Cgoの有効/無効など)に基づいて、どのソースファイルをコンパイル対象に含めるかを決定します。これにより、単一のコードベースから複数のプラットフォーム向けのバイナリを効率的に生成できます。

Androidプラットフォーム

Androidは、Googleによって開発されたモバイルオペレーティングシステムです。Go言語がAndroidをターゲットにする上で理解すべき重要な特性がいくつかあります。

  • Linuxカーネルベース: Androidは、その基盤としてLinuxカーネルを使用しています。これは、GoがLinux向けに提供する多くの低レベル機能やシステムコールが、Android環境でも理論的には利用可能であることを意味します。
  • 非標準のユーザーランド: しかし、Androidは標準的なLinuxディストリビューションとは異なり、独自のユーザーランド(ユーザー空間のライブラリやツール)を持っています。特に、標準CライブラリとしてGNU C Library (glibc) ではなく、Bionic C Libraryを使用しています。この違いが、Goのような言語がAndroidをターゲットにする際に、単にLinuxバイナリをそのまま実行するだけでは不十分である理由となります。Goのランタイムや標準ライブラリは、BionicのようなAndroid固有の環境に適応する必要があります。
  • クロスコンパイルの必要性: 開発は通常、デスクトップ環境(Linux, macOS, Windows)で行われるため、Androidデバイス向けのGoバイナリを生成するにはクロスコンパイルが不可欠です。

クロスコンパイル

クロスコンパイルとは、あるプラットフォーム(ホスト)上で、別のプラットフォーム(ターゲット)向けの実行可能ファイルを生成するプロセスです。Goは、GOOSGOARCH 環境変数を設定するだけで、非常に簡単にクロスコンパイルを実行できることで知られています。この機能は、Androidのような異なるOS/アーキテクチャをターゲットにする際に極めて重要です。

技術的詳細

このコミットは、GoのビルドシステムとランタイムにAndroidサポートを統合するために、複数のファイルにわたる変更を加えています。

go/build パッケージの変更

go/build パッケージは、Goのソースファイルを解析し、ビルド条件に基づいてどのファイルをコンパイルに含めるかを決定する役割を担っています。

  • src/pkg/go/build/build.go:
    • cgoEnabled マップに android/386, android/amd64, android/arm のエントリが追加されました。これにより、これらのAndroidターゲットアーキテクチャでCgo(GoとC言語の相互運用機能)が有効になります。
    • Context.match 関数に以下のロジックが追加されました。
      if ctxt.GOOS == "android" && name == "linux" {
          return true
      }
      
      これは、現在のビルドターゲットの GOOSandroid であり、かつビルドタグの名前が linux である場合、そのタグをマッチさせるという重要な変更です。これにより、// +build linux のようなビルドタグを持つファイルがAndroidビルドでも有効になります。
    • Context.goodOSArchFile 関数(ファイル名サフィックスに基づいてファイルをマッチさせる関数)にも同様のロジックが追加されました。
      if ctxt.GOOS == "android" && l[n-2] == "linux" {
          return true
      }
      // ...
      if ctxt.GOOS == "android" && l[n-1] == "linux" {
          return true
      }
      
      これにより、filename_linux.gofilename_linux_arm.go のようなファイルが、GOOS=android の場合でも適切にコンパイル対象に含まれるようになります。
  • src/pkg/go/build/build_test.go:
    • matchFileTestsctxtAndroid (GOOS: "android", GOARCH: "arm") を使用したテストケースが追加され、foo_linux.gofoo_android.go が正しくマッチすることを確認しています。これは、上記の Context.match および Context.goodOSArchFile の変更が意図通りに機能するかを検証するためのものです。
  • src/pkg/go/build/doc.go:
    • Goのビルドタグに関するドキュメントに、GOOS=android の場合に linux タグやファイルもマッチするという例外が明記されました。
      // Using GOOS=android matches build tags and files as for GOOS=linux
      // in addition to android tags and files.
      
  • src/pkg/go/build/syslist.go:
    • goosList 定数に "android" が追加され、Goが認識するOSのリストにAndroidが正式に加わりました。

cmd/dist の変更

cmd/dist はGoの配布ツールであり、Goのビルドプロセス自体を管理します。

  • src/cmd/dist/build.c:
    • okgoos 配列に "android" が追加され、dist ツールがAndroidを有効なターゲットOSとして認識するようになりました。
    • matchfield 関数(ビルドタグや環境変数をマッチさせる内部関数)に、GOOSandroid の場合に f"linux" と一致すれば true を返すロジックが追加されました。これは、Goのビルドシステム全体でAndroidとLinuxの関連付けを強化するものです。
  • src/cmd/dist/buildruntime.c:
    • mkzgoos 関数(ランタイムのOS定数を生成する関数)において、GOOSlinux の場合に // +build !android というビルドタグを自動的に追加するロジックが導入されました。これは、Linux固有のコードがAndroidビルドに含まれないようにするための措置であり、AndroidとLinuxの間の意図的な区別を維持しつつ、共通部分を共有するためのバランスを取っています。

liblink はGoのリンカライブラリです。

  • src/liblink/sym.c:
    • static struct { ... } の定義に、"android"Hlinux (Linuxヘッダー) にマッピングされるエントリが追加されました。これは、AndroidがLinuxカーネルを使用しているため、リンカがAndroidをLinuxと同様に扱うべきであることを示しています。

runtime パッケージの追加ファイル

Goのランタイムは、ガベージコレクション、スケジューリング、システムコールなど、Goプログラムの実行に必要な低レベルの機能を提供します。Androidサポートのために、いくつかの新しいファイルが追加されました。

  • src/pkg/runtime/defs_android_arm.h: ARMアーキテクチャのAndroid向け定義ファイル。多くの場合、defs_linux_arm.h をインクルードしています。
  • src/pkg/runtime/os_android.h: Android固有のOS定義ファイル。os_linux.h をインクルードしています。
  • src/pkg/runtime/rt0_android_arm.s: ARMアーキテクチャのAndroid向けランタイムエントリポイントのアセンブリコード。プログラムの開始時に必要な初期化処理を行います。
  • src/pkg/runtime/signal_android_386.h: x86アーキテクチャのAndroid向けシグナル定義ファイル。signal_linux_386.h をインクルードしています。
  • src/pkg/runtime/signal_android_arm.h: ARMアーキテクチャのAndroid向けシグナル定義ファイル。signal_linux_arm.h をインクルードしています。
  • src/pkg/runtime/signals_android.h: Android固有のシグナル定義ファイル。signals_linux.h をインクルードしています。

これらの追加ファイルは、Android環境でGoランタイムが正しく動作するために必要な、OS固有の低レベルなインターフェースや定義を提供します。多くの場合、Linuxの対応するファイルを再利用または参照することで、コードの重複を最小限に抑えています。

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

このコミットの核心的な変更は、GoのビルドシステムがAndroidとLinuxの間の関係をどのように扱うかを定義する部分にあります。

  1. src/pkg/go/build/build.go 内の Context.match 関数: この関数は、ビルドタグが現在のビルドコンテキストにマッチするかどうかを判断します。

    --- a/src/pkg/go/build/build.go
    +++ b/src/pkg/go/build/build.go
    @@ -1124,6 +1127,9 @@ func (ctxt *Context) match(name string, allTags map[string]bool) bool {
     	if name == ctxt.GOOS || name == ctxt.GOARCH || name == ctxt.Compiler {
     		return true
     	}
    +	if ctxt.GOOS == "android" && name == "linux" {
    +		return true
    +	}
     
     	// other tags
     	for _, tag := range ctxt.BuildTags {
    
  2. src/pkg/go/build/build.go 内の Context.goodOSArchFile 関数: この関数は、ファイル名サフィックス(例: _linux.go, _arm.go)に基づいて、ファイルが現在のOS/アーキテクチャに適切であるかを判断します。

    --- a/src/pkg/go/build/build.go
    +++ b/src/pkg/go/build/build.go
    @@ -1165,12 +1172,21 @@ func (ctxt *Context) goodOSArchFile(name string, allTags map[string]bool) bool {
     		\tallTags[l[n-2]] = true
     		\tallTags[l[n-1]] = true
     		}
    -\t\treturn l[n-2] == ctxt.GOOS && l[n-1] == ctxt.GOARCH
    +\t\tif l[n-1] != ctxt.GOARCH {\n+\t\t\treturn false\n+\t\t}\n    +\t\tif ctxt.GOOS == "android" && l[n-2] == "linux" {\n+\t\t\treturn true\n+\t\t}\n    +\t\treturn l[n-2] == ctxt.GOOS
     	}
     	if n >= 1 && knownOS[l[n-1]] {
     		if allTags != nil {
     		\tallTags[l[n-1]] = true
     		}
    +\t\tif ctxt.GOOS == "android" && l[n-1] == "linux" {\n+\t\t\treturn true\n+\t\t}\n     \t\treturn l[n-1] == ctxt.GOOS
     	}
     	if n >= 1 && knownArch[l[n-1]] {
    
  3. src/cmd/dist/build.c 内の matchfield 関数: Goのビルドツールチェーンの低レベルな部分で、ビルド条件を評価します。

    --- a/src/cmd/dist/build.c
    +++ b/src/cmd/dist/build.c
    @@ -1149,7 +1150,7 @@ matchfield(char *f)\n     \n     \tp = xstrrchr(f, ',');\n     \tif(p == nil)\n    -\t\treturn streq(f, goos) || streq(f, goarch) || streq(f, "cmd_go_bootstrap") || streq(f, "go1.1");
    +\t\treturn streq(f, goos) || streq(f, goarch) || streq(f, "cmd_go_bootstrap") || streq(f, "go1.1") || (streq(goos, "android") && streq(f, "linux"));
     \t*p = 0;\n     \tres = matchfield(f) && matchfield(p+1);\n     \t*p = ',';
    

コアとなるコードの解説

Context.match 関数 (src/pkg/go/build/build.go)

この変更は、Goのビルドタグの評価ロジックの中心に位置します。 追加された行:

if ctxt.GOOS == "android" && name == "linux" {
    return true
}

このコードは、現在のビルドコンテキストのターゲットOS (ctxt.GOOS) が "android" であり、かつ評価対象のビルドタグ名 (name) が "linux" である場合に、そのタグをマッチすると判断します。 これにより、例えば // +build linux と記述されたGoソースファイルは、GOOS=android でビルドする際にもコンパイル対象に含まれるようになります。これは、AndroidがLinuxカーネルをベースとしているため、多くのLinux向けコードがAndroidでもそのまま利用できるという事実をGoのビルドシステムに反映させるための、非常に効率的な方法です。

Context.goodOSArchFile 関数 (src/pkg/go/build/build.go)

この関数は、ファイル名に埋め込まれたOSやアーキテクチャのサフィックス(例: _linux.go, _arm.go)を処理し、そのファイルが現在のビルドコンテキストに適しているかを判断します。 変更された部分:

if ctxt.GOOS == "android" && l[n-2] == "linux" {
    return true
}
// ...
if ctxt.GOOS == "android" && l[n-1] == "linux" {
    return true
}

l[n-2]l[n-1] は、ファイル名のサフィックスから抽出されたOS名やアーキテクチャ名を表します。この変更は、GOOS"android" の場合に、ファイル名に "linux" が含まれるファイル(例: foo_linux.gobar_linux_arm.go)も有効であると判断するようにビルドシステムを拡張します。 これにより、ビルドタグと同様に、ファイル名ベースのプラットフォーム選択においてもLinux向けに書かれたファイルがAndroidビルドで自動的に考慮されるようになり、Androidサポートのためのコードの再利用性が大幅に向上します。

matchfield 関数 (src/cmd/dist/build.c)

このC言語の関数は、Goのビルドツールチェーンの内部で、ビルド条件を評価する際に使用されます。 変更された行:

return streq(f, goos) || streq(f, goarch) || streq(f, "cmd_go_bootstrap") || streq(f, "go1.1") || (streq(goos, "android") && streq(f, "linux"));

この行は、既存の条件(現在のOS、アーキテクチャ、ブートストラップモード、Goバージョンとの一致)に加えて、新たな条件 (streq(goos, "android") && streq(f, "linux")) を追加しています。 これは、goos (現在のターゲットOS) が "android" であり、かつ評価対象のフィールド f"linux" である場合に、マッチすると判断することを意味します。この変更は、Goのビルドシステム全体でAndroidとLinuxの間の特別な関係を認識させるための、低レベルかつ重要な調整です。これにより、ビルドツールがAndroidをターゲットとする際に、Linux関連のビルド設定やファイルが適切に処理されるようになります。

これらのコアな変更は、GoがAndroidを「Linuxの特殊なバリアント」として扱うという設計思想を反映しており、既存のLinux向けコード資産を最大限に活用しつつ、Android固有の要件にも対応できる柔軟なビルドシステムを構築するための基盤となっています。

関連リンク

参考にした情報源リンク

  • Go言語のコミットメッセージと差分情報
  • Go言語のビルドシステムに関する一般的な知識
  • Androidプラットフォームのアーキテクチャに関する一般的な知識
  • クロスコンパイルに関する一般的な知識