55installation, and synchronization with other tools.
66"""
77
8- import logging
98from pathlib import Path
109from typing import Any
1110
@@ -78,87 +77,139 @@ def _install_dependencies(self, *, update: bool = False) -> None:
7877 If False, use cached versions when available.
7978 """
8079 try :
81- logger = logging . getLogger ( 'cppython.conan' )
82- logger . debug ( 'Starting dependency installation/update (update=%s)' , update )
80+ # Setup environment and generate conanfile
81+ conan_api , conanfile_path = self . _prepare_installation ( )
8382
84- resolved_dependencies = [resolve_conan_dependency (req ) for req in self .core_data .cppython_data .dependencies ]
85- logger .debug (
86- 'Resolved %d dependencies: %s' , len (resolved_dependencies ), [str (dep ) for dep in resolved_dependencies ]
87- )
83+ # Load dependency graph
84+ deps_graph = self ._load_dependency_graph (conan_api , conanfile_path , update )
85+
86+ # Install dependencies
87+ self ._install_binaries (conan_api , deps_graph , update )
88+
89+ # Generate consumer files
90+ self ._generate_consumer_files (conan_api , deps_graph )
91+
92+ except Exception as e :
93+ operation = 'update' if update else 'install'
94+ raise ProviderInstallationError ('conan' , f'Failed to { operation } dependencies: { e } ' , e ) from e
95+
96+ def _prepare_installation (self ) -> tuple [ConanAPI , Path ]:
97+ """Prepare the installation environment and generate conanfile.
8898
89- # Generate conanfile.py
99+ Returns:
100+ Tuple of (ConanAPI instance, conanfile path)
101+
102+ Raises:
103+ ProviderInstallationError: If conanfile generation or setup fails
104+ """
105+ try :
106+ # Resolve dependencies and generate conanfile.py
107+ resolved_dependencies = [resolve_conan_dependency (req ) for req in self .core_data .cppython_data .dependencies ]
90108 self .builder .generate_conanfile (self .core_data .project_data .project_root , resolved_dependencies )
91- logger .debug ('Generated conanfile.py at %s' , self .core_data .project_data .project_root )
92109
93110 # Ensure build directory exists
94111 self .core_data .cppython_data .build_path .mkdir (parents = True , exist_ok = True )
95- logger .debug ('Created build path: %s' , self .core_data .cppython_data .build_path )
96112
97- # Initialize Conan API
113+ # Setup paths and API
98114 conan_api = ConanAPI ()
99-
100- # Get project paths
101115 project_root = self .core_data .project_data .project_root
102116 conanfile_path = project_root / 'conanfile.py'
103117
104118 if not conanfile_path .exists ():
105119 raise ProviderInstallationError ('conan' , 'Generated conanfile.py not found' )
106120
107- # Get all remotes
108- all_remotes = conan_api .remotes .list ()
109- logger .debug ('Available remotes: %s' , [remote .name for remote in all_remotes ])
121+ return conan_api , conanfile_path
110122
111- # Get profiles from resolved data
112- profile_host , profile_build = self .data .host_profile , self .data .build_profile
123+ except Exception as e :
124+ raise ProviderInstallationError ('conan' , f'Failed to prepare installation environment: { e } ' , e ) from e
125+
126+ def _load_dependency_graph (self , conan_api : ConanAPI , conanfile_path : Path , update : bool ):
127+ """Load and build the dependency graph.
128+
129+ Args:
130+ conan_api: The Conan API instance
131+ conanfile_path: Path to the conanfile.py
132+ update: Whether to check for updates
133+
134+ Returns:
135+ The loaded dependency graph
113136
114- path = str (conanfile_path )
115- remotes = all_remotes
116- update_flag = None if not update else True
117- check_updates_flag = update
137+ Raises:
138+ ProviderInstallationError: If dependency graph loading fails
139+ """
140+ try :
141+ all_remotes = conan_api .remotes .list ()
142+ profile_host , profile_build = self .data .host_profile , self .data .build_profile
118143
119- deps_graph = conan_api .graph .load_graph_consumer (
120- path = path ,
144+ return conan_api .graph .load_graph_consumer (
145+ path = str ( conanfile_path ) ,
121146 name = None ,
122147 version = None ,
123148 user = None ,
124149 channel = None ,
125150 lockfile = None ,
126- remotes = remotes ,
127- update = update_flag ,
128- check_updates = check_updates_flag ,
151+ remotes = all_remotes ,
152+ update = update or None ,
153+ check_updates = update ,
129154 is_build_require = False ,
130155 profile_host = profile_host ,
131156 profile_build = profile_build ,
132157 )
133158
134- logger .debug ('Dependency graph loaded with %d nodes' , len (deps_graph .nodes ))
159+ except Exception as e :
160+ raise ProviderInstallationError ('conan' , f'Failed to load dependency graph: { e } ' , e ) from e
161+
162+ def _install_binaries (self , conan_api : ConanAPI , deps_graph , update : bool ) -> None :
163+ """Analyze and install binary dependencies.
164+
165+ Args:
166+ conan_api: The Conan API instance
167+ deps_graph: The dependency graph
168+ update: Whether to check for updates
169+
170+ Raises:
171+ ProviderInstallationError: If binary analysis or installation fails
172+ """
173+ try :
174+ all_remotes = conan_api .remotes .list ()
135175
136176 # Analyze binaries to determine what needs to be built/downloaded
137177 conan_api .graph .analyze_binaries (
138178 graph = deps_graph ,
139- build_mode = ['missing' ], # Only build what's missing
179+ build_mode = ['missing' ],
140180 remotes = all_remotes ,
141- update = None if not update else True ,
181+ update = update or None ,
142182 lockfile = None ,
143183 )
144184
145185 # Install all dependencies
146186 conan_api .install .install_binaries (deps_graph = deps_graph , remotes = all_remotes )
147187
148- # Generate files for the consumer (conandata.yml, conan_toolchain.cmake, etc.)
188+ except Exception as e :
189+ raise ProviderInstallationError ('conan' , f'Failed to install binary dependencies: { e } ' , e ) from e
190+
191+ def _generate_consumer_files (self , conan_api : ConanAPI , deps_graph ) -> None :
192+ """Generate consumer files (CMake toolchain, deps, etc.).
193+
194+ Args:
195+ conan_api: The Conan API instance
196+ deps_graph: The dependency graph
197+
198+ Raises:
199+ ProviderInstallationError: If consumer file generation fails
200+ """
201+ try :
202+ project_root = self .core_data .project_data .project_root
203+
149204 conan_api .install .install_consumer (
150205 deps_graph = deps_graph ,
151206 generators = ['CMakeToolchain' , 'CMakeDeps' ],
152207 source_folder = str (project_root ),
153208 output_folder = str (self .core_data .cppython_data .build_path ),
154209 )
155210
156- logger .debug ('Successfully installed dependencies using Conan API' )
157-
158211 except Exception as e :
159- operation = 'update' if update else 'install'
160- error_msg = str (e )
161- raise ProviderInstallationError ('conan' , f'Failed to { operation } dependencies: { error_msg } ' , e ) from e
212+ raise ProviderInstallationError ('conan' , f'Failed to generate consumer files: { e } ' , e ) from e
162213
163214 def install (self ) -> None :
164215 """Installs the provider"""
@@ -199,7 +250,7 @@ def sync_data(self, consumer: SyncConsumer) -> SyncData:
199250 top_level_includes = self .core_data .cppython_data .install_path / 'conan_provider.cmake' ,
200251 )
201252
202- raise NotSupportedError ('OOF ' )
253+ raise NotSupportedError (f'Unsupported sync types: { consumer . sync_types () } ' )
203254
204255 @classmethod
205256 async def download_tooling (cls , directory : Path ) -> None :
@@ -208,99 +259,93 @@ async def download_tooling(cls, directory: Path) -> None:
208259
209260 def publish (self ) -> None :
210261 """Publishes the package using conan create workflow."""
211- # Get the project root directory where conanfile.py should be located
212262 project_root = self .core_data .project_data .project_root
213263 conanfile_path = project_root / 'conanfile.py'
214264
215265 if not conanfile_path .exists ():
216266 raise FileNotFoundError (f'conanfile.py not found at { conanfile_path } ' )
217267
218- # Initialize Conan API
219268 conan_api = ConanAPI ()
220-
221- # Get configured remotes from Conan API and filter by our configuration
222- # TODO: We want to replace the global conan remotes with the ones configured in CPPython.
223269 all_remotes = conan_api .remotes .list ()
224- if not self .data .local_only :
225- # Filter remotes to only include those specified in configuration
226- configured_remotes = [remote for remote in all_remotes if remote .name in self .data .remotes ]
227-
228- if not configured_remotes :
229- available_remotes = [remote .name for remote in all_remotes ]
230- raise ProviderConfigurationError (
231- 'conan' ,
232- f'No configured remotes found. Available remotes: { available_remotes } , '
233- f'Configured remotes: { self .data .remotes } ' ,
234- 'remotes' ,
235- )
236- else :
237- configured_remotes = []
238270
239- # Step 1: Export the recipe to the cache
240- # This is equivalent to the export part of `conan create`
241- ref , conanfile = conan_api .export .export (
271+ # Configure remotes for upload
272+ configured_remotes = self ._get_configured_remotes (all_remotes )
273+
274+ # Export the recipe to cache
275+ ref , _ = conan_api .export .export (
242276 path = str (conanfile_path ),
243277 name = None ,
244278 version = None ,
245279 user = None ,
246280 channel = None ,
247281 lockfile = None ,
248- remotes = all_remotes , # Use all remotes for dependency resolution during export
282+ remotes = all_remotes ,
249283 )
250284
251- # Step 2: Get profiles from resolved data
285+ # Build dependency graph and install
252286 profile_host , profile_build = self .data .host_profile , self .data .build_profile
253-
254- # Step 3: Build dependency graph for the package - prepare parameters
255- path = str (conanfile_path )
256- remotes = all_remotes # Use all remotes for dependency resolution
257-
258287 deps_graph = conan_api .graph .load_graph_consumer (
259- path = path ,
288+ path = str ( conanfile_path ) ,
260289 name = None ,
261290 version = None ,
262291 user = None ,
263292 channel = None ,
264293 lockfile = None ,
265- remotes = remotes ,
294+ remotes = all_remotes ,
266295 update = None ,
267296 check_updates = False ,
268297 is_build_require = False ,
269298 profile_host = profile_host ,
270299 profile_build = profile_build ,
271300 )
272301
273- # Step 4: Analyze binaries and install/ build them if needed
302+ # Analyze and build binaries
274303 conan_api .graph .analyze_binaries (
275304 graph = deps_graph ,
276- build_mode = ['*' ], # Build from source (equivalent to the create behavior)
277- remotes = all_remotes , # Use all remotes for dependency resolution
305+ build_mode = ['*' ],
306+ remotes = all_remotes ,
278307 update = None ,
279308 lockfile = None ,
280309 )
281310
282- # Step 5: Install all dependencies and build the package
283311 conan_api .install .install_binaries (deps_graph = deps_graph , remotes = all_remotes )
284312
285- # If not local only, upload the package
313+ # Upload if not local only
286314 if not self .data .local_only :
287- # Get all packages matching the created reference
288- ref_pattern = ListPattern (f'{ ref .name } /*' , package_id = '*' , only_recipe = False )
289- package_list = conan_api .list .select (ref_pattern )
290-
291- if package_list .recipes :
292- # Use the first configured remote for upload
293- remote = configured_remotes [0 ]
294-
295- # Upload the package to configured remotes
296- conan_api .upload .upload_full (
297- package_list = package_list ,
298- remote = remote ,
299- enabled_remotes = configured_remotes , # Only upload to configured remotes
300- check_integrity = False ,
301- force = False ,
302- metadata = None ,
303- dry_run = False ,
304- )
305- else :
306- raise ProviderInstallationError ('conan' , 'No packages found to upload' )
315+ self ._upload_package (conan_api , ref , configured_remotes )
316+
317+ def _get_configured_remotes (self , all_remotes ):
318+ """Get and validate configured remotes for upload."""
319+ if self .data .local_only :
320+ return []
321+
322+ configured_remotes = [remote for remote in all_remotes if remote .name in self .data .remotes ]
323+
324+ if not configured_remotes :
325+ available_remotes = [remote .name for remote in all_remotes ]
326+ raise ProviderConfigurationError (
327+ 'conan' ,
328+ f'No configured remotes found. Available: { available_remotes } , Configured: { self .data .remotes } ' ,
329+ 'remotes' ,
330+ )
331+
332+ return configured_remotes
333+
334+ def _upload_package (self , conan_api , ref , configured_remotes ):
335+ """Upload the package to configured remotes."""
336+ ref_pattern = ListPattern (f'{ ref .name } /*' , package_id = '*' , only_recipe = False )
337+ package_list = conan_api .list .select (ref_pattern )
338+
339+ if not package_list .recipes :
340+ raise ProviderInstallationError ('conan' , 'No packages found to upload' )
341+
342+ remote = configured_remotes [0 ]
343+ conan_api .upload .upload_full (
344+ package_list = package_list ,
345+ remote = remote ,
346+ enabled_remotes = configured_remotes ,
347+ check_integrity = False ,
348+ force = False ,
349+ metadata = None ,
350+ dry_run = False ,
351+ )
0 commit comments