2525import types
2626
2727from typing import (AbstractSet , Any , Dict , Iterable , Iterator , List ,
28- Mapping , NamedTuple , Optional , Set , Tuple , Union , Callable )
28+ Mapping , NamedTuple , Optional , Set , Tuple , Union , Callable , TextIO )
2929MYPY = False
3030if MYPY :
3131 from typing import ClassVar
@@ -128,6 +128,8 @@ def build(sources: List[BuildSource],
128128 alt_lib_path : Optional [str ] = None ,
129129 flush_errors : Optional [Callable [[List [str ], bool ], None ]] = None ,
130130 fscache : Optional [FileSystemCache ] = None ,
131+ stdout : Optional [TextIO ] = None ,
132+ stderr : Optional [TextIO ] = None ,
131133 ) -> BuildResult :
132134 """Analyze a program.
133135
@@ -159,9 +161,11 @@ def default_flush_errors(new_messages: List[str], is_serious: bool) -> None:
159161 messages .extend (new_messages )
160162
161163 flush_errors = flush_errors or default_flush_errors
164+ stdout = stdout or sys .stdout
165+ stderr = stderr or sys .stderr
162166
163167 try :
164- result = _build (sources , options , alt_lib_path , flush_errors , fscache )
168+ result = _build (sources , options , alt_lib_path , flush_errors , fscache , stdout , stderr )
165169 result .errors = messages
166170 return result
167171 except CompileError as e :
@@ -180,6 +184,8 @@ def _build(sources: List[BuildSource],
180184 alt_lib_path : Optional [str ],
181185 flush_errors : Callable [[List [str ], bool ], None ],
182186 fscache : Optional [FileSystemCache ],
187+ stdout : TextIO ,
188+ stderr : TextIO ,
183189 ) -> BuildResult :
184190 # This seems the most reasonable place to tune garbage collection.
185191 gc .set_threshold (150 * 1000 )
@@ -197,7 +203,7 @@ def _build(sources: List[BuildSource],
197203
198204 source_set = BuildSourceSet (sources )
199205 errors = Errors (options .show_error_context , options .show_column_numbers )
200- plugin , snapshot = load_plugins (options , errors )
206+ plugin , snapshot = load_plugins (options , errors , stdout )
201207
202208 # Construct a build manager object to hold state during the build.
203209 #
@@ -212,12 +218,14 @@ def _build(sources: List[BuildSource],
212218 plugins_snapshot = snapshot ,
213219 errors = errors ,
214220 flush_errors = flush_errors ,
215- fscache = fscache )
221+ fscache = fscache ,
222+ stdout = stdout ,
223+ stderr = stderr )
216224 manager .trace (repr (options ))
217225
218226 reset_global_state ()
219227 try :
220- graph = dispatch (sources , manager )
228+ graph = dispatch (sources , manager , stdout )
221229 if not options .fine_grained_incremental :
222230 TypeState .reset_all_subtype_caches ()
223231 return BuildResult (manager , graph )
@@ -319,7 +327,10 @@ def import_priority(imp: ImportBase, toplevel_priority: int) -> int:
319327 return toplevel_priority
320328
321329
322- def load_plugins (options : Options , errors : Errors ) -> Tuple [Plugin , Dict [str , str ]]:
330+ def load_plugins (options : Options ,
331+ errors : Errors ,
332+ stdout : TextIO ,
333+ ) -> Tuple [Plugin , Dict [str , str ]]:
323334 """Load all configured plugins.
324335
325336 Return a plugin that encapsulates all plugins chained together. Always
@@ -383,7 +394,8 @@ def plugin_error(message: str) -> None:
383394 try :
384395 plugin_type = getattr (module , func_name )(__version__ )
385396 except Exception :
386- print ('Error calling the plugin(version) entry point of {}\n ' .format (plugin_path ))
397+ print ('Error calling the plugin(version) entry point of {}\n ' .format (plugin_path ),
398+ file = stdout )
387399 raise # Propagate to display traceback
388400
389401 if not isinstance (plugin_type , type ):
@@ -398,7 +410,8 @@ def plugin_error(message: str) -> None:
398410 custom_plugins .append (plugin_type (options ))
399411 snapshot [module_name ] = take_module_snapshot (module )
400412 except Exception :
401- print ('Error constructing plugin instance of {}\n ' .format (plugin_type .__name__ ))
413+ print ('Error constructing plugin instance of {}\n ' .format (plugin_type .__name__ ),
414+ file = stdout )
402415 raise # Propagate to display traceback
403416 # Custom plugins take precedence over the default plugin.
404417 return ChainedPlugin (options , custom_plugins + [default_plugin ]), snapshot
@@ -496,8 +509,10 @@ def __init__(self, data_dir: str,
496509 errors : Errors ,
497510 flush_errors : Callable [[List [str ], bool ], None ],
498511 fscache : FileSystemCache ,
512+ stdout : TextIO ,
513+ stderr : TextIO ,
499514 ) -> None :
500- super ().__init__ ()
515+ super ().__init__ (stdout , stderr )
501516 self .start_time = time .time ()
502517 self .data_dir = data_dir
503518 self .errors = errors
@@ -558,7 +573,7 @@ def __init__(self, data_dir: str,
558573 self .plugin = plugin
559574 self .plugins_snapshot = plugins_snapshot
560575 self .old_plugins_snapshot = read_plugins_snapshot (self )
561- self .quickstart_state = read_quickstart_file (options )
576+ self .quickstart_state = read_quickstart_file (options , self . stdout )
562577
563578 def dump_stats (self ) -> None :
564579 self .log ("Stats:" )
@@ -904,7 +919,9 @@ def read_plugins_snapshot(manager: BuildManager) -> Optional[Dict[str, str]]:
904919 return snapshot
905920
906921
907- def read_quickstart_file (options : Options ) -> Optional [Dict [str , Tuple [float , int , str ]]]:
922+ def read_quickstart_file (options : Options ,
923+ stdout : TextIO ,
924+ ) -> Optional [Dict [str , Tuple [float , int , str ]]]:
908925 quickstart = None # type: Optional[Dict[str, Tuple[float, int, str]]]
909926 if options .quickstart_file :
910927 # This is very "best effort". If the file is missing or malformed,
@@ -918,7 +935,7 @@ def read_quickstart_file(options: Options) -> Optional[Dict[str, Tuple[float, in
918935 for file , (x , y , z ) in raw_quickstart .items ():
919936 quickstart [file ] = (x , y , z )
920937 except Exception as e :
921- print ("Warning: Failed to load quickstart file: {}\n " .format (str (e )))
938+ print ("Warning: Failed to load quickstart file: {}\n " .format (str (e )), file = stdout )
922939 return quickstart
923940
924941
@@ -1769,7 +1786,8 @@ def wrap_context(self, check_blockers: bool = True) -> Iterator[None]:
17691786 except CompileError :
17701787 raise
17711788 except Exception as err :
1772- report_internal_error (err , self .path , 0 , self .manager .errors , self .options )
1789+ report_internal_error (err , self .path , 0 , self .manager .errors ,
1790+ self .options , self .manager .stdout , self .manager .stderr )
17731791 self .manager .errors .set_import_context (save_import_context )
17741792 # TODO: Move this away once we've removed the old semantic analyzer?
17751793 if check_blockers :
@@ -2429,7 +2447,10 @@ def log_configuration(manager: BuildManager) -> None:
24292447# The driver
24302448
24312449
2432- def dispatch (sources : List [BuildSource ], manager : BuildManager ) -> Graph :
2450+ def dispatch (sources : List [BuildSource ],
2451+ manager : BuildManager ,
2452+ stdout : TextIO ,
2453+ ) -> Graph :
24332454 log_configuration (manager )
24342455
24352456 t0 = time .time ()
@@ -2454,11 +2475,11 @@ def dispatch(sources: List[BuildSource], manager: BuildManager) -> Graph:
24542475 fm_cache_size = len (manager .find_module_cache .results ),
24552476 )
24562477 if not graph :
2457- print ("Nothing to do?!" )
2478+ print ("Nothing to do?!" , file = stdout )
24582479 return graph
24592480 manager .log ("Loaded graph with %d nodes (%.3f sec)" % (len (graph ), t1 - t0 ))
24602481 if manager .options .dump_graph :
2461- dump_graph (graph )
2482+ dump_graph (graph , stdout )
24622483 return graph
24632484
24642485 # Fine grained dependencies that didn't have an associated module in the build
@@ -2480,7 +2501,7 @@ def dispatch(sources: List[BuildSource], manager: BuildManager) -> Graph:
24802501 manager .log ("Error reading fine-grained dependencies cache -- aborting cache load" )
24812502 manager .cache_enabled = False
24822503 manager .log ("Falling back to full run -- reloading graph..." )
2483- return dispatch (sources , manager )
2504+ return dispatch (sources , manager , stdout )
24842505
24852506 # If we are loading a fine-grained incremental mode cache, we
24862507 # don't want to do a real incremental reprocess of the
@@ -2528,7 +2549,7 @@ def dumps(self) -> str:
25282549 json .dumps (self .deps ))
25292550
25302551
2531- def dump_graph (graph : Graph ) -> None :
2552+ def dump_graph (graph : Graph , stdout : TextIO = sys . stdout ) -> None :
25322553 """Dump the graph as a JSON string to stdout.
25332554
25342555 This copies some of the work by process_graph()
@@ -2562,7 +2583,7 @@ def dump_graph(graph: Graph) -> None:
25622583 if (dep_id != node .node_id and
25632584 (dep_id not in node .deps or pri < node .deps [dep_id ])):
25642585 node .deps [dep_id ] = pri
2565- print ("[" + ",\n " .join (node .dumps () for node in nodes ) + "\n ]" )
2586+ print ("[" + ",\n " .join (node .dumps () for node in nodes ) + "\n ]" , file = stdout )
25662587
25672588
25682589def load_graph (sources : List [BuildSource ], manager : BuildManager ,
0 commit comments