http://golang.org/doc/effective_go.html#init
ふと、呼び出される順番が気になったので調べてみました
versionは1.4(.2)
initが呼ばれるときのコードを眺める
で init 的な文字列を探すと
関数宣言でなんか分岐しているところが
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すると
/*
* 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>
をコールしているみたい
/*
* 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を呼んでいる
// 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
の出処を探すと
dirs, err := ctxt.readDir(p.Dir)
でpackage directory内のエントリ列挙
からの
なんやかんやテストがあって
p.GoFiles = append(p.GoFiles, name)
されている
そして、ctxt.readDir
の実装は↓
// 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が正式に出たら改めてソースを追ってみようと思いますん