1 #!/usr/bin/perl 2 3 use strict; 4 use warnings; 5 use Getopt::Long qw(:config gnu_getopt no_auto_abbrev); 6 use rpm_spec; 7 8 # --------- defaults ------------------------------------------------------- 9 my $default_spec_dir = `dirname $0`; 10 $default_spec_dir =~ s/\s//g; 11 12 # use "rpmbuild" (RPM v4) if present or "rpm" (RPM v3) if not 13 my $RPM_BUILD = 'rpmbuild'; 14 my $cmd_found = `which rpmbuild | wc -l`; 15 if ($cmd_found == 0) { 16 $RPM_BUILD = 'rpm'; 17 } 18 19 my $default_log_dir = '/tmp'; 20 my $default_tarball_dir = "/sgnome/tarballs/gnome2.14"; 21 my $default_source_dirs = $default_spec_dir . "/ext-sources" . 22 ":" . $default_spec_dir . "/manpages/man1" . 23 ":" . $default_spec_dir . "/manpages/man3" . 24 ":" . $default_spec_dir . "/manpages/man4" . 25 ":" . $default_spec_dir . "/manpages/man5" . 26 ":" . "/sgnome/tarballs/moz-evo"; 27 my $default_patch_dir = $default_spec_dir . "/patches"; 28 my $default_sys_rpm_dir = "/usr/src/packages"; 29 my $default_nightly_date_format = '%y%m%d'; 30 31 # --------- global vars ---------------------------------------------------- 32 # config settings 33 my $the_spec_dir = $default_spec_dir; 34 my $the_log_dir = $default_log_dir; 35 my $the_summary_log; 36 my $the_summary_title; 37 my $the_logdir_url; 38 my $the_rpm_url; 39 my $the_srpm_url; 40 my $the_source_dirs = $default_source_dirs; 41 my @the_source_dirlist; 42 my $the_tarball_dir = $default_tarball_dir; 43 my $the_good_build_dir; 44 my $the_good_rpms_copy_dir; 45 my $the_patch_dir = $default_patch_dir; 46 my $the_sys_rpm_dir = $default_sys_rpm_dir; 47 my $rpm_target = "i586"; 48 49 my $check_deps = 1; 50 my $live_summary = 0; 51 52 my $build_command; 53 my $build_and_install = 1; 54 my $prep_only = 0; 55 56 my $exit_val = 0; 57 my $verbose = 1; 58 my $debug_level = 0; 59 my $full_path = 0; 60 my $halt_on_errors = 0; 61 my $mail_errors_file; 62 my $mail_errors_to; 63 my $mail_errors_cc; 64 my $prodname = "unnamed"; 65 my $is_nightly = 0; 66 my $the_nightly_date_format = $default_nightly_date_format; 67 my $is_nodeps = 0; 68 69 # counter used as an id for the spec files 70 my $spec_id = 0; 71 72 # array of spec objects 73 my @specs_to_build = (); 74 my @build_status; 75 my @status_details; 76 my @remove_list = (); 77 78 my %provider; 79 my %warned_about; 80 81 # --------- utility functions ---------------------------------------------- 82 our $opt_specdir = $default_spec_dir; 83 84 # return the name of the log file given the id of the spec file 85 sub get_log_name ($) { 86 my $spec_id = shift; 87 my $spec = $specs_to_build[$spec_id]; 88 my $spec_file = $spec->get_param ('file_name'); 89 my $base_name = $spec_file; 90 $base_name =~ s/^.*\/([^\/]+)/$1/; 91 my $log_name = "$base_name"; 92 $log_name =~ s/(\.spec|)$/.log/; 93 94 return $log_name; 95 } 96 97 my $current_log; 98 99 sub open_log ($) { 100 my $log_filename = shift; 101 102 if (not defined ($log_filename)) { 103 return; 104 } 105 106 if (defined ($current_log)) { 107 if ($current_log ne $log_filename) { 108 close LOG_FILE; 109 } 110 } 111 112 $current_log = undef; 113 114 if (! open LOG_FILE, ">>$log_filename") { 115 msg_warning (0, "Failed to open log file $log_filename"); 116 return; 117 } 118 119 $current_log = $log_filename; 120 121 msg_log ("--- log starts --- " . `date`); 122 } 123 124 sub close_log () { 125 msg_log ("--- log ends --- " . `date`); 126 close LOG_FILE; 127 $current_log = undef; 128 } 129 130 sub print_message ($$) { 131 my $min_verbose = shift; 132 my $message = shift; 133 134 chomp $message; 135 136 if ($verbose > $min_verbose) { 137 print "$message\n"; 138 } 139 if (defined ($current_log)) { 140 print LOG_FILE "$message\n"; 141 } 142 } 143 144 sub msg_error ($) { 145 my $message = shift; 146 147 print_message (0, "ERROR: $message"); 148 if ($halt_on_errors > 0) { 149 print "ERROR: Exiting...\n"; 150 exit (255); 151 } 152 } 153 154 sub msg_warning ($$) { 155 my $min_verbose = shift; 156 my $message = shift; 157 158 print_message ($min_verbose, "WARNING: $message"); 159 } 160 161 sub msg_info ($$) { 162 my $min_verbose = shift; 163 my $message = shift; 164 165 print_message ($min_verbose, "INFO: $message"); 166 } 167 168 sub msg_debug ($$) { 169 my $min_debug = shift; 170 my $message = shift; 171 172 if ($debug_level > $min_debug) { 173 print "DEBUG: $message\n"; 174 } 175 } 176 177 sub msg_log ($) { 178 my $message = shift; 179 180 if (defined ($current_log)) { 181 print LOG_FILE "$message\n"; 182 } 183 } 184 185 sub find_file ($) { 186 my $glob = shift; 187 188 my @files = `find $glob 2>&1`; 189 if ($? == 0) { 190 my $file = $files[$#files]; 191 chomp $file; 192 return ($file); 193 } 194 195 return (undef); 196 } 197 198 sub mail_log ($) { 199 my $spec_id = shift; 200 my $address; 201 my $cc; 202 my $log_name = get_log_name ($spec_id); 203 my $spec_name = $specs_to_build[$spec_id]->{name}; 204 my $base_name = $log_name; 205 $log_name =~ s/\.log$//; 206 207 if (defined ($mail_errors_file)) { 208 $address = get_address_from_file ($mail_errors_file, $base_name); 209 } 210 211 if (not defined ($address)) { 212 $address = $mail_errors_to; 213 } 214 215 if (not defined ($address)) { 216 $address = $mail_errors_cc; 217 } else { 218 $cc = $mail_errors_cc; 219 } 220 221 if (not defined ($address)) { 222 return; 223 } 224 225 my $log_file = get_log_name ($spec_id); 226 msg_info (1, "mailing the build log to $address"); 227 228 my $subject; 229 230 my $log_pointer; 231 if (defined ($the_logdir_url)) { 232 $log_pointer = "$the_logdir_url/${log_name}.log"; 233 } else { 234 $log_pointer = "$the_log_dir/$log_file"; 235 } 236 237 if (defined ($prodname)) { 238 $subject = "BUILD FAILED ($prodname): $spec_name"; 239 } else { 240 $subject = "BUILD FAILED: $spec_name"; 241 } 242 243 if (defined ($cc)) { 244 `( echo "Full log: $log_pointer" ; echo; echo "--- tail of the log follows ---"; echo; tail -100 $the_log_dir/$log_file ) | mailx -s "$subject" -c "$cc" $address`; 245 } else { 246 `( echo "Full log: $log_pointer" ; echo; echo "--- tail of the log follows ---"; echo; tail -100 $the_log_dir/$log_file ) | mailx -s "$subject" $address`; 247 } 248 } 249 250 sub get_address_from_file ($$) { 251 my $fname = shift; 252 my $pkg = shift; 253 254 $pkg =~ s/\.spec$//; 255 256 if (! open ADDR_FILE, "<$fname") { 257 msg_warning (0, "Could not open file $fname"); 258 return undef; 259 } 260 my @lines = <ADDR_FILE>; 261 my @addresses = grep /^$pkg:/, @lines; 262 my $address = $addresses[0]; 263 if (not defined ($address)) { 264 return undef; 265 } 266 $address =~ s/^$pkg://; 267 $address =~ s/\s//g; 268 close ADDR_FILE; 269 return $address; 270 } 271 272 # --------- functions to process the command line args --------------------- 273 sub add_spec ($) { 274 my $spec_name = shift; 275 276 my $spec; 277 278 if (-f $spec_name) { 279 $spec = rpm_spec->new ($spec_name, $rpm_target); 280 } else { 281 if (not $spec_name =~ /\.spec$/) { 282 $spec_name = "${opt_specdir}/${spec_name}.spec"; 283 } 284 $spec = rpm_spec->new ("$spec_name", $rpm_target); 285 } 286 287 if (not defined ($spec)) { 288 msg_error ("$spec_name not found\n"); 289 } else { 290 if ($is_nightly) { 291 $spec->add_define ("nightly", "1"); 292 } 293 my $this_spec_id = $spec_id ++; 294 $specs_to_build[$this_spec_id] = $spec; 295 my @packages = $spec->get_param_array ('packages'); 296 297 $build_status[$this_spec_id] = 'NOT_BUILT'; 298 $status_details[$this_spec_id] = ''; 299 300 foreach my $pkg (@packages) { 301 my @provides = $spec->get_pkg_param_array ($pkg, 'provides'); 302 foreach my $prov (@provides) { 303 if (not defined $prov) { 304 next; 305 } 306 $prov =~ s/ .*//; 307 $provider{$prov} = $this_spec_id; 308 } 309 } 310 } 311 } 312 313 sub set_quiet { 314 $verbose = 0; 315 } 316 317 sub process_args { 318 my $arg = shift; 319 320 if (not defined ($build_command)) { 321 if (($arg ne "build") and ($arg ne "build-order") and 322 ($arg ne "uninstall-pkgs") and 323 ($arg ne "build-install") and ($arg ne "build-only") and 324 ($arg ne "prep") and 325 ($arg ne "install-order")) { 326 msg_error ("unknown command: $arg"); 327 usage (1); 328 } 329 $build_command = $arg; 330 } else { 331 add_spec ($arg); 332 } 333 } 334 335 sub process_options { 336 337 Getopt::Long::Configure ("bundling"); 338 339 our $opt_logdir = $default_log_dir; 340 our $opt_sourcedirs = $default_source_dirs; 341 our $opt_tarballdir = $default_tarball_dir; 342 our $opt_good_build_dir; 343 our $opt_good_rpms_copy_dir; 344 our $opt_patchdir = $default_patch_dir; 345 our $opt_rpmdir = $default_sys_rpm_dir; 346 our $opt_deps = 1; 347 our $opt_live_summary = 0; 348 our $opt_summary_log; 349 our $opt_summary_title; 350 our $opt_logdir_url; 351 our $opt_rpm_url; 352 our $opt_srpm_url; 353 our $opt_date = $default_nightly_date_format; 354 355 GetOptions ('v|verbose+' => \$verbose, 356 'debug=n' => \$debug_level, 357 'q|quiet' => \&set_quiet, 358 'specdir|spec|S=s', 359 'halt-on-errors!', \$halt_on_errors, 360 'mail-errors-to=s' => \$mail_errors_to, 361 'mail-errors-cc=s' => \$mail_errors_cc, 362 'mail-errors-file=s' => \$mail_errors_file, 363 'prodname=s' => \$prodname, 364 'sourcedirs|sourcedir|src|srcdirs|srcdir|sources|source|s=s', 365 'logdir|log|l=s', 366 'summary-log=s', 367 'live-summary|live!', 368 'summary-title=s', 369 'logdir-url=s', 370 'rpm-url=s', 371 'srpm-url=s', 372 'target=s' => \$rpm_target, 373 'deps!', 374 'tarballdir|tar|tarball|tardir|t=s', 375 'good-build-dir=s', 376 'good-rpms-copy-dir=s', 377 'nightly!' => \$is_nightly, 378 'date|date-format|nightly-date-format=s', 379 'patchdir|patch|p=s', 380 'rpmdir|rpm|r=s', 381 'full-path' => \$full_path, 382 'nodeps!' => \$is_nodeps, 383 'help' => \&usage, 384 '<>' => \&process_args); 385 386 $the_spec_dir = $opt_specdir; 387 $the_log_dir = $opt_logdir; 388 $the_summary_log = $opt_summary_log; 389 $the_summary_title = $opt_summary_title; 390 $the_logdir_url = $opt_logdir_url; 391 $the_rpm_url = $opt_rpm_url; 392 $the_srpm_url = $opt_srpm_url; 393 $the_source_dirs = $opt_sourcedirs; 394 $the_tarball_dir = $opt_tarballdir; 395 $the_patch_dir = $opt_patchdir; 396 $the_sys_rpm_dir = $opt_rpmdir; 397 $the_good_build_dir = $opt_good_build_dir; 398 $the_good_rpms_copy_dir = $opt_good_rpms_copy_dir; 399 $check_deps = $opt_deps; 400 $live_summary = $opt_live_summary; 401 $the_nightly_date_format = $opt_date; 402 403 if ($is_nodeps) { 404 $check_deps = 0; 405 } 406 407 @the_source_dirlist = split /:/, $the_source_dirs; 408 } 409 410 sub usage (;$) { 411 my $retval = shift; 412 if (not defined ($retval)) { 413 $retval = 0; 414 } 415 416 print << "EOF"; 417 build-gnome2 [options] [command] specs... 418 419 Options: 420 421 -v|--verbose: 422 Increase verbosity: the more -v's the more diag messages 423 424 -q|--quiet: 425 Silent operation. 426 427 --specdir=dir, --spec=dir: 428 Use dir as the default directory for spec files. 429 Default: $default_spec_dir 430 431 --tarballdir=dir, --tardir=dir, --tar=dir: 432 Search for source tarballs in dir. 433 Default: $default_tarball_dir 434 435 --sourcedir=dir, --srcdir=dir, --src=dir: 436 Location of the additional sources referenced in the spec 437 files. Default: $default_spec_dir 438 439 --patchdir=dir, --patch=dir: 440 Look for the patches referenced in the spec files in dir. 441 Default: $default_patch_dir 442 443 --rpmdir=dir --rpm=dir: 444 Use dir as the rpm base directory (where the SPECS, 445 SOURCES, RPMS, SRPMS, BUILD directories are found). 446 Default: $default_sys_rpm_dir 447 448 --logdir=dir, --log=dir: 449 Write build logs to dir. Default: $default_log_dir 450 451 --nodeps, --deps: 452 Ignore/verify dependencies before building a component. 453 Default: --deps 454 455 --target=arch 456 If used, the same option will be passed to rpm when 457 building the package(s) 458 459 --halt-on-errors: 460 Halt on the first build error, do not attempt to continue. 461 462 --full-path: 463 Print the full path to the rpm when running install-order. 464 465 --nightly, --nonightly: 466 Suffix/Don't suffix the rpm Release with the current date. 467 Default: --nonightly; 468 469 --date-format=format, --date=format: 470 Use "date +format" to generate the date suffix for 471 the nightly builds. Default: $default_nightly_date_format 472 473 474 Commands: 475 476 build Build and install the specs listed on the command line 477 478 build-only Build the specs listed on the command line, don't install 479 them. 480 481 prep run the %prep section of the spec files listed on the 482 command line 483 484 build-order Print the build order of the specs listed on the 485 command line. 486 487 install-order Print the rpms in the order they should be installed 488 489 install-pkgs (not implemented yet): install the packages defined 490 by the spec files listed on the command line from 491 the PKGS directory. 492 493 uninstall-pkgs Remove all rpms defined in the spec files listed 494 on the command line. (runs rpm --erase --nodeps) 495 496 specs... 497 498 List of spec files to build. Either full path names or names of spec 499 files in the default spec directory (with or without the .spec suffix). 500 EOF 501 exit $retval; 502 } 503 504 # --------- print reports -------------------------------------------------- 505 # plain text report on stdout and 506 # html report if summary log file name 507 # was specified on the command line 508 sub print_status { 509 print_status_text (); 510 print_status_html (); 511 } 512 513 sub print_live_status { 514 if ($live_summary) { 515 print_status_html (); 516 } 517 } 518 519 sub print_status_text { 520 my $spec_name; 521 print "\nSummary:\n\n"; 522 printf "%20s | %10s | %s\n", "package", "status", "details"; 523 print "---------------------+------------+------------------------------------------\n"; 524 for (my $i = 0; $i <= $#specs_to_build; $i++) { 525 $spec_name=$specs_to_build[$i]->{name}; 526 printf "%20s | %10s | %s\n", $spec_name, $build_status[$i], 527 $status_details[$i]; 528 } 529 } 530 531 sub print_status_html { 532 my $spec_name; 533 534 if (not defined ($the_summary_log)) { 535 return; 536 } 537 538 if (! open SUM_LOG, ">$the_summary_log") { 539 msg_warning (0, "Failed to open file $the_summary_log for writing"); 540 return; 541 } 542 543 print SUM_LOG "<HTML>\n<HEAD>\n<TITLE>Build report</TITLE>\n"; 544 print SUM_LOG "</HEAD>\n<BODY BGCOLOR=#FFFFFF>\n"; 545 if (defined ($the_summary_title)) { 546 print SUM_LOG "<P><H3>$the_summary_title</H3><P>\n"; 547 } 548 print SUM_LOG "<TABLE BORDER=1 CELLSPACING=0 CELLPADDING=5 WIDTH=90%>\n"; 549 print SUM_LOG " <TR>\n"; 550 print SUM_LOG " <TD BGCOLOR=#666699><B>package</B></TD>\n"; 551 print SUM_LOG " <TD BGCOLOR=#666699><B>version</B></TD>\n"; 552 print SUM_LOG " <TD BGCOLOR=#666699><B>release</B></TD>\n"; 553 print SUM_LOG " <TD BGCOLOR=#666699><B>status</B></TD>"; 554 print SUM_LOG " <TD BGCOLOR=#666699><B>details</B></TD>\n"; 555 print SUM_LOG " </TR>\n"; 556 for (my $i = 0; $i <= $#specs_to_build; $i++) { 557 my $log_name = get_log_name ($i); 558 $spec_name=$specs_to_build[$i]->{name}; 559 my $version = $specs_to_build[$i]->{version}; 560 my $release = $specs_to_build[$i]->{release}; 561 print SUM_LOG " <TR>\n"; 562 if (defined ($the_srpm_url)) { 563 if (($build_status[$i] eq "PASSED") and defined ($the_srpm_url)) { 564 print SUM_LOG " <TD><A HREF=\"$the_srpm_url/$spec_name-$version-$release.src.rpm\">$spec_name</A></TD>\n"; 565 } else { 566 print SUM_LOG " <TD>$spec_name</TD>\n"; 567 } 568 } else { 569 print SUM_LOG " <TD>$spec_name</TD>\n"; 570 } 571 print SUM_LOG " <TD>", $version, "</TD>\n"; 572 print SUM_LOG " <TD>", $release, "</TD>\n"; 573 my $color_start = ""; 574 my $color_end = ""; 575 if ($build_status[$i] eq "PASSED") { 576 $color_start = "<FONT COLOR=#11AA11>"; 577 $color_end = "</FONT>"; 578 } elsif ($build_status[$i] eq "BEING_BUILT") { 579 $color_start = "<FONT COLOR=#FFA500>"; 580 $color_end = "</FONT>"; 581 } elsif ($build_status[$i] eq "DEP") { 582 $color_start = "<FONT COLOR=#FFA500>"; 583 $color_end = "</FONT>"; 584 } elsif ($build_status[$i] eq "NOT_BUILT") { 585 $color_start = ""; 586 $color_end = ""; 587 } else { 588 $color_start = "<FONT COLOR=#CC1111>"; 589 $color_end = "</FONT>"; 590 } 591 if (defined ($the_logdir_url) and ($build_status[$i] ne "NOT_BUILT")) { 592 print SUM_LOG " <TD><A HREF=\"$the_logdir_url/$log_name\">", 593 $color_start, $build_status[$i], $color_end, 594 "</A></TD>\n"; 595 } else { 596 print SUM_LOG " <TD>", 597 $color_start, $build_status[$i], $color_end, 598 "</A></TD>\n"; 599 } 600 if ($build_status[$i] eq "PASSED") { 601 if (defined ($the_rpm_url)) { 602 print SUM_LOG " <TD>RPMS: \n"; 603 my @rpms = $specs_to_build[$i]-> get_param_array ('rpms'); 604 my $ctr = 1; 605 for my $rpm (@rpms) { 606 print SUM_LOG " <A HREF=\"$the_rpm_url/$rpm\">[$ctr]</a> \n"; 607 $ctr++; 608 } 609 print SUM_LOG " </TD>\n"; 610 } else { 611 print SUM_LOG " <TD> </TD>\n"; 612 } 613 } else { 614 print SUM_LOG " <TD><PRE>", $status_details[$i], "</PRE></TD>\n"; 615 } 616 print SUM_LOG " </TR>\n"; 617 } 618 print SUM_LOG "</TABLE><P>\n"; 619 print SUM_LOG "<SMALL>", `date`, "</SMALL>\n"; 620 print SUM_LOG "</BODY>\n</HTML>\n"; 621 close SUM_LOG; 622 } 623 624 # --------- rpm utility functions ------------------------------------------ 625 sub is_provided ($) { 626 my $capability = shift; 627 628 $capability =~ s/\s.*//; 629 `sh -c "rpm -q --whatprovides $capability" >/dev/null 2>&1`; 630 my $result = (! $?); 631 `sh -c "rpm -q $capability" >/dev/null 2>&1`; 632 $result = ($result or (! $?)); 633 634 return ($result); 635 } 636 637 sub what_provides ($) { 638 my $capability = shift; 639 640 $capability =~ s/ .*//; 641 my $rpm=`sh -c "rpm -q --whatprovides $capability" 2>&1`; 642 if ($?) { 643 $rpm=`sh -c "rpm -q $capability" 2>&1`; 644 } 645 chomp $rpm; 646 return ($rpm); 647 } 648 649 sub spec_id_of ($) { 650 my $name = shift; 651 652 for (my $i = 0; $i <= $#specs_to_build; $i++) { 653 if ($specs_to_build[$i]->{name} eq $name) { 654 return $i; 655 } 656 } 657 658 return undef; 659 } 660 661 sub install_good_rpms ($) { 662 my $spec_id = shift; 663 664 return if not defined($spec_id); 665 my $spec = $specs_to_build[$spec_id]; 666 667 my @rpms = $spec->get_param_array ('rpms'); 668 map $_ =~ s/$spec->{version}-$spec->{release}.$spec->{buildarchitecture}.rpm/[0-9]*-[0-9]*.*.rpm/, @rpms; 669 map $_ =~ s/^/$the_good_build_dir\//, @rpms; 670 671 for (my $i = 0; $i <= $#rpms; $i++) { 672 my $current_rpm = $rpms[$i]; 673 msg_info (1, "Looking for last known good rpm as $current_rpm"); 674 $rpms[$i] = find_file ($current_rpm); 675 if (not defined ($rpms[$i])) { 676 msg_error ("No file matches $current_rpm"); 677 return 0; 678 } else { 679 msg_info (1, "Found $rpms[$i]"); 680 } 681 } 682 683 my $command = "rpm --upgrade -v @rpms"; 684 685 if ($verbose > 0) { 686 map msg_info (0, "Installing last known good rpm: $_\n"), @rpms; 687 } 688 689 my $msg=`$command 2>&1`; 690 691 if ($? > 0) { 692 msg_error "failed to install last known good rpm: $msg"; 693 $status_details[$spec_id] = $status_details[$spec_id] . 694 "; Failed to install last known good rpm: " . $msg; 695 return 0; 696 } 697 698 if (defined ($the_good_rpms_copy_dir)) { 699 `mkdir -p $the_good_rpms_copy_dir`; 700 `cp @rpms $the_good_rpms_copy_dir`; 701 } 702 703 return 1; 704 } 705 706 sub install_rpms ($) { 707 my $spec_id = shift; 708 my $spec = $specs_to_build[$spec_id]; 709 710 my @rpms = $spec->get_param_array ('rpm_paths'); 711 map $_ =~ s/^/$the_sys_rpm_dir\//, @rpms; 712 my $command = "rpm --upgrade -v @rpms"; 713 714 if ($verbose > 0) { 715 map msg_info (0, "Installing $_\n"), @rpms; 716 } 717 718 my $msg=`$command 2>&1`; 719 if ($? > 0) { 720 msg_error "failed to install rpm: $msg"; 721 $build_status[$spec_id] = 'FAILED'; 722 $status_details[$spec_id] = $msg; 723 return 0; 724 } 725 726 return 1; 727 } 728 729 sub print_rpms ($) { 730 my $spec_id = shift; 731 my $spec = $specs_to_build[$spec_id]; 732 733 if ($full_path) { 734 my @rpms = $spec->get_param_array ('rpm_paths'); 735 map print("$the_sys_rpm_dir\/$_\n"), @rpms; 736 } else { 737 my @rpms = $spec->get_param_array ('rpms'); 738 map print("$_\n"), @rpms; 739 } 740 } 741 742 sub print_spec ($) { 743 my $spec_id = shift; 744 my $spec = $specs_to_build[$spec_id]; 745 746 print "$spec->{file_name}\n"; 747 } 748 749 sub push_to_remove_list ($) { 750 my $spec_id = shift; 751 my $spec = $specs_to_build[$spec_id]; 752 753 my @rpms = $spec->get_param_array ('packages'); 754 map unshift (@remove_list, $_), @rpms; 755 } 756 757 # --------- dependency checking code --------------------------------------- 758 sub warn_always ($$$) { 759 my $spec_name = shift; 760 my $dep = shift; 761 my $reason = shift; 762 763 if ($reason eq "DEP_FAILED") { 764 msg_warning (1, "$spec_name requires $dep"); 765 msg_warning (1, "but that failed to build"); 766 msg_warning (1, "Can't build $spec_name"); 767 } elsif ($reason eq "NOT_FOUND") { 768 msg_warning (0, "no spec file or installed package provides $dep"); 769 msg_warning (0, "can't build $spec_name"); 770 } 771 } 772 773 sub warn_once ($$$) { 774 my $spec_name = shift; 775 my $dep = shift; 776 my $reason = shift; 777 778 if ($reason eq "DEP_FAILED") { 779 # should not happen 780 msg_error ("assertion failed: warn_once / DEP_FAILED"); 781 } elsif ($reason eq "NOT_FOUND") { 782 if (not defined ($warned_about{$dep})) { 783 msg_warning (1, "$dep is required but not found"); 784 $warned_about{$dep}=1; 785 } 786 } 787 } 788 789 sub get_dependencies ($@) { 790 my $spec_id = shift; 791 my @packages = @_; 792 my $spec = $specs_to_build[$spec_id]; 793 794 my @dependencies = (); 795 my @this_pkg_requires; 796 foreach my $pkg (@packages) { 797 @this_pkg_requires = $spec->get_pkg_param_array ($pkg, 'requires'); 798 msg_debug (3, "adding \"@this_pkg_requires\" to the dependencies of $spec->{name}"); 799 push (@dependencies, @this_pkg_requires); 800 } 801 802 return @dependencies; 803 } 804 805 sub check_dependency ($$&&@) { 806 my $spec_id = shift; 807 my $capability = shift; 808 my $recursive_callback = shift; 809 my $warning_callback = shift; 810 my @rec_cb_opts = @_; 811 my $spec_name = $specs_to_build[$spec_id]->{name}; 812 813 my $result = 1; 814 815 if (not defined ($capability)) { 816 return 1; 817 } 818 819 if ((defined ($provider{$capability})) and 820 ($provider{$capability} == $spec_id)) { 821 return 1; 822 } 823 824 if (defined $provider{$capability}) { 825 if ($build_status[$provider{$capability}] eq "PASSED") { 826 return 1; 827 } elsif ($build_status[$provider{$capability}] ne "NOT_BUILT") { 828 if (!is_provided ($capability)) { 829 &$warning_callback ($spec_name, $capability, "DEP_FAILED"); 830 return 0; 831 } 832 return 1; 833 } 834 835 msg_info (1, "need to build $capability first"); 836 my $save_log_name = $current_log; 837 my $result = &$recursive_callback ($provider{$capability}, @rec_cb_opts); 838 open_log ($save_log_name); 839 return $result; 840 } elsif (!is_provided ($capability)) { 841 &$warning_callback ($spec_name, $capability, "NOT_FOUND"); 842 return 0; 843 } else { 844 return 1; 845 } 846 847 # should not happen 848 msg_error("Assertion failed: check_dependency: return 0"); 849 return 0; 850 } 851 852 # --------- copy build spec files, tarballs, patches ----------------------- 853 sub copy_spec ($) { 854 my $spec_id = shift; 855 my $spec = $specs_to_build[$spec_id]; 856 my $spec_file = $spec->get_param ('file_name'); 857 my $base_name = $spec_file; 858 $base_name =~ s/^.*\/([^\/]+)/$1/; 859 my $target = "$the_sys_rpm_dir/SPECS/$base_name"; 860 861 msg_info (1, "copying spec file $base_name to the SPECS dir"); 862 863 if ($is_nightly) { 864 my $the_nightly_date = `date "+$the_nightly_date_format"`; 865 if ($? > 0) { 866 msg_error ("incorrect date format: $the_nightly_date_format"); 867 $build_status[$spec_id] = 'ERROR'; 868 $status_details[$spec_id] = "incorrect nightly date format: $the_nightly_date"; 869 return 0; 870 } 871 872 open SPEC_OUT, ">$target"; 873 my $msg=`cp $spec_file /tmp/.gnome_build.tmp.$$ 2>&1`; 874 if ($? > 0) { 875 msg_error ("failed to copy $spec_file"); 876 msg_error ("can't continue with $spec->{name}"); 877 $build_status[$spec_id] = 'FAILED'; 878 $status_details[$spec_id] = "cound not copy spec file: $msg"; 879 return 0; 880 } 881 open SPEC_IN, "</tmp/.gnome_build.tmp.$$"; 882 my $line; 883 while (1) { 884 $line = <SPEC_IN>; 885 if (not defined ($line)) { 886 last; 887 } 888 if ($line =~ /^Release\s*:/) { 889 $line =~ s/\s*$/.$the_nightly_date/; 890 } 891 print SPEC_OUT $line; 892 } 893 close SPEC_OUT; 894 close SPEC_IN; 895 896 `rm -f /tmp/.gnome_build.tmp.$$`; 897 my $s = rpm_spec->new ($target, $rpm_target); 898 $specs_to_build[$spec_id] = $s; 899 $s->add_define ("nightly", "1"); 900 } else { 901 `cmp -s $spec_file $target`; 902 if ($? != 0) { # the files differ 903 my $msg=`cp $spec_file $target 2>&1`; 904 if ($? > 0) { 905 msg_error "failed to copy $spec_file"; 906 $build_status[$spec_id] = 'FAILED'; 907 $status_details[$spec_id] = "failed to copy spec file: $msg"; 908 return 0; 909 } 910 } else { 911 msg_info (3, " $spec_file and"); 912 msg_info (3, " $target"); 913 msg_info (3, " are the same; not copying"); 914 } 915 } 916 917 return 1; 918 } 919 920 sub find_source ($$) { 921 my $spec_id = shift; 922 my $src = shift; 923 my $is_tarball = 0; 924 my $src_path; 925 926 if ($src =~ /\.(tar\.gz|tgz|tar\.bz2|tar\.bzip2)$/) { 927 $is_tarball = 1; 928 929 $src_path = "$the_tarball_dir/$src"; 930 if (! -f "$src_path") { 931 msg_info (3, " $src not found in $the_tarball_dir"); 932 } else { 933 msg_info (3, " found in $the_tarball_dir"); 934 return $src_path; 935 } 936 } 937 foreach my $extsrcdir (@the_source_dirlist) { 938 $src_path = "$extsrcdir/$src"; 939 if (! -f "$src_path") { 940 msg_info (3, " $src not found in $extsrcdir"); 941 } else { 942 msg_info (3, " found in $extsrcdir"); 943 return "$src_path"; 944 } 945 } 946 947 if (!$is_tarball) { 948 $src_path = "$the_tarball_dir/$src"; 949 if (! -f "$src_path") { 950 msg_info (3, " trying the tarball directory"); 951 msg_info (3, " $src not found in $the_tarball_dir"); 952 } else { 953 msg_info (3, " found in $the_tarball_dir"); 954 msg_warning (1, "$src is not expected to be in the tarball dir"); 955 return $src_path; 956 } 957 } 958 959 $build_status[$spec_id] = 'FAILED'; 960 $status_details[$spec_id] = "Source $src not found"; 961 msg_error ("Source file $src not found"); 962 return undef; 963 } 964 965 sub copy_sources ($) { 966 my $spec_id = shift; 967 my $spec = $specs_to_build[$spec_id]; 968 969 my @sources = $spec->get_param_array ('sources'); 970 my $src_path; 971 my $target; 972 973 msg_info (1, "copying sources to $the_sys_rpm_dir/SOURCES"); 974 975 foreach my $src (@sources) { 976 if (not defined ($src)) { 977 next; 978 } 979 $src_path = find_source ($spec_id, $src); 980 if (not defined ($src_path)) { 981 return 0; 982 } 983 984 msg_info (2, " copying $src"); 985 986 $target = "$the_sys_rpm_dir/SOURCES/$src"; 987 988 `cmp -s $src_path $target`; 989 990 if ($? != 0) { # the files differ 991 my $msg=`cp $src_path $target 2>&1`; 992 if ($? > 0) { 993 msg_error ("failed to copy $src_path"); 994 $build_status[$spec_id] = 'ERROR'; 995 $status_details[$spec_id] = $msg; 996 return 0; 997 } 998 } 999 1000 } 1001 1002 return 1; 1003 } 1004 1005 sub copy_patches ($) { 1006 my $spec_id = shift; 1007 my $spec = $specs_to_build[$spec_id]; 1008 1009 my @patches = $spec->get_param_array ('patches'); 1010 my $patch_path; 1011 my $target; 1012 foreach my $patch (@patches) { 1013 if (not defined ($patch)) { 1014 next; 1015 } 1016 1017 msg_info (2, "copying patch $patch"); 1018 1019 $patch_path = "$the_patch_dir/$patch"; 1020 1021 if (! -f "$patch_path") { 1022 $build_status[$spec_id] = 'ERROR'; 1023 $status_details[$spec_id] = "Patch $patch_path not found"; 1024 msg_error ("Patch $patch_path not found"); 1025 return 0; 1026 } 1027 1028 $target = "$the_sys_rpm_dir/SOURCES/$patch"; 1029 1030 `cmp -s $patch_path $target`; 1031 1032 if ($? != 0) { # the files differ 1033 my $msg=`cp $patch_path $target 2>&1`; 1034 if ($? > 0) { 1035 msg_error "failed to copy $patch_path"; 1036 $build_status[$spec_id] = 'ERROR'; 1037 $status_details[$spec_id] = $msg; 1038 return 0; 1039 } 1040 } else { 1041 msg_info (3, " $patch and"); 1042 msg_info (3, " $the_sys_rpm_dir/SOURCES/$patch"); 1043 msg_info (3, " are the same; not copying"); 1044 } 1045 1046 } 1047 1048 return 1; 1049 } 1050 1051 # --------- build command -------------------------------------------------- 1052 sub do_build () { 1053 print_live_status; 1054 1055 for (my $i = 0; $i <= $#specs_to_build; $i++) { 1056 if ($build_status[$i] eq "NOT_BUILT") { 1057 if (! build_spec ($i)) { 1058 if (defined ($the_good_build_dir)) { 1059 msg_info (0, "Attempting to use a known good rpm"); 1060 install_good_rpms ($i); 1061 } 1062 } 1063 print_live_status; 1064 } 1065 if ($build_status[$i] ne "PASSED") { 1066 if ($build_status[$i] ne "DEP_FAILED") { 1067 mail_log ($i); 1068 } 1069 $exit_val++; 1070 } 1071 } 1072 1073 if ($verbose > 0) { 1074 print_status; 1075 } 1076 } 1077 1078 sub build_spec ($) { 1079 my $spec_id = shift; 1080 my $spec = $specs_to_build[$spec_id]; 1081 my $logname = $the_log_dir . "/" . get_log_name ($spec_id); 1082 1083 msg_debug (0, "Trying to build $spec->{name}"); 1084 open_log ($logname); 1085 1086 if ($build_status[$spec_id] ne "NOT_BUILT") { 1087 if ($build_status[$spec_id] eq "DEP") { 1088 $build_status[$spec_id]="ERROR"; 1089 $status_details[$spec_id]="Circular dependency detected"; 1090 msg_error ("Circular dependency detected " . 1091 "while trying to build $spec->{name}"); 1092 } 1093 return 0; 1094 } 1095 1096 my @packages = $spec->get_param_array ('packages'); 1097 msg_debug (3, "packages: $spec->{file_name} defines: @packages"); 1098 1099 foreach my $pkg (@packages) { 1100 if (not defined ($pkg)) { 1101 next; 1102 } 1103 if ($build_and_install) { 1104 msg_info (2, "Checking if $pkg is installed"); 1105 if (is_provided ($pkg) and $check_deps) { 1106 my $provider = what_provides ($pkg); 1107 msg_warning (0, "$pkg is already provided by $provider"); 1108 msg_warning (0, "not building $pkg"); 1109 $build_status[$spec_id]='SKIPPED'; 1110 $status_details[$spec_id]="$pkg already installed"; 1111 1112 return 0; 1113 } 1114 } 1115 } 1116 1117 if ($check_deps) { 1118 my @dependencies = get_dependencies ($spec_id, @packages); 1119 1120 my $this_result; 1121 my $result = 1; 1122 1123 $build_status[$spec_id] = "DEP"; 1124 $status_details[$spec_id] = "building dependencies first"; 1125 1126 msg_info (1, "Checking dependencies of $spec->{name}"); 1127 1128 foreach my $dep (@dependencies) { 1129 $dep =~ s/\s.*//; 1130 1131 $this_result = check_dependency ($spec_id, $dep, 1132 \&build_spec, \&warn_always, ()); 1133 if (!$this_result and $build_and_install) { 1134 if (defined ($the_good_build_dir)) { 1135 msg_info (0, "Attempting to use a known good rpm"); 1136 $this_result = install_good_rpms (spec_id_of ($dep)); 1137 } 1138 if (! $this_result) { 1139 msg_warning (1, "$spec->{name} requires $dep"); 1140 } 1141 } 1142 $result = ($this_result and $result); 1143 } 1144 1145 if (! $result) { 1146 $build_status[$spec_id]="DEP_FAILED"; 1147 $status_details[$spec_id]="Dependency check failed"; 1148 1149 msg_warning (0, "Dependency check for $spec->{name} failed"); 1150 1151 return 0; 1152 } 1153 } 1154 1155 copy_spec ($spec_id) || return 0; 1156 copy_sources ($spec_id) || return 0; 1157 copy_patches ($spec_id) || return 0; 1158 1159 if ($live_summary) { 1160 $build_status[$spec_id] = 'BEING_BUILT'; 1161 $status_details[$spec_id] = "$RPM_BUILD running"; 1162 print_live_status; 1163 } 1164 run_rpmbuild_ba ($spec_id) || return 0; 1165 1166 if ($build_and_install) { 1167 install_rpms ($spec_id) || return 0; 1168 } 1169 1170 $build_status[$spec_id] = "PASSED"; 1171 $status_details[$spec_id] = ""; 1172 1173 close_log; 1174 1175 return 1; 1176 1177 } 1178 1179 sub run_rpmbuild_ba ($) { 1180 my $spec_id = shift; 1181 my $spec = $specs_to_build[$spec_id]; 1182 my $spec_file = $spec->get_param ('file_name'); 1183 my $base_name = $spec_file; 1184 $base_name =~ s/^.*\/([^\/]+)/$1/; 1185 my $log_name = "$base_name"; 1186 $log_name = get_log_name ($spec_id); 1187 1188 msg_info (0, "Running rpm build of $spec->{name} ($base_name)"); 1189 msg_info (1, "Log file: $the_log_dir/$log_name"); 1190 1191 my $save_log_name = $current_log; 1192 msg_log ("INFO: Starting $RPM_BUILD at " . `date`); 1193 close_log; 1194 my $tempfile = "/tmp/rpm.out.$$"; 1195 my $defnightly = ""; 1196 if ($is_nightly) { 1197 $defnightly = " --define \"nightly 1\"" 1198 } 1199 my $rpmopt = "-ba"; 1200 if ($prep_only) { 1201 $rpmopt = "-bp"; 1202 } 1203 if (!$check_deps) { 1204 $rpmopt = "$rpmopt --nodeps"; 1205 } 1206 if (defined ($the_sys_rpm_dir) and $the_sys_rpm_dir ne "/usr/src/packages") { 1207 $rpmopt = "$rpmopt --define \"_topdir $the_sys_rpm_dir\""; 1208 } 1209 if (defined($rpm_target) and ($rpm_target eq $spec->{buildarchitecture})) { 1210 system ("$RPM_BUILD --target $rpm_target $rpmopt $the_sys_rpm_dir/SPECS/$base_name $defnightly > $tempfile 2>&1"); 1211 } else { 1212 system ("$RPM_BUILD $rpmopt $the_sys_rpm_dir/SPECS/$base_name $defnightly > $tempfile 2>&1"); 1213 } 1214 my $rpm_result = $?; 1215 system ("sed -e 's/^/RPM: /' $tempfile >> $the_log_dir/$log_name 2>&1; rm -f $tempfile"); 1216 open_log ($save_log_name); 1217 msg_log ("INFO: $RPM_BUILD finished at " . `date`); 1218 1219 if ($rpm_result) { 1220 msg_error ("Build of $spec->{name} FAILED"); 1221 $build_status[$spec_id] = "FAILED"; 1222 $status_details[$spec_id] = 'RPM build failed'; 1223 return 0; 1224 } 1225 1226 msg_info (0, "$spec->{name} PASSED"); 1227 return 1; 1228 } 1229 1230 # --------- build-order and install-order commands ------------------------- 1231 sub do_build_order () { 1232 for (my $i = 0; $i <= $#specs_to_build; $i++) { 1233 print_order ($i, \&print_spec); 1234 } 1235 } 1236 1237 sub do_install_order () { 1238 for (my $i = 0; $i <= $#specs_to_build; $i++) { 1239 print_order ($i, \&print_rpms); 1240 } 1241 } 1242 1243 sub print_order ($&) { 1244 my $spec_id = shift; 1245 my $print_command = shift; 1246 my $spec = $specs_to_build[$spec_id]; 1247 1248 if ($build_status[$spec_id] ne "NOT_BUILT") { 1249 if ($build_status[$spec_id] eq "DEP") { 1250 $build_status[$spec_id]="ERROR"; 1251 $status_details[$spec_id]="Circular dependency detected"; 1252 msg_error ("Circular dependency detected " . 1253 "while checking dependencies of $spec->{name}"); 1254 } 1255 return 0; 1256 } 1257 1258 if ($check_deps) { 1259 my @packages = $spec->get_param_array ('packages'); 1260 my @dependencies = get_dependencies ($spec_id, @packages); 1261 1262 $build_status[$spec_id] = "DEP"; 1263 1264 msg_info (1, "Checking dependencies of $spec->{name}"); 1265 1266 foreach my $dep (@dependencies) { 1267 $dep =~ s/\s.*//; 1268 1269 check_dependency ($spec_id, $dep, \&print_order, \&warn_once, ($print_command)); } 1270 } 1271 1272 copy_spec ($spec_id) || return 0; 1273 1274 $build_status[$spec_id] = "PASSED"; 1275 &$print_command ($spec_id); 1276 } 1277 1278 # --------- uninstall-pkgs command ----------------------------------------- 1279 sub do_uninstall_pkgs () { 1280 for (my $i = 0; $i <= $#specs_to_build; $i++) { 1281 print_order ($i, \&push_to_remove_list); 1282 } 1283 1284 foreach my $pkg_to_remove (@remove_list) { 1285 msg_info (0, "Removing $pkg_to_remove"); 1286 my $cmd_out = `rpm -v --erase --nodeps $pkg_to_remove 2>&1`; 1287 if ($? > 0) { 1288 $exit_val++; 1289 } 1290 $cmd_out =~ s/^/INFO: rpm: /; 1291 if ($verbose > 0) { 1292 print $cmd_out . "\n"; 1293 } 1294 } 1295 } 1296 1297 # --------- main program --------------------------------------------------- 1298 sub main { 1299 process_options; 1300 1301 if (not defined ($build_command)) { 1302 usage (1); 1303 } 1304 1305 if (not defined ($specs_to_build[0])) { 1306 msg_info (0, "Nothing to do."); 1307 exit (0); 1308 } 1309 1310 if ($build_command eq "build") { 1311 do_build; 1312 } if ($build_command eq "build-install") { 1313 do_build; 1314 } if ($build_command eq "build-only") { 1315 $build_and_install = 0; 1316 do_build; 1317 } if ($build_command eq "prep") { 1318 $build_and_install = 0; 1319 $prep_only = 1; 1320 do_build; 1321 } elsif ($build_command eq "uninstall-pkgs") { 1322 do_uninstall_pkgs; 1323 } elsif ($build_command eq "build-order") { 1324 do_build_order; 1325 } elsif ($build_command eq "install-order") { 1326 do_install_order; 1327 } 1328 1329 exit ($exit_val); 1330 } 1331 1332 main; 1333