|
@@ -53,18 +53,105 @@ namespace Usm {
|
|
|
var prefix = previous_key == key ? "\x1b[1F\x1b[2K" : "";
|
|
|
previous_key = key;
|
|
|
|
|
|
- printerr(@"$prefix[$current_task/$total_tasks] $verb $subject ($percent%)\n");
|
|
|
+ printerr(@"$prefix[$(current_task+1)/$total_tasks] $verb $subject ($percent%)\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ public void print_progress_simple(TransactionTask task_type, string subject, int current_task, int total_tasks, float task_progress) {
|
|
|
+ var verb = task_type.get_verb();
|
|
|
+ verb = verb[0].toupper().to_string() + verb.substring(1);
|
|
|
+ var percent = (int)(task_progress * 100.0f);
|
|
|
+ printerr(@"[$(current_task+1)/$total_tasks] $verb $subject ($percent%)\n");
|
|
|
}
|
|
|
|
|
|
public void strategise() throws TransactionError {
|
|
|
+ task_count = (to_install.count() * 4) + (to_remove.count() * 3) + 1;
|
|
|
+ var strategise_worst_case_task_count = (to_remove.count() * to_remove.count()) + (to_install.count() * to_install.count());
|
|
|
+ var strategise_current_task = 0;
|
|
|
+
|
|
|
report_progress(TransactionTask.STRATEGISING, 0.0f);
|
|
|
+
|
|
|
+ // Installation strategy
|
|
|
install_lots = new Vector<Vector<CachedPackage>>();
|
|
|
- install_lots.add(to_install.to_vector());
|
|
|
+ var touched = new Set<CachedPackage>();
|
|
|
+ var available_resources = new Set<ResourceRef>();
|
|
|
+ var round = 0;
|
|
|
+ while(true) {
|
|
|
+ strategise_current_task = round * to_install.count();
|
|
|
+ var lot = new Vector<CachedPackage>();
|
|
|
+ var remaining = to_install.difference(touched);
|
|
|
+ if(remaining.count() == 0) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ foreach (var package in to_install.difference(touched)) {
|
|
|
+ report_progress(TransactionTask.STRATEGISING, (float)strategise_current_task / (float)strategise_worst_case_task_count);
|
|
|
+ try {
|
|
|
+ var manifest = package.get_manifest();
|
|
|
+ var installtime_dependencies = manifest.dependencies.manage.concat(manifest.dependencies.build);
|
|
|
+ if(installtime_dependencies.all(d => d.is_satisfied() || available_resources.any(r => d.satisfied_by(r)))) {
|
|
|
+ lot.add(package);
|
|
|
+ touched.add(package);
|
|
|
+ available_resources.add_all(manifest.provides.select<ResourceRef>(p => p.key));
|
|
|
+ }
|
|
|
+ strategise_current_task++;
|
|
|
+ }
|
|
|
+ catch(Error e) {
|
|
|
+ throw new TransactionError.UNKNOWN_ERROR(@"Failed to read manifest for package \"$(package.package_name)\": $(e.message)");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if(lot.count() == 0) {
|
|
|
+ var packages = to_install.difference(touched).to_string(p => p.package_name, ", ");
|
|
|
+ throw new TransactionError.INVALID_TRANSACTION(@"Could not build a transaction strategy, packages $(packages) have unmet or cyclical dependencies");
|
|
|
+ }
|
|
|
+
|
|
|
+ install_lots.add(lot);
|
|
|
+ round++;
|
|
|
+ }
|
|
|
+ var current_task_baseline = (to_install.count() * to_install.count());
|
|
|
+ strategise_current_task = current_task_baseline;
|
|
|
+ report_progress(TransactionTask.STRATEGISING, (float)strategise_current_task / (float)strategise_worst_case_task_count);
|
|
|
+
|
|
|
+ // Removal strategy
|
|
|
remove_order = new Vector<CachedPackage>();
|
|
|
+ touched = new Set<CachedPackage>();
|
|
|
+ Set<CachedPackageManifest> remaining_to_remove;
|
|
|
+ try {
|
|
|
+ remaining_to_remove = to_remove
|
|
|
+ .try_select<CachedPackageManifest>(p => new CachedPackageManifest(p))
|
|
|
+ .unwrap_to_set();
|
|
|
+
|
|
|
+ }
|
|
|
+ catch(Error e) {
|
|
|
+ throw new TransactionError.UNKNOWN_ERROR(@"Failed to read manifest: $(e.message)");
|
|
|
+ }
|
|
|
+
|
|
|
+ round = 0;
|
|
|
+ while(true) {
|
|
|
+ strategise_current_task = current_task_baseline + (round * to_remove.count());
|
|
|
+ if(remaining_to_remove.count() == 0) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ foreach (var package in remaining_to_remove) {
|
|
|
+ report_progress(TransactionTask.STRATEGISING, (float)strategise_current_task / (float)strategise_worst_case_task_count);
|
|
|
+ if(remaining_to_remove.no(p => p.manifest.dependencies.manage.any(d => package.manifest.provides.any(r => d.satisfied_by(r.key))))) {
|
|
|
+ remove_order.add(package.package);
|
|
|
+ touched.add(package.package);
|
|
|
+ remaining_to_remove.remove(package);
|
|
|
+ strategise_current_task++;
|
|
|
+ round++;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ strategise_current_task++;
|
|
|
+ }
|
|
|
+
|
|
|
+ var packages = remaining_to_remove.to_string(p => p.package.package_name, ", ");
|
|
|
+ throw new TransactionError.INVALID_TRANSACTION(@"Could not build a transaction strategy, packages $(packages) have unmet or cyclical dependencies");
|
|
|
+ }
|
|
|
|
|
|
|
|
|
report_progress(TransactionTask.STRATEGISING, 1.0f);
|
|
|
- task_count = (to_install.count() * 4) + (to_remove.count() * 3);
|
|
|
current_task++;
|
|
|
}
|
|
|
|
|
@@ -137,28 +224,7 @@ namespace Usm {
|
|
|
if(build_proc != null)
|
|
|
build_proc.wait_check();
|
|
|
|
|
|
- // Delete files and symlinks first
|
|
|
- foreach (var resource in manifest.provides.where(r => r.value.file_type != Usm.ManifestFileType.DIRECTORY)) {
|
|
|
- var path = paths.get_suggested_path(resource.key);
|
|
|
- var file = File.new_for_path(path);
|
|
|
- if(file.query_exists()) {
|
|
|
- file.delete();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // Delete directories last
|
|
|
- foreach (var resource in manifest.provides.where(r => r.value.file_type == Usm.ManifestFileType.DIRECTORY)) {
|
|
|
- var path = paths.get_suggested_path(resource.key);
|
|
|
- try {
|
|
|
- var file = File.new_for_path(path);
|
|
|
- if(file.query_exists()) {
|
|
|
- file.delete();
|
|
|
- }
|
|
|
- }
|
|
|
- catch(IOError.NOT_EMPTY e) {
|
|
|
- warning(@"Did not remove resource \"$path\": directory is not empty\n");
|
|
|
- }
|
|
|
- }
|
|
|
+ manifest.remove_resources( paths, (r, cr, tr, f) => report_progress(TransactionTask.INSTALLING, ((float)cr + (float)f) / (float)tr));
|
|
|
|
|
|
state.unmark_installed(package);
|
|
|
}
|
|
@@ -172,31 +238,8 @@ namespace Usm {
|
|
|
var manifest = new Usm.Manifest.from_file("MANIFEST.usm");
|
|
|
report_progress(TransactionTask.INSTALLING, 0.0f);
|
|
|
|
|
|
- // Install each resource speficied by the manifest
|
|
|
- var resource_count = manifest.provides.count();
|
|
|
- var resources_installed = 0;
|
|
|
- foreach (var resource in manifest.provides) {
|
|
|
- var path = paths.get_suggested_path(resource.key);
|
|
|
- if(resource.value.file_type == Usm.ManifestFileType.REGULAR) {
|
|
|
- var src = File.new_build_filename(build_dir, resource.value.path);
|
|
|
- var dest = File.new_for_path(path);
|
|
|
- src.copy(dest, FileCopyFlags.OVERWRITE);
|
|
|
- }
|
|
|
- else if(resource.value.file_type == Usm.ManifestFileType.DIRECTORY) {
|
|
|
- var dest = File.new_for_path(path);
|
|
|
- dest.make_directory();
|
|
|
- }
|
|
|
- else if(resource.value.file_type == Usm.ManifestFileType.SYMBOLIC_LINK) {
|
|
|
- var dest = File.new_for_path(path);
|
|
|
- dest.make_symbolic_link(resource.value.path);
|
|
|
- }
|
|
|
- else {
|
|
|
- throw new TransactionError.INSTALL_ERROR(@"Could not understand resource key \"$(resource.key)\"");
|
|
|
- }
|
|
|
-
|
|
|
- resources_installed++;
|
|
|
- report_progress(TransactionTask.INSTALLING, (float)resources_installed / (float)resource_count);
|
|
|
- }
|
|
|
+ // Install the package's resources
|
|
|
+ manifest.install_resources(build_dir, paths, (r, cr, tr, f) => report_progress(TransactionTask.INSTALLING, ((float)cr + (float)f) / (float)tr));
|
|
|
|
|
|
// Run install process if present
|
|
|
var build_proc = manifest.run_install(build_dir, InstallType.FRESH, SubprocessFlags.STDOUT_SILENCE);
|