find-all-conflicts 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. #!/usr/bin/perl
  2. # $OpenBSD: find-all-conflicts,v 1.20 2010/06/30 11:11:19 espie 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 conflicts which
  26. # are not apparent in @pkgcfl.
  27. use strict;
  28. use File::Spec;
  29. use File::Path;
  30. use OpenBSD::PackageInfo;
  31. use OpenBSD::PackingList;
  32. use OpenBSD::AddCreateDelete;
  33. use OpenBSD::PkgCfl;
  34. package OpenBSD::PackingElement;
  35. sub register
  36. {
  37. }
  38. package OpenBSD::PackingElement::FileBase;
  39. my $pkg_list = {};
  40. my $seen = {};
  41. sub register
  42. {
  43. my ($self, $all_conflict, $all_deps, $pkgname) = @_;
  44. my $file = File::Spec->canonpath($self->fullname);
  45. # build one single list for each pkgnames combination
  46. if (exists $all_conflict->{$file}) {
  47. $pkg_list->{$all_conflict->{$file}}->{$pkgname} ||=
  48. [@{$all_conflict->{$file}}, $pkgname ];
  49. $all_conflict->{$file} =
  50. $pkg_list->{$all_conflict->{$file}}->{$pkgname};
  51. } elsif (exists $seen->{$file}) {
  52. $pkg_list->{$seen->{$file}}->{$pkgname} ||=
  53. [ @{$seen->{$file}}, $pkgname ];
  54. $all_conflict->{$file} =
  55. $pkg_list->{$seen->{$file}}->{$pkgname};
  56. delete $seen->{$file};
  57. } else {
  58. $pkg_list->{$pkgname} ||= [$pkgname];
  59. $seen->{$file} = $pkg_list->{$pkgname};
  60. }
  61. }
  62. package OpenBSD::PackingElement::Depend;
  63. sub register
  64. {
  65. my ($self, $all_conflict, $all_deps, $pkgname) = @_;
  66. if (defined $self->{def}) {
  67. push @{$all_deps->{$pkgname}}, $self->{def};
  68. }
  69. }
  70. package main;
  71. my $cache = {};
  72. my $cache2 = {};
  73. my $cache3 = {};
  74. my $cache4 = {};
  75. sub direct_conflict
  76. {
  77. my ($conflicts, $pkg, $pkg2) = @_;
  78. return $cache3->{$pkg}{$pkg2} //= $conflicts->{$pkg}->conflicts_with($pkg2);
  79. }
  80. sub has_a_conflict
  81. {
  82. my ($conflicts, $deps, $pkg, $pkg2) = @_;
  83. return $cache4->{$pkg}{$pkg2} //= find_a_conflict($conflicts, $deps, $pkg, $pkg2);
  84. }
  85. sub find_a_conflict
  86. {
  87. my ($conflicts, $deps, $pkg, $pkg2) = @_;
  88. return 0 if $pkg eq $pkg2;
  89. if (defined $conflicts->{$pkg} && direct_conflict($conflicts, $pkg, $pkg2)) {
  90. return 1;
  91. }
  92. if (defined $deps->{$pkg}) {
  93. for my $dep (@{$deps->{$pkg}}) {
  94. if (has_a_conflict($conflicts, $deps, $dep, $pkg2)) {
  95. return 1;
  96. }
  97. }
  98. }
  99. if (defined $deps->{$pkg2}) {
  100. for my $dep (@{$deps->{$pkg2}}) {
  101. if (has_a_conflict($conflicts, $deps, $pkg, $dep)) {
  102. return 1;
  103. }
  104. }
  105. }
  106. return 0;
  107. }
  108. sub compute_true_conflicts
  109. {
  110. my ($l, $conflicts, $deps) = @_;
  111. # create a list of unconflicting packages.
  112. my $l2 = [];
  113. for my $pkg (@$l) {
  114. my $keepit = 0;
  115. for my $pkg2 (@$l) {
  116. next if $pkg eq $pkg2;
  117. if (!(has_a_conflict($conflicts, $deps, $pkg, $pkg2) ||
  118. has_a_conflict($conflicts, $deps, $pkg2, $pkg))) {
  119. $keepit = 1;
  120. last;
  121. }
  122. }
  123. if ($keepit) {
  124. push(@$l2, $pkg);
  125. }
  126. }
  127. return $l2;
  128. }
  129. sub compute_problems
  130. {
  131. my ($ui, $h, $conflicts, $deps) = @_;
  132. my $c = {};
  133. my $c2 = {};
  134. my $total = scalar(keys %$h);
  135. my $i =0;
  136. while (my ($key, $l) = each %$h) {
  137. $ui->progress->show(++$i, $total);
  138. if (!defined $c->{$l}) {
  139. my %s = map {($_, 1)} @$l;
  140. $c->{$l} = [sort keys %s];
  141. $c2->{$l} = join(',', @{$c->{$l}});
  142. }
  143. my $hv = $c2->{$l};
  144. $l = $c->{$l};
  145. next if @$l == 1;
  146. if (!defined $cache->{$hv}) {
  147. $cache->{$hv} = compute_true_conflicts($l, $conflicts, $deps);
  148. }
  149. my $result = $cache->{$hv};
  150. if (@$result != 0) {
  151. my $newkey = join(',', @$result);
  152. if (@$result == 1) {
  153. $newkey.="-> was ".join(',', @$l);
  154. }
  155. push(@{$cache2->{$newkey}}, $key);
  156. }
  157. }
  158. }
  159. my $filehash={};
  160. my %dirhash=();
  161. my $conflicts={};
  162. my $dephash={};
  163. our ($opt_d, $opt_p, $opt_v);
  164. sub handle_plist
  165. {
  166. my ($ui, $filename, $plist) = @_;
  167. if (!defined $plist) {
  168. $ui->errsay("Error reading #1", $filename);
  169. return;
  170. }
  171. $ui->say("#1 -> #2", $filename, $plist->pkgname) if $ui->verbose;
  172. $plist->forget;
  173. $conflicts->{$plist->pkgname} =
  174. OpenBSD::PkgCfl->make_conflict_list($plist);
  175. $plist->register($filehash, $dephash, $plist->pkgname);
  176. }
  177. sub handle_file
  178. {
  179. my ($ui, $filename) = @_;
  180. my $plist = OpenBSD::PackingList->fromfile($filename);
  181. handle_plist($ui, $filename, $plist);
  182. }
  183. sub handle_portsdir
  184. {
  185. my ($ui, $dir) = @_;
  186. my $make = $ENV{MAKE} || 'make';
  187. open(my $input, "cd $dir && $make print-plist-all |");
  188. my $done = 0;
  189. while (!$done) {
  190. my $plist = OpenBSD::PackingList->read($input, sub {
  191. my ($fh, $cont) = @_;
  192. local $_;
  193. while (<$fh>) {
  194. return if m/^\=\=\=\> /o;
  195. next unless m/^\@(?:cwd|name|info|man|file|lib|shell|bin|conflict|comment\s+subdir\=)\b/o || !m/^\@/o;
  196. &$cont($_);
  197. }
  198. $done = 1;
  199. });
  200. if (defined $plist && $plist->pkgname()) {
  201. handle_plist($ui, $dir, $plist);
  202. $ui->progress->working(10);
  203. }
  204. }
  205. }
  206. my $ui = OpenBSD::AddCreateDelete::State->new('find-all-conflicts');
  207. $ui->handle_options('d:p:', '[-v] [-d plist_dir] [-p ports_dir] [pkgname ...]');
  208. $ui->progress->set_header("Scanning");
  209. $opt_d = $ui->opt('d');
  210. $opt_p = $ui->opt('p');
  211. if ($opt_d) {
  212. opendir(my $dir, $opt_d);
  213. my @l = readdir $dir;
  214. closedir($dir);
  215. $ui->progress->for_list("Scanning", \@l,
  216. sub {
  217. my $pkgname = shift;
  218. return if $pkgname eq '.' or $pkgname eq '..';
  219. handle_file($ui, "$opt_d/$pkgname");
  220. });
  221. } elsif ($opt_p) {
  222. handle_portsdir($ui, $opt_p);
  223. } elsif (@ARGV==0) {
  224. @ARGV=(<*.tgz>);
  225. }
  226. $ui->progress->for_list("Scanning", \@ARGV,
  227. sub {
  228. my $pkgname = shift;
  229. my $true_package = $ui->repo->find($pkgname);
  230. return unless $true_package;
  231. my $dir = $true_package->info;
  232. $true_package->close;
  233. handle_file($ui, $dir.CONTENTS);
  234. rmtree($dir);
  235. });
  236. $ui->progress->next;
  237. $ui->progress->set_header("File problems");
  238. compute_problems($ui, $filehash, $conflicts, $dephash);
  239. for my $cfl (sort keys %$cache2) {
  240. $ui->say("#1", $cfl);
  241. for my $f (sort @{$cache2->{$cfl}}) {
  242. $ui->say("\t#1", $f);
  243. }
  244. }