github.com/maruel/nin@v0.0.0-20220112143044-f35891e3ce7e/cmd/nin/ninja.go (about)

     1  // Copyright 2011 Google Inc. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package main
    16  
    17  import (
    18  	"errors"
    19  	"flag"
    20  	"fmt"
    21  	"log"
    22  	"os"
    23  	"path/filepath"
    24  	"runtime"
    25  	"runtime/debug"
    26  	"runtime/pprof"
    27  	"runtime/trace"
    28  	"sort"
    29  	"strconv"
    30  	"strings"
    31  
    32  	"github.com/maruel/nin"
    33  )
    34  
    35  // Command-line options.
    36  type options struct {
    37  	// Build file to load.
    38  	inputFile string
    39  
    40  	// Directory to change into before running.
    41  	workingDir string
    42  
    43  	// tool to run rather than building.
    44  	tool *tool
    45  
    46  	// build.ninja parsing options.
    47  	parserOpts nin.ParseManifestOpts
    48  
    49  	cpuprofile string
    50  	memprofile string
    51  	trace      string
    52  }
    53  
    54  // The Ninja main() loads up a series of data structures; various tools need
    55  // to poke into these, so store them as fields on an object.
    56  type ninjaMain struct {
    57  	// Command line used to run Ninja.
    58  	ninjaCommand string
    59  
    60  	// Build configuration set from flags (e.g. parallelism).
    61  	config *nin.BuildConfig
    62  
    63  	// Loaded state (rules, nodes).
    64  	state nin.State
    65  
    66  	// Functions for accessing the disk.
    67  	di nin.RealDiskInterface
    68  
    69  	// The build directory, used for storing the build log etc.
    70  	buildDir string
    71  
    72  	buildLog nin.BuildLog
    73  	depsLog  nin.DepsLog
    74  
    75  	// The type of functions that are the entry points to tools (subcommands).
    76  
    77  	startTimeMillis int64
    78  }
    79  
    80  func newNinjaMain(ninjaCommand string, config *nin.BuildConfig) ninjaMain {
    81  	return ninjaMain{
    82  		ninjaCommand:    ninjaCommand,
    83  		config:          config,
    84  		state:           nin.NewState(),
    85  		buildLog:        nin.NewBuildLog(),
    86  		startTimeMillis: nin.GetTimeMillis(),
    87  	}
    88  }
    89  
    90  func (n *ninjaMain) Close() error {
    91  	// TODO(maruel): Ensure the file handle is cleanly closed.
    92  	err1 := n.depsLog.Close()
    93  	err2 := n.buildLog.Close()
    94  	if err1 != nil {
    95  		return err1
    96  	}
    97  	return err2
    98  }
    99  
   100  type toolFunc func(*ninjaMain, *options, []string) int
   101  
   102  func (n *ninjaMain) IsPathDead(s string) bool {
   103  	nd := n.state.Paths[s]
   104  	if nd != nil && nd.InEdge != nil {
   105  		return false
   106  	}
   107  	// Just checking nd isn't enough: If an old output is both in the build log
   108  	// and in the deps log, it will have a Node object in state.  (It will also
   109  	// have an in edge if one of its inputs is another output that's in the deps
   110  	// log, but having a deps edge product an output that's input to another deps
   111  	// edge is rare, and the first recompaction will delete all old outputs from
   112  	// the deps log, and then a second recompaction will clear the build log,
   113  	// which seems good enough for this corner case.)
   114  	// Do keep entries around for files which still exist on disk, for
   115  	// generators that want to use this information.
   116  	mtime, err := n.di.Stat(s)
   117  	if mtime == -1 {
   118  		errorf("%s", err) // Log and ignore Stat() errors.
   119  	}
   120  	return mtime == 0
   121  }
   122  
   123  // Subtools, accessible via "-t foo".
   124  type tool struct {
   125  	// Short name of the tool.
   126  	name string
   127  
   128  	// Description (shown in "-t list").
   129  	desc string
   130  
   131  	when when
   132  
   133  	// Implementation of the tool.
   134  	tool toolFunc
   135  }
   136  
   137  // when to run the tool.
   138  type when int32
   139  
   140  const (
   141  	// Run after parsing the command-line flags and potentially changing
   142  	// the current working directory (as early as possible).
   143  	runAfterFlags when = iota
   144  
   145  	// Run after loading build.ninja.
   146  	runAfterLoad
   147  
   148  	// Run after loading the build/deps logs.
   149  	runAfterLogs
   150  )
   151  
   152  // Print usage information.
   153  func usage() {
   154  	fmt.Fprintf(os.Stderr, "usage: nin [options] [targets...]\n\n")
   155  	fmt.Fprintf(os.Stderr, "if targets are unspecified, builds the 'default' target (see manual).\n\n")
   156  	flag.PrintDefaults()
   157  }
   158  
   159  // Choose a default value for the -j (parallelism) flag.
   160  func guessParallelism() int {
   161  	switch processors := runtime.NumCPU(); processors {
   162  	case 0, 1:
   163  		return 2
   164  	case 2:
   165  		return 3
   166  	default:
   167  		return processors + 2
   168  	}
   169  }
   170  
   171  // Rebuild the build manifest, if necessary.
   172  // Returns true if the manifest was rebuilt.
   173  // Rebuild the manifest, if necessary.
   174  // Fills in \a err on error.
   175  // @return true if the manifest was rebuilt.
   176  func (n *ninjaMain) RebuildManifest(inputFile string, status nin.Status) (bool, error) {
   177  	path := inputFile
   178  	if len(path) == 0 {
   179  		return false, errors.New("empty path")
   180  	}
   181  	node := n.state.Paths[nin.CanonicalizePath(path)]
   182  	if node == nil {
   183  		return false, errors.New("path not found")
   184  	}
   185  
   186  	builder := nin.NewBuilder(&n.state, n.config, &n.buildLog, &n.depsLog, &n.di, status, n.startTimeMillis)
   187  	if dirty, err := builder.AddTarget(node); !dirty {
   188  		return false, err
   189  	}
   190  
   191  	if builder.AlreadyUpToDate() {
   192  		return false, nil // Not an error, but we didn't rebuild.
   193  	}
   194  
   195  	if err := builder.Build(); err != nil {
   196  		return false, err
   197  	}
   198  
   199  	// The manifest was only rebuilt if it is now dirty (it may have been cleaned
   200  	// by a restat).
   201  	if !node.Dirty {
   202  		// Reset the state to prevent problems like
   203  		// https://github.com/ninja-build/ninja/issues/874
   204  		n.state.Reset()
   205  		return false, nil
   206  	}
   207  
   208  	return true, nil
   209  }
   210  
   211  // collectTarget gets the Node for a given command-line path, handling features
   212  // like spell correction.
   213  func (n *ninjaMain) collectTarget(cpath string) (*nin.Node, error) {
   214  	path := cpath
   215  	if len(path) == 0 {
   216  		return nil, errors.New("empty path")
   217  	}
   218  	path, slashBits := nin.CanonicalizePathBits(path)
   219  
   220  	// Special syntax: "foo.cc^" means "the first output of foo.cc".
   221  	firstDependent := false
   222  	if path != "" && path[len(path)-1] == '^' {
   223  		path = path[:len(path)-1]
   224  		firstDependent = true
   225  	}
   226  
   227  	node := n.state.Paths[path]
   228  	if node != nil {
   229  		if firstDependent {
   230  			if len(node.OutEdges) == 0 {
   231  				revDeps := n.depsLog.GetFirstReverseDepsNode(node)
   232  				if revDeps == nil {
   233  					// TODO(maruel): Use %q for real quoting.
   234  					return nil, fmt.Errorf("'%s' has no out edge", path)
   235  				}
   236  				node = revDeps
   237  			} else {
   238  				edge := node.OutEdges[0]
   239  				if len(edge.Outputs) == 0 {
   240  					edge.Dump("")
   241  					fatalf("edge has no outputs")
   242  				}
   243  				node = edge.Outputs[0]
   244  			}
   245  		}
   246  		return node, nil
   247  	}
   248  	// TODO(maruel): Use %q for real quoting.
   249  	err := fmt.Sprintf("unknown target '%s'", nin.PathDecanonicalized(path, slashBits))
   250  	if path == "clean" {
   251  		err += ", did you mean 'nin -t clean'?"
   252  	} else if path == "help" {
   253  		err += ", did you mean 'nin -h'?"
   254  	} else {
   255  		suggestion := n.state.SpellcheckNode(path)
   256  		if suggestion != nil {
   257  			// TODO(maruel): Use %q for real quoting.
   258  			err += fmt.Sprintf(", did you mean '%s'?", suggestion.Path)
   259  		}
   260  	}
   261  	return nil, errors.New(err)
   262  }
   263  
   264  // collectTargetsFromArgs calls collectTarget for all command-line arguments.
   265  func (n *ninjaMain) collectTargetsFromArgs(args []string) ([]*nin.Node, error) {
   266  	var targets []*nin.Node
   267  	if len(args) == 0 {
   268  		targets = n.state.DefaultNodes()
   269  		if len(targets) == 0 {
   270  			return targets, errors.New("could not determine root nodes of build graph")
   271  		}
   272  		return targets, nil
   273  	}
   274  
   275  	for i := 0; i < len(args); i++ {
   276  		node, err := n.collectTarget(args[i])
   277  		if node == nil {
   278  			return targets, err
   279  		}
   280  		targets = append(targets, node)
   281  	}
   282  	return targets, nil
   283  }
   284  
   285  // The various subcommands, run via "-t XXX".
   286  func toolGraph(n *ninjaMain, opts *options, args []string) int {
   287  	nodes, err := n.collectTargetsFromArgs(args)
   288  	if err != nil {
   289  		errorf("%s", err)
   290  		return 1
   291  	}
   292  
   293  	graph := nin.NewGraphViz(&n.state, &n.di)
   294  	graph.Start()
   295  	for _, n := range nodes {
   296  		graph.AddTarget(n)
   297  	}
   298  	graph.Finish()
   299  	return 0
   300  }
   301  
   302  func toolQuery(n *ninjaMain, opts *options, args []string) int {
   303  	if len(args) == 0 {
   304  		errorf("expected a target to query")
   305  		return 1
   306  	}
   307  
   308  	dyndepLoader := nin.NewDyndepLoader(&n.state, &n.di)
   309  
   310  	for i := 0; i < len(args); i++ {
   311  		node, err := n.collectTarget(args[i])
   312  		if err != nil {
   313  			errorf("%s", err)
   314  			return 1
   315  		}
   316  
   317  		fmt.Printf("%s:\n", node.Path)
   318  		if edge := node.InEdge; edge != nil {
   319  			if edge.Dyndep != nil && edge.Dyndep.DyndepPending {
   320  				if err := dyndepLoader.LoadDyndeps(edge.Dyndep, nin.DyndepFile{}); err != nil {
   321  					warningf("%s\n", err)
   322  				}
   323  			}
   324  			fmt.Printf("  input: %s\n", edge.Rule.Name)
   325  			for in := 0; in < len(edge.Inputs); in++ {
   326  				label := ""
   327  				if edge.IsImplicit(in) {
   328  					label = "| "
   329  				} else if edge.IsOrderOnly(in) {
   330  					label = "|| "
   331  				}
   332  				fmt.Printf("    %s%s\n", label, edge.Inputs[in].Path)
   333  			}
   334  			if len(edge.Validations) != 0 {
   335  				fmt.Printf("  validations:\n")
   336  				for _, validation := range edge.Validations {
   337  					fmt.Printf("    %s\n", validation.Path)
   338  				}
   339  			}
   340  		}
   341  		fmt.Printf("  outputs:\n")
   342  		for _, edge := range node.OutEdges {
   343  			for _, out := range edge.Outputs {
   344  				fmt.Printf("    %s\n", out.Path)
   345  			}
   346  		}
   347  		validationEdges := node.ValidationOutEdges
   348  		if len(validationEdges) != 0 {
   349  			fmt.Printf("  validation for:\n")
   350  			for _, edge := range validationEdges {
   351  				for _, out := range edge.Outputs {
   352  					fmt.Printf("    %s\n", out.Path)
   353  				}
   354  			}
   355  		}
   356  	}
   357  	return 0
   358  }
   359  
   360  func toolBrowse(n *ninjaMain, opts *options, args []string) int {
   361  	runBrowsePython(&n.state, n.ninjaCommand, opts.inputFile, args)
   362  	return 0
   363  }
   364  
   365  /* Only defined on Windows in C++.
   366  func  toolMSVC(n *ninjaMain,opts *options, args []string) int {
   367  	// Reset getopt: push one argument onto the front of argv, reset optind.
   368  	//argc++
   369  	//argv--
   370  	//optind = 0
   371  	return msvcHelperMain(args)
   372  }
   373  */
   374  
   375  func toolTargetsListNodes(nodes []*nin.Node, depth int, indent int) int {
   376  	for _, n := range nodes {
   377  		for i := 0; i < indent; i++ {
   378  			fmt.Printf("  ")
   379  		}
   380  		target := n.Path
   381  		if n.InEdge != nil {
   382  			fmt.Printf("%s: %s\n", target, n.InEdge.Rule.Name)
   383  			if depth > 1 || depth <= 0 {
   384  				toolTargetsListNodes(n.InEdge.Inputs, depth-1, indent+1)
   385  			}
   386  		} else {
   387  			fmt.Printf("%s\n", target)
   388  		}
   389  	}
   390  	return 0
   391  }
   392  
   393  func toolTargetsSourceList(state *nin.State) int {
   394  	for _, e := range state.Edges {
   395  		for _, inps := range e.Inputs {
   396  			if inps.InEdge == nil {
   397  				fmt.Printf("%s\n", inps.Path)
   398  			}
   399  		}
   400  	}
   401  	return 0
   402  }
   403  
   404  func toolTargetsListRule(state *nin.State, ruleName string) int {
   405  	rules := map[string]struct{}{}
   406  
   407  	// Gather the outputs.
   408  	for _, e := range state.Edges {
   409  		if e.Rule.Name == ruleName {
   410  			for _, outNode := range e.Outputs {
   411  				rules[outNode.Path] = struct{}{}
   412  			}
   413  		}
   414  	}
   415  
   416  	names := make([]string, 0, len(rules))
   417  	for n := range rules {
   418  		names = append(names, n)
   419  	}
   420  	sort.Strings(names)
   421  	// Print them.
   422  	for _, i := range names {
   423  		fmt.Printf("%s\n", i)
   424  	}
   425  	return 0
   426  }
   427  
   428  func toolTargetsList(state *nin.State) int {
   429  	for _, e := range state.Edges {
   430  		for _, outNode := range e.Outputs {
   431  			fmt.Printf("%s: %s\n", outNode.Path, e.Rule.Name)
   432  		}
   433  	}
   434  	return 0
   435  }
   436  
   437  func toolDeps(n *ninjaMain, opts *options, args []string) int {
   438  	var nodes []*nin.Node
   439  	if len(args) == 0 {
   440  		for _, ni := range n.depsLog.Nodes {
   441  			if n.depsLog.IsDepsEntryLiveFor(ni) {
   442  				nodes = append(nodes, ni)
   443  			}
   444  		}
   445  	} else {
   446  		var err error
   447  		nodes, err = n.collectTargetsFromArgs(args)
   448  		if err != nil {
   449  			errorf("%s", err)
   450  			return 1
   451  		}
   452  	}
   453  
   454  	di := nin.RealDiskInterface{}
   455  	for _, it := range nodes {
   456  		deps := n.depsLog.GetDeps(it)
   457  		if deps == nil {
   458  			fmt.Printf("%s: deps not found\n", it.Path)
   459  			continue
   460  		}
   461  
   462  		mtime, err := di.Stat(it.Path)
   463  		if mtime == -1 {
   464  			errorf("%s", err) // Log and ignore Stat() errors;
   465  		}
   466  		s := "VALID"
   467  		if mtime == 0 || mtime > deps.MTime {
   468  			s = "STALE"
   469  		}
   470  		fmt.Printf("%s: #deps %d, deps mtime %d (%s)\n", it.Path, len(deps.Nodes), deps.MTime, s)
   471  		for _, n := range deps.Nodes {
   472  			fmt.Printf("    %s\n", n.Path)
   473  		}
   474  		fmt.Printf("\n")
   475  	}
   476  	return 0
   477  }
   478  
   479  func toolMissingDeps(n *ninjaMain, opts *options, args []string) int {
   480  	nodes, err := n.collectTargetsFromArgs(args)
   481  	if err != nil {
   482  		errorf("%s", err)
   483  		return 1
   484  	}
   485  	printer := missingDependencyPrinter{}
   486  	scanner := nin.NewMissingDependencyScanner(&printer, &n.depsLog, &n.state, &nin.RealDiskInterface{})
   487  	for _, it := range nodes {
   488  		scanner.ProcessNode(it)
   489  	}
   490  	scanner.PrintStats()
   491  	if scanner.HadMissingDeps() {
   492  		return 3
   493  	}
   494  	return 0
   495  }
   496  
   497  func toolTargets(n *ninjaMain, opts *options, args []string) int {
   498  	depth := 1
   499  	if len(args) >= 1 {
   500  		mode := args[0]
   501  		if mode == "rule" {
   502  			rule := ""
   503  			if len(args) > 1 {
   504  				rule = args[1]
   505  			}
   506  			if len(rule) == 0 {
   507  				return toolTargetsSourceList(&n.state)
   508  			}
   509  			return toolTargetsListRule(&n.state, rule)
   510  		}
   511  		if mode == "depth" {
   512  			if len(args) > 1 {
   513  				// TODO(maruel): Handle error.
   514  				depth, _ = strconv.Atoi(args[1])
   515  			}
   516  		} else if mode == "all" {
   517  			return toolTargetsList(&n.state)
   518  		} else {
   519  			suggestion := nin.SpellcheckString(mode, "rule", "depth", "all")
   520  			if suggestion != "" {
   521  				errorf("unknown target tool mode '%s', did you mean '%s'?", mode, suggestion)
   522  			} else {
   523  				errorf("unknown target tool mode '%s'", mode)
   524  			}
   525  			return 1
   526  		}
   527  	}
   528  
   529  	if rootNodes := n.state.RootNodes(); len(rootNodes) != 0 {
   530  		return toolTargetsListNodes(rootNodes, depth, 0)
   531  	}
   532  	errorf("could not determine root nodes of build graph")
   533  	return 1
   534  }
   535  
   536  func toolRules(n *ninjaMain, opts *options, args []string) int {
   537  	// HACK: parse one additional flag.
   538  	//fmt.Printf("usage: nin -t rules [options]\n\noptions:\n  -d     also print the description of the rule\n  -h     print this message\n")
   539  	printDescription := false
   540  	for i := 0; i < len(args); i++ {
   541  		if args[i] == "-d" {
   542  			if i != len(args)-1 {
   543  				copy(args[i:], args[i+1:])
   544  				args = args[:len(args)-1]
   545  			}
   546  			printDescription = true
   547  		}
   548  	}
   549  
   550  	rules := n.state.Bindings.Rules
   551  	names := make([]string, 0, len(rules))
   552  	for n := range rules {
   553  		names = append(names, n)
   554  	}
   555  	sort.Strings(names)
   556  
   557  	// Print rules
   558  	for _, name := range names {
   559  		fmt.Printf("%s", name)
   560  		if printDescription {
   561  			rule := rules[name]
   562  			description := rule.Bindings["description"]
   563  			if description != nil {
   564  				fmt.Printf(": %s", description.Unparse())
   565  			}
   566  		}
   567  		fmt.Printf("\n")
   568  	}
   569  	return 0
   570  }
   571  
   572  func toolWinCodePage(n *ninjaMain, opts *options, args []string) int {
   573  	panic("TODO") // Windows only
   574  	/*
   575  		if len(args) != 0 {
   576  			fmt.Printf("usage: nin -t wincodepage\n")
   577  			return 1
   578  		}
   579  		cp := "ANSI"
   580  		if GetACP() == CP_UTF8 {
   581  			cp = "UTF-8"
   582  		}
   583  		fmt.Printf("Build file encoding: %s\n", cp)
   584  		return 0
   585  	*/
   586  }
   587  
   588  type printCommandMode bool
   589  
   590  const (
   591  	pcmSingle printCommandMode = false
   592  	pcmAll    printCommandMode = true
   593  )
   594  
   595  func printCommands(edge *nin.Edge, seen map[*nin.Edge]struct{}, mode printCommandMode) {
   596  	if edge == nil {
   597  		return
   598  	}
   599  	if _, ok := seen[edge]; ok {
   600  		return
   601  	}
   602  	seen[edge] = struct{}{}
   603  
   604  	if mode == pcmAll {
   605  		for _, in := range edge.Inputs {
   606  			printCommands(in.InEdge, seen, mode)
   607  		}
   608  	}
   609  
   610  	if edge.Rule != nin.PhonyRule {
   611  		fmt.Printf("%s\n", (edge.EvaluateCommand(false)))
   612  	}
   613  }
   614  
   615  func toolCommands(n *ninjaMain, opts *options, args []string) int {
   616  	// HACK: parse one additional flag.
   617  	//fmt.Printf("usage: nin -t commands [options] [targets]\n\noptions:\n  -s     only print the final command to build [target], not the whole chain\n")
   618  	mode := pcmAll
   619  	for i := 0; i < len(args); i++ {
   620  		if args[i] == "-s" {
   621  			if i != len(args)-1 {
   622  				copy(args[i:], args[i+1:])
   623  				args = args[:len(args)-1]
   624  			}
   625  			mode = pcmSingle
   626  		}
   627  	}
   628  
   629  	nodes, err := n.collectTargetsFromArgs(args)
   630  	if err != nil {
   631  		errorf("%s", err)
   632  		return 1
   633  	}
   634  
   635  	seen := map[*nin.Edge]struct{}{}
   636  	for _, in := range nodes {
   637  		printCommands(in.InEdge, seen, mode)
   638  	}
   639  	return 0
   640  }
   641  
   642  func toolClean(n *ninjaMain, opts *options, args []string) int {
   643  	// HACK: parse two additional flags.
   644  	// fmt.Printf("usage: nin -t clean [options] [targets]\n\noptions:\n  -g     also clean files marked as ninja generator output\n  -r     interpret targets as a list of rules to clean instead\n" )
   645  	generator := false
   646  	cleanRules := false
   647  	for i := 0; i < len(args); i++ {
   648  		if args[i] == "-g" {
   649  			if i != len(args)-1 {
   650  				copy(args[i:], args[i+1:])
   651  				args = args[:len(args)-1]
   652  			}
   653  			generator = true
   654  		} else if args[i] == "-r" {
   655  			if i != len(args)-1 {
   656  				copy(args[i:], args[i+1:])
   657  				args = args[:len(args)-1]
   658  			}
   659  			cleanRules = true
   660  		}
   661  	}
   662  
   663  	if cleanRules && len(args) == 0 {
   664  		errorf("expected a rule to clean")
   665  		return 1
   666  	}
   667  
   668  	cleaner := nin.NewCleaner(&n.state, n.config, &n.di)
   669  	if len(args) >= 1 {
   670  		if cleanRules {
   671  			return cleaner.CleanRules(args)
   672  		}
   673  		return cleaner.CleanTargets(args)
   674  	}
   675  	return cleaner.CleanAll(generator)
   676  }
   677  
   678  func toolCleanDead(n *ninjaMain, opts *options, args []string) int {
   679  	cleaner := nin.NewCleaner(&n.state, n.config, &n.di)
   680  	return cleaner.CleanDead(n.buildLog.Entries)
   681  }
   682  
   683  type evaluateCommandMode bool
   684  
   685  const (
   686  	ecmNormal        evaluateCommandMode = false
   687  	ecmExpandRSPFile evaluateCommandMode = true
   688  )
   689  
   690  func evaluateCommandWithRspfile(edge *nin.Edge, mode evaluateCommandMode) string {
   691  	command := edge.EvaluateCommand(false)
   692  	if mode == ecmNormal {
   693  		return command
   694  	}
   695  
   696  	rspfile := edge.GetUnescapedRspfile()
   697  	if len(rspfile) == 0 {
   698  		return command
   699  	}
   700  
   701  	index := strings.Index(command, rspfile)
   702  	if index == 0 || index == -1 || command[index-1] != '@' {
   703  		return command
   704  	}
   705  
   706  	panic("TODO")
   707  	/*
   708  			rspfileContent := edge.GetBinding("rspfile_content")
   709  		  newlineIndex := 0
   710  		  for (newlineIndex = rspfileContent.find('\n', newlineIndex)) != string::npos {
   711  		    rspfileContent.replace(newlineIndex, 1, 1, ' ')
   712  		    newlineIndex++
   713  		  }
   714  		  command.replace(index - 1, rspfile.length() + 1, rspfileContent)
   715  		  return command
   716  	*/
   717  }
   718  
   719  func printCompdb(directory string, edge *nin.Edge, evalMode evaluateCommandMode) {
   720  	fmt.Printf("\n  {\n    \"directory\": \"")
   721  	printJSONString(directory)
   722  	fmt.Printf("\",\n    \"command\": \"")
   723  	printJSONString(evaluateCommandWithRspfile(edge, evalMode))
   724  	fmt.Printf("\",\n    \"file\": \"")
   725  	printJSONString(edge.Inputs[0].Path)
   726  	fmt.Printf("\",\n    \"output\": \"")
   727  	printJSONString(edge.Outputs[0].Path)
   728  	fmt.Printf("\"\n  }")
   729  }
   730  
   731  func toolCompilationDatabase(n *ninjaMain, opts *options, args []string) int {
   732  	// HACK: parse one additional flag.
   733  	// fmt.Printf( "usage: nin -t compdb [options] [rules]\n\noptions:\n  -x     expand @rspfile style response file invocations\n" )
   734  	evalMode := ecmNormal
   735  	for i := 0; i < len(args); i++ {
   736  		if args[i] == "-x" {
   737  			if i != len(args)-1 {
   738  				copy(args[i:], args[i+1:])
   739  				args = args[:len(args)-1]
   740  			}
   741  			evalMode = ecmExpandRSPFile
   742  		}
   743  	}
   744  
   745  	first := true
   746  	cwd, err := os.Getwd()
   747  	if err != nil {
   748  		panic(err)
   749  	}
   750  	fmt.Printf("[")
   751  	for _, e := range n.state.Edges {
   752  		if len(e.Inputs) == 0 {
   753  			continue
   754  		}
   755  		if len(args) == 0 {
   756  			if !first {
   757  				fmt.Printf(",")
   758  			}
   759  			printCompdb(cwd, e, evalMode)
   760  			first = false
   761  		} else {
   762  			for i := 0; i != len(args); i++ {
   763  				if e.Rule.Name == args[i] {
   764  					if !first {
   765  						fmt.Printf(",")
   766  					}
   767  					printCompdb(cwd, e, evalMode)
   768  					first = false
   769  				}
   770  			}
   771  		}
   772  	}
   773  
   774  	fmt.Printf("\n]")
   775  	return 0
   776  }
   777  
   778  func toolRecompact(n *ninjaMain, opts *options, args []string) int {
   779  	if !n.EnsureBuildDirExists() {
   780  		return 1
   781  	}
   782  
   783  	// recompactOnly
   784  	if !n.OpenBuildLog(true) || !n.OpenDepsLog(true) {
   785  		return 1
   786  	}
   787  
   788  	return 0
   789  }
   790  
   791  func toolRestat(n *ninjaMain, opts *options, args []string) int {
   792  	if !n.EnsureBuildDirExists() {
   793  		return 1
   794  	}
   795  
   796  	logPath := ".ninja_log"
   797  	if n.buildDir != "" {
   798  		logPath = filepath.Join(n.buildDir, logPath)
   799  	}
   800  
   801  	status, err := n.buildLog.Load(logPath)
   802  	if status == nin.LoadError {
   803  		errorf("loading build log %s: %s", logPath, err)
   804  		return nin.ExitFailure
   805  	}
   806  	if status == nin.LoadNotFound {
   807  		// Nothing to restat, ignore this
   808  		return nin.ExitSuccess
   809  	}
   810  	if err != nil {
   811  		// Hack: Load() can return a warning via err by returning LOAD_SUCCESS.
   812  		warningf("%s", err)
   813  	}
   814  
   815  	if err := n.buildLog.Restat(logPath, &n.di, args); err != nil {
   816  		errorf("failed recompaction: %s", err)
   817  		return nin.ExitFailure
   818  	}
   819  
   820  	if !n.config.DryRun {
   821  		if err := n.buildLog.OpenForWrite(logPath, n); err != nil {
   822  			errorf("opening build log: %s", err)
   823  			return nin.ExitFailure
   824  		}
   825  	}
   826  
   827  	return nin.ExitSuccess
   828  }
   829  
   830  // Find the function to execute for \a toolName and return it via \a func.
   831  // Returns a Tool, or NULL if Ninja should exit.
   832  func chooseTool(toolName string) *tool {
   833  	tools := []*tool{
   834  		{"browse", "browse dependency graph in a web browser", runAfterLoad, toolBrowse},
   835  		//{"msvc", "build helper for MSVC cl.exe (EXPERIMENTAL)",runAfterFlags, toolMSVC},
   836  		{"clean", "clean built files", runAfterLoad, toolClean},
   837  		{"commands", "list all commands required to rebuild given targets", runAfterLoad, toolCommands},
   838  		{"deps", "show dependencies stored in the deps log", runAfterLogs, toolDeps},
   839  		{"missingdeps", "check deps log dependencies on generated files", runAfterLogs, toolMissingDeps},
   840  		{"graph", "output graphviz dot file for targets", runAfterLoad, toolGraph},
   841  		{"query", "show inputs/outputs for a path", runAfterLogs, toolQuery},
   842  		{"targets", "list targets by their rule or depth in the DAG", runAfterLoad, toolTargets},
   843  		{"compdb", "dump JSON compilation database to stdout", runAfterLoad, toolCompilationDatabase},
   844  		{"recompact", "recompacts ninja-internal data structures", runAfterLoad, toolRecompact},
   845  		{"restat", "restats all outputs in the build log", runAfterFlags, toolRestat},
   846  		{"rules", "list all rules", runAfterLoad, toolRules},
   847  		{"cleandead", "clean built files that are no longer produced by the manifest", runAfterLogs, toolCleanDead},
   848  		//{"wincodepage", "print the Windows code page used by nin", runAfterFlags, toolWinCodePage},
   849  	}
   850  	if toolName == "list" {
   851  		fmt.Printf("nin subtools:\n")
   852  		for _, t := range tools {
   853  			if t.desc != "" {
   854  				fmt.Printf("%11s  %s\n", t.name, t.desc)
   855  			}
   856  		}
   857  		return nil
   858  	}
   859  
   860  	for _, t := range tools {
   861  		if t.name == toolName {
   862  			return t
   863  		}
   864  	}
   865  
   866  	var words []string
   867  	for _, t := range tools {
   868  		words = append(words, t.name)
   869  	}
   870  	suggestion := nin.SpellcheckString(toolName, words...)
   871  	if suggestion != "" {
   872  		fatalf("unknown tool '%s', did you mean '%s'?", toolName, suggestion)
   873  	} else {
   874  		fatalf("unknown tool '%s'", toolName)
   875  	}
   876  	return nil // Not reached.
   877  }
   878  
   879  var (
   880  	disableExperimentalStatcache bool
   881  	metricsEnabled               bool
   882  )
   883  
   884  // debugEnable enables debugging modes.
   885  //
   886  // Returns false if Ninja should exit instead of continuing.
   887  func debugEnable(values []string) bool {
   888  	for _, name := range values {
   889  		switch name {
   890  		case "list":
   891  			// TODO(maruel): Generate?
   892  			fmt.Printf("debugging modes:\n  stats        print operation counts/timing info\n  explain      explain what caused a command to execute\n  keepdepfile  don't delete depfiles after they're read by ninja\n  keeprsp      don't delete @response files on success\n  nostatcache  don't batch stat() calls per directory and cache them\nmultiple modes can be enabled via -d FOO -d BAR\n")
   893  			//#ifdef _WIN32//#endif
   894  			return false
   895  		case "stats":
   896  			metricsEnabled = true
   897  			nin.Metrics.Enable()
   898  		case "explain":
   899  			nin.Debug.Explaining = true
   900  		case "keepdepfile":
   901  			nin.Debug.KeepDepfile = true
   902  		case "keeprsp":
   903  			nin.Debug.KeepRsp = true
   904  		case "nostatcache":
   905  			disableExperimentalStatcache = true
   906  		default:
   907  			suggestion := nin.SpellcheckString(name, "stats", "explain", "keepdepfile", "keeprsp", "nostatcache")
   908  			if suggestion != "" {
   909  				errorf("unknown debug setting '%s', did you mean '%s'?", name, suggestion)
   910  			} else {
   911  				errorf("unknown debug setting '%s'", name)
   912  			}
   913  			return false
   914  		}
   915  	}
   916  	return true
   917  }
   918  
   919  // Set a warning flag.  Returns false if Ninja should exit instead of
   920  // continuing.
   921  func warningEnable(name string, opts *options) bool {
   922  	if name == "list" {
   923  		fmt.Printf("warning flags:\n  phonycycle={err,warn}  phony build statement references itself\n")
   924  		return false
   925  	} else if name == "dupbuild=err" {
   926  		opts.parserOpts.ErrOnDupeEdge = true
   927  		return true
   928  	} else if name == "dupbuild=warn" {
   929  		opts.parserOpts.ErrOnDupeEdge = false
   930  		return true
   931  	} else if name == "phonycycle=err" {
   932  		opts.parserOpts.ErrOnPhonyCycle = true
   933  		return true
   934  	} else if name == "phonycycle=warn" {
   935  		opts.parserOpts.ErrOnPhonyCycle = false
   936  		return true
   937  	} else if name == "depfilemulti=err" || name == "depfilemulti=warn" {
   938  		warningf("deprecated warning 'depfilemulti'")
   939  		return true
   940  	} else {
   941  		suggestion := nin.SpellcheckString(name, "dupbuild=err", "dupbuild=warn", "phonycycle=err", "phonycycle=warn")
   942  		if suggestion != "" {
   943  			errorf("unknown warning flag '%s', did you mean '%s'?", name, suggestion)
   944  		} else {
   945  			errorf("unknown warning flag '%s'", name)
   946  		}
   947  		return false
   948  	}
   949  }
   950  
   951  // Open the build log.
   952  // @return false on error.
   953  func (n *ninjaMain) OpenBuildLog(recompactOnly bool) bool {
   954  	logPath := ".ninja_log"
   955  	if n.buildDir != "" {
   956  		logPath = n.buildDir + "/" + logPath
   957  	}
   958  
   959  	status, err := n.buildLog.Load(logPath)
   960  	if status == nin.LoadError {
   961  		errorf("loading build log %s: %s", logPath, err)
   962  		return false
   963  	}
   964  	if err != nil {
   965  		// Hack: Load() can return a warning via err by returning LOAD_SUCCESS.
   966  		warningf("%s", err)
   967  	}
   968  
   969  	if recompactOnly {
   970  		if status == nin.LoadNotFound {
   971  			return true
   972  		}
   973  
   974  		if err = n.buildLog.Recompact(logPath, n); err != nil {
   975  			errorf("failed recompaction: %s", err)
   976  			return false
   977  		}
   978  		return true
   979  	}
   980  
   981  	if !n.config.DryRun {
   982  		if err = n.buildLog.OpenForWrite(logPath, n); err != nil {
   983  			errorf("opening build log: %s", err)
   984  			return false
   985  		}
   986  	}
   987  
   988  	return true
   989  }
   990  
   991  // Open the deps log: load it, then open for writing.
   992  // @return false on error.
   993  // Open the deps log: load it, then open for writing.
   994  // @return false on error.
   995  func (n *ninjaMain) OpenDepsLog(recompactOnly bool) bool {
   996  	path := ".ninja_deps"
   997  	if n.buildDir != "" {
   998  		path = n.buildDir + "/" + path
   999  	}
  1000  
  1001  	status, err := n.depsLog.Load(path, &n.state)
  1002  	if status == nin.LoadError {
  1003  		errorf("loading deps log %s: %s", path, err)
  1004  		return false
  1005  	}
  1006  	if err != nil {
  1007  		// Load() can return a warning via err by returning LOAD_SUCCESS.
  1008  		warningf("%s", err)
  1009  	}
  1010  
  1011  	if recompactOnly {
  1012  		if status == nin.LoadNotFound {
  1013  			return true
  1014  		}
  1015  		if err := n.depsLog.Recompact(path); err != nil {
  1016  			errorf("failed recompaction: %s", err)
  1017  			return false
  1018  		}
  1019  		return true
  1020  	}
  1021  
  1022  	if !n.config.DryRun {
  1023  		if err := n.depsLog.OpenForWrite(path); err != nil {
  1024  			errorf("opening deps log: %s", err)
  1025  			return false
  1026  		}
  1027  	}
  1028  
  1029  	return true
  1030  }
  1031  
  1032  // Dump the output requested by '-d stats'.
  1033  func (n *ninjaMain) DumpMetrics() {
  1034  	nin.Metrics.Report()
  1035  
  1036  	fmt.Printf("\n")
  1037  	// There's no such concept in Go's map.
  1038  	//count := len(n.state.paths)
  1039  	//buckets := len(n.state.paths)
  1040  	//fmt.Printf("path.node hash load %.2f (%d entries / %d buckets)\n", count/float64(buckets), count, buckets)
  1041  }
  1042  
  1043  // Ensure the build directory exists, creating it if necessary.
  1044  // @return false on error.
  1045  func (n *ninjaMain) EnsureBuildDirExists() bool {
  1046  	n.buildDir = n.state.Bindings.LookupVariable("builddir")
  1047  	if n.buildDir != "" && !n.config.DryRun {
  1048  		if err := nin.MakeDirs(&n.di, filepath.Join(n.buildDir, ".")); err != nil {
  1049  			errorf("creating build directory %s", n.buildDir)
  1050  			return false
  1051  		}
  1052  	}
  1053  	return true
  1054  }
  1055  
  1056  // Build the targets listed on the command line.
  1057  // @return an exit code.
  1058  func (n *ninjaMain) RunBuild(args []string, status nin.Status) int {
  1059  	targets, err := n.collectTargetsFromArgs(args)
  1060  	if err != nil {
  1061  		status.Error("%s", err)
  1062  		return 1
  1063  	}
  1064  
  1065  	n.di.AllowStatCache(!disableExperimentalStatcache)
  1066  
  1067  	builder := nin.NewBuilder(&n.state, n.config, &n.buildLog, &n.depsLog, &n.di, status, n.startTimeMillis)
  1068  	for i := 0; i < len(targets); i++ {
  1069  		if dirty, err := builder.AddTarget(targets[i]); !dirty {
  1070  			if err != nil {
  1071  				status.Error("%s", err)
  1072  				return 1
  1073  			}
  1074  			// Added a target that is already up-to-date; not really
  1075  			// an error.
  1076  		}
  1077  	}
  1078  
  1079  	// Make sure restat rules do not see stale timestamps.
  1080  	n.di.AllowStatCache(false)
  1081  
  1082  	if builder.AlreadyUpToDate() {
  1083  		status.Info("no work to do.")
  1084  		return 0
  1085  	}
  1086  
  1087  	if err := builder.Build(); err != nil {
  1088  		status.Info("build stopped: %s.", err)
  1089  		if strings.Contains(err.Error(), "interrupted by user") {
  1090  			return 2
  1091  		}
  1092  		return 1
  1093  	}
  1094  	return 0
  1095  }
  1096  
  1097  /*
  1098  // This handler processes fatal crashes that you can't catch
  1099  // Test example: C++ exception in a stack-unwind-block
  1100  // Real-world example: ninja launched a compiler to process a tricky
  1101  // C++ input file. The compiler got itself into a state where it
  1102  // generated 3 GB of output and caused ninja to crash.
  1103  func terminateHandler() {
  1104    CreateWin32MiniDump(nil)
  1105    Fatal("terminate handler called")
  1106  }
  1107  
  1108  // On Windows, we want to prevent error dialogs in case of exceptions.
  1109  // This function handles the exception, and writes a minidump.
  1110  func exceptionFilter(code unsigned int, ep *struct _EXCEPTION_POINTERS) int {
  1111    Error("exception: 0x%X", code)  // e.g. EXCEPTION_ACCESS_VIOLATION
  1112    fflush(stderr)
  1113    CreateWin32MiniDump(ep)
  1114    return EXCEPTION_EXECUTE_HANDLER
  1115  }
  1116  */
  1117  
  1118  type multi []string
  1119  
  1120  func (m *multi) String() string {
  1121  	return strings.Join(*m, ", ")
  1122  }
  1123  
  1124  func (m *multi) Set(s string) error {
  1125  	if len(s) == 0 {
  1126  		return errors.New("empty value")
  1127  	}
  1128  	*m = append(*m, s)
  1129  	return nil
  1130  }
  1131  
  1132  // Parse args for command-line options.
  1133  // Returns an exit code, or -1 if Ninja should continue.
  1134  func readFlags(opts *options, config *nin.BuildConfig) int {
  1135  	// TODO(maruel): For now just do something simple to get started but we'll
  1136  	// have to make it custom if we want it to be drop-in replacement.
  1137  	// It's funny how "opts" and "config" is a bit mixed up here.
  1138  	flag.StringVar(&opts.inputFile, "f", "build.ninja", "specify input build file")
  1139  	flag.StringVar(&opts.workingDir, "C", "", "change to DIR before doing anything else")
  1140  	opts.parserOpts.ErrOnDupeEdge = true
  1141  	flag.StringVar(&opts.cpuprofile, "cpuprofile", "", "activate the CPU sampling profiler")
  1142  	flag.StringVar(&opts.memprofile, "memprofile", "", "snapshot a heap dump at the end")
  1143  	flag.StringVar(&opts.trace, "trace", "", "capture a runtime trace")
  1144  
  1145  	flag.IntVar(&config.Parallelism, "j", guessParallelism(), "run N jobs in parallel (0 means infinity)")
  1146  	flag.IntVar(&config.FailuresAllowed, "k", 1, "keep going until N jobs fail (0 means infinity)")
  1147  	flag.Float64Var(&config.MaxLoadAvg, "l", 0, "do not start new jobs if the load average is greater than N")
  1148  	flag.BoolVar(&config.DryRun, "n", false, "dry run (don't run commands but act like they succeeded)")
  1149  
  1150  	// TODO(maruel): terminates toplevel options; further flags are passed to the tool
  1151  	t := flag.String("t", "", "run a subtool (use '-t list' to list subtools)")
  1152  	// TODO(maruel): It's supposed to be accumulative.
  1153  	var dbgEnable multi
  1154  	flag.Var(&dbgEnable, "d", "enable debugging (use '-d list' to list modes)")
  1155  	verbose := flag.Bool("v", false, "show all command lines while building")
  1156  	flag.BoolVar(verbose, "verbose", false, "show all command lines while building")
  1157  	quiet := flag.Bool("quiet", false, "don't show progress status, just command output")
  1158  	warning := flag.String("w", "", "adjust warnings (use '-w list' to list warnings)")
  1159  	version := flag.Bool("version", false, fmt.Sprintf("print nin version (%q)", nin.NinjaVersion))
  1160  
  1161  	// Flags that do not exist in the C++ code:
  1162  	serial := flag.Bool("serial", false, "parse subninja files serially; default is concurrent")
  1163  	noprewarm := flag.Bool("noprewarm", false, "do not prewarm subninja files; instead process them in order")
  1164  	opts.parserOpts.Concurrency = nin.ParseManifestConcurrentParsing
  1165  
  1166  	flag.Usage = usage
  1167  	flag.Parse()
  1168  
  1169  	if *verbose && *quiet {
  1170  		fmt.Fprintf(os.Stderr, "can't use both -v and --quiet\n")
  1171  		return 2
  1172  	}
  1173  	if *verbose {
  1174  		config.Verbosity = nin.Verbose
  1175  	}
  1176  	if *quiet {
  1177  		config.Verbosity = nin.NoStatusUpdate
  1178  	}
  1179  	if *warning != "" {
  1180  		if !warningEnable(*warning, opts) {
  1181  			return 1
  1182  		}
  1183  	}
  1184  	if !debugEnable(dbgEnable) {
  1185  		return 1
  1186  	}
  1187  	if *version {
  1188  		fmt.Printf("%s\n", nin.NinjaVersion)
  1189  		return 0
  1190  	}
  1191  	if *t != "" {
  1192  		opts.tool = chooseTool(*t)
  1193  		if opts.tool == nil {
  1194  			return 0
  1195  		}
  1196  	}
  1197  	i := 0
  1198  	if opts.cpuprofile != "" {
  1199  		i++
  1200  	}
  1201  	if opts.memprofile != "" {
  1202  		i++
  1203  	}
  1204  	if opts.trace != "" {
  1205  		i++
  1206  	}
  1207  	if i > 1 {
  1208  		fmt.Fprintf(os.Stderr, "can only use one of -cpuprofile, -memprofile or -trace at a time.\n")
  1209  		return 2
  1210  	}
  1211  
  1212  	if *serial {
  1213  		opts.parserOpts.Concurrency = nin.ParseManifestPrewarmSubninja
  1214  	}
  1215  	if *noprewarm {
  1216  		opts.parserOpts.Concurrency = nin.ParseManifestSerial
  1217  	}
  1218  
  1219  	/*
  1220  		OPT_VERSION := 1
  1221  		OPT_QUIET := 2
  1222  			   option longOptions[] = {
  1223  			     { "help", noArgument, nil, 'h' },
  1224  			     { "version", noArgument, nil, OPT_VERSION },
  1225  			     { "verbose", noArgument, nil, 'v' },
  1226  			     { "quiet", noArgument, nil, OPT_QUIET },
  1227  			     { nil, 0, nil, 0 }
  1228  			   }
  1229  
  1230  			   for opts.tool ==nil {
  1231  					 opt := getoptLong(*argc, *argv, "d:f:j:k:l:nt:vw:C:h", longOptions, nil))
  1232  					 if opt == -1 {
  1233  						 continue
  1234  					 }
  1235  			     switch opt {
  1236  			       case 'd':
  1237  			         if !debugEnable(optarg) {
  1238  			           return 1
  1239  			         }
  1240  			         break
  1241  			       case 'f':
  1242  			         opts.inputFile = optarg
  1243  			         break
  1244  			       case 'j': {
  1245  			         var end *char
  1246  			         value := strtol(optarg, &end, 10)
  1247  			         if *end != 0 || value < 0 {
  1248  			           Fatal("invalid -j parameter")
  1249  			         }
  1250  
  1251  			         // We want to run N jobs in parallel. For N = 0, INT_MAX
  1252  			         // is close enough to infinite for most sane builds.
  1253  			         config.parallelism = value > 0 ? value : INT_MAX
  1254  			         break
  1255  			       }
  1256  			       case 'k': {
  1257  			         var end *char
  1258  			         value := strtol(optarg, &end, 10)
  1259  			         if *end != 0 {
  1260  			           Fatal("-k parameter not numeric; did you mean -k 0?")
  1261  			         }
  1262  
  1263  			         // We want to go until N jobs fail, which means we should allow
  1264  			         // N failures and then stop.  For N <= 0, INT_MAX is close enough
  1265  			         // to infinite for most sane builds.
  1266  			         config.failuresAllowed = value > 0 ? value : INT_MAX
  1267  			         break
  1268  			       }
  1269  			       case 'l': {
  1270  			         var end *char
  1271  			         value := strtod(optarg, &end)
  1272  			         if end == optarg {
  1273  			           Fatal("-l parameter not numeric: did you mean -l 0.0?")
  1274  			         }
  1275  			         config.maxLoadAverage = value
  1276  			         break
  1277  			       }
  1278  			       case 'n':
  1279  			         config.dryRun = true
  1280  			         break
  1281  			       case 't':
  1282  			         opts.tool = chooseTool(optarg)
  1283  			         if !opts.tool {
  1284  			           return 0
  1285  			         }
  1286  			         break
  1287  			       case 'v':
  1288  			         config.verbosity = Verbose
  1289  			         break
  1290  			       case OPT_QUIET:
  1291  			         config.verbosity = nin.NoStatusUpdate
  1292  			         break
  1293  			       case 'w':
  1294  			         if !warningEnable(optarg, opts) {
  1295  			           return 1
  1296  			         }
  1297  			         break
  1298  			       case 'C':
  1299  			         opts.workingDir = optarg
  1300  			         break
  1301  			       case OPT_VERSION:
  1302  			         fmt.Printf("%s\n", nin.NinjaVersion)
  1303  			         return 0
  1304  			       case 'h':
  1305  			       default:
  1306  			         usage()
  1307  			         return 1
  1308  			     }
  1309  			   }
  1310  			   *argv += optind
  1311  			   *argc -= optind
  1312  	*/
  1313  	return -1
  1314  }
  1315  
  1316  func mainImpl() int {
  1317  	// Use exit() instead of return in this function to avoid potentially
  1318  	// expensive cleanup when destructing ninjaMain.
  1319  	config := nin.NewBuildConfig()
  1320  	opts := options{}
  1321  
  1322  	//setvbuf(stdout, nil, _IOLBF, BUFSIZ)
  1323  	ninjaCommand := os.Args[0]
  1324  	exitCode := readFlags(&opts, &config)
  1325  	if exitCode >= 0 {
  1326  		return exitCode
  1327  	}
  1328  	// TODO(maruel): Handle os.Interrupt and cancel the context cleanly.
  1329  
  1330  	// Disable GC (TODO: unless running a stateful server).
  1331  	debug.SetGCPercent(-1)
  1332  
  1333  	if opts.cpuprofile != "" {
  1334  		f, err := os.Create(opts.cpuprofile)
  1335  		if err != nil {
  1336  			log.Fatal("could not create CPU profile: ", err)
  1337  		}
  1338  		defer f.Close()
  1339  		if err := pprof.StartCPUProfile(f); err != nil {
  1340  			log.Fatal("could not start CPU profile: ", err)
  1341  		}
  1342  		defer pprof.StopCPUProfile()
  1343  	}
  1344  
  1345  	if opts.memprofile != "" {
  1346  		// Take all memory allocation. This significantly slows down the process.
  1347  		runtime.MemProfileRate = 1
  1348  		defer func() {
  1349  			f, err := os.Create(opts.memprofile)
  1350  			if err != nil {
  1351  				log.Fatal("could not create memory profile: ", err)
  1352  			}
  1353  			defer f.Close()
  1354  			if err := pprof.Lookup("heap").WriteTo(f, 0); err != nil {
  1355  				log.Fatal("could not write memory profile: ", err)
  1356  			}
  1357  		}()
  1358  	} else {
  1359  		// No need.
  1360  		runtime.MemProfileRate = 0
  1361  	}
  1362  	if opts.trace != "" {
  1363  		f, err := os.Create(opts.trace)
  1364  		if err != nil {
  1365  			log.Fatal("could not create trace: ", err)
  1366  		}
  1367  		defer f.Close()
  1368  		// TODO(maruel): Use regions.
  1369  		if err := trace.Start(f); err != nil {
  1370  			log.Fatal("could not start trace: ", err)
  1371  		}
  1372  		defer trace.Stop()
  1373  	}
  1374  
  1375  	args := flag.Args()
  1376  
  1377  	status := newStatusPrinter(&config)
  1378  	if opts.workingDir != "" {
  1379  		// The formatting of this string, complete with funny quotes, is
  1380  		// so Emacs can properly identify that the cwd has changed for
  1381  		// subsequent commands.
  1382  		// Don't print this if a tool is being used, so that tool output
  1383  		// can be piped into a file without this string showing up.
  1384  		if opts.tool == nil && config.Verbosity != nin.NoStatusUpdate {
  1385  			status.Info("Entering directory `%s'", opts.workingDir)
  1386  		}
  1387  		if err := os.Chdir(opts.workingDir); err != nil {
  1388  			fatalf("chdir to '%s' - %s", opts.workingDir, err)
  1389  		}
  1390  	}
  1391  
  1392  	if opts.tool != nil && opts.tool.when == runAfterFlags {
  1393  		// None of the runAfterFlags actually use a ninjaMain, but it's needed
  1394  		// by other tools.
  1395  		ninja := newNinjaMain(ninjaCommand, &config)
  1396  		return opts.tool.tool(&ninja, &opts, args)
  1397  	}
  1398  
  1399  	// TODO(maruel): Let's wrap stdout/stderr with our own buffer?
  1400  
  1401  	/*
  1402  	  // It'd be nice to use line buffering but MSDN says: "For some systems,
  1403  	  // [_IOLBF] provides line buffering. However, for Win32, the behavior is the
  1404  	  //  same as _IOFBF - Full Buffering."
  1405  	  // Buffering used to be disabled in the LinePrinter constructor but that
  1406  	  // now disables it too early and breaks -t deps performance (see issue #2018)
  1407  	  // so we disable it here instead, but only when not running a tool.
  1408  	  if !opts.tool {
  1409  	    setvbuf(stdout, nil, _IONBF, 0)
  1410  	  }
  1411  	*/
  1412  	// Limit number of rebuilds, to prevent infinite loops.
  1413  	const cycleLimit = 100
  1414  	for cycle := 1; cycle <= cycleLimit; cycle++ {
  1415  		ninja := newNinjaMain(ninjaCommand, &config)
  1416  		input, err2 := ninja.di.ReadFile(opts.inputFile)
  1417  		if err2 != nil {
  1418  			status.Error("%s", err2)
  1419  			return 1
  1420  		}
  1421  		if err := nin.ParseManifest(&ninja.state, &ninja.di, opts.parserOpts, opts.inputFile, input); err != nil {
  1422  			status.Error("%s", err)
  1423  			return 1
  1424  		}
  1425  
  1426  		if opts.tool != nil && opts.tool.when == runAfterLoad {
  1427  			return opts.tool.tool(&ninja, &opts, args)
  1428  		}
  1429  
  1430  		if !ninja.EnsureBuildDirExists() {
  1431  			return 1
  1432  		}
  1433  
  1434  		if !ninja.OpenBuildLog(false) || !ninja.OpenDepsLog(false) {
  1435  			return 1
  1436  		}
  1437  
  1438  		if opts.tool != nil && opts.tool.when == runAfterLogs {
  1439  			return opts.tool.tool(&ninja, &opts, args)
  1440  		}
  1441  
  1442  		// Attempt to rebuild the manifest before building anything else
  1443  		if rebuilt, err := ninja.RebuildManifest(opts.inputFile, status); rebuilt {
  1444  			// In dryRun mode the regeneration will succeed without changing the
  1445  			// manifest forever. Better to return immediately.
  1446  			if config.DryRun {
  1447  				return 0
  1448  			}
  1449  			// Start the build over with the new manifest.
  1450  			continue
  1451  		} else if err != nil {
  1452  			status.Error("rebuilding '%s': %s", opts.inputFile, err)
  1453  			return 1
  1454  		}
  1455  
  1456  		result := ninja.RunBuild(args, status)
  1457  		if metricsEnabled {
  1458  			ninja.DumpMetrics()
  1459  		}
  1460  		return result
  1461  	}
  1462  
  1463  	status.Error("manifest '%s' still dirty after %d tries", opts.inputFile, cycleLimit)
  1464  	return 1
  1465  }