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

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

このコミットは、Go言語プロジェクトのビルドシステムの一部である deps.bash スクリプトに対する修正です。特にWindows環境でのビルドの安定性を向上させることを目的としており、よりシンプルで互換性の高いシェル構文を使用するように変更されています。具体的には、パッケージ名の変換ロジックにおいて、awk コマンドを複数の sed コマンドに置き換えることで、スクリプトの堅牢性とクロスプラットフォーム互換性を高めています。

コミット

commit d615a5255d36c976abc988218447f7bb6cf5dbd1
Author: Rob Pike <r@golang.org>
Date:   Thu Nov 3 12:19:07 2011 -0700

    deps.bash: attempt to fix the windows build
    Use simpler shell constructs.
    
    R=golang-dev
    CC=golang-dev
    https://golang.org/cl/5297080

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

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

元コミット内容

deps.bash: attempt to fix the windows build
Use simpler shell constructs.

変更の背景

このコミットの主な背景は、Go言語のビルドプロセスにおけるWindows環境での互換性の問題です。deps.bash は、Goのソースコード内の依存関係を解決し、ビルドパスを適切に設定するためのシェルスクリプトであると推測されます。しかし、Windows環境では、Unix系のシェルスクリプト(特に bash)の特定の構文や外部コマンド(awkなど)の挙動が、LinuxやmacOSとは異なる場合があります。

コミットメッセージにある「Use simpler shell constructs.(よりシンプルなシェル構文を使用する)」という記述は、この互換性の問題を解決するためのアプローチを示しています。複雑なシェル構文や、特定の環境でのみ期待通りに動作する可能性のあるコマンドの使用を避け、より普遍的で堅牢な方法に置き換えることで、Windowsを含む様々なプラットフォームでのビルドの成功率を高めることを目指しています。

また、コード内のコメント「TODO: clean up after renaming.」は、Go言語の初期段階でパッケージ名が変更された歴史的経緯があることを示唆しています。このコミットは、その古いパッケージ名から新しいパッケージ名への変換ロジックを、よりWindows互換性のある方法で実装し直す側面も持っています。

前提知識の解説

1. deps.bash とGo言語のビルドシステム

deps.bash は、Go言語のソースコードリポジトリ内の src/pkg ディレクトリに存在するシェルスクリプトです。Go言語のビルドシステムは、C言語やJavaのような伝統的なコンパイル言語とは異なり、Go自身のツールチェイン(コンパイラ、リンカなど)によって構築されます。このビルドプロセスでは、ソースファイルの依存関係を解決し、適切な順序でコンパイルする必要があります。deps.bash は、この依存関係の解決や、ビルドに必要なパスの生成、古いパッケージ名のマッピングなど、ビルドの準備段階で重要な役割を果たすスクリプトであると考えられます。

2. シェルスクリプトとクロスプラットフォーム互換性

シェルスクリプトは、Unix系OS(Linux, macOSなど)で広く使われるコマンドラインインターフェース(CLI)の自動化ツールです。しかし、Windows環境では、ネイティブのコマンドプロンプトやPowerShellとは異なる構文を持つため、直接実行することはできません。Windowsでシェルスクリプトを実行するには、Git Bash、Cygwin、WSL (Windows Subsystem for Linux) といったUnix互換環境が必要です。これらの環境はUnixコマンドをエミュレートしますが、完全に同じ挙動を保証するものではなく、特に複雑なパイプ処理や特定のコマンド(awkなど)のバージョンや実装の違いによって、予期せぬ問題が発生することがあります。

3. awk コマンド

awk は、テキストファイルを行単位で処理し、パターンマッチングとアクションに基づいてデータを操作するための強力なプログラミング言語です。複雑なデータ抽出、整形、レポート生成などに非常に優れています。しかし、その柔軟性ゆえにスクリプトが複雑になりがちで、また、異なるOSや環境で利用可能な awk のバージョン(GNU awk, BSD awkなど)によって、一部の機能や挙動に微妙な違いが生じることがあります。

4. sed コマンド

sed (Stream EDitor) は、テキストストリーム(ファイルやパイプからの入力)に対して、行単位で変換処理を行うためのコマンドラインツールです。主に正規表現を用いた文字列の置換、削除、挿入、行の選択などに使われます。awk に比べて機能は限定的ですが、シンプルな文字列置換やパターンマッチングにおいては sed の方が簡潔に記述でき、また、多くのUnix系環境で標準的に利用可能であるため、クロスプラットフォーム互換性の観点からより安全な選択肢となることがあります。

5. Go言語のパッケージ名変更

Go言語は、その歴史の中で一部の標準ライブラリパッケージの名称を変更してきました。例えば、asn1csvgobjsonxml といったパッケージは、後に encoding/asn1encoding/csv のように encoding/ プレフィックスを持つ形に変更されました。これは、パッケージの役割をより明確にし、標準ライブラリの構造を整理するための一環です。このコミットのコードには、これらの古いパッケージ名から新しいパッケージ名へのマッピング処理が含まれており、ビルドシステムが古い参照を適切に解決できるようにしています。

技術的詳細

このコミットの技術的な変更点は大きく2つあります。

1. dirpat 変数への値の追加方法の変更

dirpat 変数は、正規表現パターンを格納していると推測されます。変更前は、awk コマンドの出力に続けて、古いパッケージ名に対応する正規表現パターンを echo コマンドで個別に dirpat に追加していました。

変更後は、awk コマンドの出力全体を dirpat に格納した後、dirpat="$dirpat\n..." という形式で、古いパッケージ名に対応するパターンを直接 dirpat 変数に追記しています。この方法は、シェルスクリプトにおいて複数行の文字列を安全に構築するための一般的な手法です。特にWindows環境のシェルでは、複数の echo コマンドの出力が期待通りに結合されない、あるいは改行コードの扱いに差異が生じるなどの問題が発生する可能性があるため、この変更はより堅牢なアプローチと言えます。

2. パッケージ名変換ロジックの awk から sed への置き換え

最も重要な変更は、依存関係の解決フェーズで古いパッケージ名を新しい名前に変換するロジックが awk から sed に変更された点です。

変更前 (awk の使用):

/^asn1.install$/ { print "encoding/asn1.install"; next }
/^csv.install$/ { print "encoding/csv.install"; next }
/^gob.install$/ { print "encoding/gob.install"; next }
/^json.install$/ { print "encoding/json.install"; next }
/^xml.install$/ { print "encoding/xml.install"; next }
{print}

この awk スクリプトは、入力行が特定のパターン(例: asn1.install)にマッチした場合に、対応する新しいパッケージ名(例: encoding/asn1.install)を出力し、それ以外の場合は入力行をそのまま出力します。awk は非常に強力ですが、この用途(単純な文字列置換)にはややオーバースペックであり、また、Windows環境での awk の可用性や挙動の差異が問題となる可能性がありました。

変更後 (sed の使用):

sed 's;^asn1.install$;encoding/asn1.install;' |
sed 's;^csv.install$;encoding/csv.install;' |
sed 's;^gob.install$;encoding/gob.install;' |
sed 's;^json.install$;encoding/json.install;' |
sed 's;^xml.install$;encoding/xml.install;' |

変更後は、各パッケージ名変換に対して個別の sed コマンドがパイプで連結されています。sed 's;pattern;replacement;' は、pattern にマッチする文字列を replacement に置換するコマンドです。ここでは、正規表現の区切り文字としてスラッシュ / ではなくセミコロン ; を使用しています。これは、置換文字列にスラッシュが含まれる場合にエスケープの手間を省くための一般的なテクニックであり、スクリプトの可読性と堅牢性を向上させます。

sedawk よりも軽量で、多くのUnix系システムで標準的に利用できるため、クロスプラットフォーム互換性の観点からより安全な選択肢となります。また、この種の単純な文字列置換タスクにおいては、sed の方が awk よりも直感的で簡潔に記述できます。この変更は、Windows環境でのビルドスクリプトの信頼性を高めるための実用的な改善と言えます。

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

--- a/src/pkg/deps.bash
+++ b/src/pkg/deps.bash
@@ -21,14 +21,16 @@ dirpat=$(echo $dirs C | awk '{\
 		gsub("/", "\\/", x)
 		printf("/^(%s)$/\\n", x)
 	}
-}
-	# Add packages' old names. TODO: clean up after renaming.
-	echo "/^(asn1)$/"
-	echo "/^(csv)$/"
-	echo "/^(gob)$/"
-	echo "/^(json)$/"
-	echo "/^(xml)$/"
-)
+}\')
+\n+# Append old names of renamed packages. TODO: clean up after renaming.\n+dirpat="$dirpat\n+/^(asn1)$/\n+/^(csv)$/\n+/^(gob)$/\n+/^(json)$/\n+/^(xml)$/\n+"\n \n for dir in $dirs; do (\
 	cd $dir >/dev/null || exit 1
@@ -47,15 +49,12 @@ for dir in $dirs; do (\
 		grep -v "^$dir\$" |\
 		sed 's/$/.install/' |\
 		# TODO: rename the dependencies for renamed directories.  TODO: clean up after renaming.\
-\t\t# awk is overkill but it's easy to understand.\
-\t\tawk '\
-\t\t\t/^asn1.install$/ { print "encoding/asn1.install"; next }\
-\t\t\t/^csv.install$/ { print "encoding/csv.install"; next }\
-\t\t\t/^gob.install$/ { print "encoding/gob.install"; next }\
-\t\t\t/^json.install$/ { print "encoding/json.install"; next }\
-\t\t\t/^xml.install$/ { print "encoding/xml.install"; next }\
-\t\t\t{print}\
-\t\t' |\
+\t\tsed 's;^asn1.install$;encoding/asn1.install;' |\
+\t\tsed 's;^csv.install$;encoding/csv.install;' |\
+\t\tsed 's;^gob.install$;encoding/gob.install;' |\
+\t\tsed 's;^json.install$;encoding/json.install;' |\
+\t\tsed 's;^xml.install$;encoding/xml.install;' |\
+\t\t# TODO: end of renamings.\
 		sed 's;^C\.install;runtime/cgo.install;' |\
 		sort -u
 	)

コアとなるコードの解説

1. dirpat 変数の変更 (行 21-35)

  • 変更前:

    dirpat=$(echo $dirs C | awk '{
        gsub("/", "\\/", x)
        printf("/^(%s)$/\\n", x)
    }'
    # Add packages' old names. TODO: clean up after renaming.
    echo "/^(asn1)$/"
    echo "/^(csv)$/"
    echo "/^(gob)$/"
    echo "/^(json)$/"
    echo "/^(xml)$/"
    )
    

    dirpat 変数は、awk コマンドの出力と、その後の複数の echo コマンドの出力をまとめてコマンド置換 $(...) で取得していました。この方法では、echo コマンドが新しい行を生成し、それらがどのように結合されるかがシェル環境によって異なる可能性がありました。特にWindows環境では、改行コードの扱いやパイプの挙動がUnix系OSと異なるため、問題が生じやすかったと考えられます。

  • 変更後:

    dirpat=$(echo $dirs C | awk '{\
        gsub("/", "\\/", x)\
        printf("/^(%s)$/\\n", x)\
    }\')
    
    # Append old names of renamed packages. TODO: clean up after renaming.
    dirpat="$dirpat\n+/^(asn1)$/\n+/^(csv)$/\n+/^(gob)$/\n+/^(json)$/\n+/^(xml)$/\n+"
    

    まず、awk コマンドの出力のみを dirpat に格納します。その後、dirpat="$dirpat\n..." という形式で、古いパッケージ名に対応する正規表現パターンを明示的に改行 \n を含めて追記しています。この方法は、シェルスクリプトで複数行の文字列変数を構築する際のより標準的で堅牢なアプローチであり、異なるシェル環境間での互換性が高まります。

2. パッケージ名変換ロジックの変更 (行 47-61)

  • 変更前:

    # TODO: rename the dependencies for renamed directories.  TODO: clean up after renaming.
    # awk is overkill but it's easy to understand.
    awk '
        /^asn1.install$/ { print "encoding/asn1.install"; next }
        /^csv.install$/ { print "encoding/csv.install"; next }
        /^gob.install$/ { print "encoding/gob.install"; next }
        /^json.install$/ { print "encoding/json.install"; next }
        /^xml.install$/ { print "encoding/xml.install"; next }
        {print}
    ' |
    

    この部分では、grep -v "^$dir\$" | sed 's/$/.install/' の出力(例: asn1.install)を awk コマンドにパイプで渡していました。awk スクリプトは、入力行が特定の古いパッケージ名にマッチした場合に、対応する新しいパッケージ名(例: encoding/asn1.install)を出力し、それ以外の場合は入力行をそのまま出力していました。コメントにあるように「awk is overkill but it's easy to understand.(awkはやりすぎだが、理解しやすい)」と書かれており、機能的には問題ないものの、よりシンプルな方法があることが示唆されていました。

  • 変更後:

    sed 's;^asn1.install$;encoding/asn1.install;' |\
    sed 's;^csv.install$;encoding/csv.install;' |\
    sed 's;^gob.install$;encoding/gob.install;' |\
    sed 's;^json.install$;encoding/json.install;' |\
    sed 's;^xml.install$;encoding/xml.install;' |\
    # TODO: end of renamings.
    

    awk スクリプト全体が、複数の sed コマンドのパイプラインに置き換えられました。各 sed コマンドは、特定の古いパッケージ名(例: ^asn1.install$)を対応する新しいパッケージ名(例: encoding/asn1.install)に置換します。

    • s;pattern;replacement; 構文は、置換コマンド s の区切り文字としてセミコロン ; を使用しています。これにより、置換文字列にスラッシュ / が含まれる場合でもエスケープが不要になり、可読性が向上します。
    • ^$ はそれぞれ行の先頭と末尾を表す正規表現で、行全体が指定されたパターンに完全に一致する場合にのみ置換が行われることを保証します。 この変更により、スクリプトはより簡潔になり、sed の方が awk よりも多くの環境で安定して動作する傾向があるため、Windows環境での互換性が向上します。

これらの変更は、Go言語のビルドスクリプトが、より広範な環境、特にWindows上での bash エミュレーション環境において、より信頼性高く動作するようにするための重要な改善です。

関連リンク

参考にした情報源リンク

  • (この解説の生成には、提供されたコミット情報と一般的なシェルスクリプト、Go言語の知識に基づいており、特定の外部ウェブサイトを直接参照していません。ただし、awksed の基本的な使い方、シェルスクリプトのクロスプラットフォーム互換性に関する一般的な情報は、Stack Overflow、GNU Awk User's Guide、sed & awk bookなどのリソースで確認できます。)