From 0020a39197a53a14c130ee12759e8cb9002636ef Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sat, 27 Jun 2026 21:12:20 +0900 Subject: feat(installer): register default installers after Composer Rc is built LibraryInstaller and PluginInstaller upgrade the Composer back-reference in their constructors, so they could not be built inside Rc::new_cyclic where the weak handle is not yet upgradeable. Defer create_default_ installers until after the cyclic Rc is established, where the weak handle resolves, and implement it to register Library -> Plugin -> Metapackage with a single shared BinaryInstaller. To share one BinaryInstaller (as Composer does), LibraryInstaller's binary_installer becomes Rc> instead of an owned Box; PluginInstaller and the __set_binary_installer test seam follow. This clears "Unknown installer type: metapackage". Un-ignores the six remove tests that now pass; the remaining install/remove tests are re-labeled for the next blocker (InstallationManager::execute_batch still leaves the install/cleanup/repo.write promise chain as a todo!() stub, so package operations do not actually execute). Co-Authored-By: Claude Opus 4.8 (1M context) --- crates/shirabe/src/factory.rs | 118 ++++++++++++++++++++++++++---------------- 1 file changed, 74 insertions(+), 44 deletions(-) (limited to 'crates/shirabe/src/factory.rs') diff --git a/crates/shirabe/src/factory.rs b/crates/shirabe/src/factory.rs index 2c27015..6d1729b 100644 --- a/crates/shirabe/src/factory.rs +++ b/crates/shirabe/src/factory.rs @@ -10,7 +10,7 @@ use shirabe_php_shim::{ InvalidArgumentException, PATHINFO_EXTENSION, PHP_EOL, PHP_OS, PhpMixed, RuntimeException, UnexpectedValueException, array_replace_recursive, class_exists, dirname, extension_loaded, file_exists, file_get_contents, file_put_contents, implode, is_dir, is_file, json_decode, - mkdir, pathinfo, realpath, rename, strpos, strtr, substr, trim, + mkdir, pathinfo, realpath, rename, rtrim, strpos, strtr, substr, trim, }; use crate::autoload::AutoloadGenerator; @@ -725,8 +725,10 @@ impl Factory { .set_archive_manager(std::rc::Rc::new(std::cell::RefCell::new(am))); } - // add installers to the manager (must happen after download manager is created since they read it out of $composer) - self.create_default_installers(&im, &composer, io.clone(), Some(&process)); + // PHP adds the installers here, but LibraryInstaller/PluginInstaller upgrade the + // Composer back-reference in their constructors, which is not yet upgradeable + // inside Rc::new_cyclic. Registration is deferred until after the Rc is built + // (see create_default_installers call below). // init locker if possible if let PartialOrFullComposer::Full(ref mut composer_full) = composer { @@ -799,6 +801,26 @@ impl Factory { return Err(e); } + // add installers to the manager (must happen after download manager is created since they + // read it out of $composer). Deferred to here because LibraryInstaller/PluginInstaller + // upgrade the Composer back-reference in their constructors, which only becomes upgradeable + // once Rc::new_cyclic has returned. + { + let im = composer.borrow().get_installation_manager(); + let process = composer + .borrow() + .get_loop() + .borrow() + .get_process_executor() + .cloned(); + self.create_default_installers( + &im, + PartialComposerWeakHandle::from_weak(std::rc::Rc::downgrade(&composer)), + io.clone(), + process.as_ref(), + ); + } + // initialize plugin manager // // PluginManager::new upgrades the Composer back-reference to read its config and locker, so @@ -1195,58 +1217,66 @@ impl Factory { fn create_default_installers( &self, - im: &std::rc::Rc>, - composer: &PartialOrFullComposer, + im: &std::rc::Rc>, + composer: PartialComposerWeakHandle, io: std::rc::Rc>, process: Option<&std::rc::Rc>>, ) { let fs = std::rc::Rc::new(std::cell::RefCell::new(Filesystem::new( process.map(std::rc::Rc::clone), ))); - let bin_dir = trim( - &composer - .get_config() - .borrow_mut() - .get_str("bin-dir") - .unwrap_or_default(), - Some("/"), - ); - let bin_compat = composer - .get_config() - .borrow_mut() - .get_str("bin-compat") - .unwrap_or_default(); - let vendor_dir = trim( - &composer - .get_config() + let (bin_dir, bin_compat, vendor_dir) = { + let composer_rc = composer + .upgrade() + .expect("Composer must outlive create_default_installers"); + let composer_ref = composer_rc.borrow_partial(); + let config = composer_ref.get_config(); + let bin_dir = rtrim( + &config.borrow_mut().get_str("bin-dir").unwrap_or_default(), + Some("/"), + ); + let bin_compat = config .borrow_mut() - .get_str("vendor-dir") - .unwrap_or_default(), - Some("/"), - ); - // PHP: $binaryInstaller = new BinaryInstaller(...); - // $im->addInstaller(new LibraryInstaller($io, $composer, null, $fs, $binaryInstaller)); - // $im->addInstaller(new PluginInstaller($io, $composer, $fs, $binaryInstaller)); - // $im->addInstaller(new MetapackageInstaller($io)); + .get_str("bin-compat") + .unwrap_or_default(); + let vendor_dir = rtrim( + &config + .borrow_mut() + .get_str("vendor-dir") + .unwrap_or_default(), + Some("/"), + ); + (bin_dir, bin_compat, vendor_dir) + }; + // The same BinaryInstaller object is shared by the Library and Plugin installers. - // TODO(phase-c): two coupled blockers. (1) Sharing one BinaryInstaller requires - // Rc>; LibraryInstaller/PluginInstaller currently own a - // `BinaryInstaller` by value. (2) Both installers' constructors take a - // PartialComposerWeakHandle, but create_default_installers runs (line 737) before the - // composer is wrapped in its shared Rc, so no weak handle is obtainable here yet — - // installer registration must move after the composer Rc is established. Both are part of - // the composer construction-ordering / shared-ownership rework, so no installers are - // registered for now. - let _binary_installer = BinaryInstaller::new( + let binary_installer: std::rc::Rc< + std::cell::RefCell, + > = std::rc::Rc::new(std::cell::RefCell::new(BinaryInstaller::new( io.clone(), - bin_dir.clone(), - bin_compat.clone(), + bin_dir, + bin_compat, Some(fs.clone()), - Some(vendor_dir.clone()), - ); + Some(vendor_dir), + ))); - // TODO(phase-b): InstallationManager not clone-able; need shared Rc> - let _ = im; + im.borrow_mut() + .add_installer(Box::new(crate::installer::LibraryInstaller::new( + io.clone(), + composer.clone(), + None, + Some(fs.clone()), + Some(binary_installer.clone()), + ))); + im.borrow_mut() + .add_installer(Box::new(crate::installer::PluginInstaller::new( + io.clone(), + composer, + Some(fs), + Some(binary_installer), + ))); + im.borrow_mut() + .add_installer(Box::new(crate::installer::MetapackageInstaller::new(io))); } fn purge_packages( -- cgit v1.3.1