out-of-date 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. #!/usr/bin/perl
  2. # $OpenBSD: out-of-date,v 1.6 2013/09/15 09:17:25 rpe Exp $
  3. #
  4. # Copyright (c) 2005 Bernd Ahlers <bernd@openbsd.org>
  5. #
  6. # Permission to use, copy, modify, and distribute this software for any
  7. # purpose with or without fee is hereby granted, provided that the above
  8. # copyright notice and this permission notice appear in all copies.
  9. #
  10. # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  11. # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  12. # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  13. # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  14. # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  15. # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  16. # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  17. use strict;
  18. use warnings;
  19. use OpenBSD::Getopt;
  20. use OpenBSD::Error;
  21. use OpenBSD::PackageInfo;
  22. use OpenBSD::PackingList;
  23. use OpenBSD::PackageName;
  24. use File::Temp;
  25. use OpenBSD::AddCreateDelete;
  26. our $opt_q;
  27. my $state = OpenBSD::AddCreateDelete::State->new("out-of-date");
  28. $state->handle_options('q', "[-mvxq]");
  29. sub collect_installed
  30. {
  31. my $pkg = {};
  32. $state->progress->for_list("Collecting installed packages",
  33. [installed_packages(1)], sub {
  34. my $name = shift;
  35. my ($stem, $version) = OpenBSD::PackageName::splitname($name);
  36. my $plist = OpenBSD::PackingList->from_installation($name,
  37. \&OpenBSD::PackingList::UpdateInfoOnly);
  38. if (!defined $plist or !defined $plist->{extrainfo}->{subdir}) {
  39. $state->errsay("Package #1 has no valid packing-list",
  40. $name);
  41. return;
  42. }
  43. my $subdir = $plist->{extrainfo}->{subdir};
  44. $subdir =~ s/mystuff\///;
  45. $subdir =~ s/\/usr\/ports\///;
  46. $pkg->{$subdir}->{name} = $name;
  47. $pkg->{$subdir}->{stem} = $stem;
  48. $pkg->{$subdir}->{version} = $version;
  49. my $sig = $plist->signature;
  50. if (ref($sig)) { $sig = $sig->string; }
  51. $pkg->{$subdir}->{signature} = $sig;
  52. if (defined $plist->{'always-update'}) {
  53. $pkg->{$subdir}->{signature} = 'always-update';
  54. }
  55. });
  56. return $pkg;
  57. }
  58. sub open_cmd
  59. {
  60. my $cmd = shift;
  61. open my $fh, "-|", $cmd;
  62. # my $old = select $fh;
  63. # $| = 1;
  64. # select $old;
  65. return $fh;
  66. }
  67. sub collect_port_versions
  68. {
  69. my ($pkg, $portsdir, $notfound) = @_;
  70. my @subdirs = ();
  71. for my $subdir (keys %$pkg) {
  72. my ($dir) = split(/,/, $subdir);
  73. if (-d "$portsdir/$dir") {
  74. push(@subdirs, $subdir);
  75. } else {
  76. push(@$notfound, $subdir);
  77. }
  78. }
  79. my $cmd = "cd $portsdir && SUBDIR=\"".join(' ', @subdirs)
  80. ."\" FULLPATH=Yes REPORT_PROBLEM=true make ".'show=FULLPKGNAME\${SUBPACKAGE} '
  81. ."2>&1";
  82. my $port = {};
  83. my $error = {};
  84. my $count = 0;
  85. my $total = scalar @subdirs;
  86. $state->progress->set_header("Collecting port versions");
  87. my $fh = open_cmd($cmd);
  88. my $subdir = "";
  89. while (<$fh>) {
  90. chomp;
  91. if (/^\=\=\=\>\s+(\S+)/) {
  92. $subdir = $1;
  93. $count++;
  94. $state->progress->show($count, $total);
  95. next;
  96. }
  97. next unless $_ or $subdir;
  98. next if defined $error->{$subdir};
  99. if (/^(Fatal\:|\s+\()/) {
  100. push(@{$error->{$subdir}}, $_);
  101. next;
  102. } elsif (/^(Stop|\*\*\*)/) {
  103. next;
  104. }
  105. $port->{$subdir}->{name} = $_;
  106. my ($stem, $version) = OpenBSD::PackageName::splitname($_);
  107. $port->{$subdir}->{stem} = $stem;
  108. $port->{$subdir}->{version} = $version;
  109. }
  110. close($fh);
  111. $state->progress->next;
  112. return $port, $error;
  113. }
  114. sub collect_port_signatures
  115. {
  116. my $pkg = shift;
  117. my $port = shift;
  118. my $portsdir = shift;
  119. my $output = shift;
  120. my @subdirs = ();
  121. for my $dir (keys %$port) {
  122. if ($pkg->{$dir}->{name} eq $port->{$dir}->{name}) {
  123. push(@subdirs, $dir);
  124. }
  125. }
  126. my $TMPDIR = $ENV{'TMPDIR'} || "/tmp";
  127. my $tempdir = File::Temp::tempdir("libcache.XXXXXXXXXX", DIR => $TMPDIR, CLEANUP => 1);
  128. $ENV{'_DEPENDS_CACHE'} = $tempdir;
  129. my $cmd = "cd $portsdir && FULLPATH=Yes SUBDIR=\"".join(' ', @subdirs)
  130. ."\" REPORT_PROBLEM=true make print-package-signature";
  131. my $count = 0;
  132. my $total = scalar @subdirs;
  133. $state->progress->set_header("Collecting port signatures");
  134. my $fh = open_cmd($cmd);
  135. my $subdir = "";
  136. while (<$fh>) {
  137. chomp;
  138. if (/^\=\=\=\>\s+(\S+)/) {
  139. $subdir = $1;
  140. $count++;
  141. $state->progress->show($count, $total);
  142. next;
  143. }
  144. next unless $_ or $subdir;
  145. $port->{$subdir}->{signature} = $_;
  146. }
  147. $state->progress->next;
  148. }
  149. sub split_sig
  150. {
  151. my $sig = shift;
  152. my $ret = {};
  153. for my $item (split(/,/, $sig)) {
  154. $ret->{$item} = 1;
  155. }
  156. return $ret;
  157. }
  158. sub diff_sig
  159. {
  160. my ($dir, $pkg, $port) = @_;
  161. my $old = split_sig($pkg->{$dir}->{signature});
  162. my $new = split_sig($port->{$dir}->{signature});
  163. for my $key (keys %$old) {
  164. if (defined $new->{$key}) {
  165. delete $old->{$key};
  166. delete $new->{$key};
  167. }
  168. }
  169. return join(',', sort keys %$old), join(',', sort keys %$new);
  170. }
  171. sub find_outdated
  172. {
  173. my ($pkg, $port, $output) = @_;
  174. for my $dir (keys %$pkg) {
  175. next unless $port->{$dir};
  176. if ($pkg->{$dir}->{name} ne $port->{$dir}->{name}) {
  177. push(@$output, sprintf("%-30s # %s -> %s\n", $dir,
  178. $pkg->{$dir}->{version}, $port->{$dir}->{version}));
  179. next;
  180. }
  181. next if $opt_q;
  182. if ($pkg->{$dir}->{signature} ne $port->{$dir}->{signature}) {
  183. push(@$output, sprintf("%-30s # %s -> %s\n", $dir,
  184. diff_sig($dir, $pkg, $port)));
  185. }
  186. }
  187. }
  188. my $portsdir = $ENV{PORTSDIR} || "/usr/ports";
  189. my $pkg = collect_installed();
  190. my @output = ();
  191. my @notfound = ();
  192. my ($port, $errors) = collect_port_versions($pkg, $portsdir, \@notfound);
  193. collect_port_signatures($pkg, $port, $portsdir, \@output) unless $opt_q;
  194. find_outdated($pkg, $port, \@output);
  195. $state->errsay("Outdated ports:\n");
  196. $state->print("#1", $_) for sort @output;
  197. if ($opt_q) {
  198. $state->errsay("\nWARNING: You've used the -q option. With this,\n"
  199. . "out-of-date only looks for changed package names\nbut not "
  200. . "for changed package signatures. If you\nwant to see ALL "
  201. . "of your outdated packages,\ndon't use -q.");
  202. }
  203. if (@notfound > 0) {
  204. $state->errsay("\nPorts that can't be found in the official "
  205. . "ports tree:");
  206. for (sort @notfound) {
  207. $state->errsay("#1", $_);
  208. }
  209. }
  210. if ((keys %$errors) > 0) {
  211. $state->errsay("\nErrors:");
  212. for (sort keys %$errors) {
  213. $state->errsay(" #1", $_);
  214. $state->errsay(" #1", $_) for @{$errors->{$_}};
  215. }
  216. }