LoginSignup
8

More than 5 years have passed since last update.

posted at

Goのパッケージのinit( )はいつどういう順番で呼ばれるか

spf13/cobra をソースリーディングしていてメモっておきたかったので、
自分用のメモです。


パッケージ構成

$GOPATH
└── src
    └── myapp
        ├── main.go
        └── mymodule
            ├── aaa.go
            ├── root.go
            └── zzz.go
  • main.goは単純にmymoduleのExecute関数を実行しているだけ
  • mymoduleには、myListというパッケージローカルな変数があり、それはroot.goで初期化されている
  • パッケージには、aaa.go, root.go, zzz.go という3つのファイルがある。

ソース

main.go
package main

import (
  "fmt"
  "myapp/mymodule"
)

func main() {
  fmt.Println("main >")
  mymodule.Execute()
  fmt.Println("main <")
}
mymodule/root.go
package mymodule

import "fmt"

var myList = makeList()

func makeList() []string {
  fmt.Println("root/makeList")
  return []string{}
}

func init() {
  fmt.Println("root/init >")
  myList = append(myList, "root/init")
  fmt.Println("root/init <")
}

func Execute() {
  myList = append(myList, "root/Execute")
  fmt.Println("root/Execute")
}
mymodule/aaa.go
package mymodule

import "fmt"

func init() {
  fmt.Println("aaa/init >")
  myList = append(myList, "aaa/init")
  fmt.Println("aaa/init <")
}
mymodule/zzz.go
package mymodule

import "fmt"

func init() {
  fmt.Println("zzz/init >")
  myList = append(myList, "zzz/init")
  fmt.Println("zzz/init <")
}

Result ( ゚д゚)

$ go run main.go 
root/makeList
aaa/init >
aaa/init <
root/init >
root/init <
zzz/init >
zzz/init <
main >
root/Execute
main <

パッケージのインポート
  ↓
 パッケージ中の各種変数の初期化 (ファイル名がAから順番、だけど処理系依存かもしれない)
   ↓
 パッケージ中の各種init()関数の呼び出し (ファイル名がAから順番、だけど処理系依存かもしれない)
  ↓
main関数の呼び出し

spf13/cobra はどうやって「各コマンドは別ファイルで定義する」というスタイルを確立したか

  • main関数からは、root.Execute()を叩いているだけ。
  • root.Execute() は、それまでに作成されたコマンドパーサ(rootCmd変数に格納されている)のExecuteを叩くだけ。
  • rootCmdは cmd/root.go で初期化される。
  • 各種cmd/xxx.goのinit()で、rootCmdを拡張していく。
  • cmd/root.goのinit()で OnInitializeで、cobraがパッケージローカルで持ってるinitializerを拡張する
  • 各メソッド(cobra.Command#AddCommandやcobra#OnInitializeなど)はどういう順番で叩かれても困らないように設計されていると思われる
    • AddCommandとかOnInitializeは、単純に、いろいろなコマンドデータをリストにデータを突っ込んでいるだけ
    • Execute()やCommands()とか、いざ使われる際に、リストを整形(ソートとか)して使ったりしている

まとめ

  • パッケージ中の変数の初期化が一通り終わってから、パッケージ中のinit()が呼ばれるよ!
  • 順番はファイル名とかに依存するかもしれないし、ランダムかもしれないので、どういう順番でinitされてもバグらないようにしようね!

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
8