diff options
Diffstat (limited to 'golang/cmd/kati/main.go')
| -rw-r--r-- | golang/cmd/kati/main.go | 321 |
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(®enNinja, "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 +} |
