find-plist-issues 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. #! /usr/bin/perl
  2. # $OpenBSD: find-plist-issues,v 1.11 2014/07/19 07:04:42 ajacoutot Exp $
  3. # Copyright (c) 2000-2005
  4. # Marc Espie. All rights reserved.
  5. # Redistribution and use in source and binary forms, with or without
  6. # modification, are permitted provided that the following conditions
  7. # are met:
  8. # 1. Redistributions of code must retain the above copyright
  9. # notice, this list of conditions and the following disclaimer.
  10. # 2. Neither the name of OpenBSD nor the names of its contributors
  11. # may be used to endorse or promote products derived from this software
  12. # without specific prior written permission.
  13. #
  14. # THIS SOFTWARE IS PROVIDED BY ITS AUTHOR AND THE OpenBSD project ``AS IS'' AND
  15. # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  16. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  17. # ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  18. # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  19. # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  20. # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  21. # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  22. # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  23. # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  24. # SUCH DAMAGE.
  25. # check all packages in the current directory, and report issues apparent
  26. # in packing-lists
  27. use strict;
  28. use warnings;
  29. use File::Spec;
  30. use File::Path;
  31. use File::Basename;
  32. use OpenBSD::PackageInfo;
  33. use OpenBSD::PackingList;
  34. use OpenBSD::Mtree;
  35. use OpenBSD::Getopt;
  36. use OpenBSD::State;
  37. use OpenBSD::PkgCfl;
  38. package OpenBSD::PackingElement;
  39. use OpenBSD::PkgSpec;
  40. sub register
  41. {
  42. }
  43. sub fix
  44. {
  45. my ($self, $l) = @_;
  46. if ($self->{def} eq 'def') {
  47. my @m = OpenBSD::PkgSpec->new($self->{pattern})->match_ref($l);
  48. if (@m > 0) {
  49. $self->{def} = $m[0];
  50. } else {
  51. $self->{def} = $self->{pattern};
  52. }
  53. }
  54. }
  55. sub check_common_dirs
  56. {
  57. }
  58. package OpenBSD::PackingElement::FileBase;
  59. use File::Basename;
  60. sub register_dir
  61. {
  62. my ($self, $d, $h) = @_;
  63. return if defined $h->{$d};
  64. $h->{$d} = 1;
  65. $self->register_dir(dirname($d), $h);
  66. }
  67. sub register
  68. {
  69. my ($self, $all_conflict, $all_deps, $pkgname, $avail) = @_;
  70. my $file= File::Spec->canonpath($self->fullname());
  71. unless (defined $all_conflict->{$file}) {
  72. $all_conflict->{$file} = [];
  73. }
  74. push @{$all_conflict->{$file}}, $pkgname;
  75. }
  76. sub check_common_dirs
  77. {
  78. my ($item, $t) = @_;
  79. my $d = File::Spec->canonpath($item->fullname());
  80. $item->register_dir(dirname($d), $t->{need_dirs});
  81. }
  82. package OpenBSD::PackingElement::DirlikeObject;
  83. sub check_common_dirs
  84. {
  85. my ($item, $t) = @_;
  86. my $d = File::Spec->canonpath($item->fullname());
  87. $t->{dirs}->{$d} = 1;
  88. }
  89. package OpenBSD::PackingElement::Depend;
  90. sub register
  91. {
  92. my ($self, $all_conflict, $all_deps, $pkgname, $avail) = @_;
  93. if (defined $self->{def}) {
  94. unless (defined $all_deps->{$pkgname}) {
  95. $all_deps->{$pkgname} = [];
  96. }
  97. $self->fix($avail);
  98. push @{$all_deps->{$pkgname}}, $self->{def};
  99. }
  100. }
  101. sub check_common_dirs
  102. {
  103. my ($item, $t) = @_;
  104. $item->fix($t->{avail});
  105. $t->{deps}->{$item->{def}} = 1;
  106. }
  107. package OpenBSD::PackingElement::PkgDep;
  108. sub check_common_dirs
  109. {
  110. my ($item, $t) = @_;
  111. $t->{deps}->{$item->{name}} = 1;
  112. }
  113. package OpenBSD::PackingElement::Wantlib;
  114. sub check_common_dirs
  115. {
  116. }
  117. package main;
  118. my $cache = {};
  119. my $cache2 = {};
  120. my @available = ();
  121. my $conflicts_cache = {};
  122. sub find_a_conflict
  123. {
  124. my ($conflicts, $deps, $pkg, $pkg2) = @_;
  125. return 0 if $pkg2 eq $pkg;
  126. my $h = "$pkg/$pkg2";
  127. if (defined $conflicts_cache->{$h}) {
  128. return $conflicts_cache->{$h};
  129. }
  130. if (defined $conflicts->{$pkg} &&
  131. $conflicts->{$pkg}->conflicts_with($pkg2)) {
  132. $conflicts_cache->{$h} = 1;
  133. return 1;
  134. }
  135. if (defined $deps->{$pkg}) {
  136. for my $dep (@{$deps->{$pkg}}) {
  137. if (find_a_conflict($conflicts, $deps, $dep, $pkg2)) {
  138. $conflicts_cache->{$h} = 1;
  139. return 1;
  140. }
  141. }
  142. }
  143. if (defined $deps->{$pkg2}) {
  144. for my $dep (@{$deps->{$pkg2}}) {
  145. if (find_a_conflict($conflicts, $deps, $pkg, $dep)) {
  146. $conflicts_cache->{$h} = 1;
  147. return 1;
  148. }
  149. }
  150. }
  151. $conflicts_cache->{$h} = 0;
  152. return 0;
  153. }
  154. sub compute_conflicts
  155. {
  156. my ($h, $conflicts, $deps) = @_;
  157. while (my ($key, $l) = each %$h) {
  158. my %s = map {($_, 1)} @$l;
  159. @$l = sort keys %s;
  160. if (@$l > 1) {
  161. my $hv = join(',', @$l);
  162. if (!defined $cache->{$hv}) {
  163. # create a list of unconflicting packages.
  164. my $l2 = [];
  165. for my $pkg (@$l) {
  166. my $keepit = 0;
  167. for my $pkg2 (@$l) {
  168. next if $pkg eq $pkg2;
  169. if (!(find_a_conflict($conflicts, $deps, $pkg, $pkg2) ||
  170. find_a_conflict($conflicts, $deps, $pkg2, $pkg))) {
  171. $keepit = 1;
  172. last;
  173. }
  174. }
  175. if ($keepit) {
  176. push(@$l2, $pkg);
  177. }
  178. }
  179. $cache->{$hv} = $l2;
  180. }
  181. my $result = $cache->{$hv};
  182. if (@$result != 0) {
  183. my $newkey = join(',', @$result);
  184. if (@$result == 1) {
  185. $newkey.="-> was ".join(',', @$l);
  186. }
  187. $cache2->{$newkey} = [] unless defined($cache2->{$newkey});
  188. push(@{$cache2->{$newkey}}, $key);
  189. }
  190. }
  191. }
  192. }
  193. sub analyze_dirs
  194. {
  195. my ($plist, $db) = @_;
  196. my $pkgname = $plist->pkgname();
  197. $db->{$pkgname} = {
  198. pkgname => $pkgname,
  199. missing_deps => {},
  200. dirs => {},
  201. need_dirs => {},
  202. deps => {},
  203. problems => {},
  204. avail => \@available
  205. } unless defined $db->{$pkgname};
  206. my $t = $db->{$pkgname};
  207. $plist->check_common_dirs($t)
  208. }
  209. sub parent_has_dir
  210. {
  211. my ($db, $t, $dir) = @_;
  212. for my $dep (keys %{$t->{deps}}) {
  213. if (!defined $db->{$dep}) {
  214. if (!defined $t->{missing_deps}->{$dep}) {
  215. print $t->{pkgname}, ": $dep not found\n";
  216. $t->{missing_deps}->{$dep} = 1;
  217. }
  218. next;
  219. }
  220. if ($db->{$dep}->{dirs}->{$dir} ||
  221. parent_has_dir($db, $db->{$dep}, $dir)) {
  222. $t->{dirs}->{$dir} = 1;
  223. return 1;
  224. }
  225. }
  226. return 0;
  227. }
  228. sub parent_has_dir_issue
  229. {
  230. my ($db, $t, $dir) = @_;
  231. for my $dep (keys %{$t->{deps}}) {
  232. next if !defined $db->{$dep};
  233. if ($db->{$dep}->{problems}->{$dir}) {
  234. return 1;
  235. }
  236. }
  237. return 0;
  238. }
  239. sub build_dir_results
  240. {
  241. my ($db, $mtree) = @_;
  242. for my $pkgname (keys %$db) {
  243. my $t = $db->{$pkgname};
  244. for my $dir (keys(%{$t->{need_dirs}})) {
  245. next if $t->{dirs}->{$dir};
  246. next if $mtree->{$dir};
  247. next if parent_has_dir($db, $t, $dir);
  248. $t->{problems}->{$dir} = 1;
  249. }
  250. }
  251. }
  252. sub show_dir_results
  253. {
  254. my ($db, $mtree) = @_;
  255. # first reverse the results
  256. my $dir_db = {};
  257. for my $pkgname (keys %$db) {
  258. my @l=();
  259. my $t = $db->{$pkgname};
  260. for my $dir (keys %{$t->{problems}}) {
  261. next if parent_has_dir_issue($db, $t, $dir);
  262. $dir_db->{$dir} = [] if !defined $dir_db->{$dir};
  263. push(@{$dir_db->{$dir}}, $pkgname);
  264. }
  265. }
  266. # and print the resulting table:
  267. for my $dir (sort keys %$dir_db) {
  268. print $dir, ": ", join(',', sort @{$dir_db->{$dir}}), "\n";
  269. }
  270. }
  271. my $filehash={};
  272. my %dirhash=();
  273. my $conflicts={};
  274. my $dephash={};
  275. my $db = {};
  276. my $mtree = {};
  277. our ($opt_d, $opt_v, $opt_C, $opt_D, $opt_f);
  278. sub handle_plist
  279. {
  280. my $plist = shift;
  281. print $plist->pkgname(), "\n" if $opt_v;
  282. $plist->forget();
  283. if ($opt_C) {
  284. $conflicts->{$plist->pkgname()} =
  285. OpenBSD::PkgCfl->make_conflict_list($plist);
  286. $plist->register($filehash, $dephash, $plist->pkgname(), \@available);
  287. }
  288. if ($opt_D) {
  289. analyze_dirs($plist, $db);
  290. }
  291. }
  292. sub handle_file
  293. {
  294. my $filename = shift;
  295. my $plist = OpenBSD::PackingList->fromfile($filename);
  296. if (!defined $plist) {
  297. print STDERR "Error reading $filename\n";
  298. return;
  299. }
  300. handle_plist($plist);
  301. }
  302. my $ui = OpenBSD::State->new('find-plist-issues');
  303. $ui->usage_is('[-vCDf] [-d plist_dir] [pkgname ...]');
  304. $ui->do_options(sub { getopts('d:vCDf'); });
  305. OpenBSD::Mtree::parse($mtree, '/', '/etc/mtree/4.4BSD.dist');
  306. OpenBSD::Mtree::parse($mtree, '/', '/etc/mtree/BSD.x11.dist');
  307. $mtree->{'/usr/local/lib/X11'} = 1;
  308. $mtree->{'/usr/local/include/X11'} = 1;
  309. $mtree->{'/usr/local/lib/X11/app-defaults'} = 1;
  310. print "Scanning packages\n" if $opt_v;
  311. print "-----------------\n" if $opt_v;
  312. if ($opt_d) {
  313. for my $dirname (split(/:/, $opt_d)) {
  314. opendir(my $dir, $dirname) or next;
  315. push(@available, grep { $_ ne '.' && $_ ne '..' } readdir($dir));
  316. closedir($dir);
  317. }
  318. for my $dirname (split(/:/, $opt_d)) {
  319. if (opendir(my $dir, $dirname)) {
  320. while (my $pkgname = readdir($dir)) {
  321. next if $pkgname eq '.' or $pkgname eq '..';
  322. handle_file("$dirname/$pkgname");
  323. }
  324. closedir($dir);
  325. } else {
  326. print STDERR "No such dir: $dirname\n";
  327. }
  328. }
  329. } elsif (@ARGV==0) {
  330. @ARGV=(<*.tgz>);
  331. }
  332. my @pkgs = @ARGV;
  333. push(@available, map { s,.*/,,; s/\.tgz$//; } @pkgs);
  334. for my $pkgname (@ARGV) {
  335. print STDERR "$pkgname\n";
  336. if ($opt_f) {
  337. handle_file($pkgname);
  338. } else {
  339. my $plist = $ui->repo->grabPlist($pkgname);
  340. next unless $plist;
  341. handle_plist($plist);
  342. }
  343. }
  344. print "File problems:\n";
  345. print "-------------\n";
  346. if ($opt_C) {
  347. compute_conflicts($filehash, $conflicts, $dephash);
  348. for my $cfl (sort keys %$cache2) {
  349. print "$cfl\n";
  350. for my $f (sort @{$cache2->{$cfl}}) {
  351. print "\t$f\n";
  352. }
  353. }
  354. }
  355. if ($opt_D) {
  356. build_dir_results($db, $mtree);
  357. show_dir_results($db);
  358. }