PageRenderTime 47ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/fw/package/deb/FwDebPackage.pm

http://github.com/cliffmoon/effigy
Perl | 458 lines | 302 code | 97 blank | 59 comment | 20 complexity | cfed79ade0bff6dcd13fa31ab5bee3f4 MD5 | raw file
  1. #! /usr/bin/perl
  2. package FwDebPackage;
  3. use strict;
  4. use warnings;
  5. BEGIN {
  6. use Exporter ();
  7. our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS);
  8. # if using RCS/CVS, this may be preferred
  9. $VERSION = sprintf "%d.%03d", q$Revision: 1.5 $ =~ /(\d+)/g;
  10. @ISA = qw (Exporter);
  11. @EXPORT = qw (&proctalk &get_state &get_dependencies &closure
  12. &get_dependencies_closure &reverse_provides
  13. &parse_depends);
  14. %EXPORT_TAGS = ( ); # eg: TAG => [ qw!name1 name2! ],
  15. # your exported package globals go here,
  16. # as well as any optionally exported functions
  17. @EXPORT_OK = qw ();
  18. }
  19. our @EXPORT_OK;
  20. use IO::Pipe;
  21. use POSIX ":sys_wait_h";
  22. #---------------------------------------------------------------------
  23. # proctalk
  24. #
  25. # Fork a process and talk to it over a pipe pair.
  26. #---------------------------------------------------------------------
  27. sub proctalk ($$)
  28. {
  29. my ($parent_code, $child_code) = @_;
  30. my $par_to_child = new IO::Pipe;
  31. my $child_to_par = new IO::Pipe;
  32. my $pid;
  33. if ($pid = fork ())
  34. {
  35. # parent
  36. $par_to_child->writer ();
  37. $child_to_par->reader ();
  38. $parent_code-> ($child_to_par, $par_to_child);
  39. undef $par_to_child;
  40. undef $child_to_par;
  41. waitpid ($pid, 0);
  42. die "subprocess failed" if $?;
  43. }
  44. else
  45. {
  46. # child
  47. $par_to_child->reader ();
  48. $child_to_par->writer ();
  49. $child_code-> ($par_to_child, $child_to_par);
  50. die "child_code failed to exit";
  51. }
  52. }
  53. #---------------------------------------------------------------------
  54. # get_state
  55. #
  56. # Get all of the installed packages and versions.
  57. #---------------------------------------------------------------------
  58. sub get_state ()
  59. {
  60. my %state;
  61. proctalk (
  62. sub
  63. {
  64. my ($readfh, $writefh) = @_;
  65. while (defined ($_ = <$readfh>))
  66. {
  67. chomp;
  68. my ($package, $version, $status) = split /\s+/, $_, 3;
  69. next unless $status =~ /^install ok/;
  70. $state{$package} = $version;
  71. }
  72. },
  73. sub
  74. {
  75. my ($readfh, $writefh) = @_;
  76. close STDIN;
  77. open STDIN, "<&", $readfh or die "can't dup STDIN: $!";
  78. close STDOUT;
  79. open STDOUT, ">&", $writefh or die "can't dup STDOUT: $!";
  80. close STDERR unless $ENV{"FW_TRACE"};
  81. exec "dpkg-query",
  82. "--show",
  83. '--showformat=${Package}\t${Version}\t${Status}\n';
  84. }
  85. );
  86. # ok, here's something annoying: "fink virtual packages"
  87. # http://www.finkproject.org/faq/usage-general.php#virtpackage
  88. # these are known by apt-get but not by dpkg (?)
  89. # so they look like they are missing
  90. #
  91. # you can get them from fink-virtual-pkgs, so we'll do that
  92. my %virtual;
  93. my $fvp=`which fink-virtual-pkgs`;
  94. if ($fvp)
  95. {
  96. proctalk (
  97. sub
  98. {
  99. my ($readfh, $writefh) = @_;
  100. while (defined ($_ = <$readfh>))
  101. {
  102. chomp;
  103. next unless /^(\w+): (.+)/;
  104. my %vals;
  105. $vals{$1} = $2;
  106. while (defined ($_ = <$readfh>))
  107. {
  108. last unless /\S/;
  109. next unless /^(\w+): (.+)/;
  110. $vals{$1} = $2;
  111. }
  112. next unless exists $vals{'Package'}
  113. && exists $vals{'Status'}
  114. && exists $vals{'Version'}
  115. && $vals{'Status'} =~ /install ok installed/;
  116. $state{$vals{'Package'}} = $vals{'Version'};
  117. $virtual{$vals{'Package'}} = 1;
  118. }
  119. },
  120. sub
  121. {
  122. my ($readfh, $writefh) = @_;
  123. close STDIN;
  124. open STDIN, "<&", $readfh or die "can't dup STDIN: $!";
  125. close STDOUT;
  126. open STDOUT, ">&", $writefh or die "can't dup STDOUT: $!";
  127. close STDERR unless $ENV{"FW_TRACE"};
  128. exec "fink-virtual-pkgs";
  129. }
  130. );
  131. }
  132. return (\%state, \%virtual);
  133. }
  134. #---------------------------------------------------------------------
  135. # get_dependencies
  136. #
  137. # For a set of packages, identify the set of packages which are a
  138. # direct dependency of a member of the set.
  139. #---------------------------------------------------------------------
  140. sub get_dependencies ($$$$@)
  141. {
  142. my ($state, $virtual, $arch, $release, @packages) = @_;
  143. my %dependencies;
  144. my %deps_by_package;
  145. return () unless scalar @packages;
  146. proctalk (
  147. sub
  148. {
  149. my ($readfh, $writefh) = @_;
  150. foreach my $package (grep { ! exists $virtual->{$_} } @packages)
  151. {
  152. print $writefh "$package\n";
  153. }
  154. $writefh->close ();
  155. my $in_depends;
  156. my $package;
  157. while (defined ($_ = <$readfh>))
  158. {
  159. chomp;
  160. if (m/^Package: (\S+)/)
  161. {
  162. $package = $1;
  163. die "duplicate package $package"
  164. if exists $deps_by_package{$package};
  165. $deps_by_package{$package} = {};
  166. }
  167. elsif (defined $package && m/^Depends: (.*)/)
  168. {
  169. scalar map { $deps_by_package{$package}->{$_} = 1;
  170. $dependencies{$_} = 1 }
  171. parse_depends ($state, $arch, $1, $release);
  172. undef $package;
  173. }
  174. }
  175. },
  176. sub
  177. {
  178. my ($readfh, $writefh) = @_;
  179. close STDIN;
  180. open STDIN, "<&", $readfh or die "can't dup STDIN: $!";
  181. close STDOUT;
  182. open STDOUT, ">&", $writefh or die "can't dup STDOUT: $!";
  183. close STDERR unless $ENV{"FW_TRACE"};
  184. exec "xargs", "dpkg", "-s", "--" or die "exec failed: $!";
  185. }
  186. );
  187. return (wantarray) ? keys %dependencies : \%deps_by_package;
  188. }
  189. #---------------------------------------------------------------------
  190. # closure
  191. #
  192. # Form the closure of an operation on a set.
  193. #---------------------------------------------------------------------
  194. sub closure ($@)
  195. {
  196. my ($func, @packages) = @_;
  197. my %pkghash = map { $_ => 1 } @packages;
  198. my $finished;
  199. do
  200. {
  201. my @deps = $func-> (@packages);
  202. $finished = 1;
  203. @packages = map { $finished = 0; $pkghash{$_} = 1; $_ }
  204. grep { ! exists $pkghash{$_} } @deps;
  205. }
  206. while (! $finished);
  207. return keys %pkghash;
  208. }
  209. #---------------------------------------------------------------------
  210. # get_dependencies_closure
  211. #
  212. # For a set of packages, identify all installed packages which a
  213. # member of the set depends upon, either directly or indirectly.
  214. #---------------------------------------------------------------------
  215. sub get_dependencies_closure ($$$$@)
  216. {
  217. my ($state, $virtual, $arch, $release, @packages) = @_;
  218. return
  219. closure (sub { get_dependencies ($state, $virtual, $arch, $release, @_) },
  220. @packages);
  221. }
  222. #---------------------------------------------------------------------
  223. # reverse_provides
  224. #
  225. # (Attempt to) find an installed package which provides a given
  226. # package.
  227. #---------------------------------------------------------------------
  228. sub reverse_provides ($$)
  229. {
  230. my ($state, $package) = @_;
  231. my $reverse_provider;
  232. proctalk (
  233. sub
  234. {
  235. my ($readfh, $writefh) = @_;
  236. print $writefh "$package\n";
  237. $writefh->close ();
  238. my $in_reverse;
  239. while (defined ($_ = <$readfh>))
  240. {
  241. chomp;
  242. if (m/^Reverse Provides:/)
  243. {
  244. $in_reverse = 1;
  245. }
  246. elsif (defined $in_reverse)
  247. {
  248. m/^(\S+) / or die "unexpected apt-cache output: $_";
  249. $reverse_provider = $1 if $state->{$1};
  250. }
  251. }
  252. },
  253. sub
  254. {
  255. my ($readfh, $writefh) = @_;
  256. close STDIN;
  257. open STDIN, "<&", $readfh or die "can't dup STDIN: $!";
  258. close STDOUT;
  259. open STDOUT, ">&", $writefh or die "can't dup STDOUT: $!";
  260. close STDERR unless $ENV{"FW_TRACE"};
  261. exec "xargs", "apt-cache", "showpkg", "--" or die "exec failed: $!";
  262. }
  263. );
  264. return $reverse_provider;
  265. }
  266. #---------------------------------------------------------------------
  267. # enforce_op
  268. #
  269. # Check whether $installed $op $version is true. Passes the buck to dpkg.
  270. #---------------------------------------------------------------------
  271. sub enforce_op ($$$)
  272. {
  273. my ($operation, $installed, $version) = @_;
  274. if (! defined ($operation) || $operation eq "")
  275. {
  276. return 1;
  277. }
  278. else
  279. {
  280. return system ("dpkg",
  281. "--compare-versions",
  282. "$installed",
  283. "$operation",
  284. "$version") == 0;
  285. }
  286. }
  287. #---------------------------------------------------------------------
  288. # parse_depends
  289. #
  290. # Parse FW_PACKAGE_BUILD_DEPENDENCIES, which is in debian build-time
  291. # dependency format. Returns the set of installed packages which
  292. # satisfy the dependencies.
  293. #---------------------------------------------------------------------
  294. sub parse_depends ($$$$)
  295. {
  296. my ($state, $arch, $depends, $release) = @_;
  297. my %packages;
  298. # libc6 (>= 2.2.1), exim | mail-transport-agent
  299. # kernel-headers-2.2.10 [!hurd-i386], hurd-dev [hurd-i386]
  300. my @pkgspecs = split /,\s*/, $depends;
  301. SPEC: foreach my $spec (split /,\s*/, $depends)
  302. {
  303. OPTION: foreach my $option (split /\|\s*/, $spec)
  304. {
  305. $option =~
  306. m/^(\S+)\s*(\((<<|<=|>=|>>|<(?!=)|=|>(?!=))\s*([^\s\)]*)\))?/ or
  307. die "can't parse dependencies '$depends' (option '$option')";
  308. my $p = $1;
  309. my $op = $3;
  310. my $version = $4;
  311. if ($option =~ m/\[(!)?(.*)\]/)
  312. {
  313. my $not = $1;
  314. my $restrict = $2;
  315. next SPEC if ($not && $restrict eq $arch);
  316. next SPEC if (! $not && $restrict ne $arch);
  317. }
  318. if ($state->{$p})
  319. {
  320. if (enforce_op ($op, $state->{$p}, $version))
  321. {
  322. $packages{$p} =
  323. (defined ($op) && $op ne "") ? "$op $version"
  324. : ">= $state->{$p}";
  325. next SPEC;
  326. }
  327. }
  328. else
  329. {
  330. my $rev_p = reverse_provides ($state, $p);
  331. if ($rev_p && enforce_op ($op, $state->{$rev_p}, $version))
  332. {
  333. $packages{$rev_p} =
  334. (defined ($op) && $op ne "") ? "$op $version"
  335. : ">= $state->{$rev_p}";
  336. next SPEC;
  337. }
  338. }
  339. }
  340. die "package/deb/dependency-closure: fatal: '$spec' not installed\n"
  341. if $release eq "yes";
  342. warn "package/deb/dependency-closure: warning: '$spec' not installed\n"
  343. }
  344. return (wantarray) ? keys %packages : \%packages;
  345. }
  346. END { } # module clean-up code here (global destructor)
  347. 1; # don't forget to return a true value from the file