openpkg-build.pl 47 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826
  1. ##
  2. ## openpkg-build.pl -- create build scripts from package index
  3. ##
  4. ## Copyright (c) 2000-2003 Cable & Wireless Deutschland GmbH
  5. ## Copyright (c) 2000-2003 The OpenPKG Project <http://www.openpkg.org/>
  6. ## Copyright (c) 2000-2003 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 $opt_s $opt_S $opt_X/;
  30. my $getopts = 'R:r:f:uUaAzZP:N:E:iD:p:qsSX';
  31. getopts($getopts);
  32. ##########################################################################
  33. sub getopts ($) {
  34. my($opts) = @_;
  35. my(%optf) = map { /(\w)/; $1 => $_ } $opts =~ /(\w:|\w)/g;
  36. my(%opts,@argv,$optarg);
  37. foreach (@ARGV) {
  38. if (@argv) {
  39. push @argv, $_;
  40. } elsif (defined $optarg) {
  41. if (exists $opts{$optarg}) {
  42. $opts{$optarg} .= " $_";
  43. } else {
  44. $opts{$optarg} = $_;
  45. }
  46. $optarg = undef;
  47. } elsif (!/^[-]/) {
  48. push @argv, $_;
  49. } else {
  50. while (/^\-(\w)(.*)/) {
  51. if (exists $optf{$1}) {
  52. if (length($optf{$1}) > 1) {
  53. if ($2 ne '') {
  54. if (exists $opts{$1}) {
  55. $opts{$1} .= " $2";
  56. } else {
  57. $opts{$1} = $2;
  58. }
  59. } else {
  60. $optarg = $1;
  61. }
  62. last;
  63. } else {
  64. $opts{$1} = 1;
  65. }
  66. } else {
  67. warn "warning: unknown option $_\n";
  68. }
  69. $_ = "-$2";
  70. }
  71. }
  72. }
  73. if (defined $optarg) {
  74. warn "warning: option $optarg requires an argument\n";
  75. }
  76. foreach (keys %opts) {
  77. eval '$opt_'.$_.' = "'.quotemeta($opts{$_}).'";';
  78. }
  79. @ARGV = @argv;
  80. }
  81. my(%env) = ( '' => { } );
  82. if (open(FH, "< $ENV{'HOME'}/.openpkg/build")) {
  83. my($env) = $env{''};
  84. my($go) = $getopts;
  85. $go =~ s/[^a-zA-Z]//g;
  86. while (<FH>) {
  87. if (/^\s*\[([^\]]*)\]/) {
  88. $env{$1} = { } unless $env{$1};
  89. $env = $env{$1};
  90. } elsif (my($opt,$val) = /^\-([$go])\s*(.*?)\s*$/) {
  91. $val = 1 unless defined $val;
  92. $env->{$opt} = $val;
  93. }
  94. }
  95. close(FH);
  96. }
  97. die "openpkg:build:USAGE: $0 [-R rpm] [-r repository] [-f index.rdf] [-uUzZiqS] [-P priv-cmd] [-N non-priv-cmd] [-p platform] [-Dwith ...] [-Ename ...] ( [-aA] | patternlist )\n"
  98. unless $#ARGV >= 0 || ($#ARGV == -1 && ($opt_a || $opt_A));
  99. ##########################################################################
  100. #
  101. # evaluate a condition attribute from an option set
  102. #
  103. sub conditional ($$) {
  104. my($cond,$with) = @_;
  105. my(@s,$res);
  106. return 1 if $cond eq '';
  107. foreach (split(/\s+/,$cond)) {
  108. if ($_ eq '+') {
  109. die "FATAL: stack underflow in: $cond\n" if scalar(@s)<2;
  110. my($a) = pop @s;
  111. my($b) = pop @s;
  112. push @s, $a && $b;
  113. } elsif ($_ eq '|') {
  114. die "FATAL: stack underflow in: $cond\n" if scalar(@s)<2;
  115. my($a) = pop @s;
  116. my($b) = pop @s;
  117. push @s, $a || $b;
  118. } elsif ($_ eq '!') {
  119. die "FATAL: stack underflow in: $cond\n" if scalar(@s)<1;
  120. my($a) = pop @s;
  121. push @s, !$a;
  122. } else {
  123. push @s, ($with->{$_} eq 'yes') ? 1 : 0;
  124. }
  125. }
  126. die "FATAL: stack underflow in: $cond\n" if scalar(@s)<1;
  127. $res = pop @s;
  128. die "FATAL: stack not empty in: $cond\n" if scalar(@s)>0;
  129. return $res;
  130. }
  131. ##########################################################################
  132. my($RPM,$RPM_PRIV,$RPM_NPRIV,$CURL,$PROG);
  133. $RPM = $opt_R || $env{''}->{opt}->{'R'} || '@l_prefix@/bin/rpm';
  134. $RPM = (`which $RPM` =~ m{^(/.*)})[0];
  135. die "FATAL: cannot locate rpm in path\n" unless $RPM =~ m{^/};
  136. # augment command line parameters
  137. foreach my $env (sort { $a cmp $b } grep { $RPM =~ /^\Q$_\E/ } keys %env) {
  138. while (my($opt,$val) = each %{$env{$env}}) {
  139. eval "\$opt_$opt = '$val' unless defined \$opt_$opt;";
  140. }
  141. }
  142. $RPM_PRIV = ($opt_P ? $opt_P." ".$RPM : $RPM);
  143. $RPM_NPRIV = ($opt_N ? $opt_N." ".$RPM : $RPM);
  144. $CURL = $RPM;
  145. $CURL =~ s/\/bin\/rpm$/\/lib\/openpkg\/curl/
  146. or die "FATAL: cannot deduce curl path from $RPM\n";
  147. ($PROG) = $0 =~ /(?:.*\/)?(.*)/;
  148. sub version_cmp ($$) {
  149. my($a,$b) = @_;
  150. my(@a,@b,$c);
  151. my($ax,$bx);
  152. @a = split(/\./, $a);
  153. @b = split(/\./, $b);
  154. while (@a && @b) {
  155. if ($a[0] =~ /^\d+$/ && $b[0] =~ /^\d+$/) {
  156. $c = $a[0] <=> $b[0];
  157. } elsif ((($a,$ax) = $a[0] =~ /^(\d+)(.*)$/) &&
  158. (($b,$bx) = $b[0] =~ /^(\d+)(.*)$/)) {
  159. $c = $a <=> $b;
  160. $c = $ax cmp $bx unless $c;
  161. } else {
  162. $c = $a[0] cmp $b[0];
  163. }
  164. return $c if $c;
  165. shift @a;
  166. shift @b;
  167. }
  168. $c = scalar(@a) <=> scalar(@b);
  169. return $c;
  170. }
  171. sub release_cmp ($$) {
  172. my($a,$b) = @_;
  173. return $a cmp $b;
  174. }
  175. sub vcmp ($$) {
  176. my($a,$b) = @_;
  177. return 0 if $a eq $b;
  178. my($av,$ar) = $a =~ /^(.*?)(?:\-([\d\.]+))?$/;
  179. my($bv,$br) = $b =~ /^(.*?)(?:\-([\d\.]+))?$/;
  180. my($c);
  181. if ((defined $ar) && (defined $br)) {
  182. $c = release_cmp($ar,$br);
  183. return $c if $c;
  184. }
  185. if ((defined $av) && (defined $bv)) {
  186. $c = version_cmp($av,$bv);
  187. return $c if $c;
  188. }
  189. return 0;
  190. }
  191. sub vs ($) {
  192. my($t) = @_;
  193. return "$t->{version}-$t->{release}";
  194. }
  195. sub vsn ($) {
  196. my($t) = @_;
  197. return "$t->{name}-$t->{version}-$t->{release}";
  198. }
  199. ##########################################################################
  200. sub get_config ()
  201. {
  202. my($c,@q,@g);
  203. $c = `$RPM_NPRIV --eval '%{_rpmdir} %{_rpmfilename} %{_target_os} %{_target_cpu} %{_target_platform} %{_prefix}'`;
  204. chomp($c);
  205. (@q) = split(/\s+/,$c);
  206. $q[1] =~ s/%{OS}/$q[2]/;
  207. $q[1] =~ s/%{ARCH}/$q[3]/;
  208. $c = `$RPM_NPRIV --showrc`;
  209. @g = $c =~ /\%\{l_tool_locate\s+([^\s\}]+)/g;
  210. return {
  211. rpmdir => $q[0],
  212. template => $q[1],
  213. platform => $q[4],
  214. prefix => $q[5],
  215. optreg => '(?:'.join('|', map { "\Qwith_$_\E" } @g).')'
  216. };
  217. }
  218. sub get_release () {
  219. my($rel,$url);
  220. ($rel) =`$RPM_NPRIV -qi openpkg` =~ /Version:\s*(\S+)/m;
  221. if ($rel =~ /^\d+$/) {
  222. print "# $PROG current($rel)\n";
  223. print "# using '$RPM_NPRIV' (build) and '$RPM_PRIV' (install)\n";
  224. $url = "ftp://ftp.openpkg.org/current/";
  225. } elsif ($rel =~ /^(\d+\.\d+)/) {
  226. $rel = $1;
  227. print "# $PROG release($rel)\n";
  228. $url = "ftp://ftp.openpkg.org/release/$rel/";
  229. } else {
  230. die "FATAL: don't know how to handle this release\n";
  231. }
  232. return $url;
  233. }
  234. sub parse_provides ($) {
  235. my($s) = @_;
  236. my($nam,$val,$pre,$with,$pxy,$ver,$rel);
  237. ($nam,$val) = $s =~ /^(\S+)\s*(?:=\s*(\S*?))?$/;
  238. #
  239. # build options are encoded as a Requirement
  240. # <packagename>::<buildoption> = <value>
  241. #
  242. # since the value is interpreted as a version number
  243. # you can only do equality tests
  244. #
  245. if (($pre,$with) = $nam =~ /^(\S+?)::(\S*)$/) {
  246. $val =~ s/(?:\%([0-9a-fA-F][0-9a-fA-F]))/chr(hex($1))/eg;
  247. ($ver,$rel,$pxy) = ($val, '', undef);
  248. } else {
  249. ($ver,$rel,$pxy) = $val =~ /^([^\s\-]+)-([^\s\+]+)(\+PROXY)?$/;
  250. }
  251. return {
  252. name => $nam, # the full name of the resource
  253. version => $ver, # the version (or value)
  254. release => $rel, # and release number
  255. proxy => $pxy, # wether the resource is a PROXY resource
  256. prefix => $pre, # the packagename (if resource is an option)
  257. with => $with # the buildoption (if resource is an option)
  258. };
  259. }
  260. sub parse_depends ($) {
  261. my($dep) = @_;
  262. my($name, $op, $val);
  263. if (ref $dep) {
  264. #
  265. # dependency from new index stored as a node
  266. #
  267. # content of the node is the name
  268. # certain attributes denote the comparison operator
  269. # the value of such an attribute is the comparison operand
  270. #
  271. # the operator (and operand) are optional and there can
  272. # only be one
  273. #
  274. $name = $dep->{content};
  275. $op = undef;
  276. $op = 'equ' if exists $dep->{equ};
  277. $op = 'geq' if exists $dep->{geq};
  278. $op = 'leq' if exists $dep->{leq};
  279. $op = 'gt' if exists $dep->{gt};
  280. $op = 'lt' if exists $dep->{lt};
  281. if (defined $op) {
  282. $val = $dep->{$op};
  283. }
  284. } elsif ($dep =~ /\S/) {
  285. #
  286. # dependency from old index stored as text string
  287. #
  288. # "name operator operand"
  289. # or
  290. # "name"
  291. #
  292. ($name,$op,$val) = $dep =~ /(\S+)\s*(?:(\S+)\s*(\S+))?\s*$/;
  293. if (defined $op) {
  294. $op = {
  295. '==' => 'equ', '=' => 'equ',
  296. '>=' => 'geq', '=>' => 'geq',
  297. '<=' => 'leq', '=<' => 'leq',
  298. '>' => 'gt', '<' => 'lt'
  299. }->{$op};
  300. unless (defined $op) {
  301. print "# don't know how to handle dependency: $dep\n";
  302. return;
  303. }
  304. }
  305. }
  306. return {
  307. name => $name,
  308. op => $op,
  309. val => $val
  310. };
  311. }
  312. sub depends2provides ($) {
  313. my($dep) = @_;
  314. my($ver,$rel,$pxy,$pre,$with);
  315. ($ver,$rel,$pxy) = $dep->{val} =~ /^([^\s\-]+)-([^\s\+]+)(\+PROXY)?$/;
  316. ($pre,$with) = $dep->{name} =~ /^(\S+?)::(\S*)$/;
  317. return {
  318. name => $dep->{name},
  319. version => (defined $ver ? $ver : $dep->{val}),
  320. release => $rel,
  321. proxy => $pxy,
  322. prefix => $pre,
  323. with => $with
  324. }
  325. }
  326. #
  327. # convert parser output to dependency records
  328. #
  329. sub depend_list ($) {
  330. my($deps) = @_;
  331. foreach (@$deps) {
  332. $_ = parse_depends($_);
  333. }
  334. return $deps;
  335. }
  336. #
  337. # compute list of package names from dependency list
  338. #
  339. sub depends2pkglist ($) {
  340. my($deps) = @_;
  341. return map { $_->{name} } @$deps;
  342. }
  343. #
  344. # retrieve the local installed base
  345. #
  346. # for packages that provide option resources (packagename::buildoption)
  347. # the options are parsed into the OPTIONS hash
  348. #
  349. # other packages will query options on demand
  350. #
  351. sub get_installed () {
  352. my(%map);
  353. my(@l) = `$RPM_NPRIV --provides -qa`;
  354. my($p);
  355. my($nam,$val,%options);
  356. foreach (@l) {
  357. $p = parse_provides($_);
  358. if (defined $p->{with}) {
  359. $options{$p->{prefix}}->{$p->{with}} = $p->{version}
  360. }
  361. push @{$map{$p->{name}}->{"$p->{version}-$p->{release}"}}, {
  362. name => $p->{name},
  363. version => (defined $p->{version} ? $p->{version} : '*'),
  364. release => (defined $p->{release} ? $p->{release} : '*'),
  365. PROXY => $p->{proxy}
  366. };
  367. }
  368. #
  369. # options are provided for a package
  370. # apply them to all instances of the package
  371. #
  372. foreach $nam (keys %options) {
  373. foreach $val (keys %{$map{$nam}}) {
  374. foreach (@{$map{$nam}->{$val}}) {
  375. $_->{OPTIONS} = $options{$nam};
  376. }
  377. }
  378. }
  379. return \%map;
  380. }
  381. #
  382. # compute reverse dependency map
  383. #
  384. #
  385. sub get_revdep ($) {
  386. my($env) = @_;
  387. my($i) = $env->{'installed'};
  388. my($r) = $env->{'repository'};
  389. my($pkg, %dep, %dlist, %rev);
  390. my(@vers,$t);
  391. print "# computing reverse dependencies\n";
  392. foreach $pkg (keys %$i) {
  393. unless ($r->{$pkg}) {
  394. print "# ATTENTION: $pkg has no upgrade path\n";
  395. next;
  396. }
  397. #
  398. # get list of package versions from repository
  399. #
  400. @vers = get_versions($r->{$pkg}, sub { 1; });
  401. #
  402. # get forward dependencies from repository packages
  403. #
  404. # dep{a}{b} is true if b depends directly on a
  405. # dlist{a} is list of packages that depend on a
  406. #
  407. foreach (@vers) {
  408. foreach $t (@{$r->{$pkg}->{$_}}) {
  409. next unless $i->{$t->{name}};
  410. next unless $t->{depends};
  411. foreach (depends2pkglist($t->{depends})) {
  412. $dep{$_}{$t->{name}} = 1;
  413. push @{$dlist{$_}}, $t;
  414. }
  415. }
  416. }
  417. }
  418. #
  419. # sort reverse dependencies
  420. #
  421. foreach $pkg (keys %dep) {
  422. $rev{$pkg} = [
  423. sort {
  424. $dep{$a->{name}}{$b->{name}} ||
  425. -$dep{$b->{name}}{$a->{name}} ||
  426. $a->{name} cmp $b->{name}
  427. } @{$dlist{$pkg}}
  428. ];
  429. }
  430. return \%rev;
  431. }
  432. #
  433. # parse option from rpm output
  434. #
  435. sub parse_options ($) {
  436. my($l) = @_;
  437. $l = join("\n", @$l) if ref $l;
  438. return if ($l !~ m/(--define|\%option\s+)/s);
  439. my $with = {};
  440. $l =~ s/--define\s*'(\S+)\s+(\S+?)'/$with->{$1} = $2, ''/sge; # before openpkg-20021230
  441. $l =~ s/\%option\s+(\S+)\s+(\S+)/$with->{$1} = $2, ''/sge; # after openpkg-20021230
  442. return $with;
  443. }
  444. #
  445. # copy options from new to old
  446. # where option already exists in old or option key
  447. # matches regular expression
  448. #
  449. sub override_options ($$$) {
  450. my($old, $new, $reg) = @_;
  451. foreach my $k (keys %$new) {
  452. $old->{$k} = $new->{$k} if exists $old->{$k} || $k =~ /^$reg$/;
  453. }
  454. }
  455. #
  456. # pull in OPTIONS for a package or an RPM file
  457. #
  458. sub get_with ($;$) {
  459. my($t,$fn) = @_;
  460. my(@l,%with);
  461. unless ($t->{OPTIONS}) {
  462. if (defined $fn) {
  463. @l = `$RPM_NPRIV -qi -p $fn`;
  464. } else {
  465. @l = `$RPM_NPRIV -qi $t->{name}`;
  466. }
  467. $t->{OPTIONS} = parse_options(\@l);
  468. }
  469. return $t->{OPTIONS};
  470. }
  471. #
  472. # compute absolute paths
  473. #
  474. # (url, fn) point to a base document
  475. # the location is the file path fn if fn is
  476. # defined, otherwise it is url.
  477. #
  478. # augment the pointer with suburl
  479. #
  480. # suburl can be an absolute url
  481. # then the new pointer is (suburl, undef)
  482. #
  483. # suburl can be a absolute file path
  484. # then the new pointer is (suburl, suburl)
  485. #
  486. # suburl can be a relative path
  487. # then it augments url or fn accordingly
  488. #
  489. sub relurl ($$$) {
  490. my($url,$fn,$suburl) = @_;
  491. my($subfn);
  492. if ($suburl =~ /^\w+:\/\//) {
  493. # NOP
  494. } elsif ($suburl =~ /^\//) {
  495. $subfn = $suburl;
  496. } else {
  497. if (defined $fn) {
  498. $subfn = $fn;
  499. $subfn =~ s/\/[^\/]*$//;
  500. $subfn .= '/' unless $subfn =~ /\/$/;
  501. $subfn .= $suburl;
  502. $suburl = $subfn;
  503. } else {
  504. $subfn = $url;
  505. $subfn =~ s/\/[^\/]*$//;
  506. $subfn .= '/' unless $subfn =~ /\/$/;
  507. $suburl = "$subfn$suburl";
  508. $subfn = undef;
  509. }
  510. }
  511. return ($suburl, $subfn);
  512. }
  513. #
  514. # return node value from XML parser
  515. #
  516. sub xel($) {
  517. my($a) = @_;
  518. my($l) = $a->[0];
  519. return '' if ref $l;
  520. return $l;
  521. }
  522. #
  523. # grep XML Bag against condition
  524. # return as flat list
  525. #
  526. sub with_list ($$) {
  527. my($bags,$with) = @_;
  528. my($bag,$li,$el);
  529. my(@out);
  530. foreach $bag (@$bags) {
  531. next unless conditional($bag->{'cond'}, $with);
  532. foreach $li (@{$bag->{'rdf:bag'}}) {
  533. $el = $li->{'resource'} || $li->{'rdf:li'};
  534. push @out, @$el;
  535. }
  536. }
  537. return \@out;
  538. }
  539. sub simple_text_parser ($$$$) {
  540. my($fh,$url,$with,$map) = @_;
  541. my(@include);
  542. my($section);
  543. my($name,$version);
  544. my($href,$release,$desc);
  545. my(@prereq,@bprereq);
  546. my(@provides,@conflicts,@source,@nosource);
  547. my(%options);
  548. my($platform,$prefix);
  549. my($rec);
  550. my($tag,$cond,$attrname,$attrval,$body);
  551. my($useit);
  552. print "# using simple text parser\n";
  553. while (<$fh>) {
  554. s/&gt;/>/g;
  555. s/&lt;/</g;
  556. if (!(defined $href) && /<rdf:Description.*?href="([^"]*)"/) {
  557. $href = $1;
  558. $section = undef;
  559. $name = undef;
  560. $release = undef;
  561. $desc = '';
  562. $platform = undef;
  563. $prefix = undef;
  564. @prereq = ();
  565. @bprereq = ();
  566. @provides = ();
  567. @conflicts = ();
  568. @source = ();
  569. @nosource = ();
  570. }
  571. if (!(defined $href) && /<Repository.*?href="([^"]*)"/) {
  572. push(@include, $1);
  573. next;
  574. }
  575. next unless defined $href;
  576. ($tag,$cond,$attrname,$attrval,$body) = /
  577. <
  578. (\/?[\w:]+)
  579. \s*
  580. (?:cond="([^"]+)")?
  581. (?:(\w+)="([^"]+)")?
  582. >
  583. (.*?)
  584. (?:<\/\1>)?
  585. $
  586. /mx;
  587. $useit = conditional($cond,$with);
  588. if ($tag eq 'Description') {
  589. $section = 'description';
  590. } elsif ($tag eq '/Description') {
  591. $section = undef;
  592. } elsif ($section eq 'description') {
  593. $desc .= $_;
  594. } elsif ($tag eq 'PreReq') {
  595. $section = 'prereq' if $useit;
  596. } elsif ($tag eq '/PreReq') {
  597. $section = undef;
  598. } elsif ($tag eq 'BuildPreReq') {
  599. $section = 'bprereq' if $useit;
  600. } elsif ($tag eq '/BuildPreReq') {
  601. $section = undef;
  602. } elsif ($tag eq 'Provides') {
  603. $section = 'provides' if $useit;
  604. } elsif ($tag eq '/Provides') {
  605. $section = undef;
  606. } elsif ($tag eq 'Conflicts') {
  607. $section = 'conflicts' if $useit;
  608. } elsif ($tag eq '/Conflicts') {
  609. $section = undef;
  610. } elsif ($tag eq 'NoSource') {
  611. $section = 'nosource' if $useit;
  612. } elsif ($tag eq '/NoSource') {
  613. $section = undef;
  614. } elsif ($tag eq 'Source') {
  615. $section = 'source' if $useit;
  616. } elsif ($tag eq '/Source') {
  617. $section = undef;
  618. } elsif ($tag eq 'Name') {
  619. $name = $body;
  620. } elsif ($tag eq 'Version') {
  621. $version = $body;
  622. } elsif ($tag eq 'Release') {
  623. $release = $body;
  624. } elsif ($tag eq 'Platform') {
  625. $platform = $body;
  626. } elsif ($tag eq 'Prefixes') {
  627. $prefix = $body;
  628. } elsif ($tag eq 'rdf:li' || $tag eq 'resource') {
  629. if ($section eq 'prereq') {
  630. push(@prereq, $body);
  631. } elsif ($section eq 'bprereq') {
  632. push(@bprereq, $body);
  633. } elsif ($section eq 'provides') {
  634. push(@provides, $body);
  635. } elsif ($section eq 'conflicts') {
  636. push(@conflicts, $body);
  637. } elsif ($section eq 'source') {
  638. push(@source, $body);
  639. } elsif ($section eq 'nosource') {
  640. push(@nosource, $body);
  641. }
  642. } elsif ($tag eq '/rdf:Description') {
  643. if (defined $href &&
  644. defined $name &&
  645. defined $version &&
  646. defined $release) {
  647. @provides = map {
  648. depends2provides(parse_depends($_))
  649. } @provides;
  650. %options = map {
  651. ( $_->{with} => $_->{version} )
  652. } grep {
  653. defined $_->{with}
  654. } @provides;
  655. unless (grep($_->{name} eq $name, @provides)) {
  656. push(@provides, {
  657. name => $name,
  658. version => $version,
  659. release => $release
  660. });
  661. }
  662. $rec = {
  663. href => (relurl($url, undef, $href))[0],
  664. name => $name,
  665. version => $version,
  666. release => $release,
  667. depends => depend_list([ @bprereq ]),
  668. keeps => depend_list([ @prereq ]),
  669. conflicts => [ @conflicts ],
  670. source => [ @source ],
  671. nosource => [ @nosource ],
  672. desc => $desc,
  673. platform => $platform,
  674. prefix => $prefix
  675. };
  676. $rec->{OPTIONS} =
  677. %options
  678. ? { %options }
  679. : parse_options($rec->{desc});
  680. foreach (@provides) {
  681. push(@{$map->{$_->{name}}->{vs($_)}}, $rec);
  682. }
  683. }
  684. $href = undef;
  685. }
  686. }
  687. return \@include;
  688. }
  689. sub xml_parser ($$$$) {
  690. my($fh, $url, $with, $map) = @_;
  691. my(@include);
  692. my($xml,$desc,$sub);
  693. my($provides,@provides,%options,$rec);
  694. my($href,$name,$version,$release);
  695. print "# using XML parser\n";
  696. $xml = XML::Simple::XMLin($fh, forcearray => 1);
  697. $desc = $xml->{'Repository'}->[0]->{'rdf:Description'};
  698. $sub = $xml->{'Repository'}->[0]->{'Repository'};
  699. foreach (@$desc) {
  700. $href = $_->{'href'};
  701. $name = xel($_->{'Name'});
  702. $version = xel($_->{'Version'});
  703. $release = xel($_->{'Release'});
  704. next unless defined $href &&
  705. defined $name &&
  706. defined $version &&
  707. defined $release;
  708. $provides = $_->{'Provides'}->[0]->{'rdf:bag'}->[0];
  709. if ($provides->{'rdf:li'}) {
  710. $provides = $provides->{'rdf:li'};
  711. } else {
  712. $provides = $provides->{'resource'};
  713. }
  714. @provides = map {
  715. depends2provides(parse_depends($_))
  716. } @$provides;
  717. %options = map {
  718. ( $_->{with} => $_->{version} )
  719. } grep {
  720. defined $_->{with}
  721. } @provides;
  722. unless (grep($_->{name} eq $name, @provides)) {
  723. push(@provides, {
  724. name => $name,
  725. version => $version,
  726. release => $release
  727. });
  728. }
  729. $rec = {
  730. href => (relurl($url, undef, $href))[0],
  731. name => $name,
  732. version => $version,
  733. release => $release,
  734. platform => xel($_->{'Platform'}),
  735. prefix => xel($_->{'Prefixes'}),
  736. depends => depend_list(with_list($_->{'BuildPreReq'}, $with)),
  737. keeps => depend_list(with_list($_->{'PreReq'}, $with)),
  738. conflicts => with_list($_->{'Conflicts'}, $with),
  739. source => with_list($_->{'Source'}, $with),
  740. nosource => with_list($_->{'NoSource'}, $with),
  741. desc => xel($_->{'Description'})
  742. };
  743. $rec->{OPTIONS} =
  744. %options
  745. ? { %options }
  746. : parse_options($rec->{desc});
  747. foreach (@provides) {
  748. push(@{$map->{$_->{name}}->{vs($_)}}, $rec);
  749. }
  750. }
  751. if ($sub) {
  752. @include = map { $_->{href} } @$sub;
  753. }
  754. return \@include;
  755. }
  756. sub open_index ($$) {
  757. my($url, $fn) = @_;
  758. my($fetch,$bzip2,$path);
  759. $fetch = defined $fn ? $fn : $url;
  760. $bzip2 = $RPM;
  761. $bzip2 =~ s/bin\/rpm$/lib\/openpkg\/bzip2/
  762. or die "FATAL: cannot deduce bzip2 path from $RPM\n";
  763. $fetch !~ /\.bz2$/ || -x $bzip2
  764. or die "FATAL: $bzip2 not found\n";
  765. if ($fetch =~ /^\w+:/) { # looks like URL scheme
  766. print "# curling index $fetch\n";
  767. if ($fetch =~ /\.bz2$/) {
  768. $path = "$CURL -q -s -o - \"$fetch\" | $bzip2 -dc |";
  769. } else {
  770. $path = "$CURL -q -s -o - \"$fetch\" |";
  771. }
  772. } else {
  773. print "# reading index file $fn\n";
  774. if ($fetch =~ /\.bz2$/) {
  775. $path = "$bzip2 -dc $fetch |";
  776. } else {
  777. $path = "< $fetch";
  778. }
  779. }
  780. open(RFH, $path) or
  781. die "FATAL: cannot open '$fetch' ($!)\n";
  782. }
  783. #
  784. # fetch index from file or URL
  785. # recursively fetch sub-indexes
  786. #
  787. sub get_index ($$$$) {
  788. my($url,$fn,$with,$noxml) = @_;
  789. my(%map,$include);
  790. open_index($url,$fn);
  791. unless ($noxml) {
  792. eval {
  793. require XML::Simple;
  794. };
  795. $noxml = 1 if $@;
  796. }
  797. if ($noxml) {
  798. $include = simple_text_parser(\*RFH, $url, $with, \%map);
  799. } else {
  800. $include = xml_parser(\*RFH, $url, $with, \%map);
  801. }
  802. close(RFH)
  803. or die "FATAL: an I/O error occured\n";
  804. #
  805. # cannot do real recursions on file handles, so we simply append
  806. # all sub-RDFs, the result is flattend into a big hash anyway
  807. #
  808. foreach (@$include) {
  809. my($submap);
  810. my($suburl,$subfn) = relurl($url,$fn,$_);
  811. $submap = get_index($suburl,$subfn,$with,$noxml);
  812. while (my($name,$vmap) = each %$submap) {
  813. while (my($vs,$recs) = each %$vmap) {
  814. push @{$map{$name}->{$vs}}, @$recs;
  815. }
  816. }
  817. }
  818. return \%map;
  819. }
  820. ############################################################################
  821. #
  822. # grep all versions of a name that
  823. # satisfy a condition
  824. #
  825. sub get_versions ($$) {
  826. my($relmap, $cond) = @_;
  827. return grep { $cond->($_); }
  828. sort { vcmp($a,$b); } keys %$relmap;
  829. }
  830. #
  831. # there can be multiple sources for a target release
  832. #
  833. sub chose_source ($$@) {
  834. my($env, $name, $vmap, @vers) = @_;
  835. my(@recs,@nrecs,$rec);
  836. return unless @vers;
  837. @recs = grep {
  838. $env->{sourceonly} ? (
  839. !(defined $_->{'prefix'})
  840. ) : (
  841. !(defined $_->{'prefix'}) || (
  842. defined $_->{'platform'} &&
  843. $_->{'platform'} eq $env->{config}->{platform} &&
  844. $_->{'prefix'} eq $env->{config}->{prefix}
  845. )
  846. )
  847. } map { @{$vmap->{$_}} } @vers;
  848. return unless @recs;
  849. if (scalar(@recs) > 1) {
  850. @nrecs = grep {
  851. $env->{built}->{$_->{name}} ||
  852. $env->{installed}->{$_->{name}}
  853. } @recs;
  854. @recs = @nrecs if @nrecs;
  855. }
  856. if (scalar(@recs) > 1 && !$env->{sourceonly}) {
  857. @nrecs = grep {
  858. defined $_->{'platform'}
  859. } @recs;
  860. @recs = @nrecs if @nrecs;
  861. }
  862. if (scalar(@recs) > 1) {
  863. print "# ambigous sources for $name\n";
  864. my($i) = 0;
  865. foreach (@recs) {
  866. print "# $i: ".vsn($_)." = $_->{href}\n";
  867. $i++;
  868. }
  869. return;
  870. } else {
  871. if ($env->{upgrade}) {
  872. $rec = $recs[-1];
  873. } else {
  874. $rec = $recs[0];
  875. }
  876. }
  877. print "# source for $name is ".vsn($rec)."\n";
  878. return $rec;
  879. }
  880. #
  881. # see wether target is in map
  882. #
  883. sub target_exists ($$) {
  884. my($target, $map) = @_;
  885. my($vmap) = $map->{$target->{name}};
  886. return unless $vmap;
  887. return !defined $target->{version} ||
  888. defined $vmap->{vs($target)};
  889. }
  890. #
  891. # find target in map
  892. #
  893. sub find_target ($$) {
  894. my($name, $map) = @_;
  895. my($vmap) = $map->{$name};
  896. my(@vs);
  897. return unless $vmap;
  898. @vs = sort { vcmp($b,$a) } keys %$vmap;
  899. return $vmap->{$vs[0]}->[-1];
  900. }
  901. #
  902. # see wether target has conflicts in map
  903. #
  904. sub target_conflicts ($$) {
  905. my($target, $map) = @_;
  906. my($t);
  907. foreach (@{$target->{conflicts}}) {
  908. $t = find_target($_, $map);
  909. return $t if $t;
  910. }
  911. return;
  912. }
  913. #
  914. # retrieve build dependencies for target in map
  915. #
  916. sub target_depends ($$) {
  917. my($target, $map) = @_;
  918. my($vmap,$vers);
  919. die "FATAL: ",vsn($target)," not in depend map\n"
  920. unless
  921. ( $vmap = $map->{$target->{name}} ) &&
  922. ( defined $target->{version} ) &&
  923. ( $vers = $vmap->{vs($target)} ) &&
  924. @$vers;
  925. return $vers->[0]->{depends};
  926. }
  927. #
  928. # retrieve runtime dependencies for target in map
  929. #
  930. sub target_keeps ($$) {
  931. my($target, $map) = @_;
  932. my($vmap,$vers);
  933. die "FATAL: ",vsn($target)," not in keep map\n"
  934. unless
  935. ( $vmap = $map->{$target->{name}} ) &&
  936. ( defined $target->{version} ) &&
  937. ( $vers = $vmap->{vs($target)} ) &&
  938. @$vers;
  939. return $vers->[0]->{keeps};
  940. }
  941. #
  942. # strip doubles from depend/keep lists
  943. # and a return a map name => depend/keep
  944. #
  945. sub unique_map {
  946. my(%out);
  947. foreach (@_) {
  948. foreach (@$_) {
  949. $out{$_->{name}} = $_;
  950. }
  951. }
  952. return %out;
  953. }
  954. #
  955. # test wether target could be upgraded
  956. #
  957. sub target_newer ($$) {
  958. my($target, $map) = @_;
  959. my($vs) = vs($target);
  960. my($vmap) = $map->{$target->{name}};
  961. return 1 unless $vmap;
  962. return !grep { vcmp($vs, $_) <= 0; } keys %$vmap;
  963. }
  964. #
  965. # check wether installed package matches
  966. # build options
  967. #
  968. sub target_suitable ($$) {
  969. my($target, $with) = @_;
  970. my($iwith);
  971. my($k,$v);
  972. $iwith = $target->{OPTIONS};
  973. while (($k,$v) = each %$with) {
  974. if (exists $iwith->{$k}) {
  975. return 0 if $iwith->{$k} ne $with->{$k};
  976. }
  977. }
  978. return 1;
  979. }
  980. #
  981. # record target status
  982. #
  983. sub target_setstatus ($$$) {
  984. my($target, $status, $pri) = @_;
  985. if ($pri > $target->{STATUSPRI}) {
  986. $target->{STATUSPRI} = $pri;
  987. $target->{STATUS} = $status;
  988. }
  989. }
  990. #
  991. # report options that are not used for
  992. #
  993. sub warn_about_options ($$$) {
  994. my($target, $with, $c) = @_;
  995. my($iwith) = $target->{OPTIONS};
  996. my($k,$v);
  997. return unless defined $iwith;
  998. while (($k,$v) = each %$with) {
  999. if (!exists $iwith->{$k} && $k !~ $c->{optreg}) {
  1000. print "# ATTENTION: $target->{name} ignores option '$k'\n";
  1001. }
  1002. }
  1003. }
  1004. ############################################################################
  1005. #
  1006. # LOGIC
  1007. #
  1008. #
  1009. # locate target for a dependency
  1010. #
  1011. sub dep2target ($$) {
  1012. my($dep, $env) = @_;
  1013. my($name,$op,@vers);
  1014. my($i,$r,$b,$cond,$version);
  1015. my($t,$tdef);
  1016. ($name, $op, $version) = ($dep->{name}, $dep->{op}, $dep->{val});
  1017. $i = $env->{installed}->{$name};
  1018. $r = $env->{repository}->{$name};
  1019. $b = $env->{built}->{$name};
  1020. return unless $i || $r || $b;
  1021. if (!defined $op) {
  1022. $cond = sub { 1; };
  1023. } elsif ($op eq 'geq') {
  1024. $cond = sub { vcmp($_[0],$version) >= 0; };
  1025. } elsif ($op eq 'leq') {
  1026. $cond = sub { vcmp($_[0],$version) <= 0; };
  1027. } elsif ($op eq 'gt') {
  1028. $cond = sub { vcmp($_[0],$version) > 0; };
  1029. } elsif ($op eq 'lt') {
  1030. $cond = sub { vcmp($_[0],$version) < 0; };
  1031. } elsif ($op eq 'equ') {
  1032. $cond = sub { vcmp($_[0],$version) == 0; };
  1033. } else {
  1034. die "FATAL: internal error in dep2target\n";
  1035. }
  1036. $tdef = undef;
  1037. if ($i && (@vers = get_versions($i, $cond))) {
  1038. foreach (@vers) {
  1039. $t = $i->{$_}->[0];
  1040. get_with($t);
  1041. if (target_suitable($t, $env->{with})) {
  1042. $tdef = $t;
  1043. unless ($env->{upgrade}) {
  1044. return ($t, 1);
  1045. }
  1046. }
  1047. }
  1048. }
  1049. if ($b && (@vers = get_versions($b, $cond))) {
  1050. return ($b->{$vers[0]}->[0], 1);
  1051. }
  1052. $t = chose_source($env, $name, $r, get_versions($r, $cond));
  1053. if ($t) {
  1054. if (!$tdef ||
  1055. ($env->{upgrade} && target_newer($t, $env->{installed}))) {
  1056. return ($t, 0);
  1057. }
  1058. }
  1059. if ($tdef) {
  1060. return ($tdef, 1);
  1061. }
  1062. return;
  1063. }
  1064. #
  1065. #
  1066. #
  1067. sub make_dep ($$$$$$) {
  1068. my($target,$depth,$env,$list,$blist,$clist) = @_;
  1069. my($d,$k,%d,%k,$t,$old);
  1070. my(@deps,$conflict);
  1071. if (target_exists($target, $env->{built})) {
  1072. print "# $target->{name} is already in list\n";
  1073. return;
  1074. }
  1075. if ($t = target_conflicts($target, $env->{installed})) {
  1076. target_setstatus($target,'CONFLICT',4);
  1077. push(@$clist,$target);
  1078. print "# $target->{name} conflicts with ",vsn($t),"\n";
  1079. return;
  1080. }
  1081. if ($t = target_conflicts($target, $env->{built})) {
  1082. target_setstatus($target,'CONFLICT',4);
  1083. push(@$clist,$target);
  1084. print "# $target->{name} conflicts with ",vsn($t),"\n";
  1085. return;
  1086. }
  1087. #
  1088. # see if a target is already installed and requires a rebuild
  1089. #
  1090. if ($t = find_target($target->{name}, $env->{installed})) {
  1091. if (exists $env->{exclude}->{$target->{name}}) {
  1092. print "# excluding $target->{name} (no upgrade allowed)\n";
  1093. return;
  1094. }
  1095. # pull in options
  1096. get_with($t);
  1097. if ($target->{REBUILD}) {
  1098. target_setstatus($target,'DEPEND',1);
  1099. print "# rebuilding $target->{name} (dependency)\n";
  1100. } elsif ($env->{zero}) {
  1101. target_setstatus($target,'ZERO',1);
  1102. print "# rebuilding $target->{name} (zero)\n";
  1103. } elsif (target_newer($target, $env->{installed})) {
  1104. target_setstatus($target,'UPGRADE',3);
  1105. print "# rebuilding $target->{name} (upgrade)\n";
  1106. } elsif (!target_suitable($t, $env->{with})) {
  1107. target_setstatus($target,'MISMATCH',2);
  1108. print "# rebuilding $target->{name} (parameter mismatch)\n";
  1109. } else {
  1110. print "# $target->{name} is already installed\n";
  1111. return;
  1112. }
  1113. # use options from installed base
  1114. override_options(get_with($target), get_with($t),
  1115. $env->{config}->{optreg});
  1116. # remember this is a rebuild for a proxy package
  1117. $target->{PROXY} = $t->{PROXY};
  1118. $target->{REBUILD} = 1;
  1119. } else {
  1120. target_setstatus($target,'ADD',3);
  1121. }
  1122. if (exists $env->{exclude}->{$target->{name}}) {
  1123. die "FATAL: target ".vsn($target)." is forbidden\n";
  1124. }
  1125. # mark this as a target before reverse dependencies trigger
  1126. # it again
  1127. push(@{$env->{built}->{$target->{name}}->{vs($target)}}, $target);
  1128. $d = target_depends($target, $env->{repository});
  1129. $k = target_keeps($target, $env->{repository});
  1130. #
  1131. # recurse over dependencies
  1132. #
  1133. if (@$d || @$k) {
  1134. %d = unique_map($d, $k);
  1135. %k = unique_map($k);
  1136. @deps = ();
  1137. $conflict = 0;
  1138. foreach (keys %d) {
  1139. # old index misses a OpenPKG provider in the index... skip it
  1140. next if $_ eq 'OpenPKG';
  1141. ($t,$old) = dep2target($d{$_}, $env);
  1142. if ($t) {
  1143. if ($old) {
  1144. print "# $target->{name} uses ".vsn($t)." for $_\n";
  1145. next;
  1146. }
  1147. # record which targets to keep in blist
  1148. if ($k{$_}) {
  1149. push @$blist,$t;
  1150. print "# $target->{name} installs ".vsn($t)." for $_\n";
  1151. } else {
  1152. print "# $target->{name} requires ".vsn($t)." for $_\n";
  1153. }
  1154. push @deps, $t;
  1155. } else {
  1156. print "# $target->{name} searches a frood called '$_'\n";
  1157. push(@{$env->{fatal}},vsn($target));
  1158. target_setstatus($target,'UNDEF',4);
  1159. push @$clist, $target;
  1160. $conflict = 1;
  1161. }
  1162. }
  1163. unless ($conflict) {
  1164. foreach $t (@deps) {
  1165. make_dep($t,$depth+1,$env,$list,$blist,$clist);
  1166. }
  1167. }
  1168. }
  1169. print "# adding ".vsn($target)." to list\n";
  1170. push(@$list, $target);
  1171. foreach (@{$target->{nosource}}) {
  1172. print "# ATTENTION: unpackaged source $_: $target->{source}->[$_]\n";
  1173. }
  1174. #
  1175. # a dependency could not be resolved, don't bother with reverse
  1176. # dependencies for this target
  1177. #
  1178. return if $conflict;
  1179. if (!$env->{quick} &&
  1180. $target->{name} ne 'openpkg' &&
  1181. $target->{REBUILD}) {
  1182. unless ($env->{revdep}) {
  1183. $env->{revdep} = get_revdep($env);
  1184. }
  1185. foreach $t (@{$env->{revdep}->{$target->{name}}}) {
  1186. # this is a rebuild, triggering further revdeps
  1187. $t->{REBUILD} = 1;
  1188. # this is a rebuild, keep this installed
  1189. push(@$blist, $t);
  1190. print "# rebuilding revdep ".vsn($t)."\n";
  1191. make_dep($t,$depth+1,$env,$list,$blist,$clist);
  1192. }
  1193. }
  1194. }
  1195. #
  1196. # generate build lists for targets matched by pattern
  1197. #
  1198. # all input and output is passed in 'env' hash
  1199. #
  1200. sub build_list ($$) {
  1201. my($pattern, $env) = @_;
  1202. my(@goals,@targets,@keeps,@conflicts,@bonly,$t);
  1203. my($name,$r,$i,@vers);
  1204. my(@todo,%keep);
  1205. #
  1206. # handle various patterns
  1207. #
  1208. if (defined $pattern) {
  1209. @todo = ();
  1210. foreach (split(/\s+/,$pattern)) {
  1211. next unless /\S/;
  1212. if (s/\*+$//) {
  1213. push @todo, '^'.quotemeta($_).'';
  1214. } else {
  1215. push @todo, '^'.quotemeta($_).'$';
  1216. }
  1217. }
  1218. @todo = map {
  1219. my($p) = $_;
  1220. grep(/$p/, keys %{$env->{repository}})
  1221. } @todo;
  1222. } else {
  1223. #
  1224. # undefined pattern means -a option that selects
  1225. # all packages from repository that are installed
  1226. #
  1227. @todo = grep {
  1228. my($n) = $_;
  1229. (ref $env->{installed}->{$n}) &&
  1230. grep { $_ ne '-' } keys %{$env->{installed}->{$n}}
  1231. } keys %{$env->{repository}};
  1232. }
  1233. #
  1234. # chose sources for goals from repository
  1235. #
  1236. foreach $name (@todo) {
  1237. $t = undef;
  1238. #
  1239. # keeping installed packages for goals is ugly
  1240. # -> we currently do not support installed source RPMs
  1241. # -> source RPMs might already have expired from repository
  1242. #
  1243. # consequence:
  1244. # -> goals are always upgraded to repository versions
  1245. #
  1246. #unless ($env->{upgrade}) {
  1247. # $i = $env->{installed}->{$name};
  1248. # if (@vers = get_versions($i, sub { 1; })) {
  1249. # $t = chose_source($env, $name, $i, @vers);
  1250. # }
  1251. #}
  1252. unless ($t) {
  1253. $r = $env->{repository}->{$name};
  1254. if (@vers = get_versions($r, sub { 1; })) {
  1255. $t = chose_source($env, $name, $r, @vers);
  1256. }
  1257. }
  1258. if ($t) {
  1259. warn_about_options($t, $env->{with}, $env->{config});
  1260. push(@goals, $t);
  1261. } else {
  1262. if ($env->{status}) {
  1263. print "# dropping goal '$name'\n";
  1264. } else {
  1265. die "FATAL: cannot find source for '$name'\n";
  1266. }
  1267. }
  1268. }
  1269. return unless @goals;
  1270. @targets = ();
  1271. @keeps = @goals;
  1272. foreach $t (@goals) {
  1273. print "# recursing over dependencies for ".vsn($t)."\n";
  1274. make_dep($t,0,$env,\@targets,\@keeps,\@conflicts);
  1275. }
  1276. %keep = map { $_ => 1 } @keeps;
  1277. @bonly = grep {
  1278. !$keep{$_} && !$env->{installed}->{$_->{name}}->{vs($_)};
  1279. } @targets;
  1280. return (\@targets, \@bonly, \@conflicts);
  1281. }
  1282. #######################################################################
  1283. #
  1284. # OUTPUT
  1285. #
  1286. #
  1287. # compute path to binary RPM from rpm config and target data
  1288. #
  1289. sub target2rpm ($$) {
  1290. my($target,$c) = @_;
  1291. my($tmpl) = $c->{template};
  1292. my($popt) = $target->{PROXY} ? '+PROXY' : '';
  1293. $tmpl =~ s/%{NAME}/$target->{name}/;
  1294. $tmpl =~ s/%{VERSION}/$target->{version}/;
  1295. $tmpl =~ s/%{RELEASE}/$target->{release}$popt/;
  1296. return $c->{rpmdir}.'/'.$tmpl;
  1297. }
  1298. #
  1299. # compute new target based on old target augmented with options from
  1300. # a binary RPM file
  1301. #
  1302. sub binary_target ($$) {
  1303. my($t, $fn) = @_;
  1304. my(%target) = %$t;
  1305. # pull in options from binary RPM file
  1306. get_with(\%target, $fn);
  1307. return \%target;
  1308. }
  1309. #
  1310. # return path to master package for a proxy package
  1311. #
  1312. sub find_proxy ($$) {
  1313. my($t,$bpkg) = @_;
  1314. my(@l) = `$RPM_NPRIV -ql $t->{name}`;
  1315. my($link) = (grep { $_ =~ /\/\.prefix-$t->{name}$/ } @l)[0];
  1316. return unless defined $link;
  1317. chomp $link;
  1318. my($prefix) = readlink($link);
  1319. return unless defined $prefix;
  1320. $bpkg =~ s/.*\///;
  1321. $bpkg =~ s/\+PROXY(\.[^-]+-[^-]+)-[^-]+\.rpm$/$1-*.rpm/;
  1322. return (glob("$prefix/RPM/PKG/$bpkg"))[0];
  1323. }
  1324. #
  1325. # merge parameters from installed package
  1326. # with new parameter set and global parameters
  1327. # from configuration
  1328. #
  1329. # then map the result to --define command line arguments
  1330. # suitable for rpm
  1331. #
  1332. sub make_defines ($$$) {
  1333. my($old, $new, $c) = @_;
  1334. my($with);
  1335. #
  1336. # override old parameters with new parameters
  1337. # drop new parameters that do not exist in old set
  1338. #
  1339. # if there is no old set at all (which happens if there
  1340. # is no template and no installed package), just use the
  1341. # new parameters and assume these are useful.
  1342. #
  1343. if ($old) {
  1344. $old = { %$old };
  1345. override_options($old, $new, $c->{optreg});
  1346. } else {
  1347. $old = $new;
  1348. }
  1349. #
  1350. # convert parameters to --define command line options
  1351. # skip parameter templates from index
  1352. #
  1353. $with = join(' ',map { "--define '$_ $old->{$_}'" }
  1354. sort grep { $old->{$_} !~ /^%/ } keys %$old);
  1355. $with = ' '.$with if $with ne '';
  1356. return $with;
  1357. }
  1358. #
  1359. # print commands from package build list
  1360. #
  1361. # c -> configuration to derive paths from
  1362. # uncond -> always do the --rebuild
  1363. # with -> parameter set passed to build tool
  1364. # ignore -> generate script that does not stop on error
  1365. #
  1366. sub print_list1 ($$$@$) {
  1367. my($list,$c,$uncond,$with,$ignore) = @_;
  1368. my($spkg,$bpkg,$ppkg);
  1369. my($opt);
  1370. my($cmd1, $cmd2, $mark);
  1371. $mark = '::::';
  1372. foreach (@$list) {
  1373. $spkg = $_->{href};
  1374. $bpkg = target2rpm($_, $c);
  1375. #
  1376. # rebuild binary package IF
  1377. #
  1378. # 'unconditional' option
  1379. # OR there is no binary package
  1380. # OR dependency check found that installed package is not suitable
  1381. # OR existing binary package doesn't satisfy wanted options
  1382. #
  1383. $cmd1 = undef;
  1384. if ($uncond || !-f $bpkg || $_->{REBUILD} ||
  1385. !target_suitable(binary_target($_, $bpkg),$with)) {
  1386. $opt = make_defines($_->{OPTIONS}, $with, $c);
  1387. #
  1388. # proxy packages are rebuilt from their maste
  1389. # hierachy
  1390. #
  1391. # someone preferred a binary from the repository
  1392. # just copy it to the local store
  1393. #
  1394. if ($_->{PROXY}) {
  1395. $ppkg = find_proxy($_,$bpkg) or
  1396. die "FATAL: proxy package ",vsn($_)," does not exist\n";
  1397. #
  1398. # rpm doesn't support additional parameters to the
  1399. # mkproxy script
  1400. # $cmd1 = "$RPM_NPRIV$opt --makeproxy $ppkg -- -o $bpkg";
  1401. #
  1402. $cmd1 = "( cd $c->{rpmdir} && $RPM_NPRIV$opt --makeproxy $ppkg )";
  1403. } elsif (defined $_->{prefix}) {
  1404. $cmd1 = "$CURL -q -s -o $bpkg $spkg";
  1405. } else {
  1406. $cmd1 = "$RPM_NPRIV$opt --rebuild $spkg";
  1407. }
  1408. }
  1409. #
  1410. # if package exist force rpm to copy over new files
  1411. # better than erasing everything and losing configuration
  1412. # files
  1413. #
  1414. $opt = $_->{REBUILD} ? ' --force' : '';
  1415. $cmd2 = "$RPM_PRIV$opt -Uvh $bpkg";
  1416. if ($ignore) {
  1417. $cmd2 = "$cmd1 && \\\n$cmd2" if defined $cmd1;
  1418. } else {
  1419. if (defined $cmd1) {
  1420. $cmd2 = "$cmd1 || exit \$?\n$cmd2 || exit \$?"
  1421. } else {
  1422. $cmd2 = "$cmd2 || exit \$?";
  1423. }
  1424. }
  1425. print "echo $mark $spkg $mark\n$cmd2\necho $mark $spkg = \$? $mark\n";
  1426. }
  1427. }
  1428. #
  1429. # print commands for the temporary package list
  1430. #
  1431. # temporary packages are only used for building other packages
  1432. # and are removed when everything is done
  1433. #
  1434. sub print_list2 ($$) {
  1435. my($list,$c) = @_;
  1436. my($pkg);
  1437. foreach (@$list) {
  1438. $pkg = "$_->{name}-$_->{version}-$_->{release}";
  1439. print "$RPM_PRIV -e $pkg\n";
  1440. }
  1441. }
  1442. #
  1443. # instead of printing a command list, print a status map
  1444. # that shows all packages and how the build process would
  1445. # change their status
  1446. #
  1447. sub print_status ($$$$$) {
  1448. my($installed,$repository,$list,$bonly,$clist) = @_;
  1449. my(%bonly) = map { $_ => 1 } @$bonly;
  1450. my(%map,$n,@names,$t);
  1451. my($old,$tag,$new);
  1452. foreach (@$list, @$clist) {
  1453. next unless defined $_->{release};
  1454. $map{$_->{name}} = {
  1455. rel => "$_->{version}-$_->{release}",
  1456. status => $_->{STATUS}
  1457. };
  1458. }
  1459. foreach (@$bonly) {
  1460. $map{$_->{name}} = {
  1461. rel => "$_->{version}-$_->{release}",
  1462. status => 'TEMP'
  1463. };
  1464. }
  1465. @names = keys %map;
  1466. foreach $n (keys %$installed) {
  1467. next if $n =~ /::/;
  1468. next if exists $map{$n};
  1469. next unless grep { $_ ne '-' } keys %{$installed->{$n}};
  1470. $map{$n}->{'status'} = 'OK';
  1471. push @names,$n;
  1472. }
  1473. foreach $n (keys %$repository) {
  1474. next if $n =~ /::/;
  1475. next if exists $map{$n};
  1476. next unless grep { $_ ne '-' } keys %{$repository->{$n}};
  1477. $t = find_target($n, $repository);
  1478. $map{$n}->{'status'} = 'NEW';
  1479. $map{$n}->{'rel'} = vs($t);
  1480. push @names,$n;
  1481. }
  1482. foreach $n (sort @names) {
  1483. $old = join ',',
  1484. map { "$n-$_" }
  1485. sort
  1486. grep { $_ ne '-' }
  1487. keys %{$installed->{$n}};
  1488. $old = $n if $old eq '';
  1489. $tag = $map{$n}->{status};
  1490. $new = defined $map{$n}->{rel} ? " $n-$map{$n}->{rel}" : '';
  1491. printf "%-35s %-8s%s\n", $old, $tag, $new;
  1492. }
  1493. }
  1494. #######################################################################
  1495. my($config,$url,$repository,$installed,$env,$list,$bonly,$clist);
  1496. my($pattern,%with,%exclude);
  1497. if ($opt_a) {
  1498. $pattern = undef;
  1499. } else {
  1500. $pattern = join(' ', @ARGV);
  1501. }
  1502. if ($opt_A) {
  1503. $pattern = '*';
  1504. }
  1505. %with = map {
  1506. /([^\s=]+)(?:\=(\S+))?/
  1507. ? ($1 => (defined $2 ? $2 : 'yes'))
  1508. : ()
  1509. } split(/\s+/, $opt_D);
  1510. %exclude = map { $_ => 1 } split(/\s+/, $opt_E);
  1511. $config = get_config();
  1512. if (defined $opt_p) {
  1513. $config->{platform} = $opt_p;
  1514. }
  1515. if (defined $opt_r) {
  1516. $url = $opt_r;
  1517. $url .= '/' unless $url =~ /\/$/;
  1518. } else {
  1519. $url = get_release();
  1520. }
  1521. # if we read the index from a file we can no longer deduce
  1522. # repository paths from index paths. For now lets assume
  1523. # that everything is below SRC/ to be compatible with
  1524. # existing file indexes.
  1525. if (defined $opt_f && !defined $opt_r) {
  1526. $url .= 'SRC/';
  1527. }
  1528. $installed = $opt_Z ? {} : get_installed();
  1529. $repository = get_index($url.'00INDEX.rdf',$opt_f,\%with,$opt_X);
  1530. $env = {
  1531. config => $config,
  1532. installed => $installed,
  1533. repository => $repository,
  1534. built => {},
  1535. revdep => undef,
  1536. with => \%with,
  1537. exclude => \%exclude,
  1538. upgrade => ($opt_a || $opt_U),
  1539. zero => ($opt_z || $opt_Z),
  1540. quick => $opt_q,
  1541. status => ($opt_s || $opt_S),
  1542. fatal => [],
  1543. sourceonly => ($opt_u ||
  1544. $opt_U ||
  1545. $opt_z ||
  1546. $opt_Z ||
  1547. scalar(%with) > 0 )
  1548. };
  1549. ($list,$bonly,$clist) = build_list($pattern, $env);
  1550. die "FATAL: cannot find package\n" unless defined $list;
  1551. if ($opt_S) {
  1552. print_status($installed,$repository,$list,$bonly,$clist);
  1553. } elsif ($opt_s) {
  1554. print_status($installed,{},$list,$bonly,$clist);
  1555. } else {
  1556. if (@{$env->{fatal}}) {
  1557. die "FATAL errors occured while building:\n",
  1558. join (',', @{$env->{fatal}}),
  1559. "\n";
  1560. }
  1561. print_list1($list,$config,$opt_a || $opt_u || $opt_U,\%with,$opt_i);
  1562. print_list2($bonly,$config);
  1563. }