github.com/maruel/nin@v0.0.0-20220112143044-f35891e3ce7e/manifest_parser.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 nin
    16  
    17  // ParseManifestConcurrency defines the concurrency parameters when parsing
    18  // manifest (build.ninja files).
    19  type ParseManifestConcurrency int32
    20  
    21  const (
    22  	// ParseManifestSerial parses files serially in the same way that the C++
    23  	// ninja implementation does. It is the most compatible way.
    24  	//
    25  	// This reduces CPU usage, at the cost of higher latency.
    26  	ParseManifestSerial ParseManifestConcurrency = iota
    27  	// ParseManifestPrewarmSubninja loads files serially except that subninjas
    28  	// are processed at the very end. This gives a latency improvement when a
    29  	// significant number of subninjas are processed because subninja files can
    30  	// be read from disk concurrently. This causes subninja files to be processed
    31  	// out of order.
    32  	ParseManifestPrewarmSubninja
    33  	// ParseManifestConcurrentParsing parses all subninjas concurrently.
    34  	ParseManifestConcurrentParsing
    35  )
    36  
    37  func (p ParseManifestConcurrency) String() string {
    38  	switch p {
    39  	case ParseManifestSerial:
    40  		return "Serial"
    41  	case ParseManifestPrewarmSubninja:
    42  		return "PrewarmSubninja"
    43  	case ParseManifestConcurrentParsing:
    44  		return "Concurrent"
    45  	default:
    46  		return "Invalid"
    47  	}
    48  }
    49  
    50  // ParseManifestOpts are the options when parsing a build.ninja file.
    51  type ParseManifestOpts struct {
    52  	// ErrOnDupeEdge causes duplicate rules for one target to print an error,
    53  	// otherwise warns.
    54  	ErrOnDupeEdge bool
    55  	// ErrOnPhonyCycle causes phony cycles to print an error, otherwise warns.
    56  	ErrOnPhonyCycle bool
    57  	// Quiet silences warnings.
    58  	Quiet bool
    59  	// Concurrency defines the parsing concurrency.
    60  	Concurrency ParseManifestConcurrency
    61  }
    62  
    63  // ParseManifest parses a manifest file (i.e. build.ninja).
    64  //
    65  // The input must contain a trailing terminating zero byte.
    66  func ParseManifest(state *State, fr FileReader, options ParseManifestOpts, filename string, input []byte) error {
    67  	if options.Concurrency != ParseManifestConcurrentParsing {
    68  		m := manifestParserSerial{
    69  			fr:      fr,
    70  			options: options,
    71  			state:   state,
    72  			env:     state.Bindings,
    73  		}
    74  		return m.parse(filename, input)
    75  	}
    76  	m := manifestParserConcurrent{
    77  		manifestParserRoutine: manifestParserRoutine{
    78  			manifestParserContext: manifestParserContext{
    79  				env: state.Bindings,
    80  				doneParsing: barrier{
    81  					want: make(chan struct{}),
    82  				},
    83  			},
    84  		},
    85  		manifestParserState: manifestParserState{
    86  			state:   state,
    87  			options: options,
    88  			fr:      fr,
    89  		},
    90  	}
    91  	return m.parseMain(filename, input)
    92  }
    93  
    94  // subninja is a struct used to manage parallel reading of subninja files.
    95  type subninja struct {
    96  	filename string
    97  	input    []byte
    98  	err      error
    99  	ls       lexerState // lexer state when the subninja statement was parsed.
   100  	env      *BindingEnv
   101  }
   102  
   103  // readSubninjaAsync is the goroutine that reads the subninja file in parallel
   104  // to the main build.ninja to reduce overall latency.
   105  func readSubninjaAsync(fr FileReader, filename string, ch chan<- subninja, ls lexerState, env *BindingEnv) {
   106  	input, err := fr.ReadFile(filename)
   107  	ch <- subninja{
   108  		filename: filename,
   109  		input:    input,
   110  		err:      err,
   111  		ls:       ls,
   112  		env:      env,
   113  	}
   114  }