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

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

このコミットは、Goコンパイラ(cmd/gc)がWindows環境で出力ファイル名を生成する際のバグを修正するものです。具体的には、ファイルパスの区切り文字の扱いがUnix-likeシステムとWindowsシステムで異なることに起因する問題に対応し、Windows上でも正しい出力ファイル名が生成されるように改善しています。

コミット

Author: Shenghou Ma minux.ma@gmail.com Date: Sun Oct 7 14:14:46 2012 +0800 Subject: cmd/gc: fix output filename generation on Windows

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

https://github.com/golang/go/commit/9224b4c873f09c310787b12830acdb5577a099c9

元コミット内容

commit 9224b4c873f09c310787b12830acdb5577a099c9
Author: Shenghou Ma <minux.ma@gmail.com>
Date:   Sun Oct 7 14:14:46 2012 +0800

    cmd/gc: fix output filename generation on Windows
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/6610060

変更の背景

Goコンパイラ(cmd/gc)は、ソースファイルからコンパイルされたオブジェクトファイルや実行可能ファイルの出力ファイル名を決定する際に、入力ファイルのパスからファイル名を抽出する必要があります。Unix-likeシステム(Linux, macOSなど)ではディレクトリの区切り文字としてスラッシュ(/)が使用されますが、Windowsシステムではバックスラッシュ(\)が使用されます。

このコミット以前のcmd/gcのコードでは、ファイルパスから最後のディレクトリ区切り文字を探す際に、Unix-likeシステムの慣習に従いスラッシュ(/)のみを考慮していました。このため、Windows環境でバックスラッシュ(\)を含むパスが入力された場合、strrchr関数が正しくファイル名の開始位置を見つけられず、誤った出力ファイル名が生成される可能性がありました。このバグは、Windows上でのGoのビルドプロセスにおいて、コンパイルされたファイルの命名規則に問題を引き起こしていました。

前提知識の解説

  • cmd/gc: Go言語のコンパイラの一つで、Goのソースコードを機械語に変換する役割を担います。Goのツールチェインの重要な部分です。
  • lex.c: cmd/gcのソースコードの一部で、字句解析(lexical analysis)やその他のコンパイラのフロントエンド処理に関連するコードが含まれていると推測されます。このファイル内で出力ファイル名の生成ロジックが扱われています。
  • strrchr関数: C言語の標準ライブラリ関数で、文字列内で指定された文字が最後に現れる位置へのポインタを返します。例えば、strrchr("path/to/file.go", '/')file.gofを指すポインタを返します。
  • ファイルパスの区切り文字:
    • Unix-likeシステム: スラッシュ(/)がディレクトリの区切り文字として使用されます(例: /home/user/document.txt)。
    • Windowsシステム: バックスラッシュ(\)がディレクトリの区切り文字として使用されます(例: C:\Users\User\Document.txt)。
  • クロスプラットフォーム開発: ソフトウェアを複数の異なるオペレーティングシステムで動作させるための開発手法。このコミットは、GoコンパイラがWindows環境でも正しく動作するように、クロスプラットフォーム対応を強化する一環です。

技術的詳細

このコミットの技術的詳細は、ファイルパスの解析におけるオペレーティングシステム固有の挙動の違いを吸収することにあります。

既存のコードでは、入力ファイルパスinfileからファイル名部分を抽出するために、strrchr(infile, '/')を使用して最後のスラッシュ(/)の位置を探していました。これはUnix-likeシステムでは問題ありませんが、Windowsではパス区切り文字がバックスラッシュ(\)であるため、Windows形式のパス(例: C:\Users\foo\bar.go)に対してこの処理を行うと、strrchrはスラッシュを見つけられず、pnilになるか、あるいは意図しない位置を指すことになります。結果として、出力ファイル名が正しく生成されませんでした。

この修正では、windowsというグローバル変数(またはマクロ)が真である場合、つまりコンパイルがWindows環境で行われている場合に、追加でバックスラッシュ(\)の検索を行うようにしています。

  1. まず、従来通りスラッシュ(/)でstrrchrを呼び出し、その結果をpに格納します。
  2. 次に、windowsフラグが立っている場合、バックスラッシュ(\)でstrrchrを呼び出し、その結果を新しい変数qに格納します。
  3. qpよりも後方(つまり、よりファイル名の開始に近い位置)を指している場合、pqで上書きします。これは、Windowsパスではバックスラッシュが正しい区切り文字であるため、スラッシュよりもバックスラッシュの位置を優先するというロジックです。

この変更により、pは常に、Unix-likeパスでは最後のスラッシュ、Windowsパスでは最後のバックスラッシュ(またはスラッシュも存在する場合はより後方のもの)を指すようになり、その結果、infileから正しいファイル名部分を抽出できるようになります。

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

src/cmd/gc/lex.cファイルのmkpackage関数内の変更です。

--- a/src/cmd/gc/lex.c
+++ b/src/cmd/gc/lex.c
@@ -2190,7 +2190,7 @@ mkpackage(char* pkgname)
  {\n \tSym *s;\n \tint32 h;\n-\tchar *p;\n+\tchar *p, *q;\n \n \tif(localpkg->name == nil) {\n \t\tif(strcmp(pkgname, \"_\") == 0)\n@@ -2230,6 +2230,11 @@ mkpackage(char* pkgname)\n \n \tif(outfile == nil) {\n \t\tp = strrchr(infile, \'/\');\n+\t\tif(windows) {\n+\t\t\tq = strrchr(infile, \'\\\\\');\n+\t\t\tif(q > p)\n+\t\t\t\tp = q;\n+\t\t}\n \t\tif(p == nil)\n \t\t\tp = infile;\n \t\telse

コアとなるコードの解説

変更はmkpackage関数内で行われています。この関数は、パッケージ名を基に出力ファイル名を決定するロジックの一部を担っています。

  1. char *p, *q;: pは既存のコードで使用されていたポインタで、ファイルパス内の最後のスラッシュ(/)の位置を保持します。 qは新しく追加されたポインタで、ファイルパス内の最後のバックスラッシュ(\)の位置を保持するために導入されました。

  2. p = strrchr(infile, '/');: これは既存の行で、入力ファイルパスinfileから最後のスラッシュ(/)を探し、その位置をpに格納します。

  3. if(windows) { ... }: このブロックは、コンパイルがWindows環境で行われている場合にのみ実行されます。windowsは、GoコンパイラがWindows上で動作しているかどうかを示すフラグです。

  4. q = strrchr(infile, '\\\\');: Windows環境の場合、infileから最後のバックスラッシュ(\)を探し、その位置をqに格納します。C言語の文字列リテラルではバックスラッシュ自体をエスケープする必要があるため、'\\\\'と記述されています。

  5. if(q > p): この条件式がこの修正の核心です。

    • qpよりも大きい(アドレス値が大きい)ということは、qpよりも文字列の終端に近い位置を指していることを意味します。
    • これは、Windowsパスにおいて、バックスラッシュがスラッシュよりもファイル名の開始位置に近い(つまり、より適切な区切り文字である)ことを示します。
    • 例えば、C:/foo\bar.goのようなパスがあった場合、/fooの後に、\barの前に来ます。この場合、\の方がファイル名bar.goに近いため、qが指す位置が優先されるべきです。
  6. p = q;: 上記の条件が真の場合、pqの値で上書きします。これにより、pはWindows環境でより適切なファイル名開始位置を指すようになります。

この修正により、outfilenilの場合のファイル名生成ロジックにおいて、Windowsのパス区切り文字(\)が正しく考慮されるようになり、クロスプラットフォームでの互換性が向上しました。

関連リンク

参考にした情報源リンク

  • 特になし(コミット情報と一般的なプログラミング知識に基づく)