PlistScanner.pm 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. # $OpenBSD: PlistScanner.pm,v 1.10 2015/06/22 09:33:03 espie Exp $
  2. # Copyright (c) 2014 Marc Espie <espie@openbsd.org>
  3. #
  4. # Permission to use, copy, modify, and distribute this software for any
  5. # purpose with or without fee is hereby granted, provided that the above
  6. # copyright notice and this permission notice appear in all copies.
  7. #
  8. # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  9. # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  10. # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  11. # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  12. # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  13. # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  14. # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. use strict;
  16. use warnings;
  17. package OpenBSD::PlistScanner;
  18. use OpenBSD::PackageInfo;
  19. use OpenBSD::AddCreateDelete;
  20. use OpenBSD::PackingList;
  21. sub handle_plist
  22. {
  23. my ($self, $filename, $plist) = @_;
  24. if (!defined $plist) {
  25. $self->ui->errsay("Error reading #1", $filename);
  26. return;
  27. }
  28. if (!defined $plist->pkgname) {
  29. if (-z $filename) {
  30. $self->ui->errsay("Empty plist file #1", $filename);
  31. } else {
  32. $self->ui->errsay("Invalid package #1", $filename);
  33. }
  34. return;
  35. }
  36. $self->{name2path}{$plist->pkgname} = $plist->fullpkgpath;
  37. $self->{currentname} = $plist->pkgname." - ".$plist->fullpkgpath;
  38. $self->say("#1 -> #2", $filename, $plist->pkgname)
  39. if $self->ui->verbose;
  40. $self->register_plist($plist);
  41. $plist->forget;
  42. }
  43. sub progress
  44. {
  45. return shift->ui->progress;
  46. }
  47. sub handle_file
  48. {
  49. my ($self, $filename) = @_;
  50. return if -d $filename;
  51. my $plist = OpenBSD::PackingList->fromfile($filename);
  52. $self->handle_plist($filename, $plist);
  53. }
  54. sub handle_portspath
  55. {
  56. my ($self, $path) = @_;
  57. foreach (split(/:/, $path)) {
  58. $self->handle_portsdir($_);
  59. }
  60. }
  61. sub find_current_pkgnames
  62. {
  63. my ($self, $dir) = @_;
  64. my $done = {};
  65. my @todo = ();
  66. while (my ($name, $path) = each %{$self->{name2path}}) {
  67. next if $self->{current}{$name};
  68. next if $done->{$path};
  69. push(@todo, $path);
  70. }
  71. my $total = scalar(@todo);
  72. my $i = 0;
  73. while (my @l = (splice @todo, 0, 1000)) {
  74. my $pid = open(my $output, "-|");
  75. if ($pid == 0) {
  76. $DB::inhibit_exit = 0;
  77. chdir($dir) or die "bad directory $dir";
  78. $ENV{SUBDIR} = join(' ', @l);
  79. open STDERR, ">", "/dev/null";
  80. exec { $self->{make} }
  81. ("make", 'show=FULLPKGNAME${SUBPACKAGE}',
  82. 'REPORT_PROBLEM=true', 'ECHO_MSG=:');
  83. exit(1);
  84. }
  85. while (<$output>) {
  86. $i++;
  87. $self->progress->show($i, $total);
  88. chomp;
  89. $self->{current}{$_} = 1;
  90. }
  91. close($output);
  92. }
  93. }
  94. sub find_all_current_pkgnames
  95. {
  96. my ($self, $dir) = @_;
  97. $self->progress->set_header("Figuring out current names");
  98. open(my $input, "cd $dir && $self->{make} show='PKGPATHS PKGNAMES' ECHO_MSG=:|");
  99. while (<$input>) {
  100. chomp;
  101. my @values = split(/\s+/, $_);
  102. my $line2 = <$input>;
  103. chomp $line2;
  104. my @keys = split(/\s+/, $line2);
  105. $self->progress->message($values[0]);
  106. while (my $key = shift @keys) {
  107. my $value = shift @values;
  108. $self->{name2path}{$key} = $value;
  109. $self->{current}{$key} = 1;
  110. # $self->ui->say("pkgname: #1", $key);
  111. }
  112. }
  113. $self->progress->next;
  114. }
  115. sub reader
  116. {
  117. my ($self, $rdone) = @_;
  118. return
  119. sub {
  120. my ($fh, $cont) = @_;
  121. local $_;
  122. while (<$fh>) {
  123. return if m/^\=\=\=\> /o;
  124. &$cont($_);
  125. }
  126. $$rdone = 1;
  127. };
  128. }
  129. sub handle_portsdir
  130. {
  131. my ($self, $dir) = @_;
  132. open(my $input, "cd $dir && $self->{make} print-plist-all |");
  133. my $done = 0;
  134. while (!$done) {
  135. my $plist = OpenBSD::PackingList->read($input,
  136. $self->reader(\$done));
  137. if (defined $plist && $plist->pkgname) {
  138. $self->progress->message($plist->fullpkgpath ||
  139. $plist->pkgname);
  140. $self->handle_plist($dir, $plist);
  141. }
  142. }
  143. }
  144. sub rescan_dependencies
  145. {
  146. my ($self, $dir) = @_;
  147. $self->progress->set_header("Scanning extra dependencies");
  148. my $notfound = {};
  149. my $todo;
  150. do {
  151. $todo = {};
  152. while (my ($pkg, $reason) = each %{$self->{wanted}}) {
  153. next if $self->{got}{$pkg};
  154. next if $notfound->{$pkg};
  155. $todo->{$pkg} = $reason;
  156. }
  157. while (my ($pkgname, $reason) = each %$todo) {
  158. $self->progress->say("rescanning: #1 (#2)",
  159. $pkgname, $reason);
  160. my $file = "$dir/$pkgname";
  161. if (-f $file) {
  162. $self->handle_file($file);
  163. } else {
  164. $notfound->{$pkgname} = $reason;
  165. }
  166. }
  167. } while (keys %$todo > 0);
  168. $self->progress->next;
  169. }
  170. sub scan
  171. {
  172. my $self = shift;
  173. $self->progress->set_header("Scanning");
  174. if ($self->ui->opt('d')) {
  175. opendir(my $dir, $self->ui->opt('d'));
  176. my @l = readdir $dir;
  177. closedir($dir);
  178. $self->progress->for_list("Scanning", \@l,
  179. sub {
  180. my $pkgname = shift;
  181. return if $pkgname eq '.' or $pkgname eq '..';
  182. if ($self->ui->opt('f') &&
  183. !defined $self->{current}{$pkgname}) {
  184. return;
  185. }
  186. # $self->ui->say("doing: #1", $pkgname);
  187. $self->handle_file($self->ui->opt('d')."/$pkgname");
  188. });
  189. if ($self->ui->opt('f')) {
  190. }
  191. } elsif ($self->ui->opt('p')) {
  192. $self->handle_portspath($self->ui->opt('p'));
  193. } elsif (@ARGV==0) {
  194. @ARGV=(<*.tgz>);
  195. }
  196. if (@ARGV > 0) {
  197. $self->progress->for_list("Scanning", \@ARGV,
  198. sub {
  199. my $pkgname = shift;
  200. my $true_package = $self->ui->repo->find($pkgname);
  201. return unless $true_package;
  202. my $dir = $true_package->info;
  203. $true_package->close;
  204. $self->handle_file($dir.CONTENTS);
  205. rmtree($dir);
  206. });
  207. }
  208. if ($self->ui->opt('d')) {
  209. $self->rescan_dependencies($self->ui->opt('d'));
  210. }
  211. }
  212. sub run
  213. {
  214. my $self = shift;
  215. if ($self->ui->opt('p') && $self->ui->opt('f')) {
  216. $self->find_all_current_pkgnames($self->ui->opt('p'));
  217. }
  218. $self->scan;
  219. if ($self->ui->opt('d') && $self->ui->opt('p')) {
  220. $self->progress->set_header("Computing current pkgnames");
  221. $self->find_current_pkgnames($self->ui->opt('p'));
  222. }
  223. $self->display_results;
  224. }
  225. sub say
  226. {
  227. my $self = shift;
  228. my $msg = $self->ui->f(@_)."\n";
  229. $self->ui->_print($msg) unless $self->ui->opt('s');
  230. if (defined $self->{output}) {
  231. print {$self->{output}} $msg;
  232. }
  233. }
  234. sub fullname
  235. {
  236. my ($self, $pkgname) = @_;
  237. my $path = $self->{name2path}{$pkgname};
  238. if ($self->{current}{$pkgname}) {
  239. return "!$pkgname($path)";
  240. } else {
  241. return "$pkgname($path)";
  242. }
  243. }
  244. sub ui
  245. {
  246. my $self = shift;
  247. return $self->{ui};
  248. }
  249. sub handle_options
  250. {
  251. my ($self, $extra, $usage) = @_;
  252. $usage //= "[-vefS] [-d plist_dir] [-o output] [-p ports_dir] [pkgname ...]";
  253. $extra //= '';
  254. $self->ui->handle_options($extra.'d:efo:p:sS', $usage);
  255. }
  256. sub new
  257. {
  258. my ($class, $cmd) = @_;
  259. my $ui = OpenBSD::AddCreateDelete::State->new($cmd);
  260. my $o = bless {ui => $ui,
  261. make => $ENV{MAKE} || 'make',
  262. name2path => {},
  263. current => {}
  264. }, $class;
  265. $o->handle_options;
  266. if ($ui->opt('o')) {
  267. open $o->{output}, '>', $ui->opt('o')
  268. or $ui->fatal("Can't write to #1: #2", $ui->opt('o'), $!);
  269. }
  270. return $o;
  271. }
  272. 1;