packageに複数のinitがあるときの挙動



http://golang.org/doc/effective_go.html#init
ふと、呼び出される順番が気になったので調べてみました
versionは1.4(.2)


# initが呼ばれるときのコードを眺める

https://github.com/golang/go/blob/release-branch.go1.4/src/cmd/gc/go.y

で init 的な文字列を探すと
関数宣言でなんか分岐しているところが

https://github.com/golang/go/blob/release-branch.go1.4/src/cmd/gc/go.y#L1334

```yacc:src/cmd/gc/go.y#L1325
fndcl:
  sym '(' oarg_type_list_ocomma ')' fnres
  {
    Node *t;

    $$ = N;
    $3 = checkarglist($3, 1);

    if(strcmp($1->name, "init") == 0) {
      $1 = renameinit();
      if($3 != nil || $5 != nil)
        yyerror("func init must have no arguments and no return values");
    }
    if(strcmp(localpkg->name, "main") == 0 && strcmp($1->name, "main") == 0) {
      if($3 != nil || $5 != nil)
        yyerror("func main must have no arguments and no return values");
    }

    t = nod(OTFUNC, N, N);
```


```renameinit``` ?
grepすると


https://github.com/golang/go/blob/release-branch.go1.4/src/cmd/gc/init.c#L17

```c:src/cmd/gc/init.c
/*
 * a function named init is a special case.
 * it is called by the initialization before
 * main is run. to make it unique within a
 * package and also uncallable, the name,
 * normally "pkg.init", is altered to "pkg.init·1".
 */
Sym*
renameinit(void)
{
    static int initgen;

    snprint(namebuf, sizeof(namebuf), "init·%d", ++initgen);
    return lookup(namebuf);
}
```

middle dotと連番をsuffixとしてつけたシンボルを探してる(作っている)
middle dotつけるから、普段書いているコードから呼ばれちゃう心配がないのか


そして、すぐ下の方には、実際に呼ばれるinit関数を組み立てているコードがあって
そこで```init·<n>```をコールしているみたい


https://github.com/golang/go/blob/release-branch.go1.4/src/cmd/gc/init.c#L26

```c:src/cmd/gc/init.c
/*
 * hand-craft the following initialization code
 *	var initdone· uint8 				(1)
 *	func init()					(2)
 *		if initdone· != 0 {			(3)
 *			if initdone· == 2		(4)
 *				return
 *			throw();			(5)
 *		}
 *		initdone· = 1;				(6)
 *		// over all matching imported symbols
 *			<pkg>.init()			(7)
 *		{ <init stmts> }			(8)
 *		init·<n>() // if any			(9)
 *		initdone· = 2;				(10)
 *		return					(11)
 *	}
 */
static int
anyinit(NodeList *n)
{
...
}
```

コメントは```anyinit```の方に付いているけど、実際は更に下の```fninit```の方でやっている
https://github.com/golang/go/blob/release-branch.go1.4/src/cmd/gc/init.c#L92


なるほろ

# package内の複数ファイルにinitがあった場合は？

go build のソースをながめてみると
このへんでcompilerを呼んでいる

https://github.com/golang/go/blob/release-branch.go1.4/src/cmd/go/build.go#L944

```go:src/cmd/go/build.go
	// Prepare Go import path list.
	inc := b.includeArgs("-I", a.deps)

	// Compile Go.
	ofile, out, err := buildToolchain.gc(b, a.p, a.objpkg, obj, inc, gofiles)
	if len(out) > 0 {
		b.showOutput(a.p.Dir, a.p.ImportPath, b.processOutput(out))
		if err != nil {
			return errPrintedOutput
		}
	}
```

この```buildToolchain.gc```に渡されている```gofiles```の出処を探すと

https://github.com/golang/go/blob/release-branch.go1.4/src/go/build/build.go#L604

```go:src/go/build/build.go
dirs, err := ctxt.readDir(p.Dir)
```

でpackage directory内のエントリ列挙
からの
なんやかんやテストがあって

https://github.com/golang/go/blob/release-branch.go1.4/src/go/build/build.go#L766

```go:src/go/build/build.go
      p.GoFiles = append(p.GoFiles, name)
```

されている
そして、```ctxt.readDir```の実装は↓


https://github.com/golang/go/blob/release-branch.go1.4/src/go/build/build.go#L171

```go:src/go/build/build.go
// readDir calls ctxt.ReadDir (if not nil) or else ioutil.ReadDir.
func (ctxt *Context) readDir(path string) ([]os.FileInfo, error) {
  if f := ctxt.ReadDir; f != nil {
    return f(path)
  }
  return ioutil.ReadDir(path)
}
```

```ctxt.ReadDir``` は、コマンドラインでファイル列挙してビルドする場合と
swigのサポート時のintサイズ確認でつかわれているみたいなので
通常のパッケージビルドは```ioutil.ReadDir```でよさそう

ファイル名のascii順ですね

＿人人人人人人人人人人人人＿
＞　じゃないかと思ってた　＜
￣Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y￣

1.5が正式に出たら改めてソースを追ってみようと思いますん