python module: cache dependency() lookup between invocations
Modeled similarly after the installations cache, but using the coredata
dependency cache because it has a nice mechanism for handling the cache
key.
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
+from __future__ import annotations
from pathlib import Path
import functools
@@ -26,6 +27,7 @@ from ..coredata import UserFeatureOption
from ..build import known_shmod_kwargs
from ..dependencies import DependencyMethods, PkgConfigDependency, NotFoundDependency, SystemDependency, ExtraFrameworkDependency
from ..dependencies.base import process_method_kw
+from ..dependencies.detect import get_dep_identifier
from ..environment import detect_cpu_family
from ..interpreter import ExternalProgramHolder, extract_required_kwarg, permitted_dependency_kwargs
from ..interpreter.type_checking import NoneType
@@ -539,30 +541,40 @@ class PythonInstallation(ExternalProgramHolder):
return self.interpreter.func_shared_module(None, args, kwargs)
+ def _dependency_method_impl(self, kwargs: TYPE_kwargs) -> Dependency:
+ for_machine = self.interpreter.machine_from_native_kwarg(kwargs)
+ identifier = get_dep_identifier(self._full_path(), kwargs)
+
+ dep = self.interpreter.coredata.deps[for_machine].get(identifier)
+ if dep is not None:
+ return dep
+
+ new_kwargs = kwargs.copy()
+ new_kwargs['required'] = False
+ methods = process_method_kw({DependencyMethods.PKGCONFIG, DependencyMethods.SYSTEM}, kwargs)
+ # it's theoretically (though not practically) possible to not bind dep, let's ensure it is.
+ dep: Dependency = NotFoundDependency('python', self.interpreter.environment)
+ for d in python_factory(self.interpreter.environment, for_machine, new_kwargs, methods, self):
+ dep = d()
+ if dep.found():
+ break
+
+ self.interpreter.coredata.deps[for_machine].put(identifier, dep)
+ return dep
+
@disablerIfNotFound
@permittedKwargs(permitted_dependency_kwargs | {'embed'})
@FeatureNewKwargs('python_installation.dependency', '0.53.0', ['embed'])
@noPosargs
def dependency_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> 'Dependency':
disabled, required, feature = extract_required_kwarg(kwargs, self.subproject)
- # it's theoretically (though not practically) possible for the else clse
- # to not bind dep, let's ensure it is.
- dep: 'Dependency' = NotFoundDependency('python', self.interpreter.environment)
if disabled:
mlog.log('Dependency', mlog.bold('python'), 'skipped: feature', mlog.bold(feature), 'disabled')
+ return NotFoundDependency('python', self.interpreter.environment)
else:
- new_kwargs = kwargs.copy()
- new_kwargs['required'] = False
- methods = process_method_kw({DependencyMethods.PKGCONFIG, DependencyMethods.SYSTEM}, kwargs)
- for d in python_factory(self.interpreter.environment,
- MachineChoice.BUILD if kwargs.get('native', False) else MachineChoice.HOST,
- new_kwargs, methods, self):
- dep = d()
- if dep.found():
- break
+ dep = self._dependency_method_impl(kwargs)
if required and not dep.found():
raise mesonlib.MesonException('Python dependency not found')
-
return dep
@typed_pos_args('install_data', varargs=(str, mesonlib.File))
