[インデックス 18503] ファイルの概要
このコミットは、Go言語のlib9ライブラリがSolaris上でビルドされる際に発生していた「nilが再定義される」というエラーを修正するものです。具体的には、src/lib9/utf/utfdef.hファイルから、#define nil ((void*)0)という行を削除することで、システムが提供するnilの定義との衝突を解消しています。
コミット
- コミットハッシュ:
98a76029ea9e63f278c0173a944b7debc9e49b70 - 作者: David du Colombier 0intro@gmail.com
- コミット日時: 2014年2月13日 木曜日 20:30:42 +0100
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/98a76029ea9e63f278c0173a944b7debc9e49b70
元コミット内容
lib9: fix Solaris build
In file included from src/lib9/utf/utfecpy.c:17:0:
src/lib9/utf/utfdef.h:28:0: error: "nil" redefined [-Werror]
In file included from src/lib9/utf/utfrrune.c:17:0:
src/lib9/utf/utfdef.h:28:0: error: "nil" redefined [-Werror]
LGTM=bradfitz
R=golang-codereviews, bradfitz
CC=golang-codereviews
https://golang.org/cl/63410043
変更の背景
このコミットは、Go言語のビルドプロセスにおいて、特にSolarisオペレーティングシステム上で問題が発生していたことに対処するために行われました。エラーメッセージ「"nil" redefined [-Werror]」が示すように、src/lib9/utf/utfdef.hファイル内で定義されているnilマクロが、Solarisのシステムヘッダーファイルによって既に定義されているnil(またはそれに相当するヌルポインタ定数)と衝突していました。
C言語やC++では、異なるヘッダーファイルが同じマクロ名を異なる定義で提供すると、このような「再定義エラー」が発生します。特に、システム固有のヘッダーファイルは、そのプラットフォームの標準や慣習に従ってマクロを定義しているため、プロジェクト独自の定義がこれらと衝突することは珍しくありません。この場合、lib9が独自にnilを定義していたことが、Solaris環境でのビルド失敗の原因となっていました。
前提知識の解説
nilとヌルポインタ定数
C言語やC++において、nil(またはより一般的にはNULL)は、有効なオブジェクトを指さないポインタを表すために使用されるマクロまたはキーワードです。これは通常、((void*)0)や単に0として定義されます。ポインタが何も指していない状態を示すために不可欠な概念です。
プリプロセッサマクロ (#define)
#defineはC/C++のプリプロセッサディレクティブの一つで、ソースコードがコンパイルされる前に、指定された識別子(マクロ名)をその定義内容に置き換える機能を提供します。これにより、定数の定義、短い関数のインライン化、条件付きコンパイルなど、様々な用途に利用されます。しかし、異なる場所で同じマクロ名が異なる内容で#defineされると、今回のような「再定義エラー」が発生します。
クロスプラットフォーム開発とビルドの問題
ソフトウェアを複数のオペレーティングシステム(OS)やアーキテクチャで動作させる「クロスプラットフォーム開発」では、OSやコンパイラ、標準ライブラリの違いに起因する問題が頻繁に発生します。特に、システムヘッダーファイルが提供するマクロや型定義はプラットフォームによって異なることがあり、これが移植性の課題となります。今回のnilの再定義問題も、Solaris環境特有のシステムヘッダーの振る舞いが原因で顕在化した典型的なクロスプラットフォームビルドの問題です。
lib9
lib9は、Go言語の初期のランタイムやツールチェインの一部で利用されていたライブラリ群です。これは、Plan 9オペレーティングシステム(Go言語の設計に大きな影響を与えたOS)の標準ライブラリから派生したもので、Goのクロスプラットフォーム対応や基本的なシステム操作を支える役割を担っていました。lib9はC言語で書かれており、GoのランタイムとOSの間の低レベルなインターフェースを提供していました。
技術的詳細
この問題の技術的な核心は、Cプリプロセッサの動作と、異なるプラットフォームにおける標準的なヌルポインタ定数の定義方法の差異にあります。
src/lib9/utf/utfdef.hには、以下の行がありました。
#define nil ((void*)0)
これは、nilというマクロを、型なしポインタ(void*)にキャストされたアドレス0として定義しています。これはC言語におけるヌルポインタの一般的な表現方法の一つです。
しかし、Solarisのシステムヘッダーファイル(例えば、stddef.hや他の標準ライブラリヘッダー)もまた、nilまたはNULLという名前でヌルポインタ定数を定義している可能性が高いです。もしSolarisのヘッダーが、例えば以下のようにnilを定義していた場合:
// Solarisのシステムヘッダーの例
#define nil 0L // あるいは別の形式
lib9のutfdef.hとSolarisのヘッダーが両方ともインクルードされると、プリプロセッサは同じマクロ名nilに対して異なる定義を見つけることになり、これが「"nil" redefined」エラーとして報告されます。-Werrorフラグが有効になっているため、この警告はエラーとして扱われ、ビルドが停止していました。
この問題を解決する最も直接的で安全な方法は、lib9が独自にnilを定義するのをやめることです。C言語の標準では、NULLマクロが標準ライブラリヘッダー(例: <stddef.h>, <stdlib.h>, <stdio.h>など)で提供されることが保証されています。多くのシステムでは、nilもNULLと同様に定義されているか、あるいはNULLがnilとしてエイリアスされていることがあります。
#define nil ((void*)0)を削除することで、lib9のコードがnilを使用する際には、コンパイラが自動的にシステムが提供するnilまたはNULLの定義(あるいは、単に0をヌルポインタとして解釈するC言語の規則)を利用するようになります。これにより、定義の衝突が解消され、Solaris上でのビルドが可能になります。
コアとなるコードの変更箇所
--- a/src/lib9/utf/utfdef.h
+++ b/src/lib9/utf/utfdef.h
@@ -25,4 +25,3 @@ typedef unsigned int uint;
typedef unsigned long ulong;
#define nelem(x) (sizeof(x)/sizeof((x)[0]))
-#define nil ((void*)0)
コアとなるコードの解説
変更はsrc/lib9/utf/utfdef.hファイルの一行の削除のみです。
削除された行:
#define nil ((void*)0)
この行を削除することで、lib9はもはや独自のnilマクロを定義しなくなります。その結果、Solaris環境でコンパイルされる際、システムが提供する標準的なnilまたはNULLの定義が使用されるようになります。これにより、以前発生していた「nilの再定義」エラーが解消され、ビルドが正常に完了するようになりました。
この修正は、Go言語のクロスプラットフォーム対応における、低レベルなC言語コードの移植性に関する課題を解決する一例と言えます。
関連リンク
- Go Code Review (Gerrit): https://golang.org/cl/63410043
参考にした情報源リンク
特になし。この問題はC言語のプリプロセッサとクロスプラットフォーム開発における一般的な知識に基づいています。