using Invercargill; using Invercargill.DataStructures; /** * `usm update [-y|--yes] []` — bring installed packages up to * the newest version their repositories offer. * * Without package names every installed package with a newer repository * version becomes a resolution root; with names exactly those packages * do (already-current names are skipped with a note). Either way there * is one resolution, one {@link Usm.TransactionSummary}, one confirm — * and the downloads happen only once the plan is accepted. * * Dependencies already installed at the resolved version are left alone; * an installed package moving to a different version is removed * (RemoveType.UPGRADE) and the new version installed in its place. The * old version's state directory is kept, which is what makes `usm * downgrade` possible. */ private int update_main(string[] args) { var assume_yes = false; var package_names = new Vector(); for(int i = 2; i < args.length; i++) { var argument = args[i]; if(argument == "-y" || argument == "--yes") { assume_yes = true; } else if(argument.has_prefix("-")) { printerr(@"Unknown option \"$argument\"\n"); return update_usage(); } else { package_names.add(argument); } } var progress = new Usm.Cli.ProgressBar(); Usm.SystemState state = null; try { state = new Usm.SystemState(paths); } catch(Error e) { printerr(@"This system is not managed by usm: $(e.message)\n"); return 245; } try { printerr("Refreshing repositories...\n"); var resolver = new Usm.Resolver(new Usm.ResourceFinder()); foreach(var repo in state.get_repositories()) { state.refresh_list(repo, (f, c, t) => printerr(@"Refreshing list for $(repo.name): downloading $f $c/$t bytes\r")); var listing = state.get_latest_list(repo); if(listing != null) { resolver.load_listing(repo, listing); } printerr("\n"); } // Cached packages can satisfy dependencies, including the old // versions kept behind by earlier updates resolver.load_cache(paths); var installed_by_name = new Dictionary(); var installed_versions = new Dictionary(); foreach(var installed in state.get_installed_packages()) { var manifest = installed.get_manifest(); Usm.Version seen; if(installed_versions.try_get(manifest.name, out seen) && !manifest.version.greater_than(seen)) { continue; } installed_by_name.set(manifest.name, state.resolve_installed(installed)); installed_versions.set(manifest.name, manifest.version); } var roots = new Vector(); if(package_names.length > 0) { foreach(var package_name in package_names) { Usm.CachedPackage installed; if(!installed_by_name.try_get(package_name, out installed)) { printerr(@"No installed package named \"$package_name\"\n"); return 254; } var target = resolver.find_package(package_name); if(target == null) { printerr(@"No package named \"$package_name\" found in any repository or the cache\n"); return 254; } Usm.Version installed_version; installed_versions.try_get(package_name, out installed_version); if(!target.manifest.version.greater_than(installed_version)) { print(@"\"$package_name\" is already up to date.\n"); continue; } roots.add(target); } if(roots.length == 0) { return 0; } } else { foreach(var pair in installed_by_name) { var target = resolver.find_package(pair.key); if(target != null && target.manifest.version.greater_than(installed_versions[pair.key])) { roots.add(target); } } if(roots.length == 0) { print("All installed packages are up to date.\n"); return 0; } } var spm = new Usm.SystemPackageManager(state.config); Usm.ResolutionResult resolution; try { resolution = resolver.resolve(roots, spm); } catch(Usm.ResolverError e) { printerr("%s\n".printf(e.message)); return 240; } // Build the transaction set WITHOUT downloading: dependencies // already installed at the resolved version stay as-is (so an // update plan names only what actually moves), and repository // packages join as their cache directories carrying the listing // manifest — the plan is confirmed before a single byte moves var current_names = new HashSet(); foreach(var package in resolution.install_order) { Usm.Version installed_version; if(installed_versions.try_get(package.manifest.name, out installed_version) && installed_version.compare(package.manifest.version) == 0) { current_names.add(package.manifest.name); } } var cached_packages = new HashSet(); uint download_count = 0; int64 download_bytes = 0; var download_sizes_known = true; foreach(var package in resolution.install_order) { if(current_names.contains(package.manifest.name)) { continue; } if(package.repository == null) { if(package.package_path != null) { cached_packages.add(new Usm.CachedPackage(Path.get_dirname((!)package.package_path))); } continue; } var path = state.generate_cache_path(package.manifest); cached_packages.add(new Usm.CachedPackage(path) { known_manifest = package.manifest }); if(File.new_for_path(Path.build_filename(path, "package.usmc")).query_exists()) { continue; } download_count++; var size = install_package_download_size(package.repository, package.repository_entry); if(size == null) { download_sizes_known = false; } else { download_bytes += size; } } // The installed versions being replaced by a different version var to_remove = new HashSet(); foreach(var package in resolution.install_order) { Usm.CachedPackage installed; Usm.Version installed_version; if(!installed_by_name.try_get(package.manifest.name, out installed)) { continue; } if(installed_versions.try_get(package.manifest.name, out installed_version) && installed_version.compare(package.manifest.version) != 0) { to_remove.add(installed); } } var install_order = new Vector(); foreach(var package in resolution.install_order) { install_order.add(package.manifest.name); } // The exact reverse: dependents are removed before their providers var remove_order = new Vector(); foreach(var package in resolution.removal_order) { remove_order.add(package.manifest.name); } var transaction = new Usm.Transaction() { paths = paths, resource_finder = new Usm.ResourceFinder(paths), to_remove = to_remove, to_install = cached_packages, install_order = install_order, remove_order = remove_order, state = state }; // Planning before the prompt populates the rebuildDependants // entries so the summary shows the whole transaction try { transaction.strategise(); } catch(Usm.TransactionError e) { printerr(@"$(e.message)\n"); return 240; } var summary = new Usm.TransactionSummary() { to_install = cached_packages.to_vector(), to_remove = to_remove.to_vector(), to_rebuild = transaction.rebuilds, download_count = download_count, download_bytes = download_sizes_known ? download_bytes : -1 }; if(!Usm.Cli.confirm_transaction(summary, assume_yes)) { print("Aborted.\n"); return 1; } // Only now that the plan is confirmed do the packages transfer install_download_packages(state, resolution, current_names); if(resolution.system_packages.any()) { if(!install_system_packages(spm, resolution.system_packages)) { return 239; } } printerr("\nRunning transaction...\n"); transaction.progress_updated.connect(progress.on_transaction_progress); transaction.run(); progress.finish(); } catch(Error e) { progress.fail(); printerr(@"Error: $(e.message)\n"); return 238; } return 0; } private int update_usage() { printerr("USAGE:\n\tusm update [-y|--yes] []\n"); return 255; }