aboutsummaryrefslogtreecommitdiffstats
path: root/golang/cmd/kati/main.go
diff options
context:
space:
mode:
Diffstat (limited to 'golang/cmd/kati/main.go')
-rw-r--r--golang/cmd/kati/main.go321
1 files changed, 321 insertions, 0 deletions
diff --git a/golang/cmd/kati/main.go b/golang/cmd/kati/main.go
new file mode 100644
index 0000000..e27c6c0
--- /dev/null
+++ b/golang/cmd/kati/main.go
@@ -0,0 +1,321 @@
+// 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"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "runtime/pprof"
+ "text/template"
+ "time"
+
+ "github.com/golang/glog"
+ "github.com/google/kati/golang/kati"
+)
+
+const shellDateTimeformat = time.RFC3339
+
+var (
+ makefileFlag string
+ jobsFlag int
+
+ loadJSON string
+ saveJSON string
+ loadGOB string
+ saveGOB string
+ useCache bool
+
+ m2n bool
+ goma bool
+
+ cpuprofile string
+ heapprofile string
+ memstats string
+ traceEventFile string
+ syntaxCheckOnlyFlag bool
+ queryFlag string
+ eagerCmdEvalFlag bool
+ generateNinja bool
+ regenNinja bool
+ ninjaSuffix string
+ gomaDir string
+ detectAndroidEcho bool
+ 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.BoolVar(&m2n, "m2n", false, "m2n mode")
+ flag.BoolVar(&goma, "goma", false, "ensure goma start")
+
+ 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.BoolVar(&regenNinja, "gen_regen_rule", false, "Generate regenerate build.ninja rule.")
+ flag.StringVar(&ninjaSuffix, "ninja_suffix", "", "suffix for ninja files.")
+ flag.StringVar(&gomaDir, "goma_dir", "", "If specified, use goma to build C/C++ files.")
+ // TODO(ukai): implement --regen
+ flag.BoolVar(&detectAndroidEcho, "detect_android_echo", false, "detect echo as ninja description.")
+
+ flag.StringVar(&shellDate, "shell_date", "", "specify $(shell date) time as "+shellDateTimeformat)
+
+ 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.UseFindEmulator, "use_find_emulator", false, "use find emulator")
+ 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 load(req kati.LoadReq) (*kati.DepGraph, error) {
+ if loadGOB != "" {
+ g, err := kati.GOB.Load(loadGOB)
+ return g, err
+ }
+ if loadJSON != "" {
+ g, err := kati.JSON.Load(loadJSON)
+ return g, err
+ }
+ g, err := kati.Load(req)
+ return g, err
+}
+
+func save(g *kati.DepGraph, targets []string) error {
+ var err error
+ if saveGOB != "" {
+ err = kati.GOB.Save(g, saveGOB, targets)
+ }
+ if saveJSON != "" {
+ serr := kati.JSON.Save(g, saveJSON, targets)
+ if err == nil {
+ err = serr
+ }
+ }
+ return err
+}
+
+func m2nsetup() {
+ fmt.Println("kati: m2n mode")
+ generateNinja = true
+ kati.IgnoreOptionalInclude = "out/%.P"
+ kati.UseFindEmulator = true
+}
+
+func gomasetup() {
+ for _, k := range []string{"CC_WRAPPER", "CXX_WRAPPER", "JAVAC_WRAPPER"} {
+ v := os.Getenv(k)
+ if v != "" {
+ fmt.Printf("Note: %s=%s may confuse m2n --goma, unsetting", k, v)
+ os.Unsetenv(k)
+ }
+ }
+
+ if gomaDir == "" {
+ gomaDir = os.Getenv("GOMA_DIR")
+ if gomaDir == "" {
+ gomaDir = os.ExpandEnv("${HOME}/goma")
+ }
+ }
+ fmt.Printf("kati: setup goma: %s\n", gomaDir)
+ cmd := exec.Command(filepath.Join(gomaDir, "goma_ctl.py"), "ensure_start")
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ err := cmd.Run()
+ if err != nil {
+ fmt.Printf("goma failed to start: %v", err)
+ os.Exit(1)
+ }
+}
+
+func main() {
+ runtime.GOMAXPROCS(runtime.NumCPU())
+ m2ncmd := false
+ if filepath.Base(os.Args[0]) == "m2n" {
+ m2nsetup()
+ m2ncmd = true
+ }
+ flag.Parse()
+ args := flag.Args()
+ if m2n {
+ generateNinja = true
+ if !m2ncmd {
+ m2nsetup()
+ }
+ if len(args) > 1 {
+ fmt.Println("use only first argument as ONE_SHOT_MAKEFILE. ignore rest")
+ }
+ if len(args) > 0 {
+ err := os.Setenv("ONE_SHOT_MAKEFILE", filepath.Join(args[0], "Android.mk"))
+ if err != nil {
+ fmt.Println(err)
+ os.Exit(1)
+ }
+ fmt.Printf("ONE_SHOT_MAKEFILE=%s\n", os.ExpandEnv("${ONE_SHOT_MAKEFILE}"))
+ }
+ args = args[:0]
+ }
+ if goma {
+ gomasetup()
+ }
+ err := katiMain(args)
+ if err != nil {
+ fmt.Println(err)
+ // http://www.gnu.org/software/make/manual/html_node/Running.html
+ os.Exit(2)
+ }
+}
+
+func katiMain(args []string) error {
+ defer glog.Flush()
+ if cpuprofile != "" {
+ f, err := os.Create(cpuprofile)
+ if err != nil {
+ return err
+ }
+ pprof.StartCPUProfile(f)
+ defer pprof.StopCPUProfile()
+ }
+ if heapprofile != "" {
+ defer writeHeapProfile()
+ }
+ defer kati.DumpStats()
+ if memstats != "" {
+ ms := memStatsDumper{
+ Template: template.Must(template.New("memstats").Parse(memstats)),
+ }
+ ms.dump()
+ defer ms.dump()
+ }
+ if traceEventFile != "" {
+ f, err := os.Create(traceEventFile)
+ if err != nil {
+ panic(err)
+ }
+ kati.TraceEventStart(f)
+ defer 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
+ }
+
+ req := kati.FromCommandLine(args)
+ if makefileFlag != "" {
+ req.Makefile = makefileFlag
+ }
+ req.EnvironmentVars = os.Environ()
+ req.UseCache = useCache
+ req.EagerEvalCommand = eagerCmdEvalFlag
+
+ g, err := load(req)
+ if err != nil {
+ return err
+ }
+
+ err = save(g, req.Targets)
+ if err != nil {
+ return err
+ }
+
+ if generateNinja {
+ var args []string
+ if regenNinja {
+ args = os.Args
+ }
+ n := kati.NinjaGenerator{
+ Args: args,
+ Suffix: ninjaSuffix,
+ GomaDir: gomaDir,
+ DetectAndroidEcho: detectAndroidEcho,
+ }
+ return n.Save(g, "", req.Targets)
+ }
+
+ if syntaxCheckOnlyFlag {
+ return nil
+ }
+
+ if queryFlag != "" {
+ kati.Query(os.Stdout, queryFlag, g)
+ return nil
+ }
+
+ execOpt := &kati.ExecutorOpt{
+ NumJobs: jobsFlag,
+ }
+ ex, err := kati.NewExecutor(execOpt)
+ if err != nil {
+ return err
+ }
+ err = ex.Exec(g, req.Targets)
+ if err != nil {
+ return err
+ }
+ return nil
+}