openpkg-build.pl 54 KB

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