@@ -80,6 +80,7 @@ class BuildResult:
8080 manager: The build manager.
8181 files: Dictionary from module name to related AST node.
8282 types: Dictionary from parse tree node to its inferred type.
83+ used_cache: Whether the build took advantage of a cache
8384 errors: List of error messages.
8485 """
8586
@@ -88,6 +89,7 @@ def __init__(self, manager: 'BuildManager', graph: Graph) -> None:
8889 self .graph = graph
8990 self .files = manager .modules
9091 self .types = manager .all_types # Non-empty for tests only or if dumping deps
92+ self .used_cache = manager .cache_enabled
9193 self .errors = [] # type: List[str] # Filled in by build if desired
9294
9395
@@ -569,6 +571,9 @@ class BuildManager:
569571 flush_errors: A function for processing errors after each SCC
570572 saved_cache: Dict with saved cache state for coarse-grained dmypy
571573 (read-write!)
574+ cache_enabled: Whether cache usage is enabled. This is set based on options,
575+ but is disabled if fine-grained cache loading fails
576+ and after an initial fine-grained load.
572577 stats: Dict with various instrumentation numbers
573578 """
574579
@@ -588,7 +593,6 @@ def __init__(self, data_dir: str,
588593 self .data_dir = data_dir
589594 self .errors = errors
590595 self .errors .set_ignore_prefix (ignore_prefix )
591- self .only_load_from_cache = options .use_fine_grained_cache
592596 self .lib_path = tuple (lib_path )
593597 self .source_set = source_set
594598 self .reports = reports
@@ -607,9 +611,14 @@ def __init__(self, data_dir: str,
607611 self .rechecked_modules = set () # type: Set[str]
608612 self .plugin = plugin
609613 self .flush_errors = flush_errors
614+ self .cache_enabled = options .incremental and (
615+ not options .fine_grained_incremental or options .use_fine_grained_cache )
610616 self .saved_cache = saved_cache if saved_cache is not None else {} # type: SavedCache
611617 self .stats = {} # type: Dict[str, Any] # Values are ints or floats
612618
619+ def use_fine_grained_cache (self ) -> bool :
620+ return self .cache_enabled and self .options .use_fine_grained_cache
621+
613622 def maybe_swap_for_shadow_path (self , path : str ) -> str :
614623 if (self .options .shadow_file and
615624 os .path .samefile (self .options .shadow_file [0 ], path )):
@@ -1157,7 +1166,7 @@ def validate_meta(meta: Optional[CacheMeta], id: str, path: Optional[str],
11571166 # changed since the cache was generated. We *don't* want to do a
11581167 # coarse-grained incremental rebuild, so we accept the cache
11591168 # metadata even if it doesn't match the source file.
1160- if manager .options . use_fine_grained_cache :
1169+ if manager .use_fine_grained_cache () :
11611170 manager .log ('Using potentially stale metadata for {}' .format (id ))
11621171 return meta
11631172
@@ -1655,7 +1664,7 @@ def __init__(self,
16551664 self .path = path
16561665 self .xpath = path or '<string>'
16571666 self .source = source
1658- if path and source is None and self .options . incremental :
1667+ if path and source is None and self .manager . cache_enabled :
16591668 self .meta = find_cache_meta (self .id , path , manager )
16601669 # TODO: Get mtime if not cached.
16611670 if self .meta is not None :
@@ -1675,10 +1684,10 @@ def __init__(self,
16751684 for id , line in zip (self .meta .dependencies , self .meta .dep_lines )}
16761685 self .child_modules = set (self .meta .child_modules )
16771686 else :
1678- # In fine-grained cache mode , pretend we only know about modules that
1679- # have cache information and defer handling new modules until the
1680- # fine-grained update.
1681- if manager .only_load_from_cache :
1687+ # When doing a fine-grained cache load , pretend we only
1688+ # know about modules that have cache information and defer
1689+ # handling new modules until the fine-grained update.
1690+ if manager .use_fine_grained_cache () :
16821691 manager .log ("Deferring module to fine-grained update %s (%s)" % (path , id ))
16831692 raise ModuleNotFound
16841693
@@ -1795,13 +1804,15 @@ def load_tree(self) -> None:
17951804
17961805 def fix_cross_refs (self ) -> None :
17971806 assert self .tree is not None , "Internal error: method must be called on parsed file only"
1807+ # We need to set quick_and_dirty when doing a fine grained
1808+ # cache load because we need to gracefully handle missing modules.
17981809 fixup_module_pass_one (self .tree , self .manager .modules ,
1799- self .manager .options .quick_and_dirty )
1810+ self .manager .options .quick_and_dirty or
1811+ self .manager .use_fine_grained_cache ())
18001812
18011813 def calculate_mros (self ) -> None :
18021814 assert self .tree is not None , "Internal error: method must be called on parsed file only"
1803- fixup_module_pass_two (self .tree , self .manager .modules ,
1804- self .manager .options .quick_and_dirty )
1815+ fixup_module_pass_two (self .tree , self .manager .modules )
18051816
18061817 def patch_dependency_parents (self ) -> None :
18071818 """
@@ -2058,7 +2069,7 @@ def valid_references(self) -> Set[str]:
20582069
20592070 def write_cache (self ) -> None :
20602071 assert self .tree is not None , "Internal error: method must be called on parsed file only"
2061- if not self .path or self .options . cache_dir == os . devnull :
2072+ if not self .path or not self .manager . cache_enabled :
20622073 return
20632074 if self .manager .options .quick_and_dirty :
20642075 is_errors = self .manager .errors .is_errors_for_file (self .path )
@@ -2105,9 +2116,12 @@ def dispatch(sources: List[BuildSource], manager: BuildManager) -> Graph:
21052116 # This is a kind of unfortunate hack to work around some of fine-grained's
21062117 # fragility: if we have loaded less than 50% of the specified files from
21072118 # cache in fine-grained cache mode, load the graph again honestly.
2108- if manager .options .use_fine_grained_cache and len (graph ) < 0.50 * len (sources ):
2109- manager .log ("Redoing load_graph because too much was missing" )
2110- manager .only_load_from_cache = False
2119+ # In this case, we just turn the cache off entirely, so we don't need
2120+ # to worry about some files being loaded and some from cache and so
2121+ # that fine-grained mode never *writes* to the cache.
2122+ if manager .use_fine_grained_cache () and len (graph ) < 0.50 * len (sources ):
2123+ manager .log ("Redoing load_graph without cache because too much was missing" )
2124+ manager .cache_enabled = False
21112125 graph = load_graph (sources , manager )
21122126
21132127 t1 = time .time ()
@@ -2128,7 +2142,13 @@ def dispatch(sources: List[BuildSource], manager: BuildManager) -> Graph:
21282142 if manager .options .dump_graph :
21292143 dump_graph (graph )
21302144 return graph
2131- process_graph (graph , manager )
2145+ # If we are loading a fine-grained incremental mode cache, we
2146+ # don't want to do a real incremental reprocess of the graph---we
2147+ # just want to load in all of the cache information.
2148+ if manager .use_fine_grained_cache ():
2149+ process_fine_grained_cache_graph (graph , manager )
2150+ else :
2151+ process_graph (graph , manager )
21322152 updated = preserve_cache (graph )
21332153 set_updated = set (updated )
21342154 manager .saved_cache .clear ()
@@ -2437,14 +2457,6 @@ def process_graph(graph: Graph, manager: BuildManager) -> None:
24372457 manager .log ("Processing SCC of size %d (%s) as %s" % (size , scc_str , fresh_msg ))
24382458 process_stale_scc (graph , scc , manager )
24392459
2440- # If we are running in fine-grained incremental mode with caching,
2441- # we always process fresh SCCs so that we have all of the symbol
2442- # tables and fine-grained dependencies available.
2443- if manager .options .use_fine_grained_cache :
2444- for prev_scc in fresh_scc_queue :
2445- process_fresh_scc (graph , prev_scc , manager )
2446- fresh_scc_queue = []
2447-
24482460 sccs_left = len (fresh_scc_queue )
24492461 nodes_left = sum (len (scc ) for scc in fresh_scc_queue )
24502462 manager .add_stats (sccs_left = sccs_left , nodes_left = nodes_left )
@@ -2456,6 +2468,25 @@ def process_graph(graph: Graph, manager: BuildManager) -> None:
24562468 manager .log ("No fresh SCCs left in queue" )
24572469
24582470
2471+ def process_fine_grained_cache_graph (graph : Graph , manager : BuildManager ) -> None :
2472+ """Finish loading everything for use in the fine-grained incremental cache"""
2473+
2474+ # If we are running in fine-grained incremental mode with caching,
2475+ # we process all SCCs as fresh SCCs so that we have all of the symbol
2476+ # tables and fine-grained dependencies available.
2477+ # We fail the loading of any SCC that we can't load a meta for, so we
2478+ # don't have anything *but* fresh SCCs.
2479+ sccs = sorted_components (graph )
2480+ manager .log ("Found %d SCCs; largest has %d nodes" %
2481+ (len (sccs ), max (len (scc ) for scc in sccs )))
2482+
2483+ for ascc in sccs :
2484+ # Order the SCC's nodes using a heuristic.
2485+ # Note that ascc is a set, and scc is a list.
2486+ scc = order_ascc (graph , ascc )
2487+ process_fresh_scc (graph , scc , manager )
2488+
2489+
24592490def order_ascc (graph : Graph , ascc : AbstractSet [str ], pri_max : int = PRI_ALL ) -> List [str ]:
24602491 """Come up with the ideal processing order within an SCC.
24612492
0 commit comments