|
1 | 1 | """Provides functionality to resolve Conan-specific data for the CPPython project.""" |
2 | 2 |
|
3 | | -import importlib |
4 | | -import logging |
5 | 3 | from pathlib import Path |
6 | 4 | from typing import Any |
7 | 5 |
|
8 | | -from conan.api.conan_api import ConanAPI |
9 | | -from conan.internal.model.profile import Profile |
10 | 6 | from packaging.requirements import Requirement |
11 | 7 |
|
12 | 8 | from cppython.core.exception import ConfigException |
|
18 | 14 | ConanVersion, |
19 | 15 | ConanVersionRange, |
20 | 16 | ) |
21 | | -from cppython.utility.exception import ProviderConfigurationError |
22 | | - |
23 | | - |
24 | | -def _detect_cmake_program() -> str | None: |
25 | | - """Detect CMake program path from the cmake module if available. |
26 | | -
|
27 | | - Returns: |
28 | | - Path to cmake executable, or None if not found |
29 | | - """ |
30 | | - try: |
31 | | - # Try to import cmake module and get its executable path |
32 | | - # Note: cmake is an optional dependency, so we import it conditionally |
33 | | - cmake = importlib.import_module('cmake') |
34 | | - |
35 | | - cmake_bin_dir = Path(cmake.CMAKE_BIN_DIR) |
36 | | - |
37 | | - # Try common cmake executable names (pathlib handles platform differences) |
38 | | - for cmake_name in ['cmake.exe', 'cmake']: |
39 | | - cmake_exe = cmake_bin_dir / cmake_name |
40 | | - if cmake_exe.exists(): |
41 | | - return str(cmake_exe) |
42 | | - |
43 | | - return None |
44 | | - except ImportError: |
45 | | - # cmake module not available |
46 | | - return None |
47 | | - except (AttributeError, Exception): |
48 | | - # If cmake module doesn't have expected attributes |
49 | | - return None |
50 | | - |
51 | | - |
52 | | -def _profile_post_process( |
53 | | - profiles: list[Profile], conan_api: ConanAPI, cache_settings: Any, cmake_program: str | None = None |
54 | | -) -> None: |
55 | | - """Apply profile plugin and settings processing to a list of profiles. |
56 | | -
|
57 | | - Args: |
58 | | - profiles: List of profiles to process |
59 | | - conan_api: The Conan API instance |
60 | | - cache_settings: The settings configuration |
61 | | - cmake_program: Optional path to cmake program to configure in profiles |
62 | | - """ |
63 | | - logger = logging.getLogger('cppython.conan') |
64 | | - |
65 | | - # Get global configuration |
66 | | - global_conf = conan_api.config.global_conf |
67 | | - |
68 | | - # Apply profile plugin processing |
69 | | - try: |
70 | | - profile_plugin = conan_api.profiles._load_profile_plugin() |
71 | | - if profile_plugin is not None: |
72 | | - for profile in profiles: |
73 | | - try: |
74 | | - profile_plugin(profile) |
75 | | - except Exception as plugin_error: |
76 | | - logger.warning('Profile plugin failed for profile: %s', str(plugin_error)) |
77 | | - except (AttributeError, Exception): |
78 | | - logger.debug('Profile plugin not available or failed to load') |
79 | | - |
80 | | - # Apply the full profile processing pipeline for each profile |
81 | | - for profile in profiles: |
82 | | - # Set cmake program configuration if provided |
83 | | - if cmake_program is not None: |
84 | | - try: |
85 | | - # Set the tools.cmake:cmake_program configuration in the profile |
86 | | - profile.conf.update('tools.cmake:cmake_program', cmake_program) |
87 | | - logger.debug('Set tools.cmake:cmake_program=%s in profile', cmake_program) |
88 | | - except (AttributeError, Exception) as cmake_error: |
89 | | - logger.debug('Failed to set cmake program configuration: %s', str(cmake_error)) |
90 | | - |
91 | | - # Process settings to initialize processed_settings |
92 | | - try: |
93 | | - profile.process_settings(cache_settings) |
94 | | - except (AttributeError, Exception) as settings_error: |
95 | | - logger.debug('Settings processing failed for profile: %s', str(settings_error)) |
96 | | - |
97 | | - # Validate configuration |
98 | | - try: |
99 | | - profile.conf.validate() |
100 | | - except (AttributeError, Exception) as conf_error: |
101 | | - logger.debug('Configuration validation failed for profile: %s', str(conf_error)) |
102 | | - |
103 | | - # Apply global configuration to the profile |
104 | | - try: |
105 | | - if global_conf is not None: |
106 | | - profile.conf.rebase_conf_definition(global_conf) |
107 | | - except (AttributeError, Exception) as rebase_error: |
108 | | - logger.debug('Configuration rebase failed for profile: %s', str(rebase_error)) |
109 | | - |
110 | | - |
111 | | -def _apply_cmake_config_to_profile(profile: Profile, cmake_program: str | None, profile_type: str) -> None: |
112 | | - """Apply cmake program configuration to a profile. |
113 | | -
|
114 | | - Args: |
115 | | - profile: The profile to configure |
116 | | - cmake_program: Path to cmake program to configure |
117 | | - profile_type: Type of profile (for logging) |
118 | | - """ |
119 | | - if cmake_program is not None: |
120 | | - logger = logging.getLogger('cppython.conan') |
121 | | - try: |
122 | | - profile.conf.update('tools.cmake:cmake_program', cmake_program) |
123 | | - logger.debug('Set tools.cmake:cmake_program=%s in %s profile', cmake_program, profile_type) |
124 | | - except (AttributeError, Exception) as cmake_error: |
125 | | - logger.debug('Failed to set cmake program in %s profile: %s', profile_type, str(cmake_error)) |
126 | | - |
127 | | - |
128 | | -def _resolve_profiles( |
129 | | - host_profile_name: str | None, build_profile_name: str | None, conan_api: ConanAPI, cmake_program: str | None = None |
130 | | -) -> tuple[Profile, Profile]: |
131 | | - """Resolve host and build profiles, with fallback to auto-detection. |
132 | | -
|
133 | | - Args: |
134 | | - host_profile_name: The host profile name to resolve, or None for auto-detection |
135 | | - build_profile_name: The build profile name to resolve, or None for auto-detection |
136 | | - conan_api: The Conan API instance |
137 | | - cmake_program: Optional path to cmake program to configure in profiles |
138 | | -
|
139 | | - Returns: |
140 | | - A tuple of (host_profile, build_profile) |
141 | | - """ |
142 | | - logger = logging.getLogger('cppython.conan') |
143 | | - |
144 | | - def _resolve_profile(profile_name: str | None, is_host: bool) -> Profile: |
145 | | - """Helper to resolve a single profile.""" |
146 | | - profile_type = 'host' if is_host else 'build' |
147 | | - |
148 | | - if profile_name is not None and profile_name != 'default': |
149 | | - # Explicitly specified profile name (not the default) - fail if not found |
150 | | - try: |
151 | | - logger.debug('Loading %s profile: %s', profile_type, profile_name) |
152 | | - profile = conan_api.profiles.get_profile([profile_name]) |
153 | | - logger.debug('Successfully loaded %s profile: %s', profile_type, profile_name) |
154 | | - _apply_cmake_config_to_profile(profile, cmake_program, profile_type) |
155 | | - return profile |
156 | | - except Exception as e: |
157 | | - logger.error('Failed to load %s profile %s: %s', profile_type, profile_name, str(e)) |
158 | | - raise ProviderConfigurationError( |
159 | | - 'conan', |
160 | | - f'Failed to load {profile_type} profile {profile_name}: {str(e)}', |
161 | | - f'{profile_type}_profile', |
162 | | - ) from e |
163 | | - elif profile_name == 'default': |
164 | | - # Try to load default profile, but fall back to auto-detection if it fails |
165 | | - try: |
166 | | - logger.debug('Loading %s profile: %s', profile_type, profile_name) |
167 | | - profile = conan_api.profiles.get_profile([profile_name]) |
168 | | - logger.debug('Successfully loaded %s profile: %s', profile_type, profile_name) |
169 | | - _apply_cmake_config_to_profile(profile, cmake_program, profile_type) |
170 | | - return profile |
171 | | - except Exception as e: |
172 | | - logger.debug( |
173 | | - 'Failed to load %s profile %s: %s. Falling back to auto-detection.', |
174 | | - profile_type, |
175 | | - profile_name, |
176 | | - str(e), |
177 | | - ) |
178 | | - # Fall back to auto-detection |
179 | | - |
180 | | - try: |
181 | | - if is_host: |
182 | | - default_profile_path = conan_api.profiles.get_default_host() |
183 | | - else: |
184 | | - default_profile_path = conan_api.profiles.get_default_build() |
185 | | - |
186 | | - profile = conan_api.profiles.get_profile([default_profile_path]) |
187 | | - logger.debug('Using default %s profile', profile_type) |
188 | | - _apply_cmake_config_to_profile(profile, cmake_program, profile_type) |
189 | | - return profile |
190 | | - except Exception as e: |
191 | | - logger.debug('Default %s profile not available, using auto-detection: %s', profile_type, str(e)) |
192 | | - |
193 | | - # Create auto-detected profile |
194 | | - profile = conan_api.profiles.detect() |
195 | | - cache_settings = conan_api.config.settings_yml |
196 | | - |
197 | | - # Apply profile plugin processing |
198 | | - _profile_post_process([profile], conan_api, cache_settings, cmake_program) |
199 | | - |
200 | | - logger.debug('Auto-detected %s profile with plugin processing applied', profile_type) |
201 | | - return profile |
202 | | - |
203 | | - # Resolve both profiles |
204 | | - host_profile = _resolve_profile(host_profile_name, is_host=True) |
205 | | - build_profile = _resolve_profile(build_profile_name, is_host=False) |
206 | | - |
207 | | - return host_profile, build_profile |
208 | 17 |
|
209 | 18 |
|
210 | 19 | def _handle_single_specifier(name: str, specifier) -> ConanDependency: |
@@ -257,7 +66,7 @@ def resolve_conan_dependency(requirement: Requirement) -> ConanDependency: |
257 | 66 | return _handle_single_specifier(requirement.name, next(iter(specifiers))) |
258 | 67 |
|
259 | 68 | # Handle multiple specifiers - convert to Conan range syntax |
260 | | - range_parts = [] |
| 69 | + range_parts: list[str] = [] |
261 | 70 |
|
262 | 71 | # Define order for operators to ensure consistent output |
263 | 72 | operator_order = ['>=', '>', '<=', '<', '!='] |
@@ -305,20 +114,13 @@ def resolve_conan_data(data: dict[str, Any], core_data: CorePluginData) -> Conan |
305 | 114 | """ |
306 | 115 | parsed_data = ConanConfiguration(**data) |
307 | 116 |
|
308 | | - # Initialize Conan API for profile resolution |
309 | | - conan_api = ConanAPI() |
| 117 | + profile_dir = Path(parsed_data.profile_dir) |
310 | 118 |
|
311 | | - # Try to detect cmake program path from current virtual environment |
312 | | - cmake_program = _detect_cmake_program() |
313 | | - |
314 | | - # Resolve profiles |
315 | | - host_profile, build_profile = _resolve_profiles( |
316 | | - parsed_data.host_profile, parsed_data.build_profile, conan_api, cmake_program |
317 | | - ) |
| 119 | + if not profile_dir.is_absolute(): |
| 120 | + profile_dir = core_data.cppython_data.tool_path / profile_dir |
318 | 121 |
|
319 | 122 | return ConanData( |
320 | 123 | remotes=parsed_data.remotes, |
321 | 124 | skip_upload=parsed_data.skip_upload, |
322 | | - host_profile=host_profile, |
323 | | - build_profile=build_profile, |
| 125 | + profile_dir=profile_dir, |
324 | 126 | ) |
0 commit comments