autoupdate.pl 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823
  1. #! /usr/bin/perl -w
  2. # $Id: autoupdate.pl,v 1.25 2009/09/20 08:21:58 markus Exp $
  3. # Copyright (c) 2007,2008,2009
  4. # Markus Hennecke <markus-hennecke@markus-hennecke.de>
  5. #
  6. # Permission to use, copy, modify, and/or distribute this software for any
  7. # purpose with or without fee is hereby granted, provided that the above
  8. # copyright notice and this permission notice appear in all copies.
  9. #
  10. # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  11. # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  12. # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  13. # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  14. # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  15. # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  16. # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  17. use strict;
  18. use warnings;
  19. use Cwd;
  20. use File::Temp qw/tempfile tempdir/;
  21. use List::Util qw/reduce/;
  22. use OpenBSD::PackageName;
  23. use Getopt::Long;
  24. use FindBin;
  25. # Silence the warning that is issued because List::Util won't register
  26. # $a and $b in a correct way for us
  27. $a = $a;
  28. $b = $b;
  29. # Defaults to 1, can be set via the --verbose switch
  30. my $verbose = 1;
  31. # If set logs of the build are written under this directory
  32. my $logdir = undef;
  33. # If set it will show the usage information and exit afterwards
  34. my $show_help = 0;
  35. # If set to more than one it will try to build ports in parallel
  36. my $num_jobs = 1;
  37. # If defined via command line the script will use that file as input for
  38. # out of date ports. If the filename is '-' it will use stdin.
  39. my $out_of_date = undef;
  40. # The default location of the ports tree and the path where we should look
  41. # for ports. TODO: Read these variables from /etc/mk.conf and fall back
  42. # to the default only if we do not find it either there or in the environment.
  43. my $portsdir = $ENV{PORTSDIR} || '/usr/ports';
  44. my $portsdir_path = $ENV{PORTSDIR_PATH} || "$portsdir:$portsdir/mystuff";
  45. # Number of jobs currently active
  46. my $jobs = 0;
  47. # This hash will hold the PIDs of the forked build processes
  48. my %forked_builds = ();
  49. # Regex used to match a flavor
  50. my $regex_flavor = "((,[a-z][a-z0-9_]*)*)";
  51. # Regex used to match a subpackage
  52. my $regex_subpkg = "(,-[a-z][a-z0-9_-]*)?";
  53. # List of pseudo flavors we apply if the port supports them
  54. my %pseudo_flavors = ();
  55. # List of bad behaving ports that need libtool set to the in-tree libtool
  56. my %libtool_ports = ();
  57. # Global abort flag, only set from the reaper func
  58. my $abort = 0;
  59. # This function will remove the finished forked build from the list of
  60. # currently build ports.
  61. sub REAPER {
  62. my $wpid = wait();
  63. my $status = $?;
  64. $abort = 1 if ($status != 0);
  65. if (exists $forked_builds{$wpid}) {
  66. $jobs--;
  67. if ($status == 0) {
  68. print STDOUT "Finished build of ";
  69. }
  70. else {
  71. print STDOUT "Build aborted of ";
  72. }
  73. print $forked_builds{$wpid} . "\n";
  74. delete $forked_builds{$wpid};
  75. }
  76. }
  77. # Reads a config file and parses the options set in the config file.
  78. # A list of valid options must be passed to the function.
  79. sub read_rc_file {
  80. my $valid_vars = shift;
  81. my ($fh, @lines, %config);
  82. open($fh, '<', "$ENV{HOME}/.autoupdaterc")
  83. or return \%config;
  84. while (<$fh>) {
  85. chomp;
  86. s/\t/ /g;
  87. push @lines, ($_);
  88. }
  89. close($fh);
  90. my $temp_line = '';
  91. my $append = 0;
  92. my $real_lineno = 0;
  93. my $lineno = 0;
  94. foreach (@lines) {
  95. $lineno++;
  96. # Check if we have a multiline statement
  97. if (m/\\$/) {
  98. $append++;
  99. s/\\$//g;
  100. $temp_line .= $_;
  101. next;
  102. }
  103. elsif ($append != 0) {
  104. $_ = $temp_line . $_;
  105. $real_lineno = $lineno - $append;
  106. $append = 0;
  107. $temp_line = '';
  108. }
  109. # Remove comments and unnecessary whitespace
  110. s/\#.*$//g;
  111. s/^[[:space:]]//g;
  112. s/ *= */=/g;
  113. s/ +/ /g;
  114. next if (m/^$/);
  115. # Check if the line has a = character, if not issue an error
  116. if (! m/=/) {
  117. print STDERR "Not a valid config in line "
  118. . $real_lineno . "\n";
  119. return undef;
  120. }
  121. # Split the line at the first '='
  122. my $split_index = index($_, '=');
  123. my $var = substr($_, 0, $split_index);
  124. my $val = substr($_, $split_index + 1);
  125. # Check if the varible is valid
  126. if (defined $valid_vars && defined $valid_vars->{$var}) {
  127. $config{$var} = $val;
  128. }
  129. else {
  130. print STDERR "Warning: Unknown variable '$var' "
  131. . "in config file\n";
  132. }
  133. }
  134. return \%config;
  135. }
  136. # Fill the pseudo flavors hash.
  137. sub setup_lookup_hash {
  138. my $config_line = shift;
  139. my $hash = shift;
  140. my $info = shift;
  141. if (defined $config_line) {
  142. $config_line =~ tr/,/ /;
  143. foreach (split / /, $config_line) {
  144. print STDOUT "Adding $_ to $info\n"
  145. unless ($verbose < 2);
  146. $hash->{$_} = 1;
  147. }
  148. }
  149. }
  150. # Sets up the log directory
  151. sub setup_logging {
  152. my $config = shift;
  153. if (defined $config->{logging} && $config->{logging} == 1) {
  154. $logdir = tempdir('/tmp/autoupdate.XXXXXXXXXX');
  155. my $logfile = "$logdir/autoupdate.pl.log";
  156. open TEE, "| tee $logfile"
  157. or die "Unable to open log file \"$logfile\"\n";
  158. open STDOUT, ">&TEE"
  159. or die "Unable to redirect STDOUT to log file.\n";
  160. open STDERR, ">&TEE"
  161. or die "Unable to redirect STDERR to log file.\n";
  162. print STDOUT "Logging builds in '$logdir'\n";
  163. print STDOUT "Using \"$logfile\" as mainlog\n";
  164. }
  165. }
  166. # Returns the ports version or undef if no such version could be deduced
  167. sub get_ports_version {
  168. my $port = shift;
  169. chdir "$port" || return undef;
  170. my $cmd = 'make show=FULLPKGNAME';
  171. my $pkgname = '';
  172. open(my $in, "$cmd 2>&1 |")
  173. or die "Unable to get version for \"$port\"\n";
  174. while (<$in>) {
  175. chomp;
  176. $pkgname .= $_;
  177. }
  178. close($in);
  179. my ($stem, $version, @flavors)
  180. = OpenBSD::PackageName::splitname($pkgname);
  181. return $version;
  182. }
  183. # Returns an integer according to the version numbers supplied.
  184. # -1 if the first argument is the higher version number
  185. # 0 if both version numbers are equal
  186. # +1 if the second argument is the higher version number
  187. sub get_higher_version {
  188. my ($ver1, $ver2) = @_;
  189. if (! defined($ver1)) {
  190. return 0 if (! defined($ver2));
  191. return 1;
  192. }
  193. elsif (! defined($ver2)) {
  194. return -1;
  195. }
  196. my @vers1 = split /[\.pv]/,$ver1;
  197. my @vers2 = split /[\.pv]/,$ver2;
  198. my $max_len = scalar @vers1;
  199. $max_len = scalar @vers2 if (scalar @vers2 > scalar @vers1);
  200. my $index;
  201. for ($index = 0; $index < $max_len; $index++) {
  202. return -1 if ($index >= scalar @vers2);
  203. return 1 if ($index >= scalar @vers1);
  204. my $vers1 = $vers1[$index];
  205. my $vers2 = $vers2[$index];
  206. my $cmp;
  207. if (($vers1 =~ /[a-z]/) || ($vers2 =~ /[a-z]/)) {
  208. $cmp = ($vers2 cmp $vers1);
  209. }
  210. else {
  211. $cmp = ($vers2 <=> $vers1);
  212. }
  213. return $cmp if ($cmp);
  214. }
  215. return 0;
  216. }
  217. # Creates an array with the name of all packages that need updates
  218. sub read_update_package_list {
  219. my $input = shift;
  220. my ($in, @package_list, $out);
  221. # Save the output from out-of-date in a log file
  222. if (defined $logdir && not defined $input) {
  223. open($out, '>', "$logdir/out-of-date");
  224. print STDERR "Warning: Unable to open log for out-of-date\n"
  225. if (not defined $out);
  226. }
  227. if (not defined $input) {
  228. my $cmd = "env PORTSDIR=\"$portsdir\" "
  229. . "\"$portsdir/infrastructure/build/out-of-date\" ";
  230. unless (open($in, $cmd . '2>/dev/null |')) {
  231. print STDERR "Unable to execute $cmd\n";
  232. exit 1;
  233. }
  234. }
  235. elsif ($input eq '-') {
  236. unless (open($in, "<&=STDIN")) {
  237. print STDERR "Unable to open stdin\n";
  238. exit 1;
  239. }
  240. }
  241. else {
  242. unless (open($in, '<', $input)) {
  243. print STDERR "Unable to open file: " . $input
  244. . " for reading\n";
  245. exit 1;
  246. }
  247. }
  248. while (<$in>) {
  249. print $out $_ if (defined $out);
  250. chomp;
  251. my $pkg = $_;
  252. $pkg =~ s/ +#.*$//g;
  253. push @package_list, ($pkg);
  254. }
  255. close($in);
  256. close($out) if (defined $out);
  257. return \@package_list;
  258. }
  259. # Creates an array with a hash for each port that needs an update
  260. sub create_package_information {
  261. my $pkg_list = shift;
  262. # Create an array of hashes that has the following data:
  263. # category => name of the ports category
  264. # port => name of the port (the directory in the ports tree)
  265. # subdir => version of the port (for ports with more than one
  266. # version, e.q. python)
  267. # flavor => list of flavors as an array reference
  268. # subpackage => name of the subpackage
  269. my @pkg_information = ();
  270. foreach my $pkg (@$pkg_list) {
  271. # Get all the variables from the port identification string
  272. my ($category, $port, $subdir, $flavor, $subpackage);
  273. # Each group in the regex has the following meaning:
  274. # $1 : category main which may consist of the alphabet
  275. # $2 : port name, which must start with at least one alpha
  276. # char and may have a number appended.
  277. # Some examples may be gtk+2, libIDL etc.
  278. # $3 : optional subdirs for the port, $4 will hold the last
  279. # $5 : an optional subpackage name, this will start with a
  280. # comma followed by an hyphen and have an alphanumeric
  281. # identifier
  282. # $6 : a list with optional flavors. Each flavor starts with
  283. # a comma and may be an alphanumeric identifier
  284. if ($pkg =~ m/^([a-z][a-z0-9]*)\/([A-Za-z][\.A-Za-z0-9-+_]*)((\/[a-z0-9._-]*)*)?$regex_subpkg$regex_flavor/) {
  285. $category = $1;
  286. $port = $2;
  287. $subdir = $3;
  288. $subpackage = $5;
  289. $flavor = $6;
  290. $subdir = '' if (not defined $subdir);
  291. $subdir =~ s/(^\/|\/$)//g;
  292. $subpackage = '' if (not defined $subpackage);
  293. $subpackage =~ s/^,//g;
  294. $flavor = '' if (not defined $flavor);
  295. $flavor =~ s/,/ /g;
  296. $flavor =~ s/^ //g;
  297. my %p = (
  298. category => $category,
  299. port => $port,
  300. subdir => $subdir,
  301. subpkg => $subpackage,
  302. flavor => $flavor,
  303. pkg => $pkg
  304. );
  305. $p{dependencies} = create_dependencies_list(\%p);
  306. $p{deppkgs}
  307. = create_dependencies_hash($p{dependencies});
  308. $p{pseudo_flavors} = create_pseudo_flavors_list(\%p);
  309. add_pseudo_flavors(\%p);
  310. push @pkg_information, (\%p);
  311. }
  312. else {
  313. next if ((not defined $pkg) || ($pkg eq ''));
  314. print STDERR "Unknown port name: $pkg\n";
  315. exit 2;
  316. }
  317. print STDOUT "Category:\t$category\n"
  318. . "Port:\t\t$port\n"
  319. . "Subdir:\t\t$subdir\n"
  320. . "Subpackage:\t$subpackage\n"
  321. . "Flavor:\t\t$flavor\n\n" unless ($verbose < 5);
  322. }
  323. return \@pkg_information;
  324. }
  325. # Adds pseudo flavors to the flavors list if they appear both in the
  326. # pseudo_flavors array from the ports info hash and the %pseudo_flavors
  327. # global hash set via command line.
  328. sub add_pseudo_flavors {
  329. my $info = shift;
  330. my $flavors = $info->{flavor};
  331. foreach my $pseudo_flavor (@{$info->{pseudo_flavors}}) {
  332. if (exists $pseudo_flavors{$pseudo_flavor}) {
  333. $flavors .= ' ';
  334. $flavors .= $pseudo_flavor;
  335. }
  336. }
  337. $info->{flavor} = $flavors;
  338. }
  339. # Returns true if the port is listed in the %libtool_ports hash
  340. sub needs_intree_libtool {
  341. my $info = shift;
  342. my $portdesc = $info->{category} . '/' . $info->{port};
  343. return (exists($libtool_ports{$portdesc}));
  344. }
  345. # Creates an array with pseudo flavors. We can check this array against the
  346. # %pseudo_flavors hash to add flavors on demand.
  347. sub create_pseudo_flavors_list {
  348. my $info = shift;
  349. my $cur_dir = getcwd;
  350. my $cmd = 'make show=PSEUDO_FLAVORS';
  351. # chdir into the ports directory
  352. my ($port, $port_dir) = find_newer_ports_dir($info);
  353. chdir $port_dir or die "Unable to change to \"$port_dir\"";
  354. open (my $in, "$cmd |")
  355. or die "Unable to get pseudo flavors for \"$port\"";
  356. my $output;
  357. while (<$in>) {
  358. chomp;
  359. $output .= $_ . ' ';
  360. }
  361. chop $output;
  362. close($in);
  363. if ($? != 0) {
  364. die "Unable to get pseudo flavors for \"$port\"";
  365. }
  366. my @pseudo_flavors = split / /,$output;
  367. return \@pseudo_flavors;
  368. }
  369. # Returns the reference of the package information belonging to a package
  370. # name from the package info array. The sub will die if there is no such
  371. # package listed.
  372. # A package listed more than one time, e.g. several subpackages, will
  373. # always return the first subpackage. It is taken care of the update of the
  374. # other subpackages via the ports Makefile, so we should only need this first
  375. # one.
  376. sub get_pkg_info {
  377. my ($info_array, $pkg_name) = @_;
  378. foreach my $info (@$info_array) {
  379. my $info_pkg_name = $info->{pkg};
  380. $info_pkg_name =~ s/$regex_subpkg$regex_flavor//g;
  381. return $info if ($info_pkg_name eq $pkg_name);
  382. }
  383. die "ERROR: Internal error, package information not consistent.\n";
  384. }
  385. # Tries to find the newest port from directories listed in $PORTSDIR_PATH
  386. # by comparing the output from make show=FULLPKGNAME.
  387. sub find_newer_ports_dir {
  388. my $info = shift;
  389. my $port = $info->{category} . '/' . $info->{port};
  390. $port .= '/' . $info->{subdir} if ($info->{subdir} ne '');
  391. return ($port, $info->{portdir})
  392. if ((defined $info->{portdir}) && ($info->{portdir} ne ''));
  393. my @port_locations = split /:/,$portsdir_path;
  394. # Take a shortcut here if we got only one location
  395. if (scalar @port_locations == 1) {
  396. $info->{portdir} = "$portsdir_path/$port";
  397. return ($port, $info->{portdir});
  398. }
  399. my @versions = ();
  400. foreach my $dir (@port_locations) {
  401. my $port_dir = "$dir/$port";
  402. my $version = get_ports_version($port_dir);
  403. push @versions, $version;
  404. }
  405. my $highest
  406. = reduce { get_higher_version($a, $b) <= 0 ? $a : $b } @versions;
  407. my $idx = 0;
  408. $idx = grep
  409. { defined($versions[$_])
  410. && ($versions[$_] eq $highest) ? $_ : -1 } 0..$#versions
  411. if (scalar @versions > 1);
  412. $info->{portdir} = "$port_locations[$idx - 1]/$port";
  413. return ($port, $info->{portdir});
  414. }
  415. # (Re)builds a package and updates it.
  416. # The following targets for make are given: repackage, update, clean
  417. # All output from the build process is logged and printed if the package did
  418. # not build.
  419. sub build_pkg {
  420. my $info = shift;
  421. my $cur_dir = getcwd();
  422. my ($port, $port_dir) = find_newer_ports_dir($info);
  423. my $pid = fork();
  424. if (! defined $pid) {
  425. die "Cannot fork to build $port\n";
  426. } elsif ($pid) {
  427. $forked_builds{$pid} = $port;
  428. $jobs++;
  429. return;
  430. }
  431. # Give the parent time to update the forked_builds hash
  432. sleep 1;
  433. # The child must use the default sig handler for SIGCHLD
  434. $SIG{CHLD} = 'DEFAULT';
  435. # Create the command that will build the package
  436. my $cmd = 'env ';
  437. $cmd .= "LIBTOOL=\"$portsdir/infrastructure/build/libtool\" "
  438. if (needs_intree_libtool($info));
  439. $cmd .= "FLAVOR=\"$info->{flavor}\" " if ($info->{flavor} ne '');
  440. $cmd .= "SUBPACKAGE=$info->{subpkg} " if ($info->{subpkg} ne '');
  441. $cmd .= 'make repackage';
  442. # chdir into the ports directory
  443. chdir $port_dir or die "Unable to change to \"$port_dir\"\n";
  444. print STDOUT "Building $info->{pkg}\n";
  445. my (@log, $logfile, $logfilename);
  446. open(my $in, "$cmd 2>&1 |")
  447. or die "Unable to make \"$port\"\n";
  448. if (defined $logdir) {
  449. $logfilename = "$logdir/$info->{port}.log";
  450. open($logfile, '>>', $logfilename)
  451. or print STDERR "Warning: Unable to create log file"
  452. . " for $info->{pkg}\n";
  453. }
  454. while (<$in>) {
  455. print STDOUT $_ unless (($verbose < 2) || ($num_jobs > 1));
  456. print $logfile $_ if (defined $logfile);
  457. push @log, ($_);
  458. }
  459. close($in);
  460. my $build_result = $?;
  461. my $update_result = 1;
  462. if ($build_result == 0) {
  463. $cmd = 'env FORCE_UPDATE=Yes CLEANDEPENDS=Yes ';
  464. $cmd .= "FLAVOR=\"$info->{flavor}\" "
  465. if ($info->{flavor} ne '');
  466. $cmd .= "SUBPACKAGE=$info->{subpkg} "
  467. if ($info->{subpkg} ne '');
  468. $cmd .= 'make update clean';
  469. open($in, "$cmd 2>&1 |")
  470. or die "Unable to update \"$port\"\n";
  471. while (<$in>) {
  472. print STDOUT $_
  473. unless (($verbose < 2) || ($num_jobs > 1));
  474. print $logfile $_ if (defined $logfile);
  475. push @log, ($_);
  476. }
  477. close($in);
  478. $update_result = $?;
  479. }
  480. close($logfile) if (defined $logfile);
  481. if ($build_result != 0 || $update_result != 0) {
  482. # Something went wrong. We list the output of the make process
  483. # and kill the update process
  484. if (!defined $logdir) {
  485. foreach my $line (@log) {
  486. print STDOUT $line;
  487. }
  488. }
  489. else {
  490. print STDERR "Failed: See log in '$logfilename'\n";
  491. }
  492. print STDERR "Aborting build process...\n";
  493. exit 3;
  494. }
  495. chdir $cur_dir;
  496. exit 0;
  497. }
  498. # Creates the dependencies list for a given port from the port information
  499. # hash
  500. sub create_dependencies_list {
  501. my $info = shift;
  502. my $cur_dir = getcwd;
  503. my @dep_list = ();
  504. # Create the command that will give us the dependencies list
  505. my $cmd = 'env ';
  506. $cmd .= "FLAVOR=\"$info->{flavor}\" " if ($info->{flavor} ne '');
  507. $cmd .= "SUBPACKAGE=$info->{subpkg} " if ($info->{subpkg} ne '');
  508. $cmd .= 'make build-dir-depends';
  509. # chdir into the ports directory
  510. my ($port, $port_dir) = find_newer_ports_dir($info);
  511. chdir $port_dir or die "Unable to change to \"$port_dir\"\n";
  512. open(my $in, "$cmd |")
  513. or die "Unable to get get dependencies for \"$port\"\n";
  514. while (<$in>) {
  515. chomp;
  516. push @dep_list, ($_);
  517. }
  518. close($in);
  519. if ($? != 0) {
  520. die "Unable to gather information for " . $port;
  521. }
  522. # Add the port itself to the list
  523. $port = "$info->{category}/$info->{port}";
  524. $port .= "/$info->{subdir}" if ($info->{subdir} ne '');
  525. $port .= ",$info->{subpkg}" if ($info->{subpkg} ne '');
  526. # Add the flavors in raw form
  527. if ($info->{flavor} ne '') {
  528. $port .= ',';
  529. my $flavs = $info->{flavor};
  530. $flavs =~ s/ /,/g;
  531. $port .= $flavs;
  532. }
  533. #push @dep_list, ("$port $port");
  534. chdir $cur_dir;
  535. return \@dep_list;
  536. }
  537. # Creates a single hash with all dependant packages from the array created
  538. # via create_dependencies_list
  539. sub create_dependencies_hash {
  540. my $dep_list = shift;
  541. my %dep_hash;
  542. foreach my $pkg (@$dep_list) {
  543. my $key = $pkg;
  544. $key =~ s/^.* //g;
  545. $key =~ s/$regex_subpkg$regex_flavor//g;
  546. $dep_hash{$key} = 1;
  547. }
  548. return \%dep_hash;
  549. }
  550. # Returns true if a package with a given name is in the package list array
  551. # The function will ignore flavors and subpackages.
  552. sub is_pkg_in_list {
  553. my ($pkg_name, $pkgs) = @_;
  554. foreach my $p (@$pkgs) {
  555. $p =~ s/$regex_subpkg$regex_flavor//g;
  556. return 1 if ($p eq $pkg_name);
  557. }
  558. return 0;
  559. }
  560. # Returns 1 if one of the dependencies from the arguments pkg info is currently
  561. # build by a child
  562. sub can_pkg_be_build {
  563. my $pkg = shift;
  564. my $pkgs = $pkg->{deppkgs};
  565. my @pkg_list = keys %$pkgs;
  566. foreach my $pid (keys %forked_builds) {
  567. my $build_pkg = $forked_builds{$pid};
  568. return 1 if (is_pkg_in_list($build_pkg, \@pkg_list));
  569. }
  570. return 0;
  571. }
  572. # Prints out the usage information on STDERR
  573. sub usage {
  574. print STDERR "USAGE: autoupdate.pl [options]\n";
  575. print STDERR "OPTIONS:\n"
  576. . " -v|--verbose\t\tSet verbosity level (1..5)\n"
  577. . " -f|--outofdate\t\tUse file as output from the out-of-date script\n"
  578. . " -j|--jobs\t\tTry to build packages in parallel\n"
  579. . " \t\t\tIf the filename is '-' the input will be\n"
  580. . " \t\t\tread from stdin\n"
  581. . " -h|--help\t\tShow this help\n";
  582. exit (1);
  583. }
  584. ##############################################################################
  585. $SIG{CHLD} = 'DEFAULT';
  586. my $basedir = $FindBin::Bin;
  587. chdir($basedir);
  588. # Read the command line params
  589. my $result = GetOptions("v|verbose=i" => \$verbose,
  590. "f|outofdate=s" => \$out_of_date,
  591. "j|jobs=i" => \$num_jobs,
  592. "h|help" => \$show_help);
  593. usage() if ($show_help != 0 || ! $result || $num_jobs < 1);
  594. # Fill the hash with the allowed variables
  595. my %valid_vars = ( 'logging', 1,
  596. 'libtool_ports', 1,
  597. 'pseudo_flavors', 1 );
  598. # Read the config and set everything up
  599. my $config = read_rc_file(\%valid_vars);
  600. setup_logging($config);
  601. setup_lookup_hash($config->{pseudo_flavors}, \%pseudo_flavors,
  602. 'pseudo flavors');
  603. setup_lookup_hash($config->{libtool_ports}, \%libtool_ports,
  604. 'libtool ports');
  605. print STDOUT "Reading package list scheduled for update...\n";
  606. my $package_list = read_update_package_list($out_of_date);
  607. print STDOUT "Got " . @$package_list . " packages for update\n";
  608. print STDOUT "Gathering package information...\n";
  609. my $port_info = create_package_information($package_list);
  610. print STDOUT "Merging dependencies...\n";
  611. my @dep_list = ();
  612. my @package_dep_list = ();
  613. # Get the dep list of each port
  614. foreach my $info (@$port_info) {
  615. my $deps = $info->{dependencies};
  616. push @dep_list, @$deps
  617. }
  618. print STDOUT "Create dependencies...\n";
  619. my ($fh, $file, $pipe);
  620. ($fh, $file) = tempfile(UNLINK => 1);
  621. open($pipe, "| tsort -r >$file") or die "ERROR: Unable to spawn tsort\n";
  622. foreach my $dep_entry (@dep_list) {
  623. # Zap the flavors and the subpackages for our dep list
  624. $dep_entry =~ s/$regex_subpkg$regex_flavor//g;
  625. print $dep_entry . "\n" unless ($verbose < 5);
  626. print $pipe $dep_entry . "\n";
  627. }
  628. close $pipe;
  629. die "ERROR: Internal error" if ($? != 0);
  630. # Read the result
  631. while (<$fh>) {
  632. my $pkg = $_;
  633. chomp $pkg;
  634. push @package_dep_list, ($pkg);
  635. }
  636. close $fh;
  637. print STDOUT "Got " . @package_dep_list . " packages as dependencies\n"
  638. unless ($verbose < 1);
  639. print STDOUT "Removing unneeded packages...\n";
  640. # Remove all packages from @package_dep_list that are not in the @package_list
  641. # array, so that only those packages may be build that are in need.
  642. my @pkg_list = ();
  643. foreach my $pkg_name (@package_dep_list) {
  644. if (is_pkg_in_list($pkg_name, $package_list) == 1) {
  645. push @pkg_list, ($pkg_name);
  646. }
  647. }
  648. if ($verbose > 1) {
  649. print STDOUT "Got " . @pkg_list . " packages for building:\n";
  650. foreach my $pkg_name (@pkg_list) {
  651. print STDOUT $pkg_name . "\n";
  652. }
  653. }
  654. $SIG{CHLD} = \&REAPER;
  655. # Build all the packages. Packages that are still unknown to us are ignored.
  656. foreach my $pkg_name (@pkg_list) {
  657. # Get the package information from the info array
  658. my $info = get_pkg_info($port_info, $pkg_name);
  659. if (not defined $info) {
  660. print STDERR "Unable to retrieve package information for "
  661. . $pkg_name . "\n";
  662. next;
  663. }
  664. my $wait_for_dep_notice = 0;
  665. # XXX Here we must check if the package depends on a currently build
  666. # package too.
  667. while ($jobs >= $num_jobs || can_pkg_be_build($info)) {
  668. if ($wait_for_dep_notice == 0 && !can_pkg_be_build($info)) {
  669. print STDOUT "Waiting for dependant package to finish "
  670. . "building (" . $pkg_name . ")\n"
  671. unless ($verbose < 2);
  672. $wait_for_dep_notice++;
  673. }
  674. sleep 1;
  675. }
  676. last if ($abort != 0);
  677. build_pkg($info);
  678. }
  679. # Wait for all childs to finish
  680. while (keys %forked_builds) {
  681. sleep 1;
  682. }
  683. die "Abort requested by child" if ($abort != 0);
  684. print STDOUT "Done.\n";
  685. # Close the pipe to our log file if we were logging
  686. if (defined $config->{logging} && $config->{logging} != 0) {
  687. close STDOUT;
  688. close STDERR;
  689. close TEE;
  690. }