blob: bf0e9b91eb3e3b4b79dcdfb00f69df0d247f7e52 [file] [log] [blame]
Fumitoshi Ukai119dc912015-03-30 16:52:41 +09001package main
2
Shinichiro Hamaji574a4ef2015-04-06 15:20:28 +09003import (
Fumitoshi Ukai89215d02015-04-30 13:09:44 +09004 "bytes"
Shinichiro Hamaji4220be32015-05-26 13:30:18 +09005 "crypto/sha1"
Shinichiro Hamaji574a4ef2015-04-06 15:20:28 +09006 "flag"
7 "fmt"
8 "os"
Shinichiro Hamajicedc5c82015-05-13 17:03:20 +09009 "path/filepath"
Fumitoshi Ukai89215d02015-04-30 13:09:44 +090010 "runtime"
Fumitoshi Ukai5d85a702015-04-08 22:44:36 +090011 "runtime/pprof"
Shinichiro Hamaji574a4ef2015-04-06 15:20:28 +090012 "strings"
Fumitoshi Ukai89215d02015-04-30 13:09:44 +090013 "text/template"
Shinichiro Hamaji750988e2015-04-12 10:14:32 +090014 "time"
Shinichiro Hamaji574a4ef2015-04-06 15:20:28 +090015)
Shinichiro Hamaji07259e02015-04-02 03:14:41 +090016
Fumitoshi Ukai5d85a702015-04-08 22:44:36 +090017var (
Shinichiro Hamaji4926aea2015-05-25 18:32:18 +090018 katiLogFlag bool
19 makefileFlag string
20 dryRunFlag bool
21 jobsFlag int
22 cpuprofile string
23 heapprofile string
24 memstats string
25 katiStatsFlag bool
26 katiPeriodicStatsFlag bool
27 katiEvalStatsFlag bool
28 loadJson string
29 saveJson string
30 loadGob string
31 saveGob string
32 syntaxCheckOnlyFlag bool
33 queryFlag string
34 eagerCmdEvalFlag bool
35 useParaFlag bool
36 useCache bool
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +090037 generateNinja bool
Shinichiro Hamajicedc5c82015-05-13 17:03:20 +090038
39 katiDir string
Fumitoshi Ukai5d85a702015-04-08 22:44:36 +090040)
Shinichiro Hamaji07259e02015-04-02 03:14:41 +090041
Shinichiro Hamaji750ae2e2015-05-20 12:59:15 +090042type DepGraph struct {
Shinichiro Hamajif3ea5d02015-05-20 17:13:00 +090043 nodes []*DepNode
44 vars Vars
Shinichiro Hamaji45cde1d2015-05-25 18:21:23 +090045 readMks []*ReadMakefile
Shinichiro Hamajif3ea5d02015-05-20 17:13:00 +090046 isCached bool
Shinichiro Hamaji750ae2e2015-05-20 12:59:15 +090047}
48
Shinichiro Hamaji07259e02015-04-02 03:14:41 +090049func parseFlags() {
50 // TODO: Make this default and replace this by -d flag.
Fumitoshi Ukaib07bcc32015-04-07 12:03:31 +090051 flag.BoolVar(&katiLogFlag, "kati_log", false, "Verbose kati specific log")
Shinichiro Hamaji601cbb72015-04-02 05:55:47 +090052 flag.StringVar(&makefileFlag, "f", "", "Use it as a makefile")
Shinichiro Hamaji07259e02015-04-02 03:14:41 +090053
Shinichiro Hamaji07e146b2015-04-02 16:17:24 +090054 flag.BoolVar(&dryRunFlag, "n", false, "Only print the commands that would be executed")
Shinichiro Hamaji69bb7e42015-04-27 17:54:42 +090055
56 flag.IntVar(&jobsFlag, "j", 1, "Allow N jobs at once.")
57
Shinichiro Hamaji1833c282015-04-28 05:02:27 +090058 flag.StringVar(&loadGob, "load", "", "")
59 flag.StringVar(&saveGob, "save", "", "")
Shinichiro Hamaji8c9d0e32015-04-28 03:30:19 +090060 flag.StringVar(&loadJson, "load_json", "", "")
Shinichiro Hamaji0df24ec2015-04-27 21:29:53 +090061 flag.StringVar(&saveJson, "save_json", "", "")
62
Fumitoshi Ukai5d85a702015-04-08 22:44:36 +090063 flag.StringVar(&cpuprofile, "kati_cpuprofile", "", "write cpu profile to `file`")
Shinichiro Hamaji5ff9b342015-04-12 04:17:10 +090064 flag.StringVar(&heapprofile, "kati_heapprofile", "", "write heap profile to `file`")
Fumitoshi Ukai89215d02015-04-30 13:09:44 +090065 flag.StringVar(&memstats, "kati_memstats", "", "Show memstats with given templates")
Shinichiro Hamaji750988e2015-04-12 10:14:32 +090066 flag.BoolVar(&katiStatsFlag, "kati_stats", false, "Show a bunch of statistics")
Shinichiro Hamaji4926aea2015-05-25 18:32:18 +090067 flag.BoolVar(&katiPeriodicStatsFlag, "kati_periodic_stats", false, "Show a bunch of periodic statistics")
Fumitoshi Ukai586b02a2015-05-08 00:23:10 +090068 flag.BoolVar(&katiEvalStatsFlag, "kati_eval_stats", false, "Show eval statistics")
Shinichiro Hamajib41fd502015-04-29 03:34:07 +090069 flag.BoolVar(&eagerCmdEvalFlag, "eager_cmd_eval", false, "Eval commands first.")
Shinichiro Hamajicedc5c82015-05-13 17:03:20 +090070 flag.BoolVar(&useParaFlag, "use_para", false, "Use para.")
Shinichiro Hamajibf66cb02015-04-28 16:21:35 +090071 flag.BoolVar(&syntaxCheckOnlyFlag, "c", false, "Syntax check only.")
Shinichiro Hamaji1eb71112015-04-29 02:08:52 +090072 flag.StringVar(&queryFlag, "query", "", "Show the target info")
Shinichiro Hamajib0d2e2f2015-05-20 16:42:59 +090073 // TODO: Make this default.
74 flag.BoolVar(&useCache, "use_cache", false, "Use cache.")
Shinichiro Hamaji6aa86ac2015-05-26 15:21:11 +090075 flag.BoolVar(&generateNinja, "ninja", false, "Generate build.ninja.")
Shinichiro Hamaji07259e02015-04-02 03:14:41 +090076 flag.Parse()
77}
Shinichiro Hamajib13f3d52015-03-30 19:29:44 +090078
Fumitoshi Ukai8ae323b2015-04-08 17:44:38 +090079func parseCommandLine() ([]string, []string) {
80 var vars []string
81 var targets []string
82 for _, arg := range flag.Args() {
83 if strings.IndexByte(arg, '=') >= 0 {
84 vars = append(vars, arg)
85 continue
86 }
87 targets = append(targets, arg)
88 }
89 return vars, targets
90}
91
Shinichiro Hamaji3cd63962015-04-06 16:38:45 +090092func getBootstrapMakefile(targets []string) Makefile {
Shinichiro Hamajia5dee372015-04-03 16:41:30 +090093 bootstrap := `
94CC:=cc
95CXX:=g++
Fumitoshi Ukaib75c5f32015-04-06 17:28:53 +090096AR:=ar
Shinichiro Hamajia5dee372015-04-03 16:41:30 +090097MAKE:=kati
98# Pretend to be GNU make 3.81, for compatibility.
99MAKE_VERSION:=3.81
Shinichiro Hamaji89551c92015-04-11 19:33:03 +0900100SHELL:=/bin/sh
Shinichiro Hamajia5dee372015-04-03 16:41:30 +0900101# TODO: Add more builtin vars.
102
103# http://www.gnu.org/software/make/manual/make.html#Catalogue-of-Rules
104# The document above is actually not correct. See default.c:
105# http://git.savannah.gnu.org/cgit/make.git/tree/default.c?id=4.1
106.c.o:
107 $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -o $@ $<
108.cc.o:
109 $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -o $@ $<
110# TODO: Add more builtin rules.
111`
Shinichiro Hamaji4c3dc402015-05-28 20:49:00 +0900112 bootstrap += fmt.Sprintf("MAKECMDGOALS:=%s\n", strings.Join(targets, " "))
113 cwd, err := filepath.Abs(".")
114 if err != nil {
115 panic(err)
116 }
117 bootstrap += fmt.Sprintf("CURDIR:=%s\n", cwd)
Fumitoshi Ukai103a7962015-04-08 15:53:53 +0900118 mk, err := ParseMakefileString(bootstrap, BootstrapMakefile, 0)
Shinichiro Hamajia5dee372015-04-03 16:41:30 +0900119 if err != nil {
120 panic(err)
121 }
122 return mk
123}
124
Shinichiro Hamaji5ff9b342015-04-12 04:17:10 +0900125func maybeWriteHeapProfile() {
126 if heapprofile != "" {
127 f, err := os.Create(heapprofile)
128 if err != nil {
129 panic(err)
130 }
131 pprof.WriteHeapProfile(f)
132 }
133}
134
Shinichiro Hamajib0d2e2f2015-05-20 16:42:59 +0900135func getDepGraph(clvars []string, targets []string) *DepGraph {
Shinichiro Hamaji750988e2015-04-12 10:14:32 +0900136 startTime := time.Now()
137
Shinichiro Hamaji1833c282015-04-28 05:02:27 +0900138 if loadGob != "" {
Shinichiro Hamaji750ae2e2015-05-20 12:59:15 +0900139 g := LoadDepGraph(loadGob)
Shinichiro Hamaji1833c282015-04-28 05:02:27 +0900140 LogStats("deserialize time: %q", time.Now().Sub(startTime))
Shinichiro Hamaji750ae2e2015-05-20 12:59:15 +0900141 return g
Shinichiro Hamaji1833c282015-04-28 05:02:27 +0900142 }
Shinichiro Hamaji8c9d0e32015-04-28 03:30:19 +0900143 if loadJson != "" {
Shinichiro Hamaji750ae2e2015-05-20 12:59:15 +0900144 g := LoadDepGraphFromJson(loadJson)
Shinichiro Hamaji8c9d0e32015-04-28 03:30:19 +0900145 LogStats("deserialize time: %q", time.Now().Sub(startTime))
Shinichiro Hamaji750ae2e2015-05-20 12:59:15 +0900146 return g
Shinichiro Hamaji8c9d0e32015-04-28 03:30:19 +0900147 }
148
Shinichiro Hamajib0d2e2f2015-05-20 16:42:59 +0900149 makefile := makefileFlag
150 if makefile == "" {
151 makefile = GetDefaultMakefile()
152 }
153
154 if useCache {
Shinichiro Hamajif3ea5d02015-05-20 17:13:00 +0900155 g := LoadDepGraphCache(makefile, targets)
Shinichiro Hamajib0d2e2f2015-05-20 16:42:59 +0900156 if g != nil {
157 return g
158 }
159 }
160
Shinichiro Hamaji3cd63962015-04-06 16:38:45 +0900161 bmk := getBootstrapMakefile(targets)
Shinichiro Hamajic3840812015-04-02 02:10:20 +0900162
Shinichiro Hamaji45cde1d2015-05-25 18:21:23 +0900163 content, err := readFile(makefile)
164 if err != nil {
165 panic(err)
166 }
167 mk, err := ParseMakefile(content, makefile)
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900168 if err != nil {
169 panic(err)
170 }
171
172 for _, stmt := range mk.stmts {
173 stmt.show()
174 }
175
Shinichiro Hamajic3840812015-04-02 02:10:20 +0900176 mk.stmts = append(bmk.stmts, mk.stmts...)
177
Shinichiro Hamajicc919ae2015-04-09 17:23:30 +0900178 vars := make(Vars)
Shinichiro Hamaji574a4ef2015-04-06 15:20:28 +0900179 for _, env := range os.Environ() {
180 kv := strings.SplitN(env, "=", 2)
181 Log("envvar %q", kv)
182 if len(kv) < 2 {
183 panic(fmt.Sprintf("A weird environ variable %q", kv))
184 }
185 vars.Assign(kv[0], RecursiveVar{
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900186 expr: literal(kv[1]),
Shinichiro Hamaji574a4ef2015-04-06 15:20:28 +0900187 origin: "environment",
188 })
189 }
Shinichiro Hamaji39728f12015-04-11 20:12:23 +0900190 vars.Assign("MAKEFILE_LIST", SimpleVar{value: []byte{}, origin: "file"})
Fumitoshi Ukai8ae323b2015-04-08 17:44:38 +0900191 for _, v := range clvars {
192 kv := strings.SplitN(v, "=", 2)
193 Log("cmdlinevar %q", kv)
194 if len(kv) < 2 {
195 panic(fmt.Sprintf("unexpected command line var %q", kv))
196 }
197 vars.Assign(kv[0], RecursiveVar{
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900198 expr: literal(kv[1]),
Fumitoshi Ukai8ae323b2015-04-08 17:44:38 +0900199 origin: "command line",
200 })
201 }
Shinichiro Hamaji491e73f2015-04-07 12:41:59 +0900202
Shinichiro Hamajid7bef602015-03-30 19:55:32 +0900203 er, err := Eval(mk, vars)
Fumitoshi Ukaif8efa0a2015-03-30 18:10:11 +0900204 if err != nil {
205 panic(err)
206 }
Shinichiro Hamaji574a4ef2015-04-06 15:20:28 +0900207
Shinichiro Hamaji63a77d42015-04-12 01:04:07 +0900208 vars.Merge(er.vars)
Shinichiro Hamaji574a4ef2015-04-06 15:20:28 +0900209
Shinichiro Hamaji3887c502015-04-12 22:07:56 +0900210 LogStats("eval time: %q", time.Now().Sub(startTime))
Fumitoshi Ukai586b02a2015-05-08 00:23:10 +0900211 LogStats("shell func time: %q", shellFuncTime)
Shinichiro Hamaji750988e2015-04-12 10:14:32 +0900212
213 startTime = time.Now()
Shinichiro Hamaji17a8a6e2015-04-20 21:44:42 +0900214 db := NewDepBuilder(er, vars)
Shinichiro Hamajid68b5bf2015-04-29 00:27:11 +0900215 LogStats("dep build prepare time: %q", time.Now().Sub(startTime))
Shinichiro Hamaji750988e2015-04-12 10:14:32 +0900216
Shinichiro Hamaji8843a052015-04-13 16:46:56 +0900217 startTime = time.Now()
Shinichiro Hamaji17a8a6e2015-04-20 21:44:42 +0900218 nodes, err2 := db.Eval(targets)
219 if err2 != nil {
220 panic(err2)
221 }
Shinichiro Hamajid68b5bf2015-04-29 00:27:11 +0900222 LogStats("dep build time: %q", time.Now().Sub(startTime))
Shinichiro Hamaji45cde1d2015-05-25 18:21:23 +0900223 var readMks []*ReadMakefile
Shinichiro Hamajib0d2e2f2015-05-20 16:42:59 +0900224 // Always put the root Makefile as the first element.
Shinichiro Hamaji45cde1d2015-05-25 18:21:23 +0900225 readMks = append(readMks, &ReadMakefile{
226 Filename: makefile,
Shinichiro Hamaji4220be32015-05-26 13:30:18 +0900227 Hash: sha1.Sum(content),
Shinichiro Hamaji45cde1d2015-05-25 18:21:23 +0900228 State: FILE_EXISTS,
Shinichiro Hamajif3ea5d02015-05-20 17:13:00 +0900229 })
Shinichiro Hamajib0d2e2f2015-05-20 16:42:59 +0900230 readMks = append(readMks, er.readMks...)
231 return &DepGraph{
Shinichiro Hamajif3ea5d02015-05-20 17:13:00 +0900232 nodes: nodes,
233 vars: vars,
234 readMks: readMks,
Shinichiro Hamaji750ae2e2015-05-20 12:59:15 +0900235 }
Shinichiro Hamaji1d959072015-04-27 22:57:44 +0900236}
237
Shinichiro Hamajicedc5c82015-05-13 17:03:20 +0900238func findKatiDir() {
239 switch runtime.GOOS {
240 case "linux":
241 kati, err := os.Readlink("/proc/self/exe")
242 if err != nil {
243 panic(err)
244 }
245 katiDir = filepath.Dir(kati)
246 default:
247 panic(fmt.Sprintf("unknown OS: %s", runtime.GOOS))
248 }
249}
250
Shinichiro Hamaji1d959072015-04-27 22:57:44 +0900251func main() {
Shinichiro Hamaji5e07dd32015-05-12 15:37:37 +0900252 runtime.GOMAXPROCS(runtime.NumCPU())
Shinichiro Hamajicedc5c82015-05-13 17:03:20 +0900253 findKatiDir()
Shinichiro Hamaji1d959072015-04-27 22:57:44 +0900254 parseFlags()
255 if cpuprofile != "" {
256 f, err := os.Create(cpuprofile)
257 if err != nil {
258 panic(err)
259 }
260 pprof.StartCPUProfile(f)
261 defer pprof.StopCPUProfile()
262 }
263 defer maybeWriteHeapProfile()
264 defer dumpStats()
Fumitoshi Ukai89215d02015-04-30 13:09:44 +0900265 if memstats != "" {
266 t := template.Must(template.New("memstats").Parse(memstats))
267 var ms runtime.MemStats
268 runtime.ReadMemStats(&ms)
269 var buf bytes.Buffer
270 err := t.Execute(&buf, ms)
271 fmt.Println(buf.String())
272 if err != nil {
273 panic(err)
274 }
275 defer func() {
276 var ms runtime.MemStats
277 runtime.ReadMemStats(&ms)
278 var buf bytes.Buffer
279 t.Execute(&buf, ms)
280 fmt.Println(buf.String())
281 }()
282 }
Shinichiro Hamaji1d959072015-04-27 22:57:44 +0900283
284 clvars, targets := parseCommandLine()
285
Shinichiro Hamaji750ae2e2015-05-20 12:59:15 +0900286 g := getDepGraph(clvars, targets)
287 nodes := g.nodes
288 vars := g.vars
Shinichiro Hamaji17a8a6e2015-04-20 21:44:42 +0900289
Shinichiro Hamajib41fd502015-04-29 03:34:07 +0900290 if eagerCmdEvalFlag {
291 startTime := time.Now()
292 EvalCommands(nodes, vars)
293 LogStats("eager eval command time: %q", time.Now().Sub(startTime))
294 }
295
Shinichiro Hamaji1833c282015-04-28 05:02:27 +0900296 if saveGob != "" {
297 startTime := time.Now()
Shinichiro Hamaji750ae2e2015-05-20 12:59:15 +0900298 DumpDepGraph(g, saveGob, targets)
Shinichiro Hamaji1833c282015-04-28 05:02:27 +0900299 LogStats("serialize time: %q", time.Now().Sub(startTime))
300 }
Shinichiro Hamaji0df24ec2015-04-27 21:29:53 +0900301 if saveJson != "" {
Shinichiro Hamajic8bc7312015-04-28 02:48:03 +0900302 startTime := time.Now()
Shinichiro Hamaji750ae2e2015-05-20 12:59:15 +0900303 DumpDepGraphAsJson(g, saveJson, targets)
Shinichiro Hamajic8bc7312015-04-28 02:48:03 +0900304 LogStats("serialize time: %q", time.Now().Sub(startTime))
Shinichiro Hamaji0df24ec2015-04-27 21:29:53 +0900305 }
306
Shinichiro Hamajif3ea5d02015-05-20 17:13:00 +0900307 if useCache && !g.isCached {
Shinichiro Hamajib0d2e2f2015-05-20 16:42:59 +0900308 startTime := time.Now()
309 DumpDepGraphCache(g, targets)
310 LogStats("serialize time: %q", time.Now().Sub(startTime))
311 }
312
Shinichiro Hamajie0eb0312015-05-27 18:33:18 +0900313 if generateNinja {
314 startTime := time.Now()
315 GenerateNinja(g)
316 LogStats("generate ninja time: %q", time.Now().Sub(startTime))
317 return
318 }
319
Shinichiro Hamajibf66cb02015-04-28 16:21:35 +0900320 if syntaxCheckOnlyFlag {
321 return
322 }
323
Shinichiro Hamaji1eb71112015-04-29 02:08:52 +0900324 if queryFlag != "" {
325 HandleQuery(queryFlag, nodes, vars)
326 return
327 }
328
Shinichiro Hamaji1d959072015-04-27 22:57:44 +0900329 startTime := time.Now()
Shinichiro Hamaji17a8a6e2015-04-20 21:44:42 +0900330 ex := NewExecutor(vars)
Shinichiro Hamaji1d959072015-04-27 22:57:44 +0900331 err := ex.Exec(nodes)
Fumitoshi Ukai3e5161e2015-04-01 22:42:10 +0900332 if err != nil {
333 panic(err)
334 }
Shinichiro Hamaji3887c502015-04-12 22:07:56 +0900335 LogStats("exec time: %q", time.Now().Sub(startTime))
Fumitoshi Ukai119dc912015-03-30 16:52:41 +0900336}