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

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

コミット

commit 2ae860585920e17c1d43098c476ffb11c21b35f8
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date:   Sun Mar 11 21:02:40 2012 -0700

    misc/dist: use archive/zip, seek out windows deps, add --upload flag
    
    Use archive/zip instead of 7z on Windows.
    
    Look for all Windows deps before starting build, and include looking
    for them in their common locations instead of making users update
    their PATHs.
    
    Add an --upload flag that, if set to false, doesn't require credential
    files.
    
    R=golang-dev, alex.brainman, adg
    CC=golang-dev
    https://golang.org/cl/5794046

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

https://github.com/golang/go/commit/2ae860585920e17c1d43098c476ffb11c21b35f8

元コミット内容

misc/dist: use archive/zip, seek out windows deps, add --upload flag

Use archive/zip instead of 7z on Windows.

Look for all Windows deps before starting build, and include looking
for them in their common locations instead of making users update
their PATHs.

Add an --upload flag that, if set to false, doesn't require credential
files.

R=golang-dev, alex.brainman, adg
CC=golang-dev
https://golang.org/cl/5794046

変更の背景

このコミットは、Go言語のディストリビューションツール(misc/dist/bindist.go)におけるWindows環境でのビルドおよびパッケージングプロセスの改善を目的としています。主な背景は以下の通りです。

  1. 外部依存の削減とポータビリティの向上: 以前はWindows環境でZIPアーカイブを作成するために外部ツールである7z(7-Zip)に依存していました。これは、ビルド環境に7zがインストールされ、かつPATHが適切に設定されていることを前提としており、ユーザーにとってセットアップの負担となっていました。Go標準ライブラリのarchive/zipパッケージを使用することで、外部ツールへの依存をなくし、Goのビルドシステム自体のポータビリティと自己完結性を高めることが可能になります。
  2. Windows依存関係の検出とユーザーエクスペリエンスの改善: GoのWindows向けバイナリディストリビューションをビルドするには、MinGW (GCC)、WiXツールセット、Mercurialといった特定の外部ツールが必要でした。これらのツールがユーザーのPATHに設定されていない場合、ビルドが失敗し、ユーザーは手動でPATHを設定する必要がありました。このコミットでは、これらの依存関係をビルド開始前に自動的に検出し、一般的なインストールパスも探索することで、ユーザーが手動でPATHを設定する手間を省き、ビルドプロセスの堅牢性とユーザーフレンドリーさを向上させています。
  3. アップロード機能の柔軟性: 生成されたバイナリパッケージをGoogle Code(当時のGoプロジェクトのホスティング先)にアップロードする機能がありましたが、これは常に認証情報を必要としました。--uploadフラグを追加することで、アップロードが不要な場合に認証情報を求めないようにし、ツールの利用シナリオを広げています。例えば、単にパッケージを生成したいだけで、公開はしないといったケースに対応できます。

これらの変更は、Go言語のディストリビューション作成プロセスをより堅牢で、ユーザーにとって使いやすいものにすることを目指しています。

前提知識の解説

このコミットを理解するためには、以下の技術的知識が役立ちます。

  1. Go言語のビルドシステム: Go言語は、go buildコマンドを通じてソースコードから実行可能ファイルを生成します。また、Goプロジェクト自体もGoで書かれており、そのビルドプロセスはmake.bash(Unix系)やmake.bat(Windows系)といったスクリプトによって制御されます。misc/distディレクトリ内のツールは、Goの公式バイナリディストリビューション(SDK)を作成するための特別なビルドスクリプトやツール群を含んでいます。
  2. archive/zipパッケージ: Go言語の標準ライブラリに含まれるパッケージで、ZIPアーカイブの読み書きをサポートします。これにより、外部のZIPツールに依存することなく、Goプログラム内で直接ZIPファイルの作成や展開が可能です。
  3. 7-Zip (7z): 高い圧縮率を誇るオープンソースのファイルアーカイバです。Windows環境ではコマンドラインツールとしても広く利用されており、以前のGoのビルドシステムではZIPファイルの作成にこの7zコマンドを外部プロセスとして呼び出していました。
  4. MinGW (Minimalist GNU for Windows): Windows上でGCC (GNU Compiler Collection) などのGNU開発ツールを使用できるようにする環境です。GoのWindows向けバイナリをビルドする際には、C言語で書かれた部分(例: ランタイムや一部の標準ライブラリ)をコンパイルするためにMinGWのGCCが必要となる場合があります。
  5. WiX Toolset (Windows Installer XML Toolset): Windows Installer (MSI) パッケージを作成するためのオープンソースのツールセットです。GoのWindows向けディストリビューションには、MSIインストーラが含まれることがあり、その作成にWiXツールセット(特にheat, candle, lightといったコマンド)が使用されます。
  6. Mercurial (hg): 分散型バージョン管理システムの一つです。GoプロジェクトはかつてMercurialで管理されており、ソースコードの取得(チェックアウト)にhgコマンドが使用されていました。
  7. PATH環境変数: オペレーティングシステムが実行可能ファイルを探すディレクトリのリストです。コマンドラインでプログラム名を入力した際に、OSはこのPATHリストを順に検索して該当する実行ファイルを見つけます。
  8. Google Code: かつてGoogleが提供していたオープンソースプロジェクトのホスティングサービスです。Goプロジェクトもここでホストされており、生成されたバイナリディストリビューションがアップロードされていました。
  9. exec.LookPath: Go言語のos/execパッケージに含まれる関数で、指定された実行可能ファイルがシステムのPATH環境変数内でどこにあるかを検索します。

技術的詳細

このコミットは、misc/dist/bindist.goファイルに以下の主要な変更を加えています。

  1. archive/zipの導入:

    • import ("archive/zip")が追加されました。
    • Windows向けのZIPファイル作成処理において、以前はb.run(work, "7z", "a", "-tzip", zip, "go")という形で外部の7zコマンドを呼び出していた箇所が、新しく定義されたmakeZip(zip, work)関数に置き換えられました。
    • makeZip関数は、archive/zipパッケージを使用して、指定されたディレクトリ(work)の内容をZIPファイル(targ)に圧縮します。filepath.Walkを使ってディレクトリツリーを走査し、各ファイルをzip.NewWriterを通じてZIPアーカイブに追加しています。特に、Windowsのパス区切り文字\をUnixスタイルの/に変換する処理が含まれており、ZIPアーカイブ内のパスの互換性を確保しています。
  2. Windows依存関係の自動検出:

    • checkWindowsDeps()関数が新しく追加されました。この関数は、GoのWindowsビルドに必要な外部ツール(gcc, heat, candle, light, cmd, hg)がシステムに存在するかどうかをチェックします。
    • windowsDepsというmap[string]tool型のグローバル変数が定義され、各ツール名とそのツールに関する情報(ヘルプURL、一般的なインストールディレクトリのリスト)が格納されています。
    • lookPath(prog string)関数が追加されました。これはexec.LookPathのラッパーであり、もしexec.LookPathでツールが見つからなかった場合でも、windowsDepsに定義されたcommonDirs(一般的なインストールディレクトリ)を探索し、実行可能ファイルを見つけようとします。見つかった場合は、そのディレクトリを一時的にPATH環境変数に追加します。これにより、ユーザーが手動でPATHを設定していなくても、ツールが検出される可能性が高まります。
    • main関数内でruntime.GOOS == "windows"の場合にcheckWindowsDeps()が呼び出されるようになりました。これにより、Windows環境でのビルド開始前に必要な依存関係が揃っているかどうかが確認され、不足している場合はエラーメッセージが表示されます。
  3. --uploadフラグの追加:

    • upload = flag.Bool("upload", true, "upload resulting files to Google Code")という新しいコマンドラインフラグが追加されました。デフォルト値はtrueです。
    • main関数内で、*uploadtrueの場合にのみreadCredentials()(Google Codeへのアップロードに必要な認証情報を読み込む関数)が呼び出されるようになりました。
    • 同様に、ビルド完了後のファイルアップロード処理もif err == nil && *upload { ... }という条件で囲まれ、--upload=falseが指定された場合はアップロード処理がスキップされるようになりました。
  4. 詳細出力 (-vフラグ):

    • verbose = flag.Bool("v", false, "verbose output")というフラグが追加されました。
    • run関数内で、*verbosetrueの場合、実行されるコマンドとその引数がログに出力されるようになりました。これにより、デバッグや問題の特定が容易になります。
  5. misc/dist/windows/README.txtの更新:

    • 7zipの依存関係がREADMEから削除されました。これは、内部のarchive/zipパッケージを使用するようになったためです。

これらの変更により、GoのWindows向けディストリビューションビルドプロセスは、外部依存が減り、依存関係の検出が自動化され、アップロード機能の制御がより柔軟になりました。

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

このコミットのコアとなる変更は、主にmisc/dist/bindist.goファイルに集中しています。

  1. archive/zipパッケージのインポート:

    --- a/misc/dist/bindist.go
    +++ b/misc/dist/bindist.go
    @@ -7,6 +7,7 @@
     package main
     
     import (
    +	"archive/zip"
     	"bufio"
     	"bytes"
     	"encoding/base64"
    
  2. --uploadフラグと-vフラグの追加:

    --- a/misc/dist/bindist.go
    +++ b/misc/dist/bindist.go
    @@ -26,8 +27,10 @@ import (
     )
     
     var (
    -	tag  = flag.String("tag", "weekly", "mercurial tag to check out")
    -	repo = flag.String("repo", "https://code.google.com/p/go", "repo URL")
    +	tag     = flag.String("tag", "weekly", "mercurial tag to check out")
    +	repo    = flag.String("repo", "https://code.google.com/p/go", "repo URL")
    +	verbose = flag.Bool("v", false, "verbose output")
    +	upload  = flag.Bool("upload", true, "upload resulting files to Google Code")
     
     	username, password string // for Google Code upload
     )
    
  3. Windows依存関係チェックと認証情報読み込みの条件化:

    --- a/misc/dist/bindist.go
    +++ b/misc/dist/bindist.go
    @@ -59,8 +62,14 @@ func main() {
     	if flag.NArg() == 0 {
     		flag.Usage()
     	}
    -	if err := readCredentials(); err != nil {
    -		log.Println("readCredentials:", err)
    +	if runtime.GOOS == "windows" {
    +		checkWindowsDeps()
    +	}
    +
    +	if *upload {
    +		if err := readCredentials(); err != nil {
    +			log.Println("readCredentials:", err)
    +		}
     	}
     	for _, targ := range flag.Args() {
     		var b Build
    
  4. WindowsでのZIP作成にmakeZip関数を使用:

    --- a/misc/dist/bindist.go
    +++ b/misc/dist/bindist.go
    @@ -208,7 +221,7 @@ func (b *Build) Do() error {
     	case "windows":
     		// Create ZIP file.
     		zip := filepath.Join(work, base+".zip")
    -		_, err = b.run(work, "7z", "a", "-tzip", zip, "go")
    +		err = makeZip(zip, work)
     		// Copy zip to target file.
     		targ := base + ".zip"
     		err = cp(targ, zip)
    
  5. アップロード処理の条件化:

    --- a/misc/dist/bindist.go
    +++ b/misc/dist/bindist.go
    @@ -260,7 +273,7 @@ func (b *Build) Do() error {
     		err = cp(targ, msi)
     		targs = append(targs, targ)
     	}
    -	if err == nil && password != "" {
    +	if err == nil && *upload {
     		for _, targ := range targs {
     			err = b.upload(version, targ)
     			if err != nil {
    
  6. run関数の詳細出力とlookPathの利用:

    --- a/misc/dist/bindist.go
    +++ b/misc/dist/bindist.go
    @@ -273,9 +286,18 @@ func (b *Build) Do() error {
     
     func (b *Build) run(dir, name string, args ...string) ([]byte, error) {
     	buf := new(bytes.Buffer)
    -	cmd := exec.Command(name, args...)
    -	cmd.Stdout = buf
    -	cmd.Stderr = buf
    +	absName, err := lookPath(name)
    +	if err != nil {
    +		return nil, err
    +	}
    +	cmd := exec.Command(absName, args...)
    +	var output io.Writer = buf
    +	if *verbose {
    +		log.Printf("Running %q %q", absName, args)
    +		output = io.MultiWriter(buf, os.Stdout)
    +	}
    +	cmd.Stdout = output
    +	cmd.Stderr = output
     	cmd.Dir = dir
     	cmd.Env = b.env()
     	if err := cmd.Run(); err != nil {\
    
  7. makeZip関数の追加:

    --- a/misc/dist/bindist.go
    +++ b/misc/dist/bindist.go
    @@ -471,3 +493,125 @@ func cp(dst, src string) error {
     	_, err = io.Copy(df, sf)
     	return err
     }\n+\n+func makeZip(targ, workdir string) error {\n+	f, err := os.Create(targ)\n+	if err != nil {\n+		return err\n+	}\n+	zw := zip.NewWriter(f)\n+\n+	filepath.Walk(workdir, filepath.WalkFunc(func(path string, fi os.FileInfo, err error) error {\n+		if fi.IsDir() {\n+			return nil\n+		}\n+		if !strings.HasPrefix(path, workdir) {\n+			log.Panicf("walked filename %q doesn't begin with workdir %q", path, workdir)\n+		}\n+		name := path[len(workdir):]\n+\n+		// Convert to Unix-style named paths, as that's the\n+		// type of zip file that archive/zip creates.\n+		name = strings.Replace(name, "\\\\", "/", -1)\n+		// Chop of any leading / from filename, leftover from removing workdir.\n+		if strings.HasPrefix(name, "/") {\n+			name = name[1:]\n+		}\n+		// Don't include things outside of the go subdirectory (for instance,\n+		// the zip file that we're currently writing here.)\n+		if !strings.HasPrefix(name, "go/") {\n+			return nil\n+		}\n+		if *verbose {\n+			log.Printf("adding to zip: %s", name)\n+		}\n+		fh, err := zip.FileInfoHeader(fi)\n+		if err != nil {\n+			return err\n+		}\n+		fh.Name = name\n+		fh.Method = zip.Deflate\n+		w, err := zw.CreateHeader(fh)\n+		if err != nil {\n+			return err\n+		}\n+		r, err := os.Open(path)\n+		if err != nil {\n+			return err\n+		}\n+		defer r.Close()\n+		_, err = io.Copy(w, r)\n+		return err\n+	}))\n+\n+	if err := zw.Close(); err != nil {\n+		return err\n+	}\n+	return f.Close()\n+}\n+\n+type tool struct {\n+	name       string\n+	commonDirs []string\n+}\n+\n+var wixTool = tool{\n+	"http://wix.sourceforge.net/, version 3.5",\n+	[]string{`C:\\Program Files\\Windows Installer XML v3.5\\bin`,\n+		`C:\\Program Files (x86)\\Windows Installer XML v3.5\\bin`},\n+}\n+\n+var hgTool = tool{\n+	"http://mercurial.selenic.com/wiki/WindowsInstall",\n+	[]string{`C:\\Program Files\\Mercurial`,\n+		`C:\\Program Files (x86)\\Mercurial`,\n+	},\n+}\n+\n+var gccTool = tool{\n+	"Mingw gcc; http://sourceforge.net/projects/mingw/files/Installer/mingw-get-inst/",\n+	[]string{`C:\\Mingw\\bin`},\n+}\n+\n+var windowsDeps = map[string]tool{\n+	"gcc":    gccTool,\n+	"heat":   wixTool,\n+	"candle": wixTool,\n+	"light":  wixTool,\n+	"cmd":    {"Windows cmd.exe", nil},\n+	"hg":     hgTool,\n+}\n+\n+func checkWindowsDeps() {\n+	for prog, help := range windowsDeps {\n+		absPath, err := lookPath(prog)\n+		if err != nil {\n+			log.Fatalf("Failed to find necessary binary %q in path or common locations; %s", prog, help)\n+		}\n+		if *verbose {\n+			log.Printf("found windows dep %s at %s", prog, absPath)\n+		}\n+	}\n+}\n+\n+func lookPath(prog string) (absPath string, err error) {\n+	absPath, err = exec.LookPath(prog)\n+	if err == nil {\n+		return\n+	}\n+	t, ok := windowsDeps[prog]\n+	if !ok {\n+		return\n+	}\n+	for _, dir := range t.commonDirs {\n+		for _, ext := range []string{"exe", "bat"} {\n+			absPath = filepath.Join(dir, prog+"."+ext)\n+			if _, err1 := os.Stat(absPath); err1 == nil {\n+				err = nil\n+				os.Setenv("PATH", os.Getenv("PATH")+";"+dir)\n+				return\n+			}\n+		}\n+	}\n+	return\n+}\n```
    
    
  8. misc/dist/windows/README.txtからの7zipの削除:

    --- a/misc/dist/windows/README.txt
    +++ b/misc/dist/windows/README.txt
    @@ -4,7 +4,6 @@ Windows build dependencies
     - Mercurial (hg): http://mercurial.selenic.com/\n - MinGW: http://www.mingw.org/\n - Windows Installer XML (WiX) toolset: http://wix.sourceforge.net/\n-- 7zip\n \n Packaging
    

コアとなるコードの解説

このコミットの核となるのは、Goの標準ライブラリであるarchive/zipパッケージを導入し、Windows環境でのZIPアーカイブ作成に外部ツール7zへの依存をなくした点、そしてWindowsビルドに必要な外部依存関係を自動的に検出・解決するメカニズムを導入した点です。

  1. makeZip関数:

    • この関数は、Goのarchive/zipパッケージを使用して、指定されたworkdir(作業ディレクトリ)の内容をtarg(ターゲットZIPファイル)に圧縮します。
    • filepath.Walkは、workdir以下のすべてのファイルとディレクトリを再帰的に走査します。
    • 各ファイルについて、zip.FileInfoHeaderを使ってファイル情報からZIPヘッダーを作成し、zw.CreateHeaderでZIPアーカイブ内にエントリを作成します。
    • 特に重要なのは、name = strings.Replace(name, "\\\\", "/", -1)の部分です。Windowsのパス区切り文字であるバックスラッシュ\を、ZIPアーカイブ内で一般的に使用されるUnixスタイルのスラッシュ/に変換しています。これにより、生成されるZIPファイルのクロスプラットフォーム互換性が向上します。
    • if !strings.HasPrefix(name, "go/")のチェックは、ZIPアーカイブにgo/ディレクトリ以下のファイルのみを含めるためのものです。これは、Goのディストリビューションが通常goというルートディレクトリを持つためです。
    • io.Copy(w, r)でファイルの内容をZIPエントリにコピーしています。
  2. checkWindowsDeps関数とlookPath関数:

    • checkWindowsDepsは、windowsDepsマップに定義された各ツール(gcc, heat, candle, light, cmd, hg)について、lookPath関数を呼び出してシステムに存在するかを確認します。
    • lookPath関数は、まずexec.LookPath(prog)を呼び出し、システムのPATH環境変数からツールを探します。
    • もしexec.LookPathで見つからなかった場合、windowsDepsマップからそのツールのcommonDirs(一般的なインストールディレクトリのリスト)を取得します。
    • commonDirs内の各ディレクトリと、一般的な実行可能ファイルの拡張子(.exe, .bat)を組み合わせて、ツールのフルパスを構築し、os.Statでファイルが存在するかを確認します。
    • ツールが見つかった場合、そのツールのディレクトリを現在のPATH環境変数に追加します(os.Setenv("PATH", os.Getenv("PATH")+";"+dir))。これにより、後続のexec.Command呼び出しでツールが正しく見つけられるようになります。
    • このメカニズムにより、ユーザーが手動でPATHを設定していなくても、GoのビルドツールがWindowsの依存関係を自動的に見つけ出し、ビルドプロセスをよりスムーズに進めることができます。
  3. --uploadフラグ:

    • このフラグは、Goのディストリビューションツールが生成したファイルをGoogle Codeにアップロードするかどうかを制御します。
    • *uploadというブール値のフラグによって、認証情報の読み込みと実際のアップロード処理が条件付けられています。これにより、開発者がローカルでパッケージを生成するだけでよい場合など、アップロードが不要なシナリオで、不要な認証情報の入力をスキップできるようになりました。

これらの変更は、GoのビルドシステムがWindows環境でより堅牢に、かつユーザーフレンドリーに動作するための重要な改善です。

関連リンク

参考にした情報源リンク

  • Go Change-Id: I2ae860585920e17c1d43098c476ffb11c21b35f8 (Gerrit Change-Id): https://go-review.googlesource.com/c/go/+/5794046 (これはコミットメッセージに記載されているhttps://golang.org/cl/5794046の新しいURL形式です。GoプロジェクトはGerritというコードレビューシステムを使用しており、各変更には一意のChange-Idが割り当てられます。)
  • Goのmisc/distディレクトリの目的に関する一般的な情報 (Goのソースコードリポジトリ): https://github.com/golang/go/tree/master/misc/dist
  • GoのWindowsビルドに関する一般的な情報 (Goの公式ドキュメントやWiki): https://go.dev/doc/install/source (Goのソースからのインストールに関するドキュメント。Windows固有の依存関係についても言及されることがあります。)
  • 7-Zip 公式サイト: https://www.7-zip.org/
  • Google Code (アーカイブ): https://code.google.com/archive/ (Google Codeはサービス終了しており、現在はアーカイブされています。)