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

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

このコミットは、Go言語のビルドシステムにおけるgobuildコマンドの挙動を修正し、生成されるMakefileのコメント行にGOOSGOARCHといった環境変数をより適切に挿入するように変更します。具体的には、シェル形式の変数展開(${VAR})とMakefile形式の変数展開($(VAR))を区別して出力するよう改善されています。

コミット

commit 97dcc68f1ec4202b467210dcd2607c7630bb9d6e
Author: Russ Cox <rsc@golang.org>
Date:   Sun Feb 15 19:20:35 2009 -0800

    insert ${GOOS} and ${GOARCH} in
    command-line comment.
    
    R=r
    DELTA=11  (6 added, 0 deleted, 5 changed)
    OCL=25051
    CL=25051

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

https://github.com/golang/go/commit/97dcc68f1ec4202b467210dcd2607c7630bb9d6e

元コミット内容

このコミットの元の内容は、gobuildが生成するMakefileのコメント行に、GOOSGOARCHというGoの環境変数を挿入する際の書式を改善することです。以前は常に$(VAR)形式で出力されていましたが、この変更により、シェルコマンドとして解釈されるべき箇所では${VAR}形式で出力されるようになります。

変更の背景

Go言語の初期のビルドシステムでは、gobuildというツールがGoソースコードからMakefileを生成していました。このMakefileには、gobuildが実行された際のコマンドライン引数がコメントとして記録されていました。このコメント行は、主にデバッグや履歴追跡のために利用されます。

問題は、このコメント行にGOOSGOARCHといった環境変数が含まれる場合、gobuildがそれらを常にMakefileの変数展開形式である$(GOOS)$(GOARCH)として出力していた点にありました。しかし、このコメント行はしばしばシェルコマンドとして解釈される文脈で利用されることがあり、その場合、シェルでは$(VAR)ではなく${VAR}が一般的な変数展開の書式となります。

この不一致は、コメント行をコピーしてシェルで直接実行しようとした際に問題を引き起こす可能性がありました。例えば、gobuildの実行コマンドがコメントとして記録されている場合、そのコマンドをそのままシェルに貼り付けて実行すると、$(GOOS)がシェルによって正しく解釈されず、予期せぬエラーや挙動につながる可能性がありました。

このコミットは、このような潜在的な問題を解決し、生成されるMakefileのコメントがより汎用的に、特にシェル環境での利用において、より正確な変数展開形式を反映するようにするために行われました。

前提知識の解説

このコミットを理解するためには、以下の前提知識が必要です。

  1. Go言語のビルドシステム(初期): Go言語の初期のビルドシステムは、現在のgo buildコマンドとは異なり、gobuildというツールを使用していました。gobuildは、GoのソースファイルからC言語のコードを生成し、それをコンパイルして実行可能ファイルを生成するプロセスを自動化していました。この過程で、ビルドに必要な情報やコマンドライン引数を記録したMakefileを生成していました。

  2. Makefileと変数展開: Makefileは、makeユーティリティがプロジェクトのビルドを自動化するために使用するファイルです。Makefileでは、変数を定義し、それらを$(VAR_NAME)という形式で参照します。makeは、これらの変数をその値に展開してからコマンドを実行します。

    例:

    CC = gcc
    CFLAGS = -Wall
    
    all:
    	$(CC) $(CFLAGS) main.c -o main
    

    この例では、$(CC)gccに、$(CFLAGS)-Wallに展開されます。

  3. シェルスクリプトと変数展開: Bashなどのシェルスクリプトでは、変数を参照する際に${VAR_NAME}または$VAR_NAMEという形式を使用します。${VAR_NAME}は、変数名が他の文字列と連結される場合など、曖昧さを避けるためによく使われます。

    例:

    #!/bin/bash
    
    GOOS="linux"
    echo "Operating system: ${GOOS}"
    

    この例では、${GOOS}linuxに展開されます。

  4. GOOSGOARCH: これらはGo言語のクロスコンパイルにおいて非常に重要な環境変数です。

    • GOOS: ターゲットとするオペレーティングシステム(例: linux, windows, darwin)。
    • GOARCH: ターゲットとするアーキテクチャ(例: amd64, arm, 386)。 これらの変数を設定することで、異なるOSやアーキテクチャ向けのバイナリをビルドできます。
  5. fmtパッケージとFmtSharpフラグ: Go言語の初期のコードベースでは、C言語のprintfライクなフォーマット機能を提供する内部的なfmtパッケージ(またはそれに類するもの)が使用されていました。このコミットで登場するFmtSharpフラグは、C言語のprintfにおける#フラグ(代替形式)に似た概念で、出力の形式を微調整するために使用される可能性があります。この文脈では、おそらく「シェル形式」の出力が必要な場合に設定されるフラグとして機能しています。

技術的詳細

このコミットは、主にsrc/cmd/gobuild/gobuild.cファイルのdollarfmt関数とwritemakefile関数に変更を加えています。

gobuildは、ビルドプロセスの一環としてMakefileを生成します。このMakefileには、gobuildが実行された際のコマンドラインがコメントとして含まれていました。このコメントは、gobuildが内部的に持っているGOOSGOARCHといった値を、生成されるMakefileのコメント行に挿入する際に、常に$(GOOS)$(GOARCH)という形式で出力していました。

変更の核心は、dollarfmt関数にあります。この関数は、文字列中の特定のパターン(この場合はgoosgoarchの値)を、対応する変数形式($(GOOS)$(GOARCH))に置き換える役割を担っています。

コミット前のコードでは、goosgoarchが見つかると、無条件に$(GOOS)$(GOARCH)に置き換えていました。

// コミット前のコード (抜粋)
	if(strncmp(s, goarch, n) == 0){
		fmtstrcpy(f, "$(GOARCH)");
		continue;
	}
	// ...
	if(strncmp(s, goos, n) == 0){
		fmtstrcpy(f, "$(GOOS)");
		continue;
	}

このコミットでは、dollarfmt関数内でf->flags & FmtSharpという条件分岐が追加されました。これは、フォーマットフラグにFmtSharpが設定されているかどうかをチェックしています。

  • もしFmtSharpフラグが設定されていれば、変数はシェル形式の${VAR}として出力されます。
  • そうでなければ、従来通りMakefile形式の$(VAR)として出力されます。
// コミット後のコード (抜粋)
	if(strncmp(s, goarch, n) == 0){
		if(f->flags & FmtSharp)
			fmtstrcpy(f, "${GOARCH}");  // shell
		else
			fmtstrcpy(f, "$(GOARCH)");  // make
		continue;
	}
	// ...
	if(strncmp(s, goos, n) == 0){
		if(f->flags & FmtSharp)
			fmtstrcpy(f, "${GOOS}");  // shell
		else
			fmtstrcpy(f, "$(GOOS)");  // make
		continue;
	}

この変更に伴い、writemakefile関数内のBprint呼び出しも修正されました。以前はBprint(&bout, " %s", oargv[i]);としていた箇所が、Bprint(&bout, " %#$", oargv[i]);に変更されています。この%#$というフォーマット指定子が、dollarfmt関数内でFmtSharpフラグを有効にする役割を果たしています。これにより、gobuildのコマンドライン引数をコメントとして出力する際に、GOOSGOARCH${VAR}形式で挿入されるようになります。

src/lib/net/Makefileの変更は、このgobuildの変更によって生成されるMakefileのコメント行の例を示しています。以前はgobuild -m ... >Makefileというコメントでしたが、変更後はgobuild -m ... fd_${GOOS}.go ... net_${GOOS}.go >Makefileのように、GOOS${GOOS}としてコメント内に表示されるようになります。これは、gobuildが生成するMakefileのコメントが、よりシェルフレンドリーな形式になったことを示しています。

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

src/cmd/gobuild/gobuild.c

--- a/src/cmd/gobuild/gobuild.c
+++ b/src/cmd/gobuild/gobuild.c
@@ -263,12 +263,18 @@ dollarfmt(Fmt *f)
 	for(; *s; s+=n){
 		n = strlen(goarch);
 		if(strncmp(s, goarch, n) == 0){
-			fmtstrcpy(f, "$(GOARCH)");
+			if(f->flags & FmtSharp)
+				fmtstrcpy(f, "${GOARCH}");  // shell
+			else
+				fmtstrcpy(f, "$(GOARCH)");  // make
 			continue;
 		}
 		n = strlen(goos);
 		if(strncmp(s, goos, n) == 0){
-			fmtstrcpy(f, "$(GOOS)");
+			if(f->flags & FmtSharp)
+				fmtstrcpy(f, "${GOOS}");  // shell
+			else
+				fmtstrcpy(f, "$(GOOS)");  // make
 			continue;
 		}
 		n = chartorune(&r, s);
@@ -327,7 +333,7 @@ writemakefile(void)
 			Bprint(&bout, "\\\n#   ");
 			o = Boffset(&bout);
 		}
-		Bprint(&bout, " %s", oargv[i]);
+		Bprint(&bout, " %#$", oargv[i]);
 	}
 	Bprint(&bout, " >Makefile\\n");
 	Bprint(&bout, preamble, thechar);

src/lib/net/Makefile

--- a/src/lib/net/Makefile
+++ b/src/lib/net/Makefile
@@ -3,8 +3,8 @@
 # license that can be found in the LICENSE file.
 
 # DO NOT EDIT.  Automatically generated by gobuild.
-# gobuild -m dnsclient.go dnsconfig.go dnsmsg.go fd.go fd_darwin.go\
-#    ip.go net.go net_darwin.go parse.go port.go >Makefile
+# gobuild -m dnsclient.go dnsconfig.go dnsmsg.go fd.go fd_${GOOS}.go\
+#    ip.go net.go net_${GOOS}.go parse.go port.go >Makefile
 O=6
 GC=$(O)g
 CC=$(O)c -w

コアとなるコードの解説

src/cmd/gobuild/gobuild.c の変更

  1. dollarfmt関数の変更: この関数は、gobuildが文字列をフォーマットする際に、GOOSGOARCHといった特定のキーワードを対応する変数形式に変換する役割を担っています。

    • 変更前は、goarchgoosという文字列が見つかると、無条件に$(GOARCH)$(GOOS)というMakefile形式の変数展開文字列に置き換えていました。
    • 変更後は、if(f->flags & FmtSharp)という条件が追加されました。これは、フォーマット指定子に#フラグ(代替形式)が指定されているかどうかをチェックしています。
      • もし#フラグが設定されていれば、fmtstrcpy(f, "${GOARCH}");またはfmtstrcpy(f, "${GOOS}");が実行され、シェル形式の変数展開(${VAR})が出力されます。コメントには「// shell」と書かれており、これがシェルで解釈されることを意図していることがわかります。
      • #フラグが設定されていなければ、従来通りfmtstrcpy(f, "$(GOARCH)");またはfmtstrcpy(f, "$(GOOS)");が実行され、Makefile形式の変数展開($(VAR))が出力されます。コメントには「// make」と書かれており、これがMakefileで解釈されることを意図していることがわかります。 この変更により、gobuildは出力のコンテキストに応じて適切な変数展開形式を選択できるようになりました。
  2. writemakefile関数の変更: この関数は、最終的なMakefileの内容を書き出す役割を担っています。

    • 変更前は、コマンドライン引数をBprint(&bout, " %s", oargv[i]);という形式で出力していました。ここで%sは単純な文字列として引数を挿入します。
    • 変更後は、Bprint(&bout, " %#$", oargv[i]);に変更されました。ここで注目すべきは%#$という新しいフォーマット指定子です。
      • %はフォーマット指定子の開始を示します。
      • #は、前述のdollarfmt関数でチェックされるFmtSharpフラグを有効にする役割を果たします。これにより、dollarfmtがシェル形式の変数展開を選択するようになります。
      • $は、おそらくdollarfmt関数を呼び出すためのカスタムフォーマット指定子です。 この変更により、gobuildが生成するMakefileのコメント行に、GOOSGOARCHといった環境変数が含まれる場合、それらがシェルで正しく解釈される${VAR}形式で出力されるようになりました。

src/lib/net/Makefile の変更

このファイルは、gobuildによって自動生成されるMakefileの一例です。このコミットにおける変更は、gobuildの挙動変更によって、このMakefileのコメント行がどのように変化するかを示しています。

  • 変更前は、コメント行にfd_darwin.gonet_darwin.goのように、特定のOS名がハードコードされていました。これは、gobuildが実行された環境のGOOSが直接埋め込まれていたことを示唆しています。
  • 変更後は、コメント行がfd_${GOOS}.gonet_${GOOS}.goのように、${GOOS}というシェル形式の変数展開を含むようになりました。これは、gobuildが生成するコメントが、実行時の環境変数ではなく、より汎用的な変数形式でコマンドラインを表現するようになったことを示しています。これにより、このコメント行をコピーして別の環境で実行する際に、その環境のGOOSが自動的に適用されるようになり、利便性が向上します。

関連リンク

  • Go言語の初期のビルドシステムに関する情報(歴史的な文脈)
  • Makefileの変数展開に関するドキュメント
  • シェルスクリプトの変数展開に関するドキュメント

参考にした情報源リンク

  • Go言語の公式ドキュメント (特に初期のビルドシステムに関するアーカイブ情報があれば)
  • GNU Make マニュアル
  • Bash マニュアル
  • Go言語のソースコードリポジトリ (過去のコミット履歴)
  • Go言語のIssueトラッカー (関連する議論があれば)