- 投稿日:2020-06-04T22:42:57+09:00
Golang環境整える。Windows(WSL2、WSL)、Mac、Docker、VSCode(+Remote Docker)Golint・・・
motivation
今、仕事にメインで使ってるPCが3台あり、それぞれの環境が異なります
デスクトップ:Windows10 Pro InsidePreview + WSL2
RazerBlade ゲーミングPC: Windows10 Pro + WSL
Macbook Pro: Catalina
また、近々、ゲーミングPCの買い替え、デスクトップの再インストール等を予定していて
それぞれの環境構築を色々と試した結果、現在の環境構築のメモ一応、WSLはUbuntu18.04 を想定してますが、他のLinuxでもだいたい同じはず
完全に自分用です
Windows: WSL&Docker
Goはサーバインフラで使われることが多いので、たいていの場合はDockerが必要かと思います
また、仕事では他の人はBashでスクリプトを書いてくると思います
そのため、Bashはほぼ必須といってよいです
もちろん古風なVirtualBoxにLinuxをインストールしてもいいですが
WSLやWSL2を使う方が現代的で好みですWSL、WSL2、Dockerまわりの環境構築は下記にメモしてあります
https://qiita.com/YukiMiyatake/items/c7896a0fc5abfa6c2300
https://qiita.com/YukiMiyatake/items/73c7d6c4f2c9739ebe60Goのインストール
公式の通りに行います
https://golang.org/doc/install以前はgoenv等のバージョニングシステムを使っていたのですが
なぜかVSCode等の相性がわるく(goenvが設定した$GOPATH等を見てくれない事がある)
使うのをやめました。しかも公式でバージョニングの方法も書いてありました
インストール
まずはGoがインストールされていたら消します
その後、インストールしますが、パッケージからインストールするとバージョンが古いため
公式が配布しているイメージからインストールします
公式ページには下記のスクリプトが書いてありますtar -C /usr/local -xzf go$VERSION.$OS-$ARCH.tar.gzOSや取得バージョンに合わせてダウンロードファイルを変更しますが
具体的には下記のようにします(Goのバージョンは現時点で最新の 1.14.3)Windows(WSL、WSL2)wget https://dl.google.com/go/go1.14.3.linux-amd64.tar.gz sudo tar -C /usr/local -xzf go1.14.3.linux-amd64.tar.gzMacwget https://dl.google.com/go/go1.14.3.darwin-amd64.tar.gz sudo tar -C /usr/local -xzf go1.14.3.darwin-amd64.tar.gzこれで、
/usr/local/go
にインストールされる。念のため
go version
で正しくインストールされているか確認を行い
go env
で、GOの環境変数を確認しましょうおそらく、
GOROOT="/usr/local/go"
GOPATH="$HOME/go"
になっているはずGOROOTの方にGoのバイナリが入り、GOPATHにはダウンロードしたパッケージやバイナリが入る
ので、ここにパスを通しておくたとえば.bashrc等に
bash: bashrc
export GOPATH="$HOME/go"
export PATH=$PATH:$GOPATH/bin
export PATH=$PATH:/usr/local/go/bin
と書く(GOPATHはおそらくデフォルトで指定されてるが念のためかいてる)
$GOPATH/binにも通しておくバージョニング
公式によると
go get golang.org/dl/go1.10.7 go1.10.7 downloadgo getで任意のバージョンをインストールし
go1.10.7 versionで、使うバージョンを指定できるらしい。
非常に便利だただし
$HOME/go/bin
やsrcが同じなため、複数バージョンのファイルが混在する事になるので
以前はGoのバージョンごとに $GOPATHを切り替えていたが
他の人の話をきくかぎり、混在しても問題がおきた事がないとの話なので
GOPATHをわけない方針に変更したgcc
GoのパッケージにはGCCを使うものも多いためGCCをインストールする
Clang派ではあるが、手軽にaptやbrewを使ってGCCをインストールするmakeのインストール
GoのプロジェクトはMakeで行う事が多いので
パッケージマネージャを使いMakeをインストールしておくVSCode
VSCodeのインストールはWindowsもMacもパッケージでインストールした
リモートWSL: Windows
MacではデフォルトでBashになっていて、ネイティブにGoをインストールしているので対応なくてよい
Windowsは今回はWSL上で動かす
リモートWSLを使うと、WSL上でそのままデバッグしたり実行したり非常に便利なので
VSCode+リモートWSLをお勧めするWSL2ではなぜか、VSCodeが初期状態でリモートWSLが有効になっていた気がするが・・
左下にWSLアイコンが出ない場合は、VSCode上からリモートWSLをインストールしようリモートWSLでプロジェクトを開く
必ずリモートWSLで開くこと!
左下のWSLボタンを押し、Remote-WSL:NewWindowで新規ウインドウを開き、OpenFolderする
気を付けるのは、この時にWSLのパスを入力する事。
WindowsパスやShowLocalで選ぶと、リモートWSLにならないプラグイン(Extensions)導入
VSCodeのExtensionsメニューより、とりあえずGoプラグインをインストールする
その後、Ctrl+Shift+Pでコマンドパレットを開き
GO:Install/Update Tools
を選択必要なものにチェックボックスをつけ、インストールする
$ ls $GOPATH/bin dlv go-outline gocode godef gofmt golint gopkgs gorename impl fillstruct go-symbols gocode-gomod godoctor goimports gomodifytags goplay gotest guruこのように$GOPATH/binにインストールされる
ビルド、デバッグ実行、テスト等
適当なプロジェクトを使い
まずはVSCodeのターミナルからテストgo buid . ./実行ファイル go test .VSCodeからデバッグ実行するために、ブレークポイントをおきF5キー押しデバッグ出来る事の確認
動かない場合は Launch.jsonを修正する例えば、プロジェクトに
/server
/client
の2つのプロジェクトがある場合は{ // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "name": "Client", "type": "go", "request": "launch", "mode": "debug", "program": "${workspaceFolder}/client" }, { "name": "Server", "type": "go", "request": "launch", "mode": "debug", "program": "${workspaceFolder}/server" } ] }こんな感じで書いておく。細かい構文は調べてね・・
単体テストの確認
Goファイルの関数を右クリックして、コンテキストメニューに
Go:Generate Unit Tests For Function
がある事を確認し、クリックすると単体テストコードを自動生成してくれる
そして、テスト側のコードに移動し、関数の上にrun test | debug test
が表示される
それをクリックし、関数単位でテストの実行、デバッグの実行が出来る事を確認するExtensionsで、Go Test Explorerをインストールする。
UIを使って個別にテストを実行したり、Faildの確認したり出来て多少便利だその他パッケージ
あまりパッケージに詳しくないため、便利なパッケージを日々探している
単純に出力が色分けされるだけで快適になったりするgrpc-go
gRPCを使う場合には必須になる
go get -u google.golang.org/grpc
colorgo
go buildの出力がカラフルになる
go get -u github.com/songgao/colorgo
gotests
go testの出力がカラフルになる
go get -u github.com/cweill/gotests/...
gocheck
go testより高機能
go get -u gopkg.in/check.v1
sql-migrate
DBのマイグレーションツール
go get -u github.com/rubenv/sql-migrate/...
mockgen
インターフェースモックを作成
go get -u github.com/golang/mock/mockgen
- 投稿日:2020-06-04T20:56:28+09:00
GOプログラムの起動手順について
問題
GO のプログラムは
main
という package のmain()
方法で始めるのがよくご存知でしたが、その前に何が起こったのかな?これをちょっと調べて行きます。実行環境
- Ubuntu 18.04
- Go 1.14
もっともシンプルな GO プログラム
まずはこの何もしないプログラムを用意します
main.gopackage main func main() {}$ go build main.go
main.main
を探すELFを調べてみる
まずはコンパイルされたバイナリを
readelf
でみてみま$ readelf -h main ELF Header: Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) Machine: Advanced Micro Devices X86-64 Version: 0x1 Entry point address: 0x4552c0 Start of program headers: 64 (bytes into file) Start of section headers: 456 (bytes into file) Flags: 0x0 Size of this header: 64 (bytes) Size of program headers: 56 (bytes) Number of program headers: 7 Size of section headers: 64 (bytes) Number of section headers: 25 Section header string table index: 3その中の
Entry point address
は実行の入口です、そのアドレスは0x4552c0
。じゃ次はこのアドレスを見てみよう。$ objdump -d main | grep 4552c0 00000000004552c0 <_rt0_amd64_linux>: 4552c0: e9 4b c4 ff ff jmpq 451710 <_rt0_amd64>
0x4552c0
のとこは_rt0_amd64_linux
という関数ですね、そしてすぐ_rt0_amd64
に移行するようです。
_rt0_amd64
はどこ?ソースコードに
_rt0_amd64
を検索したら、asm_amd64.s
に見つけました。src/runtime/asm_amd64.sTEXT _rt0_amd64(SB),NOSPLIT,$-8 MOVQ 0(SP), DI // argc LEAQ 8(SP), SI // argv JMP runtime·rt0_go(SB)そしてすぐ
runtime·rt0_go
にジャンプしました。もうちょっと追ってみれば、これを見つけたsrc/runtime/asm_amd64.s... MOVL 16(SP), AX // copy argc MOVL AX, 0(SP) MOVQ 24(SP), AX // copy argv MOVQ AX, 8(SP) CALL runtime·args(SB) CALL runtime·osinit(SB) CALL runtime·schedinit(SB) // create a new goroutine to start program MOVQ $runtime·mainPC(SB), AX // entry PUSHQ AX PUSHQ $0 // arg size CALL runtime·newproc(SB) POPQ AX POPQ AX // start this M CALL runtime·mstart(SB) ... DATA runtime·mainPC+0(SB)/8,$runtime·main(SB) GLOBL runtime·mainPC(SB),RODATA,$8 ...
runtime·args
、runtime·osinit
とruntime·schedinit
を次々CALL
してから、runtime·mainPC
を引数としてruntime·newproc
をCALL
しました。このentry
で注釈したruntime·mainPC
実はruntime·main
として定義したものです。
runtime.main
とruntime.newproc
またソースに
newproc
を検索したら、proc.go
にたどり着いたsrc/runtime/proc.go... // Create a new g running fn with siz bytes of arguments. // Put it on the queue of g's waiting to run. // The compiler turns a go statement into a call to this. // // The stack layout of this call is unusual: it assumes that the // arguments to pass to fn are on the stack sequentially immediately // after &fn. Hence, they are logically part of newproc's argument // frame, even though they don't appear in its signature (and can't // because their types differ between call sites). // // This must be nosplit because this stack layout means there are // untyped arguments in newproc's argument frame. Stack copies won't // be able to adjust them and stack splits won't be able to copy them. // //go:nosplit func newproc(siz int32, fn *funcval) { ...コメントによると、
runtime.newproc
は一つの goroutine を起動してfn
を実行するということですね。そしてここのfn
は先のruntime.main
のようです。次はruntime.main
を見てみましょ。src/runtime/proc.go... // The main goroutine. func main() { g := getg() ... fn := main_main // make an indirect call, as the linker doesn't know the address of the main package when laying down the runtime fn() ...
runtime.main
はいろんなことをやっていて、main_main
をコールした。このmain_main
はもしかして。。。src/runtime/proc.go... //go:linkname main_main main.main func main_main() ...そうか、ここは
go:linkname
という Compiler Directive に通してmain_main
をmain.main
に変換しました。まとめ
- GO プログラムのほんとのエントリーポイントは
_rt0_amd64_linux
です(Linuxの場合)。- いろんな初期化は
xxxinit
でやっています。特にruntime.schedinit
。main.main
の実行も goroutine の一つです参考資料