go gettable for github.com/google/kati
diff --git a/.gitignore b/.gitignore
index 6fa2b88..61de556 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
 kati
+!cmd/kati/
 ckati
 para
 *.o
@@ -12,3 +13,4 @@
 bench-new.out
 string_piece_test
 strutil_test
+go_src_stamp
diff --git a/Makefile b/Makefile
index 57e82c5..1641884 100644
--- a/Makefile
+++ b/Makefile
@@ -41,8 +41,18 @@
 
 all: kati para ckati $(CXX_TEST_EXES)
 
-kati: $(GO_SRCS)
-	env $(shell go env) go build -o $@ *.go
+kati: go_src_stamp
+	GOPATH=$$(pwd)/out go install github.com/google/kati/cmd/kati
+	cp out/bin/kati $@
+
+go_src_stamp: $(GO_SRCS) cmd/*/*.go
+	-rm -rf out/src/github.com/google/kati
+	mkdir -p out/src/github.com/google/kati
+	cp -a $(GO_SRCS) cmd out/src/github.com/google/kati
+	touch $@
+
+go_test: $(GO_SRCS) para
+	go test *.go
 
 ckati: $(CXX_OBJS)
 	$(CXX) -std=c++11 $(CXXFLAGS) -o $@ $(CXX_OBJS)
@@ -54,9 +64,6 @@
 $(CXX_TEST_EXES): %: %.o
 	$(CXX) $^ -o $@
 
-go_test: $(GO_SRCS) para
-	env $(shell go env) go test *.go
-
 para: para.cc
 	$(CXX) -std=c++11 -g -O -W -Wall -MMD -o $@ $<
 
diff --git a/ast.cc b/ast.cc
index 344b42c..fdcfeb3 100644
--- a/ast.cc
+++ b/ast.cc
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// +build ignore
+
 #include "ast.h"
 
 #include "eval.h"
diff --git a/ast.go b/ast.go
index a8a2d8b..05256ff 100644
--- a/ast.go
+++ b/ast.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package main
+package kati
 
 import (
 	"bytes"
@@ -20,8 +20,6 @@
 	"strings"
 )
 
-const BootstrapMakefile = "*bootstrap*"
-
 type AST interface {
 	eval(*Evaluator)
 	show()
@@ -46,7 +44,7 @@
 
 func (ast *AssignAST) evalRHS(ev *Evaluator, lhs string) Var {
 	origin := "file"
-	if ast.filename == BootstrapMakefile {
+	if ast.filename == bootstrapMakefileName {
 		origin = "default"
 	}
 	if ast.opt == "override" {
diff --git a/bootstrap.go b/bootstrap.go
new file mode 100644
index 0000000..de13b2b
--- /dev/null
+++ b/bootstrap.go
@@ -0,0 +1,56 @@
+// Copyright 2015 Google Inc. All rights reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package kati
+
+import (
+	"fmt"
+	"path/filepath"
+	"strings"
+)
+
+const bootstrapMakefileName = "*bootstrap*"
+
+func BootstrapMakefile(targets []string) Makefile {
+	bootstrap := `
+CC:=cc
+CXX:=g++
+AR:=ar
+MAKE:=kati
+# Pretend to be GNU make 3.81, for compatibility.
+MAKE_VERSION:=3.81
+SHELL:=/bin/sh
+# TODO: Add more builtin vars.
+
+# http://www.gnu.org/software/make/manual/make.html#Catalogue-of-Rules
+# The document above is actually not correct. See default.c:
+# http://git.savannah.gnu.org/cgit/make.git/tree/default.c?id=4.1
+.c.o:
+	$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -o $@ $<
+.cc.o:
+	$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -o $@ $<
+# TODO: Add more builtin rules.
+`
+	bootstrap += fmt.Sprintf("MAKECMDGOALS:=%s\n", strings.Join(targets, " "))
+	cwd, err := filepath.Abs(".")
+	if err != nil {
+		panic(err)
+	}
+	bootstrap += fmt.Sprintf("CURDIR:=%s\n", cwd)
+	mk, err := ParseMakefileString(bootstrap, bootstrapMakefileName, 0)
+	if err != nil {
+		panic(err)
+	}
+	return mk
+}
diff --git a/cmd/kati/main.go b/cmd/kati/main.go
new file mode 100644
index 0000000..2128a07
--- /dev/null
+++ b/cmd/kati/main.go
@@ -0,0 +1,289 @@
+// Copyright 2015 Google Inc. All rights reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+	"bytes"
+	"flag"
+	"fmt"
+	"os"
+	"path/filepath"
+	"runtime"
+	"runtime/pprof"
+	"strings"
+	"text/template"
+	"time"
+
+	"github.com/google/kati"
+)
+
+const shellDateTimeformat = time.RFC3339
+
+var (
+	makefileFlag string
+	jobsFlag     int
+
+	loadJSON string
+	saveJSON string
+	loadGOB  string
+	saveGOB  string
+	useCache bool
+
+	cpuprofile          string
+	heapprofile         string
+	memstats            string
+	traceEventFile      string
+	syntaxCheckOnlyFlag bool
+	queryFlag           string
+	eagerCmdEvalFlag    bool
+	generateNinja       bool
+	gomaDir             string
+	usePara             bool
+	findCachePrunes     string
+	findCacheLeafNames  string
+	shellDate           string
+)
+
+func init() {
+	// TODO: Make this default and replace this by -d flag.
+	flag.StringVar(&makefileFlag, "f", "", "Use it as a makefile")
+	flag.IntVar(&jobsFlag, "j", 1, "Allow N jobs at once.")
+
+	flag.StringVar(&loadGOB, "load", "", "")
+	flag.StringVar(&saveGOB, "save", "", "")
+	flag.StringVar(&loadJSON, "load_json", "", "")
+	flag.StringVar(&saveJSON, "save_json", "", "")
+	flag.BoolVar(&useCache, "use_cache", false, "Use cache.")
+
+	flag.StringVar(&cpuprofile, "kati_cpuprofile", "", "write cpu profile to `file`")
+	flag.StringVar(&heapprofile, "kati_heapprofile", "", "write heap profile to `file`")
+	flag.StringVar(&memstats, "kati_memstats", "", "Show memstats with given templates")
+	flag.StringVar(&traceEventFile, "kati_trace_event", "", "write trace event to `file`")
+	flag.BoolVar(&syntaxCheckOnlyFlag, "c", false, "Syntax check only.")
+	flag.StringVar(&queryFlag, "query", "", "Show the target info")
+	flag.BoolVar(&eagerCmdEvalFlag, "eager_cmd_eval", false, "Eval commands first.")
+	flag.BoolVar(&generateNinja, "ninja", false, "Generate build.ninja.")
+	flag.StringVar(&gomaDir, "goma_dir", "", "If specified, use goma to build C/C++ files.")
+	flag.BoolVar(&usePara, "use_para", false, "Use para.")
+
+	flag.StringVar(&findCachePrunes, "find_cache_prunes", "",
+		"space separated prune directories for find cache.")
+	flag.StringVar(&findCacheLeafNames, "find_cache_leaf_names", "",
+		"space separated leaf names for find cache.")
+	flag.StringVar(&shellDate, "shell_date", "", "specify $(shell date) time as "+shellDateTimeformat)
+
+	flag.BoolVar(&kati.LogFlag, "kati_log", false, "Verbose kati specific log")
+	flag.BoolVar(&kati.StatsFlag, "kati_stats", false, "Show a bunch of statistics")
+	flag.BoolVar(&kati.PeriodicStatsFlag, "kati_periodic_stats", false, "Show a bunch of periodic statistics")
+	flag.BoolVar(&kati.EvalStatsFlag, "kati_eval_stats", false, "Show eval statistics")
+
+	flag.BoolVar(&kati.DryRunFlag, "n", false, "Only print the commands that would be executed")
+
+	// TODO: Make this default.
+	flag.BoolVar(&kati.UseFindCache, "use_find_cache", false, "Use find cache.")
+	flag.BoolVar(&kati.UseWildcardCache, "use_wildcard_cache", true, "Use wildcard cache.")
+	flag.BoolVar(&kati.UseShellBuiltins, "use_shell_builtins", true, "Use shell builtins")
+	flag.StringVar(&kati.IgnoreOptionalInclude, "ignore_optional_include", "", "If specified, skip reading -include directives start with the specified path.")
+}
+
+func writeHeapProfile() {
+	f, err := os.Create(heapprofile)
+	if err != nil {
+		panic(err)
+	}
+	pprof.WriteHeapProfile(f)
+	f.Close()
+}
+
+type memStatsDumper struct {
+	*template.Template
+}
+
+func (t memStatsDumper) dump() {
+	var ms runtime.MemStats
+	runtime.ReadMemStats(&ms)
+	var buf bytes.Buffer
+	err := t.Template.Execute(&buf, ms)
+	fmt.Println(buf.String())
+	if err != nil {
+		panic(err)
+	}
+}
+
+func findPara() string {
+	switch runtime.GOOS {
+	case "linux":
+		katicmd, err := os.Readlink("/proc/self/exe")
+		if err != nil {
+			panic(err)
+		}
+		return filepath.Join(filepath.Dir(katicmd), "para")
+	default:
+		panic(fmt.Sprintf("unknown OS: %s", runtime.GOOS))
+	}
+}
+
+func load(makefile string, opt kati.LoadOpt) (*kati.DepGraph, error) {
+	startTime := time.Now()
+
+	if loadGOB != "" {
+		g := kati.LoadDepGraph(loadGOB)
+		kati.LogStats("deserialize time: %q", time.Since(startTime))
+		return g, nil
+	}
+	if loadJSON != "" {
+		g := kati.LoadDepGraphFromJSON(loadJSON)
+		kati.LogStats("deserialize time: %q", time.Since(startTime))
+		return g, nil
+	}
+	return kati.Load(makefile, opt)
+}
+
+func save(g *kati.DepGraph, targets []string) {
+	startTime := time.Now()
+	if saveGOB != "" {
+		kati.DumpDepGraph(g, saveGOB, targets)
+		kati.LogStats("serialize time: %q", time.Since(startTime))
+	}
+	if saveJSON != "" {
+		kati.DumpDepGraphAsJSON(g, saveJSON, targets)
+		kati.LogStats("serialize time: %q", time.Since(startTime))
+	}
+
+	if useCache && !g.IsCached() {
+		kati.DumpDepGraphCache(g, targets)
+		kati.LogStats("serialize time: %q", time.Since(startTime))
+	}
+}
+
+func main() {
+	runtime.GOMAXPROCS(runtime.NumCPU())
+	flag.Parse()
+	if cpuprofile != "" {
+		f, err := os.Create(cpuprofile)
+		if err != nil {
+			panic(err)
+		}
+		pprof.StartCPUProfile(f)
+		defer pprof.StopCPUProfile()
+		kati.AtError(pprof.StopCPUProfile)
+	}
+	if heapprofile != "" {
+		defer writeHeapProfile()
+		kati.AtError(writeHeapProfile)
+	}
+	defer kati.DumpStats()
+	kati.AtError(kati.DumpStats)
+	if memstats != "" {
+		ms := memStatsDumper{
+			Template: template.Must(template.New("memstats").Parse(memstats)),
+		}
+		ms.dump()
+		defer ms.dump()
+		kati.AtError(ms.dump)
+	}
+	if traceEventFile != "" {
+		f, err := os.Create(traceEventFile)
+		if err != nil {
+			panic(err)
+		}
+		kati.TraceEventStart(f)
+		defer kati.TraceEventStop()
+		kati.AtError(kati.TraceEventStop)
+	}
+
+	if shellDate != "" {
+		if shellDate == "ref" {
+			shellDate = shellDateTimeformat[:20] // until Z, drop 07:00
+		}
+		t, err := time.Parse(shellDateTimeformat, shellDate)
+		if err != nil {
+			panic(err)
+		}
+		kati.ShellDateTimestamp = t
+	}
+
+	var leafNames []string
+	if findCacheLeafNames != "" {
+		leafNames = strings.Fields(findCacheLeafNames)
+	}
+	if findCachePrunes != "" {
+		kati.UseFindCache = true
+		kati.AndroidFindCacheInit(strings.Fields(findCachePrunes), leafNames)
+	}
+
+	clvars, targets := kati.ParseCommandLine(flag.Args())
+
+	g, err := load(makefileFlag, kati.LoadOpt{
+		Targets:         targets,
+		CommandLineVars: clvars,
+		EnvironmentVars: os.Environ(),
+		UseCache:        useCache,
+	})
+	if err != nil {
+		panic(err)
+	}
+	nodes := g.Nodes()
+	vars := g.Vars()
+
+	if eagerCmdEvalFlag {
+		startTime := time.Now()
+		kati.EvalCommands(nodes, vars)
+		kati.LogStats("eager eval command time: %q", time.Since(startTime))
+	}
+
+	save(g, targets)
+
+	if generateNinja {
+		startTime := time.Now()
+		kati.GenerateNinja(g, gomaDir)
+		kati.LogStats("generate ninja time: %q", time.Since(startTime))
+		return
+	}
+
+	if syntaxCheckOnlyFlag {
+		return
+	}
+
+	if queryFlag != "" {
+		kati.HandleQuery(queryFlag, g)
+		return
+	}
+
+	// TODO: Handle target specific variables.
+	ev := kati.NewEvaluator(vars)
+	for name, export := range g.Exports() {
+		if export {
+			os.Setenv(name, ev.EvaluateVar(name))
+		} else {
+			os.Unsetenv(name)
+		}
+	}
+
+	startTime := time.Now()
+	execOpt := &kati.ExecutorOpt{
+		NumJobs: jobsFlag,
+	}
+	if usePara {
+		execOpt.ParaPath = findPara()
+	}
+	ex := kati.NewExecutor(vars, execOpt)
+	err = ex.Exec(nodes)
+	if err != nil {
+		panic(err)
+	}
+	kati.LogStats("exec time: %q", time.Since(startTime))
+
+}
diff --git a/cmdline.go b/cmdline.go
new file mode 100644
index 0000000..fa75451
--- /dev/null
+++ b/cmdline.go
@@ -0,0 +1,48 @@
+// Copyright 2015 Google Inc. All rights reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package kati
+
+import (
+	"fmt"
+	"strings"
+)
+
+func ParseCommandLine(cmdline []string) ([]string, []string) {
+	var vars []string
+	var targets []string
+	for _, arg := range cmdline {
+		if strings.IndexByte(arg, '=') >= 0 {
+			vars = append(vars, arg)
+			continue
+		}
+		targets = append(targets, arg)
+	}
+	return vars, targets
+}
+
+func InitVars(vars Vars, kvlist []string, origin string) error {
+	for _, v := range kvlist {
+		kv := strings.SplitN(v, "=", 2)
+		Logf("%s var %q", origin, v)
+		if len(kv) < 2 {
+			return fmt.Errorf("A weird %s variable %q", origin, kv)
+		}
+		vars.Assign(kv[0], &RecursiveVar{
+			expr:   literal(kv[1]),
+			origin: origin,
+		})
+	}
+	return nil
+}
diff --git a/dep.cc b/dep.cc
index 0c429d2..9668400 100644
--- a/dep.cc
+++ b/dep.cc
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// +build ignore
+
 #include "dep.h"
 
 #include <algorithm>
diff --git a/dep.go b/dep.go
index 0e29789..1126336 100644
--- a/dep.go
+++ b/dep.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package main
+package kati
 
 import (
 	"fmt"
@@ -272,7 +272,7 @@
 				if !present || oldVar.String() == "" {
 					db.vars[name] = tsv
 				} else {
-					v = oldVar.AppendVar(newEvaluator(db.vars), tsv)
+					v = oldVar.AppendVar(NewEvaluator(db.vars), tsv)
 					db.vars[name] = v
 				}
 				tsvs[name] = v
@@ -489,7 +489,7 @@
 }
 
 func (db *DepBuilder) reportStats() {
-	if !katiLogFlag && !katiPeriodicStatsFlag {
+	if !LogFlag && !PeriodicStatsFlag {
 		return
 	}
 
diff --git a/depgraph.go b/depgraph.go
new file mode 100644
index 0000000..a349980
--- /dev/null
+++ b/depgraph.go
@@ -0,0 +1,115 @@
+// Copyright 2015 Google Inc. All rights reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package kati
+
+import (
+	"crypto/sha1"
+	"io/ioutil"
+	"time"
+)
+
+type DepGraph struct {
+	nodes    []*DepNode
+	vars     Vars
+	readMks  []*ReadMakefile
+	exports  map[string]bool
+	isCached bool
+}
+
+func (g *DepGraph) Nodes() []*DepNode        { return g.nodes }
+func (g *DepGraph) Vars() Vars               { return g.vars }
+func (g *DepGraph) Exports() map[string]bool { return g.exports }
+func (g *DepGraph) IsCached() bool           { return g.isCached }
+
+type LoadOpt struct {
+	Targets         []string
+	CommandLineVars []string
+	EnvironmentVars []string
+	UseCache        bool
+}
+
+func Load(makefile string, opt LoadOpt) (*DepGraph, error) {
+	startTime := time.Now()
+	if makefile == "" {
+		makefile = DefaultMakefile()
+	}
+
+	if opt.UseCache {
+		g := LoadDepGraphCache(makefile, opt.Targets)
+		if g != nil {
+			return g, nil
+		}
+	}
+
+	bmk := BootstrapMakefile(opt.Targets)
+
+	content, err := ioutil.ReadFile(makefile)
+	if err != nil {
+		return nil, err
+	}
+	mk, err := ParseMakefile(content, makefile)
+	if err != nil {
+		return nil, err
+	}
+
+	for _, stmt := range mk.stmts {
+		stmt.show()
+	}
+
+	mk.stmts = append(bmk.stmts, mk.stmts...)
+
+	vars := make(Vars)
+	err = InitVars(vars, opt.EnvironmentVars, "environment")
+	if err != nil {
+		return nil, err
+	}
+	err = InitVars(vars, opt.CommandLineVars, "command line")
+	if err != nil {
+		return nil, err
+	}
+	er, err := Eval(mk, vars, opt.UseCache)
+	if err != nil {
+		return nil, err
+	}
+	vars.Merge(er.vars)
+
+	LogStats("eval time: %q", time.Since(startTime))
+	LogStats("shell func time: %q %d", shellStats.Duration(), shellStats.Count())
+
+	startTime = time.Now()
+	db := NewDepBuilder(er, vars)
+	LogStats("dep build prepare time: %q", time.Since(startTime))
+
+	startTime = time.Now()
+	nodes, err := db.Eval(opt.Targets)
+	if err != nil {
+		return nil, err
+	}
+	LogStats("dep build time: %q", time.Since(startTime))
+	var readMks []*ReadMakefile
+	// Always put the root Makefile as the first element.
+	readMks = append(readMks, &ReadMakefile{
+		Filename: makefile,
+		Hash:     sha1.Sum(content),
+		State:    FileExists,
+	})
+	readMks = append(readMks, er.readMks...)
+	return &DepGraph{
+		nodes:   nodes,
+		vars:    vars,
+		readMks: readMks,
+		exports: er.exports,
+	}, nil
+}
diff --git a/doc.go b/doc.go
new file mode 100644
index 0000000..0cea5f9
--- /dev/null
+++ b/doc.go
@@ -0,0 +1,8 @@
+/*
+Package kati provides GNU make compatible functions, especially
+to speed up the continuous build of Android.
+
+*/
+package kati
+
+// TODO(ukai): cleanup API. make more symbol unexported.
diff --git a/eval.cc b/eval.cc
index 94334ed..ab99a01 100644
--- a/eval.cc
+++ b/eval.cc
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// +build ignore
+
 #include "eval.h"
 
 #include <errno.h>
diff --git a/eval.go b/eval.go
index 4d6a980..e168662 100644
--- a/eval.go
+++ b/eval.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package main
+package kati
 
 import (
 	"bytes"
@@ -53,6 +53,7 @@
 	vars         Vars
 	lastRule     *Rule
 	currentScope Vars
+	useCache     bool
 	avoidIO      bool
 	hasIO        bool
 	readMks      map[string]*ReadMakefile
@@ -62,7 +63,7 @@
 	lineno   int
 }
 
-func newEvaluator(vars map[string]Var) *Evaluator {
+func NewEvaluator(vars map[string]Var) *Evaluator {
 	return &Evaluator{
 		outVars:     make(Vars),
 		vars:        vars,
@@ -91,7 +92,7 @@
 func (ev *Evaluator) evalAssign(ast *AssignAST) {
 	ev.lastRule = nil
 	lhs, rhs := ev.evalAssignAST(ast)
-	if katiLogFlag {
+	if LogFlag {
 		Logf("ASSIGN: %s=%q (flavor:%q)", lhs, rhs, rhs.Flavor())
 	}
 	if lhs == "" {
@@ -128,7 +129,7 @@
 	}
 	ev.currentScope = vars
 	lhs, rhs := ev.evalAssignAST(assign)
-	if katiLogFlag {
+	if LogFlag {
 		Logf("rule outputs:%q assign:%q=%q (flavor:%q)", output, lhs, rhs, rhs.Flavor())
 	}
 	vars.Assign(lhs, &TargetSpecificVar{v: rhs, op: assign.op})
@@ -147,7 +148,7 @@
 	if ast.term == '=' {
 		line = append(line, ast.afterTerm...)
 	}
-	if katiLogFlag {
+	if LogFlag {
 		Logf("rule? %q=>%q", ast.expr, line)
 	}
 
@@ -166,7 +167,7 @@
 		Error(ast.filename, ast.lineno, "%v", err.Error())
 	}
 	freeBuf(buf)
-	if katiLogFlag {
+	if LogFlag {
 		Logf("rule %q => outputs:%q, inputs:%q", line, rule.outputs, rule.inputs)
 	}
 
@@ -201,7 +202,7 @@
 	if ast.term == ';' {
 		rule.cmds = append(rule.cmds, string(ast.afterTerm[1:]))
 	}
-	if katiLogFlag {
+	if LogFlag {
 		Logf("rule outputs:%q cmds:%q", rule.outputs, rule.cmds)
 	}
 	ev.lastRule = rule
@@ -288,7 +289,7 @@
 }
 
 func (ev *Evaluator) updateReadMakefile(fn string, hash [sha1.Size]byte, st FileState) {
-	if !useCache {
+	if !ev.useCache {
 		return
 	}
 
@@ -349,7 +350,7 @@
 	}
 
 	for _, fn := range files {
-		if ignoreOptionalInclude != "" && ast.op == "-include" && matchPattern(fn, ignoreOptionalInclude) {
+		if IgnoreOptionalInclude != "" && ast.op == "-include" && matchPattern(fn, IgnoreOptionalInclude) {
 			continue
 		}
 		mk, hash, err := makefileCache.parse(fn)
@@ -383,7 +384,7 @@
 		val := buf.Len()
 		freeBuf(buf)
 		isTrue = (val > 0) == (ast.op == "ifdef")
-		if katiLogFlag {
+		if LogFlag {
 			Logf("%s lhs=%q value=%q => %t", ast.op, ast.lhs, value, isTrue)
 		}
 	case "ifeq", "ifneq":
@@ -395,7 +396,7 @@
 		rhs := string(params[1])
 		freeBuf(buf)
 		isTrue = (lhs == rhs) == (ast.op == "ifeq")
-		if katiLogFlag {
+		if LogFlag {
 			Logf("%s lhs=%q %q rhs=%q %q => %t", ast.op, ast.lhs, lhs, ast.rhs, rhs, isTrue)
 		}
 	default:
@@ -441,8 +442,9 @@
 	return r
 }
 
-func Eval(mk Makefile, vars Vars) (er *EvalResult, err error) {
-	ev := newEvaluator(vars)
+func Eval(mk Makefile, vars Vars, useCache bool) (er *EvalResult, err error) {
+	ev := NewEvaluator(vars)
+	ev.useCache = useCache
 	defer func() {
 		if r := recover(); r != nil {
 			err = fmt.Errorf("panic in eval %s: %v", mk.filename, r)
@@ -450,6 +452,9 @@
 	}()
 
 	makefileList := vars.Lookup("MAKEFILE_LIST")
+	if !makefileList.IsDefined() {
+		makefileList = &SimpleVar{value: "", origin: "file"}
+	}
 	makefileList = makefileList.Append(ev, mk.filename)
 	ev.outVars.Assign("MAKEFILE_LIST", makefileList)
 
diff --git a/exec.cc b/exec.cc
index bdfa2fd..44ef68b 100644
--- a/exec.cc
+++ b/exec.cc
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// +build ignore
+
 #include "exec.h"
 
 #include <stdio.h>
diff --git a/exec.go b/exec.go
index 2cacc93..8c1ac1e 100644
--- a/exec.go
+++ b/exec.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package main
+package kati
 
 import (
 	"bytes"
@@ -207,7 +207,7 @@
 }
 
 func (ex *Executor) reportStats() {
-	if !katiLogFlag && !katiPeriodicStatsFlag {
+	if !LogFlag && !PeriodicStatsFlag {
 		return
 	}
 
@@ -218,17 +218,28 @@
 	}
 }
 
-func NewExecutor(vars Vars) *Executor {
+type ExecutorOpt struct {
+	NumJobs  int
+	ParaPath string
+}
+
+func NewExecutor(vars Vars, opt *ExecutorOpt) *Executor {
+	if opt == nil {
+		opt = &ExecutorOpt{NumJobs: 1}
+	}
+	if opt.NumJobs < 1 {
+		opt.NumJobs = 1
+	}
 	ex := &Executor{
 		rules:       make(map[string]*Rule),
 		suffixRules: make(map[string][]*Rule),
 		done:        make(map[string]*Job),
 		vars:        vars,
-		wm:          NewWorkerManager(),
+		wm:          NewWorkerManager(opt.NumJobs, opt.ParaPath),
 	}
 	// TODO: We should move this to somewhere around evalCmd so that
 	// we can handle SHELL in target specific variables.
-	ev := newEvaluator(ex.vars)
+	ev := NewEvaluator(ex.vars)
 	ex.shell = ev.EvaluateVar("SHELL")
 	for k, v := range map[string]Var{
 		"@": AutoAtVar{AutoVar: AutoVar{ex: ex}},
@@ -275,7 +286,7 @@
 		ex.vars[k] = v
 	}
 
-	ev := newEvaluator(ex.vars)
+	ev := NewEvaluator(ex.vars)
 	ev.avoidIO = avoidIO
 	ev.filename = n.Filename
 	ev.lineno = n.Lineno
@@ -297,7 +308,7 @@
 
 func EvalCommands(nodes []*DepNode, vars Vars) {
 	ioCnt := 0
-	ex := NewExecutor(vars)
+	ex := NewExecutor(vars, nil)
 	for i, n := range nodes {
 		runners, hasIO := ex.createRunners(n, true)
 		if hasIO {
diff --git a/expr.go b/expr.go
index 91c861b..f778901 100644
--- a/expr.go
+++ b/expr.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package main
+package kati
 
 import (
 	"bytes"
@@ -591,7 +591,7 @@
 	if compactor, ok := f.(Compactor); ok {
 		fv = compactor.Compact()
 	}
-	if katiEvalStatsFlag || traceEvent.enabled() {
+	if EvalStatsFlag || traceEvent.enabled() {
 		fv = funcstats{
 			Value: fv,
 			str:   fv.String(),
diff --git a/expr_test.go b/expr_test.go
index 9b776e0..a7f2fe3 100644
--- a/expr_test.go
+++ b/expr_test.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package main
+package kati
 
 import (
 	"reflect"
diff --git a/file.cc b/file.cc
index fc7969f..7217a37 100644
--- a/file.cc
+++ b/file.cc
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// +build ignore
+
 #include "file.h"
 
 #include <fcntl.h>
diff --git a/file_cache.cc b/file_cache.cc
index 0cd6ea5..b26d054 100644
--- a/file_cache.cc
+++ b/file_cache.cc
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// +build ignore
+
 #include "file_cache.h"
 
 #include <unordered_map>
diff --git a/fileutil.cc b/fileutil.cc
index 92726fa..ebf6fd4 100644
--- a/fileutil.cc
+++ b/fileutil.cc
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// +build ignore
+
 #include "fileutil.h"
 
 #include <errno.h>
diff --git a/fileutil.go b/fileutil.go
index a288b6d..cde353b 100644
--- a/fileutil.go
+++ b/fileutil.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package main
+package kati
 
 import "os"
 
diff --git a/flags.go b/flags.go
new file mode 100644
index 0000000..105df2d
--- /dev/null
+++ b/flags.go
@@ -0,0 +1,30 @@
+// Copyright 2015 Google Inc. All rights reserved
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package kati
+
+var (
+	LogFlag           bool
+	StatsFlag         bool
+	PeriodicStatsFlag bool
+	EvalStatsFlag     bool
+
+	DryRunFlag bool
+
+	UseFindCache     bool
+	UseWildcardCache bool
+	UseShellBuiltins bool
+
+	IgnoreOptionalInclude string
+)
diff --git a/func.cc b/func.cc
index c8127f7..b46e50c 100644
--- a/func.cc
+++ b/func.cc
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// +build ignore
+
 #include "func.h"
 
 #include <glob.h>
diff --git a/func.go b/func.go
index 364545b..b9f3fd9 100644
--- a/func.go
+++ b/func.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package main
+package kati
 
 import (
 	"bytes"
@@ -474,7 +474,7 @@
 	abuf := newBuf()
 	f.args[1].Eval(abuf, ev)
 	te := traceEvent.begin("wildcard", tmpval(abuf.Bytes()), traceEventMain)
-	if ev.avoidIO && !useWildcardCache {
+	if ev.avoidIO && !UseWildcardCache {
 		ev.hasIO = true
 		io.WriteString(w, "$(/bin/ls -d ")
 		w.Write(abuf.Bytes())
@@ -774,7 +774,7 @@
 	shellVar := ev.LookupVar("SHELL")
 	// TODO: Should be Eval, not String.
 	cmdline := []string{shellVar.String(), "-c", arg}
-	if katiLogFlag {
+	if LogFlag {
 		Logf("shell %q", cmdline)
 	}
 	cmd := exec.Cmd{
@@ -796,7 +796,7 @@
 	if len(f.args)-1 < 1 {
 		return f
 	}
-	if !useFindCache && !useShellBuiltins {
+	if !UseFindCache && !UseShellBuiltins {
 		return f
 	}
 
@@ -807,7 +807,7 @@
 	default:
 		expr = Expr{v}
 	}
-	if useShellBuiltins {
+	if UseShellBuiltins {
 		// hack for android
 		for _, sb := range shBuiltins {
 			if v, ok := matchExpr(expr, sb.pattern); ok {
@@ -831,7 +831,7 @@
 	varname := fargs[0]
 	variable := string(varname)
 	te := traceEvent.begin("call", literal(variable), traceEventMain)
-	if katiLogFlag {
+	if LogFlag {
 		Logf("call %q variable %q", f.args[1], variable)
 	}
 	v := ev.LookupVar(variable)
@@ -846,7 +846,7 @@
 	for i, arg := range fargs[1:] {
 		// f.args[2]=>args[1] will be $1.
 		args = append(args, tmpval(arg))
-		if katiLogFlag {
+		if LogFlag {
 			Logf("call $%d: %q=>%q", i+1, arg, fargs[i+1])
 		}
 	}
@@ -854,13 +854,13 @@
 	ev.paramVars = args
 
 	var buf bytes.Buffer
-	if katiLogFlag {
+	if LogFlag {
 		w = io.MultiWriter(w, &buf)
 	}
 	v.Eval(w, ev)
 	ev.paramVars = oldParams
 	traceEvent.end(te)
-	if katiLogFlag {
+	if LogFlag {
 		Logf("call %q variable %q return %q", f.args[1], variable, buf.Bytes())
 	}
 	freeBuf(abuf)
@@ -1040,7 +1040,7 @@
 		}
 		rvalue = &RecursiveVar{expr: tmpval(rhs), origin: "file"}
 	}
-	if katiLogFlag {
+	if LogFlag {
 		Logf("Eval ASSIGN: %s=%q (flavor:%q)", f.lhs, rvalue, rvalue.Flavor())
 	}
 	ev.outVars.Assign(f.lhs, rvalue)
diff --git a/func_test.go b/func_test.go
index 6758a9b..58d7570 100644
--- a/func_test.go
+++ b/func_test.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package main
+package kati
 
 import (
 	"bytes"
@@ -28,7 +28,7 @@
 			},
 		},
 	}
-	ev := newEvaluator(make(map[string]Var))
+	ev := NewEvaluator(make(map[string]Var))
 	var buf bytes.Buffer
 	b.ReportAllocs()
 	b.ResetTimer()
@@ -47,7 +47,7 @@
 			},
 		},
 	}
-	ev := newEvaluator(make(map[string]Var))
+	ev := NewEvaluator(make(map[string]Var))
 	var buf bytes.Buffer
 	b.ReportAllocs()
 	b.ResetTimer()
@@ -68,7 +68,7 @@
 			},
 		},
 	}
-	ev := newEvaluator(make(map[string]Var))
+	ev := NewEvaluator(make(map[string]Var))
 	var buf bytes.Buffer
 	b.ReportAllocs()
 	b.ResetTimer()
diff --git a/ioutil.go b/ioutil.go
index ef27e01..de8f88f 100644
--- a/ioutil.go
+++ b/ioutil.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package main
+package kati
 
 import "io"
 
diff --git a/log.go b/log.go
index b00e2bb..514ff9b 100644
--- a/log.go
+++ b/log.go
@@ -12,13 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package main
+package kati
 
 import (
 	"bytes"
 	"fmt"
 	"os"
-	"runtime/pprof"
 	"sync"
 )
 
@@ -35,14 +34,14 @@
 }
 
 func LogStats(f string, a ...interface{}) {
-	if !katiLogFlag && !katiStatsFlag {
+	if !LogFlag && !StatsFlag {
 		return
 	}
 	LogAlways(f, a...)
 }
 
 func Logf(f string, a ...interface{}) {
-	if !katiLogFlag {
+	if !LogFlag {
 		return
 	}
 	LogAlways(f, a...)
@@ -58,19 +57,22 @@
 	fmt.Printf(f, a...)
 }
 
+var atErrors []func()
+
+func AtError(f func()) {
+	atErrors = append(atErrors, f)
+}
+
 func Error(filename string, lineno int, f string, a ...interface{}) {
 	f = fmt.Sprintf("%s:%d: %s", filename, lineno, f)
-	maybeWriteHeapProfile()
 	ErrorNoLocation(f, a...)
 }
 
 func ErrorNoLocation(f string, a ...interface{}) {
 	f = fmt.Sprintf("%s\n", f)
 	fmt.Printf(f, a...)
-	if cpuprofile != "" {
-		pprof.StopCPUProfile()
+	for i := len(atErrors) - 1; i >= 0; i-- {
+		atErrors[i]()
 	}
-	maybeWriteHeapProfile()
-	dumpStats()
 	os.Exit(2)
 }
diff --git a/main.cc b/main.cc
index f8c05c7..2650675 100644
--- a/main.cc
+++ b/main.cc
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// +build ignore
+
 #include <limits.h>
 #include <stdio.h>
 #include <string.h>
diff --git a/main.go b/main.go
deleted file mode 100644
index 15ad142..0000000
--- a/main.go
+++ /dev/null
@@ -1,412 +0,0 @@
-// Copyright 2015 Google Inc. All rights reserved
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package main
-
-import (
-	"bytes"
-	"crypto/sha1"
-	"flag"
-	"fmt"
-	"io/ioutil"
-	"os"
-	"path/filepath"
-	"runtime"
-	"runtime/pprof"
-	"strings"
-	"text/template"
-	"time"
-)
-
-const shellDateTimeformat = time.RFC3339
-
-var (
-	katiLogFlag           bool
-	makefileFlag          string
-	dryRunFlag            bool
-	jobsFlag              int
-	cpuprofile            string
-	heapprofile           string
-	memstats              string
-	traceEventFile        string
-	katiStatsFlag         bool
-	katiPeriodicStatsFlag bool
-	katiEvalStatsFlag     bool
-	loadJSON              string
-	saveJSON              string
-	loadGOB               string
-	saveGOB               string
-	syntaxCheckOnlyFlag   bool
-	queryFlag             string
-	eagerCmdEvalFlag      bool
-	useParaFlag           bool
-	useCache              bool
-	useFindCache          bool
-	findCachePrunes       string
-	findCacheLeafNames    string
-	useWildcardCache      bool
-	useShellBuiltins      bool
-	shellDate             string
-	generateNinja         bool
-	ignoreOptionalInclude string
-	gomaDir               string
-
-	katiDir string
-)
-
-type DepGraph struct {
-	nodes    []*DepNode
-	vars     Vars
-	readMks  []*ReadMakefile
-	exports  map[string]bool
-	isCached bool
-}
-
-func parseFlags() {
-	// TODO: Make this default and replace this by -d flag.
-	flag.BoolVar(&katiLogFlag, "kati_log", false, "Verbose kati specific log")
-	flag.StringVar(&makefileFlag, "f", "", "Use it as a makefile")
-
-	flag.BoolVar(&dryRunFlag, "n", false, "Only print the commands that would be executed")
-
-	flag.IntVar(&jobsFlag, "j", 1, "Allow N jobs at once.")
-
-	flag.StringVar(&loadGOB, "load", "", "")
-	flag.StringVar(&saveGOB, "save", "", "")
-	flag.StringVar(&loadJSON, "load_json", "", "")
-	flag.StringVar(&saveJSON, "save_json", "", "")
-
-	flag.StringVar(&cpuprofile, "kati_cpuprofile", "", "write cpu profile to `file`")
-	flag.StringVar(&heapprofile, "kati_heapprofile", "", "write heap profile to `file`")
-	flag.StringVar(&memstats, "kati_memstats", "", "Show memstats with given templates")
-	flag.StringVar(&traceEventFile, "kati_trace_event", "", "write trace event to `file`")
-	flag.BoolVar(&katiStatsFlag, "kati_stats", false, "Show a bunch of statistics")
-	flag.BoolVar(&katiPeriodicStatsFlag, "kati_periodic_stats", false, "Show a bunch of periodic statistics")
-	flag.BoolVar(&katiEvalStatsFlag, "kati_eval_stats", false, "Show eval statistics")
-	flag.BoolVar(&eagerCmdEvalFlag, "eager_cmd_eval", false, "Eval commands first.")
-	flag.BoolVar(&useParaFlag, "use_para", false, "Use para.")
-	flag.BoolVar(&syntaxCheckOnlyFlag, "c", false, "Syntax check only.")
-	flag.StringVar(&queryFlag, "query", "", "Show the target info")
-	// TODO: Make this default.
-	flag.BoolVar(&useCache, "use_cache", false, "Use cache.")
-	flag.BoolVar(&useFindCache, "use_find_cache", false, "Use find cache.")
-	flag.StringVar(&findCachePrunes, "find_cache_prunes", "",
-		"space separated prune directories for find cache.")
-	flag.StringVar(&findCacheLeafNames, "find_cache_leaf_names", "",
-		"space separated leaf names for find cache.")
-	flag.BoolVar(&useWildcardCache, "use_wildcard_cache", true, "Use wildcard cache.")
-	flag.BoolVar(&useShellBuiltins, "use_shell_builtins", true, "Use shell builtins")
-	flag.StringVar(&shellDate, "shell_date", "", "specify $(shell date) time as "+shellDateTimeformat)
-	flag.BoolVar(&generateNinja, "ninja", false, "Generate build.ninja.")
-	flag.StringVar(&ignoreOptionalInclude, "ignore_optional_include", "", "If specified, skip reading -include directives start with the specified path.")
-	flag.StringVar(&gomaDir, "goma_dir", "", "If specified, use goma to build C/C++ files.")
-	flag.Parse()
-}
-
-func parseCommandLine() ([]string, []string) {
-	var vars []string
-	var targets []string
-	for _, arg := range flag.Args() {
-		if strings.IndexByte(arg, '=') >= 0 {
-			vars = append(vars, arg)
-			continue
-		}
-		targets = append(targets, arg)
-	}
-	return vars, targets
-}
-
-func getBootstrapMakefile(targets []string) Makefile {
-	bootstrap := `
-CC:=cc
-CXX:=g++
-AR:=ar
-MAKE:=kati
-# Pretend to be GNU make 3.81, for compatibility.
-MAKE_VERSION:=3.81
-SHELL:=/bin/sh
-# TODO: Add more builtin vars.
-
-# http://www.gnu.org/software/make/manual/make.html#Catalogue-of-Rules
-# The document above is actually not correct. See default.c:
-# http://git.savannah.gnu.org/cgit/make.git/tree/default.c?id=4.1
-.c.o:
-	$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -o $@ $<
-.cc.o:
-	$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -o $@ $<
-# TODO: Add more builtin rules.
-`
-	bootstrap += fmt.Sprintf("MAKECMDGOALS:=%s\n", strings.Join(targets, " "))
-	cwd, err := filepath.Abs(".")
-	if err != nil {
-		panic(err)
-	}
-	bootstrap += fmt.Sprintf("CURDIR:=%s\n", cwd)
-	mk, err := ParseMakefileString(bootstrap, BootstrapMakefile, 0)
-	if err != nil {
-		panic(err)
-	}
-	return mk
-}
-
-func maybeWriteHeapProfile() {
-	if heapprofile != "" {
-		f, err := os.Create(heapprofile)
-		if err != nil {
-			panic(err)
-		}
-		pprof.WriteHeapProfile(f)
-	}
-}
-
-func getDepGraph(clvars []string, targets []string) *DepGraph {
-	startTime := time.Now()
-
-	if loadGOB != "" {
-		g := LoadDepGraph(loadGOB)
-		LogStats("deserialize time: %q", time.Since(startTime))
-		return g
-	}
-	if loadJSON != "" {
-		g := LoadDepGraphFromJSON(loadJSON)
-		LogStats("deserialize time: %q", time.Since(startTime))
-		return g
-	}
-
-	makefile := makefileFlag
-	if makefile == "" {
-		makefile = GetDefaultMakefile()
-	}
-
-	if useCache {
-		g := LoadDepGraphCache(makefile, targets)
-		if g != nil {
-			return g
-		}
-	}
-
-	bmk := getBootstrapMakefile(targets)
-
-	content, err := ioutil.ReadFile(makefile)
-	if err != nil {
-		panic(err)
-	}
-	mk, err := ParseMakefile(content, makefile)
-	if err != nil {
-		panic(err)
-	}
-
-	for _, stmt := range mk.stmts {
-		stmt.show()
-	}
-
-	mk.stmts = append(bmk.stmts, mk.stmts...)
-
-	vars := make(Vars)
-	for _, env := range os.Environ() {
-		kv := strings.SplitN(env, "=", 2)
-		Logf("envvar %q", kv)
-		if len(kv) < 2 {
-			panic(fmt.Sprintf("A weird environ variable %q", kv))
-		}
-		vars.Assign(kv[0], &RecursiveVar{
-			expr:   literal(kv[1]),
-			origin: "environment",
-		})
-	}
-	vars.Assign("MAKEFILE_LIST", &SimpleVar{value: "", origin: "file"})
-	for _, v := range clvars {
-		kv := strings.SplitN(v, "=", 2)
-		Logf("cmdlinevar %q", kv)
-		if len(kv) < 2 {
-			panic(fmt.Sprintf("unexpected command line var %q", kv))
-		}
-		vars.Assign(kv[0], &RecursiveVar{
-			expr:   literal(kv[1]),
-			origin: "command line",
-		})
-	}
-
-	er, err := Eval(mk, vars)
-	if err != nil {
-		panic(err)
-	}
-
-	vars.Merge(er.vars)
-
-	LogStats("eval time: %q", time.Since(startTime))
-	LogStats("shell func time: %q %d", shellStats.duration, shellStats.count)
-
-	startTime = time.Now()
-	db := NewDepBuilder(er, vars)
-	LogStats("dep build prepare time: %q", time.Since(startTime))
-
-	startTime = time.Now()
-	nodes, err2 := db.Eval(targets)
-	if err2 != nil {
-		panic(err2)
-	}
-	LogStats("dep build time: %q", time.Since(startTime))
-	var readMks []*ReadMakefile
-	// Always put the root Makefile as the first element.
-	readMks = append(readMks, &ReadMakefile{
-		Filename: makefile,
-		Hash:     sha1.Sum(content),
-		State:    FileExists,
-	})
-	readMks = append(readMks, er.readMks...)
-	return &DepGraph{
-		nodes:   nodes,
-		vars:    vars,
-		readMks: readMks,
-		exports: er.exports,
-	}
-}
-
-func findKatiDir() {
-	switch runtime.GOOS {
-	case "linux":
-		kati, err := os.Readlink("/proc/self/exe")
-		if err != nil {
-			panic(err)
-		}
-		katiDir = filepath.Dir(kati)
-	default:
-		panic(fmt.Sprintf("unknown OS: %s", runtime.GOOS))
-	}
-}
-
-func main() {
-	runtime.GOMAXPROCS(runtime.NumCPU())
-	findKatiDir()
-	parseFlags()
-	if cpuprofile != "" {
-		f, err := os.Create(cpuprofile)
-		if err != nil {
-			panic(err)
-		}
-		pprof.StartCPUProfile(f)
-		defer pprof.StopCPUProfile()
-	}
-	defer maybeWriteHeapProfile()
-	defer dumpStats()
-	if memstats != "" {
-		t := template.Must(template.New("memstats").Parse(memstats))
-		var ms runtime.MemStats
-		runtime.ReadMemStats(&ms)
-		var buf bytes.Buffer
-		err := t.Execute(&buf, ms)
-		fmt.Println(buf.String())
-		if err != nil {
-			panic(err)
-		}
-		defer func() {
-			var ms runtime.MemStats
-			runtime.ReadMemStats(&ms)
-			var buf bytes.Buffer
-			t.Execute(&buf, ms)
-			fmt.Println(buf.String())
-		}()
-	}
-	if traceEventFile != "" {
-		f, err := os.Create(traceEventFile)
-		if err != nil {
-			panic(err)
-		}
-		traceEvent.start(f)
-		defer traceEvent.stop()
-	}
-
-	if shellDate != "" {
-		if shellDate == "ref" {
-			shellDate = shellDateTimeformat[:20] // until Z, drop 07:00
-		}
-		t, err := time.Parse(shellDateTimeformat, shellDate)
-		if err != nil {
-			panic(err)
-		}
-		shellDateTimestamp = t
-	}
-
-	if findCacheLeafNames != "" {
-		androidDefaultLeafNames = strings.Fields(findCacheLeafNames)
-	}
-	if findCachePrunes != "" {
-		useFindCache = true
-		androidFindCache.init(strings.Fields(findCachePrunes), androidDefaultLeafNames)
-	}
-
-	clvars, targets := parseCommandLine()
-
-	g := getDepGraph(clvars, targets)
-	nodes := g.nodes
-	vars := g.vars
-
-	if eagerCmdEvalFlag {
-		startTime := time.Now()
-		EvalCommands(nodes, vars)
-		LogStats("eager eval command time: %q", time.Since(startTime))
-	}
-
-	if saveGOB != "" {
-		startTime := time.Now()
-		DumpDepGraph(g, saveGOB, targets)
-		LogStats("serialize time: %q", time.Since(startTime))
-	}
-	if saveJSON != "" {
-		startTime := time.Now()
-		DumpDepGraphAsJSON(g, saveJSON, targets)
-		LogStats("serialize time: %q", time.Since(startTime))
-	}
-
-	if useCache && !g.isCached {
-		startTime := time.Now()
-		DumpDepGraphCache(g, targets)
-		LogStats("serialize time: %q", time.Since(startTime))
-	}
-
-	if generateNinja {
-		startTime := time.Now()
-		GenerateNinja(g)
-		LogStats("generate ninja time: %q", time.Since(startTime))
-		return
-	}
-
-	if syntaxCheckOnlyFlag {
-		return
-	}
-
-	if queryFlag != "" {
-		HandleQuery(queryFlag, g)
-		return
-	}
-
-	// TODO: Handle target specific variables.
-	ev := newEvaluator(vars)
-	for name, export := range g.exports {
-		if export {
-			os.Setenv(name, ev.EvaluateVar(name))
-		} else {
-			os.Unsetenv(name)
-		}
-	}
-
-	startTime := time.Now()
-	ex := NewExecutor(vars)
-	err := ex.Exec(nodes)
-	if err != nil {
-		panic(err)
-	}
-	LogStats("exec time: %q", time.Since(startTime))
-}
diff --git a/ninja.go b/ninja.go
index ac9748f..174e54a 100644
--- a/ninja.go
+++ b/ninja.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package main
+package kati
 
 import (
 	"bytes"
@@ -33,9 +33,10 @@
 	ruleID  int
 	done    map[string]bool
 	ccRe    *regexp.Regexp
+	gomaDir string
 }
 
-func NewNinjaGenerator(g *DepGraph) *NinjaGenerator {
+func NewNinjaGenerator(g *DepGraph, gomaDir string) *NinjaGenerator {
 	ccRe, err := regexp.Compile(`^prebuilts/(gcc|clang)/.*(gcc|g\+\+|clang|clang\+\+) .* -c `)
 	if err != nil {
 		panic(err)
@@ -46,6 +47,7 @@
 		exports: g.exports,
 		done:    make(map[string]bool),
 		ccRe:    ccRe,
+		gomaDir: gomaDir,
 	}
 }
 
@@ -172,8 +174,8 @@
 		if cmd == "" {
 			cmd = "true"
 		}
-		if gomaDir != "" && n.ccRe.MatchString(cmd) {
-			cmd = fmt.Sprintf("%s/gomacc %s", gomaDir, cmd)
+		if n.gomaDir != "" && n.ccRe.MatchString(cmd) {
+			cmd = fmt.Sprintf("%s/gomacc %s", n.gomaDir, cmd)
 			useGomacc = true
 		}
 
@@ -193,7 +195,7 @@
 			buf.WriteByte(')')
 		}
 	}
-	return buf.String(), gomaDir != "" && !useGomacc
+	return buf.String(), n.gomaDir != "" && !useGomacc
 }
 
 func (n *NinjaGenerator) genRuleName() string {
@@ -283,7 +285,7 @@
 	}
 	defer f.Close()
 
-	ev := newEvaluator(n.vars)
+	ev := NewEvaluator(n.vars)
 	shell := ev.EvaluateVar("SHELL")
 	if shell == "" {
 		shell = "/bin/sh"
@@ -296,7 +298,7 @@
 			fmt.Fprintf(f, "unset %s\n", name)
 		}
 	}
-	if gomaDir == "" {
+	if n.gomaDir == "" {
 		fmt.Fprintln(f, `exec ninja "$@"`)
 	} else {
 		fmt.Fprintln(f, `exec ninja -j300 "$@"`)
@@ -319,19 +321,19 @@
 	fmt.Fprintf(n.f, "# Generated by kati\n")
 	fmt.Fprintf(n.f, "\n")
 
-	if gomaDir != "" {
+	if n.gomaDir != "" {
 		fmt.Fprintf(n.f, "pool local_pool\n")
 		fmt.Fprintf(n.f, " depth = %d\n", runtime.NumCPU())
 	}
 
-	n.ex = NewExecutor(n.vars)
+	n.ex = NewExecutor(n.vars, nil)
 	for _, node := range n.nodes {
 		n.emitNode(node)
 	}
 }
 
-func GenerateNinja(g *DepGraph) {
-	n := NewNinjaGenerator(g)
+func GenerateNinja(g *DepGraph, gomaDir string) {
+	n := NewNinjaGenerator(g, gomaDir)
 	n.generateShell()
 	n.generateNinja()
 }
diff --git a/ninja_test.go b/ninja_test.go
index c757409..8b35206 100644
--- a/ninja_test.go
+++ b/ninja_test.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package main
+package kati
 
 import "testing"
 
diff --git a/para.cc b/para.cc
index e764324..18cc4b8 100644
--- a/para.cc
+++ b/para.cc
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// +build ignore
+
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
diff --git a/para.go b/para.go
index 9ef4a11..9948866 100644
--- a/para.go
+++ b/para.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package main
+package kati
 
 import (
 	"bufio"
@@ -20,7 +20,6 @@
 	"fmt"
 	"io"
 	"os/exec"
-	"path/filepath"
 )
 
 func btoi(b bool) int {
@@ -133,9 +132,8 @@
 	doneChan chan bool
 }
 
-func NewParaWorker(paraChan chan *ParaResult) *ParaWorker {
-	bin := filepath.Join(katiDir, "para")
-	para := exec.Command(bin, fmt.Sprintf("-j%d", jobsFlag), "--kati")
+func newParaWorker(paraChan chan *ParaResult, numJobs int, paraPath string) *ParaWorker {
+	para := exec.Command(paraPath, fmt.Sprintf("-j%d", numJobs), "--kati")
 	stdin, err := para.StdinPipe()
 	if err != nil {
 		panic(err)
diff --git a/para_test.go b/para_test.go
index 0cae106..26f2669 100644
--- a/para_test.go
+++ b/para_test.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package main
+package kati
 
 import (
 	"fmt"
@@ -25,8 +25,8 @@
 	if err != nil {
 		panic(err)
 	}
-	katiDir = cwd
-	jobsFlag = 4
+	ParaPath = filepath.Join(cwd, "para")
+	JobsFlag = 4
 
 	paraChan := make(chan *ParaResult)
 	para := NewParaWorker(paraChan)
diff --git a/parser.cc b/parser.cc
index aca28b6..3bec5be 100644
--- a/parser.cc
+++ b/parser.cc
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// +build ignore
+
 #include "parser.h"
 
 #include <stack>
diff --git a/parser.go b/parser.go
index 9c61207..4e7574d 100644
--- a/parser.go
+++ b/parser.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package main
+package kati
 
 //go:generate go run testcase/gen_testcase_parse_benchmark.go
 //
@@ -684,7 +684,7 @@
 	return parser.parse()
 }
 
-func GetDefaultMakefile() string {
+func DefaultMakefile() string {
 	candidates := []string{"GNUmakefile", "makefile", "Makefile"}
 	for _, filename := range candidates {
 		if exists(filename) {
@@ -746,12 +746,12 @@
 	Logf("parse Makefile %q", filename)
 	mk, hash, ok, err := makefileCache.lookup(filename)
 	if ok {
-		if katiLogFlag {
+		if LogFlag {
 			Logf("makefile cache hit for %q", filename)
 		}
 		return mk, hash, err
 	}
-	if katiLogFlag {
+	if LogFlag {
 		Logf("reading makefile %q", filename)
 	}
 	c, err := ioutil.ReadFile(filename)
diff --git a/pathutil.go b/pathutil.go
index 38e613f..5c5649e 100644
--- a/pathutil.go
+++ b/pathutil.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package main
+package kati
 
 import (
 	"errors"
@@ -98,7 +98,7 @@
 }
 
 func wildcard(sw *ssvWriter, pat string) {
-	if useWildcardCache {
+	if UseWildcardCache {
 		// TODO(ukai): make sure it didn't chdir?
 		wildcardCache.mu.Lock()
 		files, ok := wildcardCache.m[pat]
@@ -114,7 +114,7 @@
 	for _, file := range files {
 		sw.WriteString(file)
 	}
-	if useWildcardCache {
+	if UseWildcardCache {
 		wildcardCache.mu.Lock()
 		wildcardCache.m[pat] = files
 		wildcardCache.mu.Unlock()
@@ -136,9 +136,17 @@
 }
 
 var (
-	androidFindCache androidFindCacheT
+	androidFindCache        androidFindCacheT
+	androidDefaultLeafNames = []string{"CleanSpec.mk", "Android.mk"}
 )
 
+func AndroidFindCacheInit(prunes, leafNames []string) {
+	if leafNames != nil {
+		androidDefaultLeafNames = leafNames
+	}
+	androidFindCache.init(prunes)
+}
+
 func (c *androidFindCacheT) ready() bool {
 	if c.files != nil {
 		return true
@@ -159,11 +167,11 @@
 	return c.leaves != nil
 }
 
-func (c *androidFindCacheT) init(prunes, leaves []string) {
+func (c *androidFindCacheT) init(prunes []string) {
 	c.once.Do(func() {
 		c.filesch = make(chan []fileInfo, 1)
 		c.leavesch = make(chan []fileInfo, 1)
-		go c.start(prunes, leaves)
+		go c.start(prunes, androidDefaultLeafNames)
 	})
 }
 
diff --git a/query.go b/query.go
index 7a09ff9..e77627a 100644
--- a/query.go
+++ b/query.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package main
+package kati
 
 import "fmt"
 
diff --git a/rule.cc b/rule.cc
index 0bf87bc..38a0da7 100644
--- a/rule.cc
+++ b/rule.cc
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// +build ignore
+
 #include "rule.h"
 
 #include "log.h"
diff --git a/rule_parser.go b/rule_parser.go
index 2f575e7..ccc207d 100644
--- a/rule_parser.go
+++ b/rule_parser.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package main
+package kati
 
 import (
 	"bytes"
diff --git a/rule_parser_test.go b/rule_parser_test.go
index 5dc8f24..b1db3ec 100644
--- a/rule_parser_test.go
+++ b/rule_parser_test.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package main
+package kati
 
 import (
 	"reflect"
diff --git a/serialize.go b/serialize.go
index 8ed60d2..2b8c45c 100644
--- a/serialize.go
+++ b/serialize.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package main
+package kati
 
 import (
 	"bytes"
@@ -290,7 +290,7 @@
 	cacheFile := GetCacheFilename(g.readMks[0].Filename, roots)
 	for _, mk := range g.readMks {
 		// Inconsistent, do not dump this result.
-		if mk.State == 2 {
+		if mk.State == FileInconsistent {
 			if exists(cacheFile) {
 				os.Remove(cacheFile)
 			}
@@ -551,7 +551,7 @@
 }
 
 func DeserializeGraph(g SerializableGraph) *DepGraph {
-	if katiLogFlag || katiStatsFlag {
+	if LogFlag || StatsFlag {
 		showSerializedGraphStats(g)
 	}
 	nodes := DeserializeNodes(g)
diff --git a/shellutil.go b/shellutil.go
index c73711d..93a53df 100644
--- a/shellutil.go
+++ b/shellutil.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package main
+package kati
 
 import (
 	"fmt"
@@ -21,8 +21,6 @@
 	"time"
 )
 
-var androidDefaultLeafNames = []string{"CleanSpec.mk", "Android.mk"}
-
 var shBuiltins = []struct {
 	name    string
 	pattern Expr
@@ -59,7 +57,7 @@
 			if v[0] != v[1] {
 				return sh
 			}
-			androidFindCache.init(nil, androidDefaultLeafNames)
+			androidFindCache.init(nil)
 			return &funcShellAndroidFindFileInDir{
 				funcShell: sh,
 				dir:       v[0],
@@ -78,7 +76,7 @@
 			literal(` -name "*.java" -and -not -name ".*"`),
 		},
 		compact: func(sh *funcShell, v []Value) Value {
-			androidFindCache.init(nil, androidDefaultLeafNames)
+			androidFindCache.init(nil)
 			return &funcShellAndroidFindExtFilesUnder{
 				funcShell: sh,
 				chdir:     v[0],
@@ -100,7 +98,7 @@
 			literal(" -name \"*.proto\" -and -not -name \".*\""),
 		},
 		compact: func(sh *funcShell, v []Value) Value {
-			androidFindCache.init(nil, androidDefaultLeafNames)
+			androidFindCache.init(nil)
 			return &funcShellAndroidFindExtFilesUnder{
 				funcShell: sh,
 				chdir:     v[0],
@@ -126,7 +124,7 @@
 			literal(` && find . -type d -a -name ".svn" -prune -o -type f -a \! -name "*.java" -a \! -name "package.html" -a \! -name "overview.html" -a \! -name ".*.swp" -a \! -name ".DS_Store" -a \! -name "*~" -print `),
 		},
 		compact: func(sh *funcShell, v []Value) Value {
-			androidFindCache.init(nil, androidDefaultLeafNames)
+			androidFindCache.init(nil)
 			return &funcShellAndroidFindJavaResourceFileGroup{
 				funcShell: sh,
 				dir:       Expr(v),
@@ -146,7 +144,7 @@
 			if !contains(androidDefaultLeafNames, "CleanSpec.mk") {
 				return sh
 			}
-			androidFindCache.init(nil, androidDefaultLeafNames)
+			androidFindCache.init(nil)
 			return &funcShellAndroidFindleaves{
 				funcShell: sh,
 				prunes: []Value{
@@ -175,7 +173,7 @@
 			if !contains(androidDefaultLeafNames, "Android.mk") {
 				return sh
 			}
-			androidFindCache.init(nil, androidDefaultLeafNames)
+			androidFindCache.init(nil)
 			return &funcShellAndroidFindleaves{
 				funcShell: sh,
 				prunes: []Value{
@@ -205,7 +203,7 @@
 			if !contains(androidDefaultLeafNames, "Android.mk") {
 				return sh
 			}
-			androidFindCache.init(nil, androidDefaultLeafNames)
+			androidFindCache.init(nil)
 			return &funcShellAndroidFindleaves{
 				funcShell: sh,
 				prunes: []Value{
@@ -408,7 +406,7 @@
 }
 
 var (
-	shellDateTimestamp time.Time
+	ShellDateTimestamp time.Time
 	shellDateFormatRef = map[string]string{
 		"%Y": "2006",
 		"%m": "01",
@@ -427,7 +425,7 @@
 }
 
 func compactShellDate(sh *funcShell, v []Value) Value {
-	if shellDateTimestamp.IsZero() {
+	if ShellDateTimestamp.IsZero() {
 		return sh
 	}
 	tf, ok := v[0].(literal)
@@ -445,5 +443,5 @@
 }
 
 func (f *funcShellDate) Eval(w io.Writer, ev *Evaluator) {
-	fmt.Fprint(w, shellDateTimestamp.Format(f.format))
+	fmt.Fprint(w, ShellDateTimestamp.Format(f.format))
 }
diff --git a/shellutil_test.go b/shellutil_test.go
index c54e761..e4c200d 100644
--- a/shellutil_test.go
+++ b/shellutil_test.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package main
+package kati
 
 import (
 	"testing"
@@ -42,10 +42,10 @@
 }
 
 func TestShellDate(t *testing.T) {
-	ts := shellDateTimestamp
-	shellDateTimestamp = time.Now()
+	ts := ShellDateTimestamp
+	ShellDateTimestamp = time.Now()
 	defer func() {
-		shellDateTimestamp = ts
+		ShellDateTimestamp = ts
 	}()
 	for _, tc := range []struct {
 		sharg  literal
diff --git a/stats.go b/stats.go
index efb775c..6535869 100644
--- a/stats.go
+++ b/stats.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package main
+package kati
 
 import (
 	"fmt"
@@ -39,6 +39,14 @@
 
 var traceEvent traceEventT
 
+func TraceEventStart(f io.WriteCloser) {
+	traceEvent.start(f)
+}
+
+func TraceEventStop() {
+	traceEvent.stop()
+}
+
 func (t *traceEventT) start(f io.WriteCloser) {
 	t.f = f
 	t.t0 = time.Now()
@@ -65,7 +73,7 @@
 	var e event
 	e.tid = tid
 	e.t = time.Now()
-	if t.f != nil || katiEvalStatsFlag {
+	if t.f != nil || EvalStatsFlag {
 		e.name = name
 		e.v = v.String()
 	}
@@ -123,7 +131,7 @@
 }
 
 func (s *statsT) add(name, v string, t time.Time) {
-	if !katiEvalStatsFlag {
+	if !EvalStatsFlag {
 		return
 	}
 	d := time.Since(t)
@@ -139,8 +147,8 @@
 	s.mu.Unlock()
 }
 
-func dumpStats() {
-	if !katiEvalStatsFlag {
+func DumpStats() {
+	if !EvalStatsFlag {
 		return
 	}
 	var sv byTotalTime
@@ -177,3 +185,15 @@
 	s.count++
 	s.mu.Unlock()
 }
+
+func (s *shellStatsT) Duration() time.Duration {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+	return s.duration
+}
+
+func (s *shellStatsT) Count() int {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+	return s.count
+}
diff --git a/string_piece.cc b/string_piece.cc
index e5679b0..78de4ed 100644
--- a/string_piece.cc
+++ b/string_piece.cc
@@ -17,6 +17,8 @@
 // found in the LICENSE file.
 // Copied from strings/stringpiece.cc with modifications
 
+// +build ignore
+
 #include <ctype.h>
 #include <limits.h>
 
diff --git a/string_piece_test.cc b/string_piece_test.cc
index 544c0e4..85675d8 100644
--- a/string_piece_test.cc
+++ b/string_piece_test.cc
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// +build ignore
+
 #include "string_piece.h"
 
 #include <assert.h>
diff --git a/string_pool.cc b/string_pool.cc
index 596b024..bd6a709 100644
--- a/string_pool.cc
+++ b/string_pool.cc
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// +build ignore
+
 #include "string_pool.h"
 
 #include <stdlib.h>
diff --git a/stringprintf.cc b/stringprintf.cc
index 63fd00b..4ca40d2 100644
--- a/stringprintf.cc
+++ b/stringprintf.cc
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// +build ignore
+
 #include "stringprintf.h"
 
 #include <assert.h>
diff --git a/strutil.cc b/strutil.cc
index 21dbe3b..3c84304 100644
--- a/strutil.cc
+++ b/strutil.cc
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// +build ignore
+
 #include "strutil.h"
 
 #include <ctype.h>
diff --git a/strutil.go b/strutil.go
index 689537f..d3b3792 100644
--- a/strutil.go
+++ b/strutil.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package main
+package kati
 
 import (
 	"bytes"
diff --git a/strutil_test.cc b/strutil_test.cc
index e8e773a..bcff29f 100644
--- a/strutil_test.cc
+++ b/strutil_test.cc
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// +build ignore
+
 #include "strutil.h"
 
 #include <assert.h>
diff --git a/strutil_test.go b/strutil_test.go
index 0cd77ed..362a09d 100644
--- a/strutil_test.go
+++ b/strutil_test.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package main
+package kati
 
 import (
 	"fmt"
diff --git a/symtab.go b/symtab.go
index f9f22de..f8fc1f8 100644
--- a/symtab.go
+++ b/symtab.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package main
+package kati
 
 import "sync"
 
diff --git a/value.cc b/value.cc
index 79acc71..87cc736 100644
--- a/value.cc
+++ b/value.cc
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// +build ignore
+
 #include "value.h"
 
 #include <vector>
diff --git a/var.cc b/var.cc
index 80f47bb..9ff8bb9 100644
--- a/var.cc
+++ b/var.cc
@@ -12,6 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// +build ignore
+
 #include "var.h"
 
 #include "log.h"
diff --git a/var.go b/var.go
index 7e4144e..568316d 100644
--- a/var.go
+++ b/var.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package main
+package kati
 
 import (
 	"bytes"
diff --git a/worker.go b/worker.go
index 3e61db7..fdd095a 100644
--- a/worker.go
+++ b/worker.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package main
+package kati
 
 import (
 	"container/heap"
@@ -87,7 +87,7 @@
 	return item
 }
 
-func NewWorker(wm *WorkerManager) *Worker {
+func newWorker(wm *WorkerManager) *Worker {
 	w := &Worker{
 		wm:       wm,
 		jobChan:  make(chan *Job),
@@ -154,7 +154,7 @@
 		}
 		switch s[0] {
 		case '@':
-			if !dryRunFlag {
+			if !DryRunFlag {
 				r.echo = false
 			}
 			s = s[1:]
@@ -171,10 +171,10 @@
 }
 
 func (r runner) run(output string) error {
-	if r.echo || dryRunFlag {
+	if r.echo || DryRunFlag {
 		fmt.Printf("%s\n", r.cmd)
 	}
-	if dryRunFlag {
+	if DryRunFlag {
 		return nil
 	}
 	args := []string{r.shell, "-c", r.cmd}
@@ -250,7 +250,7 @@
 
 func (wm *WorkerManager) handleJobs() {
 	for {
-		if !useParaFlag && len(wm.freeWorkers) == 0 {
+		if wm.para == nil && len(wm.freeWorkers) == 0 {
 			return
 		}
 		if wm.readyQueue.Len() == 0 {
@@ -259,7 +259,7 @@
 		j := heap.Pop(&wm.readyQueue).(*Job)
 		Logf("run: %s", j.n.Output)
 
-		if useParaFlag {
+		if wm.para != nil {
 			j.runners = j.createRunners()
 			if len(j.runners) == 0 {
 				wm.updateParents(j)
@@ -290,6 +290,7 @@
 }
 
 type WorkerManager struct {
+	maxJobs     int
 	jobs        []*Job
 	readyQueue  JobQueue
 	jobChan     chan *Job
@@ -307,8 +308,9 @@
 	finishCnt int
 }
 
-func NewWorkerManager() *WorkerManager {
+func NewWorkerManager(numJobs int, paraPath string) *WorkerManager {
 	wm := &WorkerManager{
+		maxJobs:     numJobs,
 		jobChan:     make(chan *Job),
 		resultChan:  make(chan JobResult),
 		newDepChan:  make(chan NewDep),
@@ -317,15 +319,15 @@
 		busyWorkers: make(map[*Worker]bool),
 	}
 
-	if useParaFlag {
+	if paraPath != "" {
 		wm.runnings = make(map[string]*Job)
 		wm.paraChan = make(chan *ParaResult)
-		wm.para = NewParaWorker(wm.paraChan)
+		wm.para = newParaWorker(wm.paraChan, numJobs, paraPath)
 		go wm.para.Run()
 	} else {
 		wm.busyWorkers = make(map[*Worker]bool)
-		for i := 0; i < jobsFlag; i++ {
-			w := NewWorker(wm)
+		for i := 0; i < numJobs; i++ {
+			w := newWorker(wm)
 			wm.freeWorkers = append(wm.freeWorkers, w)
 			go w.Run()
 		}
@@ -393,7 +395,7 @@
 			if pr.status < 0 && pr.signal < 0 {
 				j := wm.runnings[pr.output]
 				for _, r := range j.runners {
-					if r.echo || dryRunFlag {
+					if r.echo || DryRunFlag {
 						fmt.Printf("%s\n", r.cmd)
 					}
 				}
@@ -409,18 +411,18 @@
 		}
 		wm.handleJobs()
 
-		if useParaFlag {
+		if wm.para != nil {
 			numBusy := len(wm.runnings)
-			if numBusy > jobsFlag {
-				numBusy = jobsFlag
+			if numBusy > wm.maxJobs {
+				numBusy = wm.maxJobs
 			}
-			Logf("job=%d ready=%d free=%d busy=%d", len(wm.jobs)-wm.finishCnt, wm.readyQueue.Len(), jobsFlag-numBusy, numBusy)
+			Logf("job=%d ready=%d free=%d busy=%d", len(wm.jobs)-wm.finishCnt, wm.readyQueue.Len(), wm.maxJobs-numBusy, numBusy)
 		} else {
 			Logf("job=%d ready=%d free=%d busy=%d", len(wm.jobs)-wm.finishCnt, wm.readyQueue.Len(), len(wm.freeWorkers), len(wm.busyWorkers))
 		}
 	}
 
-	if useParaFlag {
+	if wm.para != nil {
 		Logf("Wait for para to finish")
 		wm.para.Wait()
 	} else {