@@ -80,6 +80,7 @@ class BuildResult:
80
80
manager: The build manager.
81
81
files: Dictionary from module name to related AST node.
82
82
types: Dictionary from parse tree node to its inferred type.
83
+ used_cache: Whether the build took advantage of a cache
83
84
errors: List of error messages.
84
85
"""
85
86
@@ -88,6 +89,7 @@ def __init__(self, manager: 'BuildManager', graph: Graph) -> None:
88
89
self .graph = graph
89
90
self .files = manager .modules
90
91
self .types = manager .all_types # Non-empty for tests only or if dumping deps
92
+ self .used_cache = manager .cache_enabled
91
93
self .errors = [] # type: List[str] # Filled in by build if desired
92
94
93
95
@@ -569,6 +571,9 @@ class BuildManager:
569
571
flush_errors: A function for processing errors after each SCC
570
572
saved_cache: Dict with saved cache state for coarse-grained dmypy
571
573
(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.
572
577
stats: Dict with various instrumentation numbers
573
578
"""
574
579
@@ -588,7 +593,6 @@ def __init__(self, data_dir: str,
588
593
self .data_dir = data_dir
589
594
self .errors = errors
590
595
self .errors .set_ignore_prefix (ignore_prefix )
591
- self .only_load_from_cache = options .use_fine_grained_cache
592
596
self .lib_path = tuple (lib_path )
593
597
self .source_set = source_set
594
598
self .reports = reports
@@ -607,9 +611,14 @@ def __init__(self, data_dir: str,
607
611
self .rechecked_modules = set () # type: Set[str]
608
612
self .plugin = plugin
609
613
self .flush_errors = flush_errors
614
+ self .cache_enabled = options .incremental and (
615
+ not options .fine_grained_incremental or options .use_fine_grained_cache )
610
616
self .saved_cache = saved_cache if saved_cache is not None else {} # type: SavedCache
611
617
self .stats = {} # type: Dict[str, Any] # Values are ints or floats
612
618
619
+ def use_fine_grained_cache (self ) -> bool :
620
+ return self .cache_enabled and self .options .use_fine_grained_cache
621
+
613
622
def maybe_swap_for_shadow_path (self , path : str ) -> str :
614
623
if (self .options .shadow_file and
615
624
os .path .samefile (self .options .shadow_file [0 ], path )):
@@ -1157,7 +1166,7 @@ def validate_meta(meta: Optional[CacheMeta], id: str, path: Optional[str],
1157
1166
# changed since the cache was generated. We *don't* want to do a
1158
1167
# coarse-grained incremental rebuild, so we accept the cache
1159
1168
# 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 () :
1161
1170
manager .log ('Using potentially stale metadata for {}' .format (id ))
1162
1171
return meta
1163
1172
@@ -1655,7 +1664,7 @@ def __init__(self,
1655
1664
self .path = path
1656
1665
self .xpath = path or '<string>'
1657
1666
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 :
1659
1668
self .meta = find_cache_meta (self .id , path , manager )
1660
1669
# TODO: Get mtime if not cached.
1661
1670
if self .meta is not None :
@@ -1675,10 +1684,10 @@ def __init__(self,
1675
1684
for id , line in zip (self .meta .dependencies , self .meta .dep_lines )}
1676
1685
self .child_modules = set (self .meta .child_modules )
1677
1686
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 () :
1682
1691
manager .log ("Deferring module to fine-grained update %s (%s)" % (path , id ))
1683
1692
raise ModuleNotFound
1684
1693
@@ -1795,13 +1804,15 @@ def load_tree(self) -> None:
1795
1804
1796
1805
def fix_cross_refs (self ) -> None :
1797
1806
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.
1798
1809
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 ())
1800
1812
1801
1813
def calculate_mros (self ) -> None :
1802
1814
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 )
1805
1816
1806
1817
def patch_dependency_parents (self ) -> None :
1807
1818
"""
@@ -2058,7 +2069,7 @@ def valid_references(self) -> Set[str]:
2058
2069
2059
2070
def write_cache (self ) -> None :
2060
2071
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 :
2062
2073
return
2063
2074
if self .manager .options .quick_and_dirty :
2064
2075
is_errors = self .manager .errors .is_errors_for_file (self .path )
@@ -2105,9 +2116,12 @@ def dispatch(sources: List[BuildSource], manager: BuildManager) -> Graph:
2105
2116
# This is a kind of unfortunate hack to work around some of fine-grained's
2106
2117
# fragility: if we have loaded less than 50% of the specified files from
2107
2118
# 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
2111
2125
graph = load_graph (sources , manager )
2112
2126
2113
2127
t1 = time .time ()
@@ -2128,7 +2142,13 @@ def dispatch(sources: List[BuildSource], manager: BuildManager) -> Graph:
2128
2142
if manager .options .dump_graph :
2129
2143
dump_graph (graph )
2130
2144
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 )
2132
2152
updated = preserve_cache (graph )
2133
2153
set_updated = set (updated )
2134
2154
manager .saved_cache .clear ()
@@ -2437,14 +2457,6 @@ def process_graph(graph: Graph, manager: BuildManager) -> None:
2437
2457
manager .log ("Processing SCC of size %d (%s) as %s" % (size , scc_str , fresh_msg ))
2438
2458
process_stale_scc (graph , scc , manager )
2439
2459
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
-
2448
2460
sccs_left = len (fresh_scc_queue )
2449
2461
nodes_left = sum (len (scc ) for scc in fresh_scc_queue )
2450
2462
manager .add_stats (sccs_left = sccs_left , nodes_left = nodes_left )
@@ -2456,6 +2468,25 @@ def process_graph(graph: Graph, manager: BuildManager) -> None:
2456
2468
manager .log ("No fresh SCCs left in queue" )
2457
2469
2458
2470
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
+
2459
2490
def order_ascc (graph : Graph , ascc : AbstractSet [str ], pri_max : int = PRI_ALL ) -> List [str ]:
2460
2491
"""Come up with the ideal processing order within an SCC.
2461
2492
0 commit comments