11# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
22# SPDX-License-Identifier: Apache-2.0
3-
3+ import logging
44import os
55
66from idf_component_tools .errors import DependencySolveError , FetchingError , SolverError
7+ from idf_component_tools .lock .manager import EMPTY_LOCK
78from idf_component_tools .manifest import (
89 ComponentRequirement ,
910 ComponentWithVersions ,
1718
1819from ..utils import print_info , print_warn
1920from .helper import PackageSource
21+ from .mixology .failure import SolverFailure
2022from .mixology .package import Package
2123from .mixology .version_solver import VersionSolver as Solver
2224
2527except ImportError :
2628 pass
2729
30+ logger = logging .getLogger (__name__ )
31+
2832
2933class VersionSolver (object ):
3034 """
@@ -38,18 +42,21 @@ def __init__(self, requirements, old_solution=None, component_solved_callback=No
3842 self .old_solution = old_solution
3943 self .component_solved_callback = component_solved_callback
4044
45+ self ._init ()
46+
47+ def _init (self ):
48+ # put all the intermediate generated attrs here
49+ # to reset them when the solver is re-used
4150 self ._source = PackageSource ()
4251 self ._solver = Solver (self ._source )
4352 self ._target = None
4453 self ._overriders = set () # type: set[str]
4554 self ._local_root_requirements = dict () # type: dict[str, ComponentRequirement]
46- self ._solved_requirements = set () # type: set[ComponentRequirement]
55+ self ._parse_local_root_requirements ()
4756
48- def solve (self ): # type: () -> SolvedManifest
49- # scan all root local requirements
50- # root local requirements defined in the file system manifest files
51- # would have higher priorities
57+ self ._solved_requirements = set () # type: set[ComponentRequirement]
5258
59+ def _parse_local_root_requirements (self ): # type: () -> None
5360 # scan all root local requirements
5461 for manifest in self .requirements .manifests :
5562 for requirement in manifest .dependencies : # type: ComponentRequirement
@@ -68,7 +75,7 @@ def solve(self): # type: () -> SolvedManifest
6875 else :
6976 self ._local_root_requirements [requirement .build_name ] = requirement
7077
71- # scan all root local components
78+ # add all local components, except [0] -> main component
7279 for manifest in self .requirements .manifests [1 :]:
7380 # add itself as highest priority component
7481 if manifest .name and manifest .version and manifest ._manifest_manager :
@@ -87,8 +94,17 @@ def solve(self): # type: () -> SolvedManifest
8794 version_spec = str (manifest .version ),
8895 )
8996
97+ def _solve (self , cur_solution = None ): # type: (SolvedManifest | None) -> SolvedManifest
98+ """
99+ Solve the version requirements and return the result.
100+
101+ :param cur_solution: The current solution to be used as a starting point.
102+ :raises SolverError: If the solver fails to solve the requirements.
103+ """
104+ # root local requirements defined in the file system manifest files
105+ # would have higher priorities
90106 for manifest in self .requirements .manifests :
91- self .solve_manifest (manifest )
107+ self .solve_manifest (manifest , cur_solution = cur_solution )
92108
93109 self ._source .override_dependencies (self ._overriders )
94110
@@ -108,18 +124,42 @@ def solve(self): # type: () -> SolvedManifest
108124 solved_components , self .requirements .manifest_hash , self .requirements .target
109125 )
110126
127+ def solve (self ): # type: () -> SolvedManifest
128+ if self .old_solution != SolvedManifest .fromdict (EMPTY_LOCK ):
129+ try :
130+ return self ._solve (self .old_solution )
131+ except SolverFailure as e :
132+ logger .debug (
133+ 'Solver failed to solve the requirements with the current solution. '
134+ 'Error: %s.\n '
135+ 'Retrying without the current solution. ' ,
136+ e ,
137+ )
138+
139+ self ._init ()
140+ return self ._solve ()
141+
111142 def get_versions_from_sources (
112- self , requirement
113- ): # type: (ComponentRequirement) -> tuple[ComponentWithVersions | None, BaseSource | None]
143+ self ,
144+ requirement , # type: ComponentRequirement
145+ cur_solution = None , # type: SolvedManifest | None
146+ ): # type: (...) -> tuple[ComponentWithVersions | None, BaseSource | None]
114147 latest_source = None
115148 cmp_with_versions = None
116149 for source in requirement .sources :
117150 try :
118- cmp_with_versions = source .versions (
119- name = requirement .name ,
120- spec = requirement .version_spec ,
121- target = self .requirements .target ,
122- )
151+ if cur_solution and requirement .name in cur_solution .solved_components :
152+ cmp_with_versions = requirement .source .versions (
153+ name = requirement .name ,
154+ spec = str (cur_solution .solved_components [requirement .name ].version ),
155+ target = self .requirements .target ,
156+ )
157+ else :
158+ cmp_with_versions = source .versions (
159+ name = requirement .name ,
160+ spec = requirement .version_spec ,
161+ target = self .requirements .target ,
162+ )
123163 latest_source = source
124164 if cmp_with_versions .versions :
125165 break
@@ -129,18 +169,22 @@ def get_versions_from_sources(
129169 pass
130170 return cmp_with_versions , latest_source
131171
132- def solve_manifest (self , manifest ): # type: (Manifest) -> None
172+ def solve_manifest (
173+ self ,
174+ manifest , # type: Manifest
175+ cur_solution = None , # type: SolvedManifest | None
176+ ): # type: (...) -> None
133177 for dep in self ._dependencies_with_local_precedence (
134178 manifest .dependencies , manifest_path = manifest .path
135179 ):
136180 if len (dep .sources ) == 1 :
137181 source = dep .source
138182 else :
139- _ , source = self .get_versions_from_sources (dep )
183+ _ , source = self .get_versions_from_sources (dep , cur_solution = cur_solution )
140184
141185 self ._source .root_dep (Package (dep .name , source ), dep .version_spec )
142186 try :
143- self .solve_component (dep , manifest_path = manifest .path )
187+ self .solve_component (dep , manifest_path = manifest .path , cur_solution = cur_solution )
144188 except DependencySolveError as e :
145189 raise SolverError (
146190 'Solver failed processing dependency "{dependency}" '
@@ -155,12 +199,14 @@ def solve_manifest(self, manifest): # type: (Manifest) -> None
155199 )
156200
157201 def solve_component (
158- self , requirement , manifest_path = None
159- ): # type: (ComponentRequirement, str | None) -> None
202+ self , requirement , manifest_path = None , cur_solution = None
203+ ): # type: (ComponentRequirement, str | None, SolvedManifest | None ) -> None
160204 if requirement in self ._solved_requirements :
161205 return
162206
163- cmp_with_versions , source = self .get_versions_from_sources (requirement )
207+ cmp_with_versions , source = self .get_versions_from_sources (
208+ requirement , cur_solution = cur_solution
209+ )
164210
165211 if not cmp_with_versions or not cmp_with_versions .versions or not source :
166212 print_warn ('Component "{}" not found' .format (requirement .name ))
0 commit comments