aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/src/factory.rs
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-06-27 21:12:20 +0900
committernsfisis <nsfisis@gmail.com>2026-06-27 21:12:20 +0900
commit0020a39197a53a14c130ee12759e8cb9002636ef (patch)
treefc4419caf8c24d8ab22a4999faaf255005515d4a /crates/shirabe/src/factory.rs
parent9759168c0c9b8120980b06734c0c029712bc7861 (diff)
downloadphp-shirabe-0020a39197a53a14c130ee12759e8cb9002636ef.tar.gz
php-shirabe-0020a39197a53a14c130ee12759e8cb9002636ef.tar.zst
php-shirabe-0020a39197a53a14c130ee12759e8cb9002636ef.zip
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<RefCell<dyn BinaryInstallerInterface>> 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) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe/src/factory.rs')
-rw-r--r--crates/shirabe/src/factory.rs118
1 files changed, 74 insertions, 44 deletions
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<std::cell::RefCell<InstallationManager>>,
- composer: &PartialOrFullComposer,
+ im: &std::rc::Rc<std::cell::RefCell<dyn crate::installer::InstallationManagerInterface>>,
+ composer: PartialComposerWeakHandle,
io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>,
process: Option<&std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>,
) {
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<RefCell<BinaryInstaller>>; 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<dyn crate::installer::BinaryInstallerInterface>,
+ > = 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<RefCell<>>
- 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(