ソフトウェア開発では、バイナリをコンパイルする対象のoperating systemと基盤となるプロセッサarchitectureを考慮することが重要です。 多くの場合、異なるOS /アーキテクチャプラットフォームでバイナリを実行するのは遅いか不可能であるため、プログラムの視聴者を最大化するために、多くの異なるプラットフォームで最終的なバイナリを構築するのが一般的です。 ただし、開発に使用しているプラットフォームがプログラムのデプロイ先のプラットフォームと異なる場合、これは困難な場合があります。 過去には、たとえば、Windowsでプログラムを開発してLinuxまたはmacOSマシンに展開するには、バイナリが必要な環境ごとにビルドマシンをセットアップする必要がありました。 また、コストを追加し、共同テストと配布をより困難にする他の考慮事項に加えて、ツールの同期を維持する必要があります。
Goは、複数のプラットフォームのサポートをgo buildツールと、残りのGoツールチェーンに直接組み込むことでこの問題を解決します。 environment variablesとbuild tagsを使用することで、コードベースを変更せずにプラットフォームに依存するコードの包含をすばやく切り替えることができるワークフローをまとめることに加えて、最終的なバイナリを構築するOSとアーキテクチャを制御できます。 。
このチュートリアルでは、stringsをファイルパスに結合するサンプルアプリケーションをまとめ、プラットフォームに依存するスニペットを作成して選択的に含め、独自のシステムで複数のオペレーティングシステムとシステムアーキテクチャのバイナリを構築し、その方法を示します。 Goプログラミング言語のこの強力な機能を使用します。
前提条件
この記事の例を実行するには、次のものが必要です。
-
How To Install Go and Set Up a Local Programming Environmentに従ってセットアップされたGoワークスペース。
GOOSおよびGOARCHの可能なプラットフォーム
ビルドプロセスを制御してさまざまなプラットフォームのバイナリをビルドする方法を示す前に、まずGoがビルドできるプラットフォームの種類と、環境変数GOOSおよびGOARCHを使用してGoがこれらのプラットフォームを参照する方法を調べます。 。
Goツールには、Goが構築できるプラットフォームのリストを印刷できるコマンドがあります。 このリストは、Goの新しいリリースごとに変更される可能性があるため、ここで説明する組み合わせは、Goの別のバージョンでは同じではない場合があります。 このチュートリアルを書いている時点では、現在のGoリリースは1.13です。
可能なプラットフォームのこのリストを見つけるには、次を実行します。
go tool dist list
次のような出力が表示されます。
Outputaix/ppc64 freebsd/amd64 linux/mipsle openbsd/386
android/386 freebsd/arm linux/ppc64 openbsd/amd64
android/amd64 illumos/amd64 linux/ppc64le openbsd/arm
android/arm js/wasm linux/s390x openbsd/arm64
android/arm64 linux/386 nacl/386 plan9/386
darwin/386 linux/amd64 nacl/amd64p32 plan9/amd64
darwin/amd64 linux/arm nacl/arm plan9/arm
darwin/arm linux/arm64 netbsd/386 solaris/amd64
darwin/arm64 linux/mips netbsd/amd64 windows/386
dragonfly/amd64 linux/mips64 netbsd/arm windows/amd64
freebsd/386 linux/mips64le netbsd/arm64 windows/arm
この出力は、/で区切られたキーと値のペアのセットです。 /の前の組み合わせの最初の部分は、オペレーティングシステムです。 Goでは、これらのオペレーティングシステムは、環境変数GOOSの可能な値であり、Go Operating Systemを表す「goose」と発音されます。 /の後の2番目の部分は、アーキテクチャーです。 前と同じように、これらはすべて環境変数の可能な値です:GOARCH。 これは「gore-ch」と発音され、Go Architectureを表します。
例としてlinux/386を使用して、これらの組み合わせの1つを分解して、それが何を意味し、どのように機能するかを理解しましょう。 キーと値のペアはGOOSで始まります。この例では、Linux OSを参照してlinuxになります。 ここでのGOARCHは386になり、これはIntel 80386 microprocessorを表します。
go buildコマンドで使用できるプラットフォームは多数ありますが、ほとんどの場合、linux、windows、またはdarwinを%(の値として使用することになります。 t4)s。 これらは、大きな3つのOSプラットフォーム(Linux、Windows、およびmacOS)をカバーします。これらは、Darwin operating systemに基づいているため、darwinと呼ばれます。 ただし、Goは、Google’s Native Clientを表すnaclのようなあまり主流ではないプラットフォームもカバーできます。
go buildのようなコマンドを実行すると、Goは現在のプラットフォームのGOOSとGOARCHを使用して、バイナリの構築方法を決定します。 プラットフォームの組み合わせを確認するには、go envコマンドを使用して、GOOSとGOARCHを引数として渡すことができます。
go env GOOS GOARCH
この例のテストでは、AMD64 architectureのマシン上のmacOSでこのコマンドを実行したため、次の出力が表示されます。
Outputdarwin
amd64
ここで、コマンドの出力は、システムにGOOS=darwinとGOARCH=amd64があることを示しています。
これで、GoのGOOSとGOARCHの内容、およびそれらの可能な値がわかりました。 次に、これらの環境変数を使用して他のプラットフォーム用のバイナリをビルドするタグをビルドする方法の例として使用するプログラムを作成します。
filepath.Join()を使用してプラットフォーム依存プログラムを作成する
他のプラットフォーム用のバイナリの作成を開始する前に、サンプルプログラムを作成しましょう。 この目的に適したサンプルは、Go標準ライブラリのpath/filepathパッケージにあるJoin関数です。 この関数はいくつかの文字列を受け取り、正しいファイルパス区切り文字で結合された1つの文字列を返します。
プログラムの動作は実行しているOSに依存するため、これは良いプログラム例です。 Windowsでは、パス区切り文字は円記号\ですが、Unixベースのシステムは円記号/を使用します。
filepath.Join()を使用するアプリケーションの構築から始めましょう。その後、プラットフォーム固有のバイナリに合わせてコードをカスタマイズするJoin()関数の独自の実装を作成します。
まず、srcディレクトリにアプリの名前でフォルダを作成します。
mkdir app
そのディレクトリに移動します。
cd app
次に、選択したテキストエディタでmain.goという名前の新しいファイルを作成します。 このチュートリアルでは、Nanoを使用します。
nano main.go
ファイルが開いたら、次のコードを追加します。
src/app/main.go
package main
import (
"fmt"
"path/filepath"
)
func main() {
s := filepath.Join("a", "b", "c")
fmt.Println(s)
}
このファイルのmain()関数は、filepath.Join()を使用して、プラットフォームに依存する正しいパス区切り文字とともに3つのstringsを連結します。
ファイルを保存して終了し、プログラムを実行します。
go run main.go
このプログラムを実行すると、使用しているプラットフォームに応じて異なる出力を受け取ります。 Windowsでは、\で区切られた文字列が表示されます。
Outputa\b\c
macOSやLinuxなどのUnixシステムでは、次のものを受け取ります。
Outputa/b/c
これは、これらのオペレーティングシステムで使用されるファイルシステムプロトコルが異なるため、プログラムはプラットフォームごとに異なるコードを構築する必要があることを示しています。 ただし、OSに応じてすでに異なるファイル区切り文字を使用しているため、filepath.Join()がプラットフォームの違いをすでに説明していることがわかります。 これは、GoツールチェーンがマシンのGOOSとGOARCHを自動的に検出し、この情報を使用して、適切なbuild tagsとファイルセパレータを備えたコードスニペットを使用するためです。
filepath.Join()関数がセパレータをどこから取得するかを考えてみましょう。 次のコマンドを実行して、Goの標準ライブラリから関連するスニペットを検査します。
less /usr/local/go/src/os/path_unix.go
これにより、path_unix.goの内容が表示されます。 ファイルの次の部分を探します。
/usr/local/go/os/path_unix.go
. . .
// +build aix darwin dragonfly freebsd js,wasm linux nacl netbsd openbsd solaris
package os
const (
PathSeparator = '/' // OS-specific path separator
PathListSeparator = ':' // OS-specific path list separator
)
. . .
このセクションでは、Goがサポートするすべての種類のUnixライクなシステムのPathSeparatorを定義します。 上部にあるすべてのビルドタグに注目してください。これらはそれぞれ、Unixに関連付けられている可能性のあるUnixGOOSプラットフォームの1つです。 GOOSがこれらの用語に一致すると、プログラムはUnixスタイルのファイルパスセパレータを生成します。
qを押して、コマンドラインに戻ります。
次に、Windowsで使用した場合のfilepath.Join()の動作を定義するファイルを開きます。
less /usr/local/go/src/os/path_windows.go
以下が表示されます。
/usr/local/go/os/path_unix.go
. . .
package os
const (
PathSeparator = '\\' // OS-specific path separator
PathListSeparator = ';' // OS-specific path list separator
)
. . .
ここではPathSeparatorの値は\ですが、最初の円記号はエスケープ文字としてのみ必要であるため、コードはWindowsファイルパスに必要な単一の円記号(\)をレンダリングします。
Unixファイルとは異なり、上部にビルドタグがないことに注意してください。 これは、ファイル名の接尾辞としてアンダースコア(_)と環境変数値を追加することで、GOOSとGOARCHをgo buildに渡すこともできるためです。セクションUsing GOOS and GOARCH File Name Suffixesで詳細をご覧ください。 ここで、path_windows.goの_windows部分は、ファイルの先頭にビルドタグ// +build windowsがあるかのようにファイルを動作させます。 このため、プログラムをWindowsで実行すると、path_windows.goコードスニペットのPathSeparatorとPathListSeparatorの定数が使用されます。
コマンドラインに戻るには、qを押してlessを終了します。
このステップでは、GoがGOOSとGOARCHを自動的にビルドタグに変換する方法を示すプログラムをビルドしました。 これを念頭に置いて、ビルドタグを使用してWindowsおよびUnixプラットフォーム用の正しいPathSeparatorを手動で設定し、プログラムを更新してfilepath.Join()の独自の実装を作成できるようになりました。
プラットフォーム固有の機能の実装
Goの標準ライブラリがプラットフォーム固有のコードを実装する方法がわかったので、ビルドタグを使用して、独自のappプログラムでこれを実行できます。 これを行うには、filepath.Join()の独自の実装を作成します。
main.goファイルを開きます。
nano main.go
Join()という独自の関数を使用して、main.goの内容を次のように置き換えます。
src/app/main.go
package main
import (
"fmt"
"strings"
)
func Join(parts ...string) string {
return strings.Join(parts, PathSeparator)
}
func main() {
s := Join("a", "b", "c")
fmt.Println(s)
}
Join関数は、いくつかのpartsを受け取り、strings packageからstrings.Join()メソッドを使用してそれらを結合し、PathSeparatorを使用してpartsを連結します。 s。
PathSeparatorをまだ定義していないので、別のファイルで定義します。 main.goを保存して終了し、お気に入りのエディターを開いて、path.goという名前の新しいファイルを作成します。
nano path.go
PathSeparatorを定義し、Unixファイルパスセパレーター/と等しくなるように設定します。
src/app/path.go
package main
const PathSeparator = "/"
アプリケーションをコンパイルして実行します。
go build
./app
次の出力が表示されます。
Outputa/b/c
これは正常に実行され、Unixスタイルのファイルパスを取得します。 しかし、これはまだ私たちが望んでいることではありません。出力は、実行されているプラットフォームに関係なく、常にa/b/cです。 Windowsスタイルのファイルパスを作成する機能を追加するには、WindowsバージョンのPathSeparatorを追加し、使用するバージョンをgo buildコマンドに指示する必要があります。 次のセクションでは、build tagsを使用してこれを実現します。
GOOSまたはGOARCHビルドタグの使用
Windowsプラットフォームを説明するために、path.goの代替ファイルを作成し、ビルドタグを使用して、GOOSとGOARCHが適切なプラットフォームである場合にのみコードスニペットが実行されるようにします。
ただし、最初に、ビルドタグをpath.goに追加して、Windows以外のすべてに対してビルドするように指示します。 ファイルを開きます。
nano path.go
次の強調表示されたビルドタグをファイルに追加します。
src/app/path.go
// +build !windows
package main
const PathSeparator = "/"
Goビルドタグは反転を許可します。つまり、Windows以外のプラットフォーム用にこのファイルをビルドするようにGoに指示できます。 ビルドタグを反転するには、タグの前に!を配置します。
ファイルを保存して終了します。
このプログラムをWindowsで実行すると、次のエラーが表示されます。
Output./main.go:9:29: undefined: PathSeparator
この場合、Goは変数PathSeparatorを定義するためにpath.goを含めることができません。
GOOSがWindowsの場合にpath.goが実行されないことを確認したので、新しいファイルwindows.goを追加します。
nano windows.go
windows.goで、WindowsPathSeparatorと、go buildコマンドにWindows実装であることを知らせるビルドタグを定義します。
src/app/windows.go
// +build windows
package main
const PathSeparator = "\\"
ファイルを保存し、テキストエディターを終了します。 これで、アプリケーションはWindows用に別の方法でコンパイルし、他のすべてのプラットフォーム用に別の方法でコンパイルできます。
バイナリはプラットフォーム用に正しくビルドされるようになりましたが、アクセスできないプラットフォーム用にコンパイルするためにさらに変更する必要があります。 これを行うには、次の手順でローカルのGOOSおよびGOARCH環境変数を変更します。
ローカルのGOOSおよびGOARCH環境変数の使用
以前は、go env GOOS GOARCHコマンドを実行して、作業しているOSとアーキテクチャを確認しました。 go envコマンドを実行すると、2つの環境変数GOOSとGOARCHが検索されました。見つかった場合はそれらの値が使用されますが、見つからなかった場合はGoが現在のプラットフォームの情報を設定します。 これは、GOOSまたはGOARCHを変更して、デフォルトでローカルOSおよびアーキテクチャに設定されないようにすることができることを意味します。
go buildコマンドは、go envコマンドと同じように動作します。 GOOSまたはGOARCH環境変数のいずれかを設定して、go buildを使用して別のプラットフォーム用に構築できます。
Windowsシステムを使用していない場合は、go buildコマンドの実行時にGOOS環境変数をwindowsに設定して、appのwindowsバイナリを作成します。
GOOS=windows go build
次に、現在のディレクトリ内のファイルをリストします。
ls
ディレクトリを一覧表示する出力は、プロジェクトディレクトリにapp.exeWindows実行可能ファイルがあることを示しています。
Outputapp app.exe main.go path.go windows.go
fileコマンドを使用すると、このファイルに関する詳細情報を取得して、ビルドを確認できます。
file app.exe
受け取るもの:
Outputapp.exe: PE32+ executable (console) x86-64 (stripped to external PDB), for MS Windows
ビルド時に環境変数の一方または両方を設定することもできます。 以下を実行してください。
GOOS=linux GOARCH=ppc64 go build
これで、app実行可能ファイルが別のアーキテクチャのファイルに置き換えられます。 このバイナリでfileコマンドを実行します。
file app
次のような出力を受け取ります。
app: ELF 64-bit MSB executable, 64-bit PowerPC or cisco 7500, version 1 (SYSV), statically linked, not stripped
ローカルのGOOSおよびGOARCH環境変数を設定することで、複雑な構成やセットアップを行うことなく、Goの互換性のあるプラットフォーム用のバイナリを構築できるようになりました。 次に、ファイル名の規則を使用して、ファイルをきちんと整理し、特定のプラットフォーム用にビルドタグなしで自動的にビルドします。
GOOSおよびGOARCHのファイル名サフィックスの使用
前に見たように、Go標準ライブラリはビルドタグを多用し、異なるプラットフォーム実装を異なるファイルに分離することでコードを簡素化します。 os/path_unix.goファイルを開くと、Unixライクなプラットフォームと見なされるすべての可能な組み合わせをリストしたビルドタグがありました。 ただし、os/path_windows.goファイルにはビルドタグが含まれていませんでした。これは、ファイル名のサフィックスが、ファイルの対象となるプラットフォームをGoに伝えるのに十分だったためです。
この機能の構文を見てみましょう。 .goファイルに名前を付けるときは、ファイル名のサフィックスとしてGOOSとGOARCHをこの順序で追加し、値をアンダースコア(_)で区切ることができます。 filename.goという名前のGoファイルがある場合は、ファイル名をfilename_GOOS_GOARCH.goに変更することでOSとアーキテクチャを指定できます。 たとえば、64ビットのARM architectureを使用してWindows用にコンパイルする場合は、ファイルの名前をfilename_windows_arm64.goにします。 この命名規則は、コードをきちんと整理するのに役立ちます。
ビルドタグの代わりにファイル名の接尾辞を使用するようにプログラムを更新します。 まず、path.goファイルとwindows.goファイルの名前を変更して、osパッケージで使用されている規則を使用します。
mv path.go path_unix.go
mv windows.go path_windows.go
2つのファイル名を変更したら、path_windows.goに追加したビルドタグを削除できます。
nano path_windows.go
ファイルが次のようになるように// +build windowsを削除します。
path_windows.go
package main
const PathSeparator = "\\"
ファイルを保存して終了します。
unixは有効なGOOSではないため、_unix.goサフィックスはGoコンパイラにとって意味がありません。 ただし、ファイルの意図された目的は伝えます。 os/path_unix.goファイルと同様に、path_unix.goファイルでもビルドタグを使用する必要があるため、そのファイルは変更しないでください。
ファイル名の規則を使用することで、ソースコードから不要なビルドタグを削除し、ファイルシステムをよりクリーンで明確にしました。
結論
依存関係を必要としない複数のプラットフォーム用のバイナリを生成する機能は、Goツールチェーンの強力な機能です。 このチュートリアルでは、ビルドタグとファイル名の接尾辞を追加してこの機能を使用し、特定のコードスニペットをマークして特定のアーキテクチャ用にのみコンパイルします。 独自のplatorm依存プログラムを作成し、GOOSおよびGOARCH環境変数を操作して、現在のプラットフォーム以外のプラットフォーム用のバイナリを生成しました。 これは、これらの環境変数を自動的に実行してすべてのプラットフォームのバイナリを構築する継続的な統合プロセスを持つことが一般的であるため、貴重なスキルです。
go buildの詳細については、Customizing Go Binaries with Build Tagsチュートリアルをご覧ください。 Goプログラミング言語全般について詳しく知りたい場合は、How To Code in Go series全体を確認してください。