LoginSignup
18
18

More than 5 years have passed since last update.

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

Last updated at Posted at 2015-06-04

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

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

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

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すると

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>をコールしているみたい

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を呼んでいる

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の出処を探すと

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

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

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

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

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が正式に出たら改めてソースを追ってみようと思いますん

18
18
0

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
  3. You can use dark theme
What you can do with signing up
18
18