|
1 | 1 | """Provides functionality to resolve Conan-specific data for the CPPython project.""" |
2 | 2 |
|
| 3 | +import logging |
3 | 4 | from typing import Any |
4 | 5 |
|
| 6 | +from conan.api.conan_api import ConanAPI |
| 7 | +from conan.internal.model.profile import Profile |
5 | 8 | from packaging.requirements import Requirement |
6 | 9 |
|
7 | 10 | from cppython.core.exception import ConfigException |
8 | 11 | from cppython.core.schema import CorePluginData |
9 | 12 | from cppython.plugins.conan.schema import ConanConfiguration, ConanData, ConanDependency |
| 13 | +from cppython.utility.exception import ProviderConfigurationError |
| 14 | + |
| 15 | + |
| 16 | +def _profile_post_process(profiles: list[Profile], conan_api: ConanAPI, cache_settings: Any) -> None: |
| 17 | + """Apply profile plugin and settings processing to a list of profiles. |
| 18 | +
|
| 19 | + Args: |
| 20 | + profiles: List of profiles to process |
| 21 | + conan_api: The Conan API instance |
| 22 | + cache_settings: The settings configuration |
| 23 | + """ |
| 24 | + logger = logging.getLogger('cppython.conan') |
| 25 | + |
| 26 | + # Apply profile plugin processing |
| 27 | + try: |
| 28 | + profile_plugin = conan_api.profiles._load_profile_plugin() |
| 29 | + if profile_plugin is not None: |
| 30 | + for profile in profiles: |
| 31 | + try: |
| 32 | + profile_plugin(profile) |
| 33 | + except Exception as plugin_error: |
| 34 | + logger.warning('Profile plugin failed for profile: %s', str(plugin_error)) |
| 35 | + except (AttributeError, Exception): |
| 36 | + logger.debug('Profile plugin not available or failed to load') |
| 37 | + |
| 38 | + # Process settings to initialize processed_settings |
| 39 | + for profile in profiles: |
| 40 | + try: |
| 41 | + profile.process_settings(cache_settings) |
| 42 | + except (AttributeError, Exception) as settings_error: |
| 43 | + logger.debug('Settings processing failed for profile: %s', str(settings_error)) |
| 44 | + |
| 45 | + |
| 46 | +def _resolve_profiles( |
| 47 | + host_profile_name: str | None, build_profile_name: str | None, conan_api: ConanAPI |
| 48 | +) -> tuple[Profile, Profile]: |
| 49 | + """Resolve host and build profiles, with fallback to auto-detection. |
| 50 | +
|
| 51 | + Args: |
| 52 | + host_profile_name: The host profile name to resolve, or None for auto-detection |
| 53 | + build_profile_name: The build profile name to resolve, or None for auto-detection |
| 54 | + conan_api: The Conan API instance |
| 55 | +
|
| 56 | + Returns: |
| 57 | + A tuple of (host_profile, build_profile) |
| 58 | + """ |
| 59 | + logger = logging.getLogger('cppython.conan') |
| 60 | + |
| 61 | + def _resolve_profile(profile_name: str | None, is_host: bool) -> Profile: |
| 62 | + """Helper to resolve a single profile.""" |
| 63 | + profile_type = 'host' if is_host else 'build' |
| 64 | + |
| 65 | + if profile_name is not None and profile_name != 'default': |
| 66 | + # Explicitly specified profile name (not the default) - fail if not found |
| 67 | + try: |
| 68 | + logger.debug('Loading %s profile: %s', profile_type, profile_name) |
| 69 | + profile = conan_api.profiles.get_profile([profile_name]) |
| 70 | + logger.debug('Successfully loaded %s profile: %s', profile_type, profile_name) |
| 71 | + return profile |
| 72 | + except Exception as e: |
| 73 | + logger.error('Failed to load %s profile %s: %s', profile_type, profile_name, str(e)) |
| 74 | + raise ProviderConfigurationError( |
| 75 | + 'conan', |
| 76 | + f'Failed to load {profile_type} profile {profile_name}: {str(e)}', |
| 77 | + f'{profile_type}_profile', |
| 78 | + ) from e |
| 79 | + elif profile_name == 'default': |
| 80 | + # Try to load default profile, but fall back to auto-detection if it fails |
| 81 | + try: |
| 82 | + logger.debug('Loading %s profile: %s', profile_type, profile_name) |
| 83 | + profile = conan_api.profiles.get_profile([profile_name]) |
| 84 | + logger.debug('Successfully loaded %s profile: %s', profile_type, profile_name) |
| 85 | + return profile |
| 86 | + except Exception as e: |
| 87 | + logger.debug( |
| 88 | + 'Failed to load %s profile %s: %s. Falling back to auto-detection.', |
| 89 | + profile_type, |
| 90 | + profile_name, |
| 91 | + str(e), |
| 92 | + ) |
| 93 | + # Fall back to auto-detection |
| 94 | + |
| 95 | + try: |
| 96 | + if is_host: |
| 97 | + default_profile_path = conan_api.profiles.get_default_host() |
| 98 | + else: |
| 99 | + default_profile_path = conan_api.profiles.get_default_build() |
| 100 | + |
| 101 | + profile = conan_api.profiles.get_profile([default_profile_path]) |
| 102 | + logger.debug('Using default %s profile', profile_type) |
| 103 | + return profile |
| 104 | + except Exception as e: |
| 105 | + logger.warning('Default %s profile not available, using auto-detection: %s', profile_type, str(e)) |
| 106 | + |
| 107 | + # Create auto-detected profile |
| 108 | + profile = conan_api.profiles.detect() |
| 109 | + cache_settings = conan_api.config.settings_yml |
| 110 | + |
| 111 | + # Apply profile plugin processing |
| 112 | + _profile_post_process([profile], conan_api, cache_settings) |
| 113 | + |
| 114 | + logger.debug('Auto-detected %s profile with plugin processing applied', profile_type) |
| 115 | + return profile |
| 116 | + |
| 117 | + # Resolve both profiles |
| 118 | + host_profile = _resolve_profile(host_profile_name, is_host=True) |
| 119 | + build_profile = _resolve_profile(build_profile_name, is_host=False) |
| 120 | + |
| 121 | + return host_profile, build_profile |
10 | 122 |
|
11 | 123 |
|
12 | 124 | def resolve_conan_dependency(requirement: Requirement) -> ConanDependency: |
@@ -43,4 +155,14 @@ def resolve_conan_data(data: dict[str, Any], core_data: CorePluginData) -> Conan |
43 | 155 | """ |
44 | 156 | parsed_data = ConanConfiguration(**data) |
45 | 157 |
|
46 | | - return ConanData(remotes=parsed_data.remotes) |
| 158 | + # Initialize Conan API for profile resolution |
| 159 | + conan_api = ConanAPI() |
| 160 | + |
| 161 | + # Resolve profiles |
| 162 | + host_profile, build_profile = _resolve_profiles(parsed_data.host_profile, parsed_data.build_profile, conan_api) |
| 163 | + |
| 164 | + return ConanData( |
| 165 | + remotes=parsed_data.remotes, |
| 166 | + host_profile=host_profile, |
| 167 | + build_profile=build_profile, |
| 168 | + ) |
0 commit comments