openpkg-build.pl 34 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292
  1. ##
  2. ## openpkg-build.pl -- create build scripts from package index
  3. ##
  4. ## Copyright (c) 2000-2002 Cable & Wireless Deutschland GmbH
  5. ## Copyright (c) 2000-2002 The OpenPKG Project <http://www.openpkg.org/>
  6. ## Copyright (c) 2000-2002 Ralf S. Engelschall <rse@engelschall.com>
  7. ##
  8. ## Permission to use, copy, modify, and distribute this software for
  9. ## any purpose with or without fee is hereby granted, provided that
  10. ## the above copyright notice and this permission notice appear in all
  11. ## copies.
  12. ##
  13. ## THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  14. ## WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  15. ## MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  16. ## IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
  17. ## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  18. ## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  19. ## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  20. ## USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  21. ## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  22. ## OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  23. ## OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  24. ## SUCH DAMAGE.
  25. ##
  26. require 5;
  27. $|=1; # autoflush
  28. use strict;
  29. use vars qw/$opt_R $opt_r $opt_f $opt_u $opt_U $opt_a $opt_A $opt_z $opt_Z $opt_P $opt_N $opt_E $opt_i $opt_D $opt_p $opt_q/;
  30. getopts('R:r:f:uUaAzZP:N:E:iD:p:q');
  31. ##########################################################################
  32. sub getopts ($) {
  33. my($opts) = @_;
  34. my(%optf) = map { /(\w)/; $1 => $_ } $opts =~ /(\w:|\w)/g;
  35. my(%opts,@argv,$optarg);
  36. foreach (@ARGV) {
  37. if (@argv) {
  38. push @argv, $_;
  39. } elsif (defined $optarg) {
  40. if (exists $opts{$optarg}) {
  41. $opts{$optarg} .= " $_";
  42. } else {
  43. $opts{$optarg} = $_;
  44. }
  45. $optarg = undef;
  46. } elsif (!/^[-]/) {
  47. push @argv, $_;
  48. } else {
  49. while (/^\-(\w)(.*)/) {
  50. if (exists $optf{$1}) {
  51. if (length($optf{$1}) > 1) {
  52. if ($2 ne '') {
  53. if (exists $opts{$1}) {
  54. $opts{$1} .= " $2";
  55. } else {
  56. $opts{$1} = $2;
  57. }
  58. } else {
  59. $optarg = $1;
  60. }
  61. last;
  62. } else {
  63. $opts{$1} = 1;
  64. }
  65. } else {
  66. warn "warning: unknown option $_\n";
  67. }
  68. $_ = "-$2";
  69. }
  70. }
  71. }
  72. if (defined $optarg) {
  73. warn "warning: option $optarg requires an argument\n";
  74. }
  75. foreach (keys %opts) {
  76. eval '$opt_'.$_.' = "'.quotemeta($opts{$_}).'";';
  77. }
  78. @ARGV = @argv;
  79. }
  80. my(%env) = ( '' => { } );
  81. if (open(FH, "< $ENV{'HOME'}/.openpkg/build")) {
  82. my($env) = $env{''};
  83. while (<FH>) {
  84. if (/^\s*\[([^\]]*)\]/) {
  85. $env{$1} = { } unless $env{$1};
  86. $env = $env{$1};
  87. } elsif (my($opt,$val) = /^\-([RfruUaAzPN])\s*(.*?)\s*$/) {
  88. $val = 1 unless defined $val;
  89. $env->{$opt} = $val;
  90. }
  91. }
  92. close(FH);
  93. }
  94. die "openpkg:build:USAGE: $0 [-R rpm] [-r repository] [-f index.rdf] [-uUzZiq] [-P priv-cmd] [-N non-priv-cmd] [-p platform] [-Dwith ...] [-Ename ...] ( [-aA] | patternlist )\n"
  95. unless $#ARGV >= 0 || ($#ARGV == -1 && ($opt_a || $opt_A));
  96. ##########################################################################
  97. sub conditional ($$) {
  98. my($cond,$with) = @_;
  99. my(@s,$res);
  100. return 1 if $cond eq '';
  101. foreach (split(/\s+/,$cond)) {
  102. if ($_ eq '+') {
  103. die "FATAL: stack underflow in: $cond\n" if scalar(@s)<2;
  104. my($a) = pop @s;
  105. my($b) = pop @s;
  106. push @s, $a && $b;
  107. } elsif ($_ eq '|') {
  108. die "FATAL: stack underflow in: $cond\n" if scalar(@s)<2;
  109. my($a) = pop @s;
  110. my($b) = pop @s;
  111. push @s, $a && $b;
  112. } elsif ($_ eq '!') {
  113. die "FATAL: stack underflow in: $cond\n" if scalar(@s)<1;
  114. my($a) = pop @s;
  115. push @s, !$a;
  116. } else {
  117. push @s, $with->{$_} eq 'yes';
  118. }
  119. }
  120. die "FATAL: stack underflow in: $cond\n" if scalar(@s)<1;
  121. $res = pop @s;
  122. die "FATAL: stack not empty in: $cond\n" if scalar(@s)>0;
  123. return $res;
  124. }
  125. ##########################################################################
  126. my($RPM,$RPM_PRIV,$RPM_NPRIV,$CURL,$PROG);
  127. $RPM = $opt_R || $env{''}->{opt}->{'R'} || '@l_prefix@/bin/rpm';
  128. $RPM = (`which $RPM` =~ m{^(/.*)})[0];
  129. die "FATAL: cannot locate rpm in path\n" unless $RPM =~ m{^/};
  130. # augment command line parameters
  131. foreach my $env (sort { $a cmp $b } grep { $RPM =~ /^\Q$_\E/ } keys %env) {
  132. while (my($opt,$val) = each %{$env{$env}}) {
  133. eval "\$opt_$opt = '$val' unless defined \$opt_$opt;";
  134. }
  135. }
  136. $RPM_PRIV = ($opt_P ? $opt_P." ".$RPM : $RPM);
  137. $RPM_NPRIV = ($opt_N ? $opt_N." ".$RPM : $RPM);
  138. $CURL = $RPM;
  139. $CURL =~ s/\/bin\/rpm$/\/lib\/openpkg\/curl/
  140. or die "FATAL: cannot deduce curl path from $RPM\n";
  141. ($PROG) = $0 =~ /(?:.*\/)?(.*)/;
  142. sub version_cmp ($$) {
  143. my($a,$b) = @_;
  144. my(@a,@b,$c);
  145. my($ax,$bx);
  146. @a = split(/\./, $a);
  147. @b = split(/\./, $b);
  148. while (@a && @b) {
  149. if ($a[0] =~ /^\d+$/ && $b[0] =~ /^\d+$/) {
  150. $c = $a[0] <=> $b[0];
  151. } elsif ((($a,$ax) = $a[0] =~ /^(\d+)(.*)$/) &&
  152. (($b,$bx) = $b[0] =~ /^(\d+)(.*)$/)) {
  153. $c = $a <=> $b;
  154. $c = $ax cmp $bx unless $c;
  155. } else {
  156. $c = $a[0] cmp $b[0];
  157. }
  158. return $c if $c;
  159. shift @a;
  160. shift @b;
  161. }
  162. $c = scalar(@a) <=> scalar(@b);
  163. return $c;
  164. }
  165. sub release_cmp ($$) {
  166. my($a,$b) = @_;
  167. return $a cmp $b;
  168. }
  169. sub vcmp ($$) {
  170. my($a,$b) = @_;
  171. my($av,$ar) = $a =~ /^(.*?)(?:\-([\d\.]+))?$/;
  172. my($bv,$br) = $b =~ /^(.*?)(?:\-([\d\.]+))?$/;
  173. my($c);
  174. if ((defined $ar) && (defined $br)) {
  175. $c = release_cmp($ar,$br);
  176. return $c if $c;
  177. }
  178. if ((defined $av) && (defined $bv)) {
  179. $c = version_cmp($av,$bv);
  180. return $c if $c;
  181. }
  182. return 0;
  183. }
  184. sub vs ($) {
  185. my($t) = @_;
  186. return "$t->{version}-$t->{release}";
  187. }
  188. sub vsn ($) {
  189. my($t) = @_;
  190. return "$t->{name}-$t->{version}-$t->{release}";
  191. }
  192. ##########################################################################
  193. sub get_config ()
  194. {
  195. my($c,@q);
  196. $c = `$RPM_NPRIV --eval '%{_rpmdir} %{_rpmfilename} %{_target_os} %{_target_cpu} %{_target_platform} %{_prefix}'`;
  197. chomp($c);
  198. (@q) = split(/\s+/,$c);
  199. $q[1] =~ s/%{OS}/$q[2]/;
  200. $q[1] =~ s/%{ARCH}/$q[3]/;
  201. return {
  202. rpmdir => $q[0],
  203. template => $q[1],
  204. platform => $q[4],
  205. prefix => $q[5]
  206. };
  207. }
  208. sub get_release () {
  209. my($rel,$url);
  210. ($rel) =`$RPM_NPRIV -qi openpkg` =~ /Version:\s*(\S+)/m;
  211. if ($rel =~ /^\d+$/) {
  212. print "# $PROG current($rel)\n";
  213. print "# using '$RPM_NPRIV' (build) and '$RPM_PRIV' (install)\n";
  214. $url = "ftp://ftp.openpkg.org/current/";
  215. } elsif ($rel =~ /^(\d+\.\d+)/) {
  216. $rel = $1;
  217. print "# $PROG release($rel)\n";
  218. $url = "ftp://ftp.openpkg.org/release/$rel/";
  219. } else {
  220. die "FATAL: don't know how to handle this release\n";
  221. }
  222. return $url;
  223. }
  224. sub get_installed () {
  225. my(%map);
  226. my(@l) = `$RPM_NPRIV --provides -qa`;
  227. foreach (@l) {
  228. /^(\S+)\s*(?:=\s*([^\s\-]+)-(\S+))?$/;
  229. push(@{$map{$1}->{"$2-$3"}}, {
  230. name => $1,
  231. version => (defined $2 ? $2 : '*'),
  232. release => (defined $3 ? $3 : '*')
  233. });
  234. }
  235. return \%map;
  236. }
  237. sub revdep ($$$) {
  238. my($rev,$t,$name) = @_;
  239. return 1 if $name eq $t->{name};
  240. foreach (@{$rev->{$_}}) {
  241. return 1 if revdep($rev,$t,$_->{name});
  242. }
  243. return -1;
  244. }
  245. sub get_revdep ($) {
  246. my($env) = @_;
  247. my($i) = $env->{'installed'};
  248. my($r) = $env->{'repository'};
  249. my($pkg, %rev);
  250. my(@vers,$t,@names);
  251. print "# computing reverse dependencies\n";
  252. foreach $pkg (keys %$i) {
  253. unless ($r->{$pkg}) {
  254. print "# ATTENTION: $pkg has no upgrade path\n";
  255. next;
  256. }
  257. @vers = get_versions($r->{$pkg}, sub { 1; });
  258. foreach (@vers) {
  259. foreach $t (@{$r->{$pkg}->{$_}}) {
  260. next unless $i->{$t->{name}};
  261. next unless $t->{depends};
  262. @names = grep { $_ ne '' }
  263. map { /^(\S+)/ }
  264. @{$t->{depends}};
  265. next unless @names;
  266. push @{$rev{$_}}, $t foreach @names;
  267. }
  268. }
  269. }
  270. foreach $pkg (keys %rev) {
  271. $rev{$pkg} = [
  272. sort {
  273. revdep(\%rev, $b, $a->{name});
  274. } @{$rev{$pkg}}
  275. ];
  276. }
  277. return \%rev;
  278. }
  279. sub parse_options ($) {
  280. my($l) = @_;
  281. $l = [ split(/\n+/, $l) ] unless ref $l;
  282. my(%with) = map { /--define\s*'(\S+)\s+(\S+?)'/ } @$l;
  283. return unless %with;
  284. return \%with;
  285. }
  286. sub override_options ($$) {
  287. my($old, $new) = @_;
  288. while (my ($k,$v) = each %$new) {
  289. $old->{$k} = $v if exists $old->{$k};
  290. }
  291. }
  292. sub get_with ($;$) {
  293. my($t,$fn) = @_;
  294. my(@l,%with);
  295. unless ($t->{OPTIONS}) {
  296. if (defined $fn) {
  297. @l = `$RPM_NPRIV -qi -p $fn`;
  298. } else {
  299. @l = `$RPM_NPRIV -qi $t->{name}`;
  300. }
  301. $t->{OPTIONS} = parse_options(\@l);
  302. }
  303. return $t->{OPTIONS};
  304. }
  305. sub relurl ($$$) {
  306. my($url,$fn,$suburl) = @_;
  307. my($subfn,$submap);
  308. unless ($suburl =~ /^\w+:\/\// || $suburl =~ /^\//) {
  309. if (defined $fn) {
  310. $subfn = $fn;
  311. $subfn =~ s/\/[^\/]*$//;
  312. $subfn .= '/' unless $subfn =~ /\/$/;
  313. $subfn .= $suburl;
  314. $suburl = $subfn;
  315. } else {
  316. $subfn = $url;
  317. $subfn =~ s/\/[^\/]*$//;
  318. $subfn .= '/' unless $subfn =~ /\/$/;
  319. $suburl = "$subfn$suburl";
  320. $subfn = undef;
  321. }
  322. }
  323. return ($suburl, $subfn);
  324. }
  325. sub xel($) {
  326. my($a) = @_;
  327. my($l) = $a->[0];
  328. return '' if ref $l;
  329. return $l;
  330. }
  331. sub get_index ($$$) {
  332. my($url,$fn,$with) = @_;
  333. my($ua,$req,$res,$rdf);
  334. my($bzip2,$path);
  335. my(%map,@include);
  336. my($fetch);
  337. $fetch = defined $fn ? $fn : $url;
  338. $bzip2 = $RPM;
  339. $bzip2 =~ s/bin\/rpm$/lib\/openpkg\/bzip2/
  340. or die "FATAL: cannot deduce bzip2 path from $RPM\n";
  341. $fetch !~ /\.bz2$/ || -x $bzip2
  342. or die "FATAL: $bzip2 not found\n";
  343. if ($fetch =~ /^\w+:/) { # looks like URL scheme
  344. print "# curling index $fetch\n";
  345. if ($fetch =~ /\.bz2$/) {
  346. $path = "$CURL -q -s -o - \"$fetch\" | $bzip2 -dc |";
  347. } else {
  348. $path = "$CURL -q -s -o - \"$fetch\" |";
  349. }
  350. } else {
  351. print "# reading index file $fn\n";
  352. if ($fetch =~ /\.bz2$/) {
  353. $path = "$bzip2 -dc $fetch |";
  354. } else {
  355. $path = "< $fetch";
  356. }
  357. }
  358. open(RFH, $path) or
  359. die "FATAL: cannot open '$fetch' ($!)\n";
  360. eval {
  361. require XML::Simple;
  362. };
  363. if ($@) {
  364. print "# using simple text parser\n";
  365. my($section);
  366. my($name,$version);
  367. my($href,$release,$desc);
  368. my(@prereq,@bprereq);
  369. my(@provides,@conflicts);
  370. my($platform,$prefix);
  371. my($rec);
  372. my($tag,$cond,$body);
  373. my($useit);
  374. while (<RFH>) {
  375. s/&gt;/>/g;
  376. s/&lt;/</g;
  377. if (!(defined $href) && /<rdf:Description.*?href="([^"]*)"/) {
  378. $href = $1;
  379. $section = undef;
  380. $name = undef;
  381. $release = undef;
  382. $desc = '';
  383. $platform = undef;
  384. $prefix = undef;
  385. @prereq = ();
  386. @bprereq = ();
  387. @provides = ();
  388. @conflicts = ();
  389. }
  390. if (!(defined $href) && /<Repository.*?href="([^"]*)"/) {
  391. push(@include, $1);
  392. next;
  393. }
  394. next unless defined $href;
  395. ($tag,$cond,$body) = /<(\/?[\w:]+)\s*(?:cond="([^"]+)")?>([^<]*)/;
  396. $useit = conditional($cond,$with);
  397. if ($tag eq 'Description') {
  398. $section = 'description';
  399. } elsif ($tag eq '/Description') {
  400. $section = undef;
  401. } elsif ($section eq 'description') {
  402. $desc .= $_;
  403. } elsif ($tag eq 'PreReq') {
  404. $section = 'prereq' if $useit;
  405. } elsif ($tag eq '/PreReq') {
  406. $section = undef;
  407. } elsif ($tag eq 'BuildPreReq') {
  408. $section = 'bprereq' if $useit;
  409. } elsif ($tag eq '/BuildPreReq') {
  410. $section = undef;
  411. } elsif ($tag eq 'Provides') {
  412. $section = 'provides' if $useit;
  413. } elsif ($tag eq '/Provides') {
  414. $section = undef;
  415. } elsif ($tag eq 'Conflicts') {
  416. $section = 'conflicts' if $useit;
  417. } elsif ($tag eq '/Conflicts') {
  418. $section = undef;
  419. } elsif ($tag eq 'Name') {
  420. $name = $body;
  421. } elsif ($tag eq 'Version') {
  422. $version = $body;
  423. } elsif ($tag eq 'Release') {
  424. $release = $body;
  425. } elsif ($tag eq 'Platform') {
  426. $platform = $body;
  427. } elsif ($tag eq 'Prefixes') {
  428. $prefix = $body;
  429. } elsif ($tag eq 'rdf:li') {
  430. if ($section eq 'prereq') {
  431. push(@prereq, $body);
  432. } elsif ($section eq 'bprereq') {
  433. push(@bprereq, $body);
  434. } elsif ($section eq 'provides') {
  435. push(@provides, $body);
  436. } elsif ($section eq 'conflicts') {
  437. push(@conflicts, $body);
  438. }
  439. } elsif ($tag eq '/rdf:Description') {
  440. if (defined $href &&
  441. defined $name &&
  442. defined $version &&
  443. defined $release) {
  444. @provides = map {
  445. /(\S+)\s*(?:=\s*(\S+?)\-(\S+))?$/;
  446. {
  447. name => $1,
  448. version => $2,
  449. release => $3
  450. }
  451. } @provides;
  452. unless (grep($_->{name} eq $name, @provides)) {
  453. push(@provides, {
  454. name => $name,
  455. version => $version,
  456. release => $release
  457. });
  458. }
  459. $rec = {
  460. href => (relurl($url, undef, $href))[0],
  461. name => $name,
  462. version => $version,
  463. release => $release,
  464. depends => [ @bprereq ],
  465. keeps => [ @prereq ],
  466. conflicts => [ @conflicts ],
  467. desc => $desc,
  468. platform => $platform,
  469. prefix => $prefix
  470. };
  471. $rec->{OPTIONS} = parse_options($rec->{desc});
  472. foreach (@provides) {
  473. push(@{$map{$_->{name}}->{vs($_)}}, $rec);
  474. }
  475. }
  476. $href = undef;
  477. }
  478. }
  479. } else {
  480. print "# using XML parser\n";
  481. my($xml) = XML::Simple::XMLin(\*RFH, forcearray => 1);
  482. my($desc) = $xml->{'Repository'}->[0]->{'rdf:Description'};
  483. my($sub) = $xml->{'Repository'}->[0]->{'Repository'};
  484. my($provides,@provides,$rec);
  485. my($href,$name,$version,$release);
  486. foreach (@$desc) {
  487. $href = $_->{'href'};
  488. $name = xel($_->{'Name'});
  489. $version = xel($_->{'Version'});
  490. $release = xel($_->{'Release'});
  491. next unless defined $href &&
  492. defined $name &&
  493. defined $version &&
  494. defined $release;
  495. $provides = $_->{'Provides'}->[0]->{'rdf:bag'}->[0]->{'rdf:li'};
  496. @provides = map {
  497. /(\S+)\s*(?:=\s*(\S+?)\-(\S+))?$/;
  498. {
  499. name => $1,
  500. version => $2,
  501. release => $3
  502. }
  503. } @$provides;
  504. unless (grep($_->{name} eq $name, @provides)) {
  505. push(@provides, {
  506. name => $name,
  507. version => $version,
  508. release => $release
  509. });
  510. }
  511. $rec = {
  512. href => (relurl($url, undef, $href))[0],
  513. name => $name,
  514. version => $version,
  515. release => $release,
  516. platform => xel($_->{'Platform'}),
  517. prefix => xel($_->{'Prefixes'}),
  518. depends =>
  519. ( $_->{'BuildPreReq'}->[0]->{'rdf:bag'}->[0]->{'rdf:li'}
  520. || [] ),
  521. keeps =>
  522. ( $_->{'PreReq'}->[0]->{'rdf:bag'}->[0]->{'rdf:li'}
  523. || [] ),
  524. desc => xel($_->{'Description'})
  525. };
  526. $rec->{OPTIONS} = parse_options($rec->{desc});
  527. foreach (@provides) {
  528. push(@{$map{$_->{name}}->{vs($_)}}, $rec);
  529. }
  530. }
  531. if ($sub) {
  532. @include = map { $_->{href} } @$sub;
  533. }
  534. }
  535. close(RFH)
  536. or die "FATAL: an I/O error occured\n";
  537. #
  538. # cannot do real recursions on file handles, so we simply append
  539. # all sub-RDFs, the result is flattend into a big hash anyway
  540. #
  541. foreach (@include) {
  542. my($submap);
  543. my($suburl,$subfn) = relurl($url,$fn,$_);
  544. $submap = get_index($suburl,$subfn,$with);
  545. while (my($name,$vmap) = each %$submap) {
  546. while (my($vs,$recs) = each %$vmap) {
  547. push @{$map{$name}->{$vs}}, @$recs;
  548. }
  549. }
  550. }
  551. return \%map;
  552. }
  553. #
  554. # grep all versions of a name that
  555. # satisfy a condition
  556. #
  557. sub get_versions ($$) {
  558. my($relmap, $cond) = @_;
  559. return grep { $cond->($_); }
  560. sort { vcmp($a,$b); } keys %$relmap;
  561. }
  562. #
  563. # there can be multiple sources for a target release
  564. #
  565. sub chose_source ($$@) {
  566. my($env, $name, $vmap, @vers) = @_;
  567. my(@recs,@nrecs,$rec);
  568. return unless @vers;
  569. @recs = grep {
  570. $env->{sourceonly} ? (
  571. !(defined $_->{'platform'})
  572. ) : (
  573. !(defined $_->{'platform'}) || (
  574. defined $_->{'prefix'} &&
  575. $_->{'platform'} eq $env->{config}->{platform} &&
  576. $_->{'prefix'} eq $env->{config}->{prefix}
  577. )
  578. )
  579. } map { @{$vmap->{$_}} } @vers;
  580. return unless @recs;
  581. if (scalar(@recs) > 1) {
  582. @nrecs = grep {
  583. $env->{built}->{$_->{name}} ||
  584. $env->{installed}->{$_->{name}}
  585. } @recs;
  586. @recs = @nrecs if @nrecs;
  587. }
  588. if (scalar(@recs) > 1 && !$env->{sourceonly}) {
  589. @nrecs = grep {
  590. defined $_->{'platform'}
  591. } @recs;
  592. @recs = @nrecs if @nrecs;
  593. }
  594. if (scalar(@recs) > 1) {
  595. print "# ambigous sources for $name\n";
  596. my($i) = 0;
  597. foreach (@recs) {
  598. print "# $i: ".vsn($_)." = $_->{href}\n";
  599. $i++;
  600. }
  601. die "ERROR: ambigous dependency\n";
  602. } else {
  603. if ($env->{upgrade}) {
  604. $rec = $recs[-1];
  605. } else {
  606. $rec = $recs[0];
  607. }
  608. }
  609. print "# source for $name is ".vsn($rec)."\n";
  610. return $rec;
  611. }
  612. #
  613. # see wether target is in map
  614. #
  615. sub target_exists ($$) {
  616. my($target, $map) = @_;
  617. my($vmap) = $map->{$target->{name}};
  618. return unless $vmap;
  619. return !defined $target->{version} ||
  620. defined $vmap->{vs($target)};
  621. }
  622. #
  623. # find target in map
  624. #
  625. sub find_target ($$) {
  626. my($name, $map) = @_;
  627. my($vmap) = $map->{$name};
  628. my(@vs);
  629. return unless $vmap;
  630. @vs = sort { vcmp($b,$a) } keys %$vmap;
  631. return $vmap->{$vs[0]}->[-1];
  632. }
  633. #
  634. # see wether target has conflicts in map
  635. #
  636. sub target_conflicts ($$) {
  637. my($target, $map) = @_;
  638. my($t);
  639. foreach (@{$target->{conflicts}}) {
  640. $t = find_target($_, $map);
  641. return $t if $t;
  642. }
  643. return;
  644. }
  645. #
  646. # retrieve build dependencies for target in map
  647. #
  648. sub target_depends ($$) {
  649. my($target, $map) = @_;
  650. my($vmap,$vers);
  651. die "FATAL: ",vsn($target)," not in depend map\n"
  652. unless
  653. ( $vmap = $map->{$target->{name}} ) &&
  654. ( defined $target->{version} ) &&
  655. ( $vers = $vmap->{vs($target)} ) &&
  656. @$vers;
  657. return $vers->[0]->{depends};
  658. }
  659. #
  660. # retrieve runtime dependencies for target in map
  661. #
  662. sub target_keeps ($$) {
  663. my($target, $map) = @_;
  664. my($vmap,$vers);
  665. die "FATAL: ",vsn($target)," not in keep map\n"
  666. unless
  667. ( $vmap = $map->{$target->{name}} ) &&
  668. ( defined $target->{version} ) &&
  669. ( $vers = $vmap->{vs($target)} ) &&
  670. @$vers;
  671. return $vers->[0]->{keeps};
  672. }
  673. #
  674. # test wether target could be upgraded
  675. #
  676. sub target_newer ($$) {
  677. my($target, $map) = @_;
  678. my($vs) = vs($target);
  679. my($vmap) = $map->{$target->{name}};
  680. return 1 unless $vmap;
  681. return !grep { vcmp($vs, $_) <= 0; } keys %$vmap;
  682. }
  683. #
  684. # check wether installed package matches
  685. # build options
  686. #
  687. sub target_suitable ($$) {
  688. my($target, $with) = @_;
  689. my($iwith);
  690. my($k,$v);
  691. $iwith = $target->{OPTIONS};
  692. while (($k,$v) = each %$with) {
  693. if (exists $iwith->{$k}) {
  694. return 0 if $iwith->{$k} ne $with->{$k};
  695. }
  696. }
  697. return 1;
  698. }
  699. #
  700. # report options that are not used for
  701. #
  702. sub warn_about_options ($$) {
  703. my($target, $with) = @_;
  704. my($iwith) = $target->{OPTIONS};
  705. my($k,$v);
  706. return unless defined $iwith;
  707. while (($k,$v) = each %$with) {
  708. if (!exists $iwith->{$k}) {
  709. print "# ATTENTION: $target->{name} ignores option '$k'\n";
  710. }
  711. }
  712. }
  713. #
  714. # locate target for a dependency
  715. #
  716. sub dep2target ($$) {
  717. my($dep, $env) = @_;
  718. my($name,@vers);
  719. my($i,$r,$b,$cond,$version);
  720. my($t);
  721. $dep =~ s/(\S+)\s*//;
  722. $name = $1;
  723. $i = $env->{installed}->{$name};
  724. $r = $env->{repository}->{$name};
  725. $b = $env->{built}->{$name};
  726. return unless $i || $r || $b;
  727. if ($dep =~ /^>=\s*(\S+)$/) {
  728. $version = $1;
  729. $cond = sub { vcmp($_[0],$version) >= 0; };
  730. } elsif ($dep =~ /^=\s*(\S+)$/) {
  731. $version = $1;
  732. $cond = sub { vcmp($_[0],$version) == 0; };
  733. } elsif ($dep =~ /^\s*$/) {
  734. $cond = sub { 1; };
  735. } else {
  736. die "FATAL: don't know how to handle PreReq: $name $dep\n";
  737. }
  738. if ($i && (@vers = get_versions($i, $cond))) {
  739. foreach (@vers) {
  740. $t = $i->{$_}->[0];
  741. if (get_with($t), target_suitable($t, $env->{with})) {
  742. if (!$env->{upgrade}) {
  743. return ($t, 1);
  744. }
  745. }
  746. }
  747. }
  748. if ($b && (@vers = get_versions($b, $cond))) {
  749. return ($b->{$vers[0]}->[0], 1);
  750. }
  751. return (chose_source($env, $name, $r, get_versions($r, $cond)), 0);
  752. }
  753. sub make_dep ($$$$$) {
  754. my($target,$depth,$env,$list,$blist) = @_;
  755. my($d,$k,%d,%k,$t,$old);
  756. if (target_exists($target, $env->{built})) {
  757. print "# $target->{name} is already in list\n";
  758. return;
  759. }
  760. if ($t = target_conflicts($target, $env->{installed})) {
  761. print "# $target->{name} conflicts with ",vsn($t),"\n";
  762. return;
  763. }
  764. if ($t = target_conflicts($target, $env->{built})) {
  765. print "# $target->{name} conflicts with ",vsn($t),"\n";
  766. return;
  767. }
  768. #
  769. # see if a target is already installed and requires a rebuild
  770. #
  771. if ($t = find_target($target->{name}, $env->{installed})) {
  772. if (exists $env->{exclude}->{$target->{name}}) {
  773. print "# excluding $target->{name} (no upgrade allowed)\n";
  774. return;
  775. }
  776. get_with($t);
  777. if ($target->{REBUILD}) {
  778. print "# rebuilding $target->{name} (dependency)\n";
  779. } elsif ($env->{zero}) {
  780. print "# rebuilding $target->{name} (zero)\n";
  781. } elsif ($env->{upgrade} && target_newer($target, $env->{installed})) {
  782. print "# rebuilding $target->{name} (upgrade)\n";
  783. } elsif (!target_suitable($t, $env->{with})) {
  784. print "# rebuilding $target->{name} (parameter mismatch)\n";
  785. } else {
  786. print "# $target->{name} is already installed\n";
  787. return;
  788. }
  789. # use options from installed base
  790. override_options($target->{OPTIONS}, $t->{OPTIONS});
  791. $target->{REBUILD} = 1;
  792. }
  793. if (exists $env->{exclude}->{$target->{name}}) {
  794. die "FATAL: target ".vsn($target)." is forbidden\n";
  795. }
  796. # mark this as a target before reverse dependencies trigger
  797. # it again
  798. push(@{$env->{built}->{$target->{name}}->{vs($target)}}, $target);
  799. $d = target_depends($target, $env->{repository});
  800. $k = target_keeps($target, $env->{repository});
  801. #
  802. # recurse over dependencies
  803. #
  804. if (@$d || @$k) {
  805. %d = map { $_ => 1 } @$d, @$k;
  806. %k = map { $_ => 1 } @$k;
  807. foreach (keys %d) {
  808. # old index misses a OpenPKG provider in the index... skip it
  809. next if $_ eq 'OpenPKG';
  810. ($t,$old) = dep2target($_, $env);
  811. if ($t) {
  812. if ($old) {
  813. print "# $target->{name} uses ".vsn($t)." for $_\n";
  814. next;
  815. }
  816. # record which targets to keep in blist
  817. if ($k{$_}) {
  818. push(@$blist,$t);
  819. print "# $target->{name} installs ".vsn($t)." for $_\n";
  820. } else {
  821. print "# $target->{name} requires ".vsn($t)." for $_\n";
  822. }
  823. make_dep($t,$depth+1,$env,$list,$blist);
  824. } else {
  825. die "FATAL: $target->{name} searches for a frood called '$_'\n";
  826. }
  827. }
  828. }
  829. print "# adding ".vsn($target)." to list\n";
  830. push(@$list, $target);
  831. if (!$env->{quick} &&
  832. $target->{name} ne 'openpkg' &&
  833. $target->{REBUILD}) {
  834. unless ($env->{revdep}) {
  835. $env->{revdep} = get_revdep($env);
  836. }
  837. foreach $t (@{$env->{revdep}->{$target->{name}}}) {
  838. # this is a rebuild, triggering further revdeps
  839. $t->{REBUILD} = 1;
  840. # this is a rebuild, keep this installed
  841. push(@$blist, $t);
  842. print "# rebuilding revdep ".vsn($t)."\n";
  843. make_dep($t,$depth+1,$env,$list,$blist);
  844. }
  845. }
  846. }
  847. sub remove_list ($$$) {
  848. my($targets, $keeps, $installed) = @_;
  849. my(%keep);
  850. %keep = map { $_ => 1 } @$keeps;
  851. return [ grep {
  852. !$keep{$_} && !$installed->{$_->{name}}->{vs($_)};
  853. } @$targets
  854. ];
  855. }
  856. sub build_list ($$) {
  857. my($pattern, $env) = @_;
  858. my(@goals,@targets,@keeps,$bonly,$t);
  859. my($name,$r,$i,@vers);
  860. my(@todo);
  861. if (defined $pattern) {
  862. @todo = ();
  863. foreach (split(/\s+/,$pattern)) {
  864. next unless /\S/;
  865. if (s/\*+$//) {
  866. push @todo, '^'.quotemeta($_).'';
  867. } else {
  868. push @todo, '^'.quotemeta($_).'$';
  869. }
  870. }
  871. $pattern = join('|',@todo);
  872. @todo = grep(/$pattern/, keys %{$env->{repository}});
  873. } else {
  874. @todo = grep {
  875. my($n) = $_;
  876. (ref $env->{installed}->{$n}) &&
  877. grep { $_ ne '-' } keys %{$env->{installed}->{$n}}
  878. } keys %{$env->{repository}};
  879. }
  880. #
  881. # chose sources for goals from repository
  882. #
  883. foreach $name (@todo) {
  884. $t = undef;
  885. #
  886. # keeping installed packages for goals is ugly
  887. # -> we currently do not support installed source RPMs
  888. # -> source RPMs might already have expired from repository
  889. #
  890. # consequence:
  891. # -> goals are always upgraded to repository versions
  892. #
  893. #unless ($env->{upgrade}) {
  894. # $i = $env->{installed}->{$name};
  895. # if (@vers = get_versions($i, sub { 1; })) {
  896. # $t = chose_source($env, $name, $i, @vers);
  897. # }
  898. #}
  899. unless ($t) {
  900. $r = $env->{repository}->{$name};
  901. if (@vers = get_versions($r, sub { 1; })) {
  902. $t = chose_source($env, $name, $r, @vers);
  903. }
  904. }
  905. die "FATAL: no known source found for '$name'\n" unless $t;
  906. warn_about_options($t, $env->{with});
  907. push(@goals, $t);
  908. }
  909. return unless @goals;
  910. @targets = ();
  911. @keeps = @goals;
  912. foreach $t (@goals) {
  913. print "# recursing over dependencies for ".vsn($t)."\n";
  914. make_dep($t,0,$env,\@targets,\@keeps);
  915. }
  916. $bonly = remove_list(\@targets, \@keeps, $env->{installed});
  917. return (\@targets, $bonly);
  918. }
  919. #######################################################################
  920. sub target2rpm ($$) {
  921. my($target,$c) = @_;
  922. my($tmpl) = $c->{template};
  923. $tmpl =~ s/%{NAME}/$target->{name}/;
  924. $tmpl =~ s/%{VERSION}/$target->{version}/;
  925. $tmpl =~ s/%{RELEASE}/$target->{release}/;
  926. return $c->{rpmdir}.'/'.$tmpl;
  927. }
  928. #######################################################################
  929. sub binary_target ($$) {
  930. my($t, $fn) = @_;
  931. my(%target) = %$t;
  932. get_with(\%target, $fn);
  933. return \%target;
  934. }
  935. sub make_defines ($$) {
  936. my($old, $new) = @_;
  937. my($with);
  938. #
  939. # override old parameters with new parameters
  940. # drop new parameters that do not exist in old set
  941. #
  942. # if there is no old set at all (which happens if there
  943. # is no template and no installed package), just use the
  944. # new parameters and assume these are useful.
  945. #
  946. if ($old) {
  947. $old = { %$old };
  948. override_options($old, $new);
  949. } else {
  950. $old = $new;
  951. }
  952. #
  953. # convert parameters to --define command line options
  954. # skip parameter templates from index
  955. #
  956. $with = join(' ',map { "--define '$_ $old->{$_}'" }
  957. grep { $old->{$_} !~ /^%/ } keys %$old);
  958. $with = ' '.$with if $with ne '';
  959. return $with;
  960. }
  961. sub print_list1 ($$$@$) {
  962. my($list,$c,$uncond,$with,$ignore) = @_;
  963. my($spkg,$bpkg);
  964. my($opt);
  965. my($cmd1, $cmd2, $mark);
  966. $mark = '::::';
  967. foreach (@$list) {
  968. $spkg = $_->{href};
  969. $bpkg = target2rpm($_, $c);
  970. #
  971. # rebuild binary package IF
  972. #
  973. # 'unconditional' option
  974. # OR there is no binary package
  975. # OR dependency check found that installed package is not suitable
  976. # OR existing binary package doesn't satisfy wanted options
  977. #
  978. $cmd1 = undef;
  979. if ($uncond || !-f $bpkg || $_->{REBUILD} ||
  980. !target_suitable(binary_target($_, $bpkg),$with)) {
  981. $opt = make_defines($_->{OPTIONS}, $with);
  982. #
  983. # someone preferred a binary from the repository
  984. # just copy it to the local store
  985. #
  986. if (defined $_->{platform}) {
  987. $cmd1 = "$CURL -q -s -o $bpkg $spkg";
  988. } else {
  989. $cmd1 = "$RPM_NPRIV$opt --rebuild $spkg";
  990. }
  991. }
  992. #
  993. # if package exist force rpm to copy over new files
  994. # better than erasing everything and losing configuration
  995. # files
  996. #
  997. $opt = $_->{REBUILD} ? ' --force' : '';
  998. $cmd2 = "$RPM_PRIV$opt -Uvh $bpkg";
  999. if ($ignore) {
  1000. $cmd2 = "$cmd1 && \\\n$cmd2" if defined $cmd1;
  1001. } else {
  1002. if (defined $cmd1) {
  1003. $cmd2 = "$cmd1 || exit \$?\n$cmd2 || exit \$?"
  1004. } else {
  1005. $cmd2 = "$cmd2 || exit \$?";
  1006. }
  1007. }
  1008. print "echo $mark $spkg $mark\n$cmd2\necho $mark $spkg = \$? $mark\n";
  1009. }
  1010. }
  1011. sub print_list2 ($$) {
  1012. my($list,$c) = @_;
  1013. my($pkg);
  1014. foreach (@$list) {
  1015. $pkg = "$_->{name}-$_->{version}-$_->{release}";
  1016. print "$RPM_PRIV -e $pkg\n";
  1017. }
  1018. }
  1019. #######################################################################
  1020. my($config,$url,$repository,$installed,$list,$bonly);
  1021. my($pattern,%with,%exclude);
  1022. if ($opt_a) {
  1023. $pattern = undef;
  1024. } else {
  1025. $pattern = join(' ', @ARGV);
  1026. }
  1027. if ($opt_A) {
  1028. $pattern = '*';
  1029. }
  1030. %with = map {
  1031. /([^\s=]+)(?:\=(\S+))?/
  1032. ? ($1 => (defined $2 ? $2 : 'yes'))
  1033. : ()
  1034. } split(/\s+/, $opt_D);
  1035. %exclude = map { $_ => 1 } split(/\s+/, $opt_E);
  1036. $config = get_config();
  1037. if (defined $opt_p) {
  1038. $config->{platform} = $opt_p;
  1039. }
  1040. if (defined $opt_r) {
  1041. $url = $opt_r;
  1042. $url .= '/' unless $url =~ /\/$/;
  1043. } else {
  1044. $url = get_release();
  1045. }
  1046. #
  1047. # if we read the index from a file we can no longer deduce
  1048. # repository paths from index paths. For now lets assume
  1049. # that everything is below SRC/ to be compatible with
  1050. # existing file indexes.
  1051. #
  1052. if (defined $opt_f && !defined $opt_r) {
  1053. $url .= 'SRC/';
  1054. }
  1055. $installed = $opt_Z ? {} : get_installed();
  1056. $repository = get_index($url.'00INDEX.rdf',$opt_f,\%with);
  1057. ($list,$bonly) = build_list($pattern, {
  1058. config => $config,
  1059. installed => $installed,
  1060. repository => $repository,
  1061. built => {},
  1062. revdep => undef,
  1063. with => \%with,
  1064. exclude => \%exclude,
  1065. upgrade => ($opt_a || $opt_U),
  1066. zero => ($opt_z || $opt_Z),
  1067. quick => $opt_q,
  1068. sourceonly => (
  1069. $opt_u ||
  1070. $opt_U ||
  1071. $opt_z ||
  1072. $opt_Z ||
  1073. scalar(%with) > 0 )
  1074. });
  1075. die "FATAL: cannot find package\n" unless defined $list;
  1076. print_list1($list,$config,$opt_a || $opt_u || $opt_U,\%with,$opt_i);
  1077. print_list2($bonly,$config);