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

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

このコミットは、Go言語のツールチェイン、特にcmd/gogo/buildパッケージに、Objective-Cのソースファイル(.mファイル)のサポートを追加するものです。これにより、GoプログラムからObjective-Cコードを呼び出す際に、よりシームレスな統合が可能になります。

コミット

commit 7861cd6082993becfedeaab75567eaba0c9a03f8
Author: Carlos Castillo <cookieo9@gmail.com>
Date:   Thu Feb 13 10:11:44 2014 -0800

    cmd/go, go/build: support .m files
    
    go/build is changed to list the .m files in a package, and match them for build constraints, adding them to a new field: Package.MFiles.
    
    The go tool is changed to support building .m files and linking in the results during CGO and SWIG builds. This means packages that create a C interface to calls Objective-C code from go are now go-gettable without producing and distributing .syso files. This change is analogous to the one in Go 1.2 made to support C++ built code.
    
    This change doesn't support .mm files (Objective C++).
    
    Also added support for these MFiles to go list's -json mode.
    
    Fixes #6536.
    
    LGTM=iant
    R=golang-codereviews, iant
    CC=golang-codereviews
    https://golang.org/cl/60590044

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

https://github.com/golang/go/commit/7861cd6082993becfedeaab75567eaba0c9a03f8

元コミット内容

cmd/go, go/build: support .m files

go/buildパッケージが変更され、パッケージ内の.mファイルをリストアップし、ビルド制約に合致するものを新しいフィールドPackage.MFilesに追加するようになりました。

goツールが変更され、CGOおよびSWIGビルド中に.mファイルをビルドし、その結果をリンクするようになりました。これにより、GoからObjective-Cコードを呼び出すCインターフェースを作成するパッケージが、.sysoファイルを生成・配布することなくgo-gettableになります。この変更は、Go 1.2でC++ビルドコードをサポートするために行われた変更と類似しています。

この変更は.mmファイル(Objective-C++)をサポートしていません。

また、go list-jsonモードにこれらのMFilesのサポートが追加されました。

Fixes #6536.

変更の背景

Go言語は、C言語との相互運用性を提供するためにCGOというメカニズムを持っています。これにより、GoコードからC関数を呼び出したり、CコードからGo関数を呼び出したりすることが可能です。しかし、Objective-CコードをGoプロジェクトに統合する場合、これまでは.sysoファイル(事前にコンパイルされたオブジェクトファイル)を手動で管理する必要がありました。これは、特にクロスプラットフォーム開発や依存関係の管理において、開発者に負担をかけるものでした。

このコミットの主な目的は、GoツールチェインがObjective-Cソースファイル(.m)を直接認識し、CGOおよびSWIGのビルドプロセスに組み込むことで、この統合プロセスを簡素化することです。これにより、GoパッケージがObjective-Cコードを含む場合でも、go getコマンドで簡単に取得・ビルドできるようになり、開発体験が向上します。コミットメッセージにあるFixes #6536は、この機能追加の必要性を示唆するIssueへの対応です。

Go 1.2でC++ファイルのサポートが追加されたのと同様に、Objective-Cも同様の統合パスを持つことで、Goエコシステムにおける外部言語との連携がより一貫性のあるものになります。

前提知識の解説

  • Objective-C (.mファイル): AppleのmacOSおよびiOS開発で主に用いられるオブジェクト指向プログラミング言語です。C言語のスーパーセットであり、Smalltalkスタイルのメッセージング機能が追加されています。.m拡張子はObjective-Cのソースファイルを示します。
  • CGO: Go言語がC言語のコードと相互運用するためのメカニズムです。Goプログラム内でCの関数を呼び出したり、Cのデータ構造を使用したりすることを可能にします。CGOを使用すると、Goのビルドプロセス中にCコンパイラ(通常はGCCやClang)が呼び出され、Cコードがコンパイル・リンクされます。
  • SWIG (Simplified Wrapper and Interface Generator): C/C++/Objective-Cなどの言語で書かれたライブラリを、Python, Java, Go, Rubyなどの様々なスクリプト言語から呼び出すためのインターフェースコードを自動生成するツールです。SWIGを使用することで、手動でラッパーコードを書く手間を省き、異なる言語間の連携を容易にします。
  • go get: Go言語のパッケージ管理コマンドの一つで、リモートリポジトリからGoパッケージをダウンロードし、ビルドしてインストールするために使用されます。go getが機能するためには、依存するすべてのソースファイルがGoツールチェインによって適切に処理できる必要があります。
  • .sysoファイル: システム固有のオブジェクトファイル(System Object File)の略で、Goのビルドプロセスで外部のC/C++コードなどを事前にコンパイルして生成されるバイナリファイルです。CGOやSWIGを使用する際に、Goツールチェインが直接コンパイルできない外部コードをリンクするために使用されることがあります。このコミットの目的の一つは、Objective-Cコードのために.sysoファイルを不要にすることです。
  • ビルド制約 (Build Constraints): Goのソースファイルに記述される特別なコメント行(例: // +build darwin,amd64)で、特定のOS、アーキテクチャ、またはカスタムタグが有効な場合にのみそのファイルをビルドに含めるようにGoツールチェインに指示します。これにより、プラットフォーム固有のコードを管理できます。

技術的詳細

このコミットは、GoツールチェインがObjective-Cソースファイル(.m)を認識し、ビルドプロセスに統合するための複数の変更を含んでいます。

  1. go/buildパッケージの拡張:

    • go/build.Package構造体に新しいフィールドMFilesが追加されました。これは、パッケージ内のObjective-Cソースファイル(.m)のリストを保持します。
    • go/buildパッケージ内のファイルスキャンロジック(readDir関数など)が更新され、.m拡張子を持つファイルをMFilesとして識別し、適切に分類するようになりました。これにより、GoツールチェインはObjective-CファイルをGoパッケージの一部として認識できるようになります。
    • ビルド制約の評価ロジックも更新され、.mファイルに対してもビルド制約が適用されるようになりました。
  2. cmd/goツールの変更:

    • ビルドプロセスの統合: src/cmd/go/build.go内のbuilder構造体のcgoおよびswigメソッドが変更され、Objective-Cファイル(mfiles)を引数として受け取るようになりました。これにより、CGOおよびSWIGのコンパイル・リンクプロセス中に.mファイルが適切に処理されます。
    • コンパイルとリンク:
      • cgo関数内で、Objective-Cファイルが存在する場合、-lobjcリンカフラグが自動的に追加されるようになりました。これは、Objective-Cランタイムライブラリへのリンクを保証するために必要です。
      • .mファイルは、b.gcc関数(GoツールチェインがC/C++/アセンブリファイルをコンパイルするために使用する内部関数)を通じてコンパイルされ、生成されたオブジェクトファイルがリンクプロセスに含まれるようになります。
    • エラーハンドリング: CGOまたはSWIGを使用せずにObjective-Cファイルを含むパッケージをビルドしようとした場合、Goツールはエラーを返すようになりました。これは、Objective-CコードのコンパイルとリンクにはCGOまたはSWIGのサポートが不可欠であるためです。
    • go list -jsonの更新: src/cmd/go/list.goおよびsrc/cmd/go/pkg.goが更新され、go list -jsonコマンドの出力にMFilesフィールドが含まれるようになりました。これにより、プログラムからパッケージのObjective-Cファイル情報を取得できるようになります。
    • ドキュメントの更新: doc/go1.3.txtsrc/cmd/go/doc.goが更新され、Go 1.3のリリースノートとgo buildコマンドのドキュメントに.mファイルのサポートが明記されました。

この変更は、Go 1.2でC++ファイル(.cc, .cpp, .cxx)のサポートが追加されたのと同様のアプローチを取っています。これにより、GoはC/C++だけでなく、Objective-Cとの相互運用性も向上させ、より幅広いシステムプログラミングのユースケースに対応できるようになります。ただし、Objective-C++(.mmファイル)はサポートされていません。これは、Objective-C++のコンパイルとリンクがObjective-Cよりも複雑であり、Goツールチェインにさらに多くの変更が必要となるためと考えられます。

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

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

  1. src/pkg/go/build/build.go:

    • Package構造体にMFiles []stringフィールドが追加されました。
    • readDir関数内で、ファイル拡張子が.mの場合にp.MFilesに追加するロジックが追加されました。
    • matchFile関数で、.m拡張子がビルド対象ファイルとして認識されるようになりました。
  2. src/cmd/go/build.go:

    • builder.build関数内で、CGOまたはSWIGを使用しないObjective-Cファイルが存在する場合のエラーチェックが追加されました。
    • builder.cgoおよびbuilder.swig関数のシグネチャが変更され、mfiles []string引数が追加されました。
    • builder.cgo関数内で、mfilesが存在する場合に-lobjcリンカフラグがcgoLDFLAGSに追加されるようになりました。
    • builder.cgoおよびbuilder.swig関数内で、mfiles内の各.mファイルをb.gccでコンパイルし、生成されたオブジェクトファイルをリンク対象に追加するロジックが追加されました。
    • gcToolchain.gc関数で、textFilesの計算にlen(p.MFiles)が追加されました。
    • gccgoToolchain.ld関数で、objcフラグが追加され、len(a.p.MFiles) > 0の場合にobjctrueになり、-lobjcリンカフラグが追加されるようになりました。
  3. src/cmd/go/pkg.go:

    • Package構造体にMFiles []stringフィールドが追加されました。
    • copyBuild, load, isStaleなどの関数で、MFilesフィールドのコピーや参照が適切に行われるように変更されました。
  4. src/cmd/go/list.go:

    • go list -jsonの出力構造にMFilesフィールドが追加されました。
  5. src/cmd/go/doc.go:

    • go buildコマンドのドキュメントが更新され、.mファイルがCコンパイラに渡されることが明記されました。
  6. doc/go1.3.txt:

    • Go 1.3のリリースノートに.mファイルサポートに関する項目が追加されました。

コアとなるコードの解説

src/pkg/go/build/build.go

type Package struct {
	// ... 既存のフィールド ...
	MFiles         []string // .m (Objective-C) source files
	// ... 既存のフィールド ...
}

// readDir function (抜粋)
// ...
		case ".m":
			p.MFiles = append(p.MFiles, name)
			continue
// ...

// matchFile function (抜粋)
// ...
	switch ext {
	case ".go", ".c", ".cc", ".cxx", ".cpp", ".m", ".s", ".h", ".hh", ".hpp", ".hxx", ".S", ".swig", ".swigcxx":
		// tentatively okay - read to make sure
// ...

go/buildパッケージは、Goのビルドシステムの中核をなすもので、ソースファイルの発見と分類を担当します。この変更では、Package構造体にMFilesという新しいスライスが追加され、Objective-Cソースファイルの名前を保持するようになりました。readDir関数はディレクトリ内のファイルをスキャンし、.m拡張子を持つファイルをMFilesリストに自動的に追加します。matchFile関数は、ビルドに含めるべきファイルの種類を決定する際に、.mファイルを「暫定的にOK」なファイルとして認識するように更新されました。これにより、GoツールチェインはObjective-CファイルをGoパッケージの一部として正式に扱うことができるようになります。

src/cmd/go/build.go

// func (b *builder) build(a *action) (err error) (抜粋)
// ...
	// Same as above for Objective-C files
	if len(a.p.MFiles) > 0 && !a.p.usesCgo() && !a.p.usesSwig() {
		return fmt.Errorf("can't build package %s because it contains Objective-C files (%s) but it's not using cgo nor SWIG",
			a.p.ImportPath, strings.Join(a.p.MFiles, ","))
	}
// ...

// func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles, gxxfiles, mfiles []string) (outGo, outObj []string, err error) (抜粋)
// ...
	// If we are compiling Objective-C code, then we need to link against libobjc
	if len(mfiles) > 0 {
		cgoLDFLAGS = append(cgoLDFLAGS, "-lobjc")
	}
// ...
	for _, file := range mfiles {
		// Append .o to the file, just in case the pkg has file.c and file.m
		ofile := obj + cgoRe.ReplaceAllString(file, "_") + ".o"
		if err := b.gcc(p, ofile, cflags, file); err != nil {
			return nil, nil, err
		}
		linkobj = append(linkobj, ofile)
		outObj = append(outObj, ofile)
	}
// ...

// func (b *builder) swig(p *Package, obj string, gccfiles, gxxfiles, mfiles []string) (outGo, outObj []string, err error) (抜粋)
// ...
	for _, file := range mfiles {
		// Append .o to the file, just in case the pkg has file.c and file.cpp
		ofile := obj + cgoRe.ReplaceAllString(file, "_") + ".o"
		if err := b.gcc(p, ofile, nil, file); err != nil {
			return nil, nil, err
		}
		extraObj = append(extraObj, ofile)
	}
// ...

cmd/goはGoのビルドコマンドの実装です。build関数では、Objective-Cファイルが存在するにもかかわらずCGOもSWIGも使用されていない場合にエラーを返すチェックが追加されました。これは、Objective-Cコードをコンパイル・リンクするにはこれらの外部ツールが必要であるためです。

最も重要な変更はcgo関数とswig関数にあります。これらの関数は、CGOおよびSWIGのビルドプロセスを管理します。

  • cgo関数では、mfiles(Objective-Cファイルのリスト)が空でない場合、リンカフラグに-lobjcが追加されます。これは、Objective-Cランタイムライブラリをリンクするために不可欠です。
  • cgoswigの両関数で、mfilesリスト内の各Objective-Cファイルがb.gcc関数(GoツールチェインがC/C++/アセンブリファイルをコンパイルするために使用する内部関数)によってコンパイルされます。コンパイルされたオブジェクトファイル(.o)は、最終的なバイナリにリンクされるlinkobjまたはextraObjリストに追加されます。

これらの変更により、GoツールチェインはObjective-CファイルをGoのビルドプロセスにシームレスに統合し、CGOまたはSWIGを介してGoコードとObjective-Cコード間の相互運用性を実現します。

関連リンク

参考にした情報源リンク

  • Go言語のソースコード (特にsrc/cmd/gosrc/pkg/go/buildディレクトリ)
  • Go言語のコミット履歴
  • Objective-Cの基本概念
  • CGOの利用方法に関する一般的な情報
  • SWIGの利用方法に関する一般的な情報