View file File name : lib.pl Content :#!/usr/bin/perl -w use strict; use Cwd; # depends on: dpkg, tsort, perl my $lib_dir = "/usr/lib/emacsen-common"; my $var_dir = "/var/lib/emacsen-common"; $::installed_package_state_dir = "${var_dir}/state/package/installed"; $::installed_flavor_state_dir = "${var_dir}/state/flavor/installed"; sub ex { my(@cmd) = @_; if(system(@cmd) != 0) { die join(" ", @cmd) . " failed"; } } sub glob_in_dir { my ($dir, $pattern) = @_; my $oldir = getcwd; chdir($dir) or die "chdir $dir: $!"; my @files = glob("*[!~]"); chdir($oldir); return \@files; } sub validate_add_on_pkg { my ($pkg, $script, $old_invocation_style) = @_; if($old_invocation_style) { if(-e "$lib_dir/packages/compat/$pkg") { print STDERR "ERROR: $pkg is broken - called $script as an old-style add-on, but has compat file.\n"; #exit(1); } } else # New invocation style. { unless(-e "$lib_dir/packages/compat/$pkg") { print STDERR "ERROR: $pkg is broken - called $script as a new-style add-on, but has no compat file.\n"; #exit(1); } } } sub get_installed_add_on_packages { # Return all of the old format packages, plus all of the new-format # packages that are ready (i.e. have a state/installed file). In # this case ready means ready for compilation. my $all_pkgs = glob_in_dir("$lib_dir/packages/install", '*[!~]'); my $new_format_pkgs = glob_in_dir("$lib_dir/packages/compat", '*[!~]'); my %ready_pkgs = map { $_ => 1 } @$all_pkgs; for my $p (@$new_format_pkgs) { delete $ready_pkgs{$p} unless (-e "$::installed_package_state_dir/$p"); } my @result = keys %ready_pkgs; return \@result; } sub get_installed_flavors { my $flavors = glob_in_dir($::installed_flavor_state_dir, '*[!~]'); return @$flavors; } sub get_package_status { my($pkg) = @_; my $status = `dpkg --status $pkg`; die 'emacsen-common: dpkg invocation failed' if($? != 0); $status =~ s/\n\s+//gmo; # handle any continuation lines... return $status; } sub filter_depends { my($depends_string, $installed_add_ons) = @_; # Filter out all the "noise" (version number dependencies, etc) # and handle or deps too, i.e. "Depends: foo, bar | baz" my @relevant_depends = split(/[,|]/, $depends_string); @relevant_depends = map { /\s*(\S+)/o; $1; } @relevant_depends; # Filter out all non-add-on packages. @relevant_depends = grep { my $candidate = $_; grep { $_ eq $candidate } @$installed_add_ons; } @relevant_depends; return @relevant_depends; } sub generate_relevant_tsort_dependencies_internals { my($pkglist, $installed_add_ons, $progress_hash) = @_; # print "GRD: " . join(" ", @$pkglist) . "\n"; my $pkg = shift @$pkglist; if(!$pkg || $$progress_hash{$pkg}) { return (); } else { my $status = get_package_status($pkg); $status =~ /^Depends:\s+(.*)/mo; my $depends = $1; $depends = "" if ! $depends; my @relevant_depends = filter_depends($depends, $installed_add_ons); my $newpkglist = [@$pkglist, @relevant_depends]; $$progress_hash{$pkg} = 1; # pkg is in twice so we don't have to worry about package with no # relevant dependencies. tsort can't handle that. my @tsort_strings = "$pkg $pkg\n"; map { push @tsort_strings, "$_ $pkg\n"; } @relevant_depends; return (@tsort_strings, generate_relevant_tsort_dependencies_internals($newpkglist, $installed_add_ons, $progress_hash)); } } sub generate_relevant_tsort_dependencies { my($pkglist, $installed_add_ons, $progress_hash) = @_; # Make a copy because we're going to mangle it. my @listcopy = @$pkglist; shift @_; return(generate_relevant_tsort_dependencies_internals(\@listcopy, @_)); } sub reorder_add_on_packages { my($pkglist, $installed_add_ons) = @_; my @depends = generate_relevant_tsort_dependencies($pkglist, $installed_add_ons, {}); my $pid = open(TSORT, "-|"); die "Couldn't fork for tsort: $!" unless defined($pid); # What a strange idiom... if($pid == 0) { my $sub_pid = open(IN, "|-"); die "Couldn't sub-fork for tsort: $!" unless defined($sub_pid); if($sub_pid == 0) { exec 'tsort' or die "Couldn't run tsort: $!"; } print IN @depends; exit 0; } my @ordered_pkgs = <TSORT>; chomp @ordered_pkgs; return @ordered_pkgs } sub generate_add_on_install_list { my($packages_to_sort) = @_; my @sorted_pkgs = reorder_add_on_packages($packages_to_sort, get_installed_add_on_packages()); return(@sorted_pkgs); } # Test code # my @input_packages = <STDIN>; # my @result = generate_add_on_install_list(@input_packages); # print " " . join("\n ", @result); # To make require happy... 1;