1#! /usr/bin/perl 2# ex:ts=8 sw=4: 3# $OpenBSD: PkgCreate.pm,v 1.169 2020/07/25 12:14:16 ajacoutot Exp $ 4# 5# Copyright (c) 2003-2014 Marc Espie <espie@openbsd.org> 6# 7# Permission to use, copy, modify, and distribute this software for any 8# purpose with or without fee is hereby granted, provided that the above 9# copyright notice and this permission notice appear in all copies. 10# 11# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 19use strict; 20use warnings; 21 22use OpenBSD::AddCreateDelete; 23use OpenBSD::Dependencies::SolverBase; 24use OpenBSD::SharedLibs; 25use OpenBSD::Signer; 26 27package OpenBSD::PkgCreate::State; 28our @ISA = qw(OpenBSD::CreateSign::State); 29 30sub init 31{ 32 my $self = shift; 33 34 $self->{stash} = {}; 35 $self->SUPER::init(@_); 36 $self->{simple_status} = 0; 37} 38 39sub stash 40{ 41 my ($self, $key) = @_; 42 return $self->{stash}{$key}; 43} 44 45sub error 46{ 47 my $self = shift; 48 my $msg = shift; 49 $self->{bad}++; 50 $self->progress->disable; 51 $self->errsay("Error: $msg", @_); 52} 53 54sub set_status 55{ 56 my ($self, $status) = @_; 57 if ($self->{simple_status}) { 58 print "\n$status"; 59 } else { 60 if ($self->progress->set_header($status)) { 61 $self->progress->message(''); 62 } else { 63 $| = 1; 64 print "$status..."; 65 $self->{simple_status} = 1; 66 } 67 } 68} 69 70sub end_status 71{ 72 my $self = shift; 73 74 if ($self->{simple_status}) { 75 print "\n"; 76 } else { 77 $self->progress->clear; 78 } 79} 80 81sub handle_options 82{ 83 my $state = shift; 84 85 $state->{system_version} = 0; 86 $state->{opt} = { 87 'f' => 88 sub { 89 push(@{$state->{contents}}, shift); 90 }, 91 'p' => 92 sub { 93 $state->{prefix} = shift; 94 }, 95 'P' => sub { 96 my $d = shift; 97 $state->{dependencies}{$d} = 1; 98 }, 99 'V' => sub { 100 my $d = shift; 101 if ($d !~ m/^\d+$/) { 102 $state->usage("-V option requires a number"); 103 } 104 $state->{system_version} += $d; 105 }, 106 'w' => sub { 107 my $w = shift; 108 $state->{libset}{$w} = 1; 109 }, 110 'W' => sub { 111 my $w = shift; 112 $state->{wantlib}{$w} = 1; 113 }, 114 }; 115 $state->{no_exports} = 1; 116 $state->SUPER::handle_options('p:f:d:M:U:u:A:B:P:V:w:W:qQS', 117 '[-nQqvSx] [-A arches] [-B pkg-destdir] [-D name[=value]]', 118 '[-L localbase] [-M displayfile] [-P pkg-dependency]', 119 '[-U undisplayfile] [-u userlist] [-V n] [-W wantedlib]', 120 '[-w libset] [-d desc -D COMMENT=value -f packinglist -p prefix]', 121 'pkg-name'); 122 123 my $base = '/'; 124 if (defined $state->opt('B')) { 125 $base = $state->opt('B'); 126 } 127 128 $state->{base} = $base; 129 $state->{silent} = defined $state->opt('n') && defined $state->opt('n') 130 || defined $state->opt('S'); 131 if (defined $state->opt('u')) { 132 $state->{userlist} = $state->parse_userdb($state->opt('u')); 133 } 134 $state->{wrkobjdir} = $state->defines('WRKOBJDIR'); 135} 136 137sub parse_userdb 138{ 139 my ($self, $fname) = @_; 140 my $result = {}; 141 open(my $fh, '<', $fname) or 142 $self->error("Can't open #1: #2", $fname, $!); 143 # skip header 144 my $separator_found = 0; 145 while (<$fh>) { 146 if (m/^\-\-\-\-\-\-\-/) { 147 $separator_found = 1; 148 last; 149 } 150 } 151 if (!$separator_found) { 152 $self->error("File #1 does not appear to be a user.db", $fname); 153 return; 154 } 155 # record ids and error out on duplicates 156 my $known = {}; 157 while (<$fh>) { 158 next if m/^\#/; 159 chomp; 160 my @l = split(/\s+/, $_); 161 if (@l < 3 || $l[0] !~ m/^\d+$/ || $l[1] !~ m/^_/) { 162 $self->error("Bad line: #1 at #2 of #3", 163 $_, $., $fname); 164 next; 165 } 166 if (defined $known->{$l[0]}) { 167 $self->error("Duplicate id: #1 in #2", 168 $l[0], $fname); 169 next; 170 } 171 $known->{$l[0]} = 1; 172 $result->{$l[1]} = $l[0]; 173 } 174 return $result; 175} 176 177package OpenBSD::PkgCreate; 178 179use OpenBSD::PackingList; 180use OpenBSD::PackageInfo; 181use OpenBSD::Getopt; 182use OpenBSD::Temp; 183use OpenBSD::Error; 184use OpenBSD::Ustar; 185use OpenBSD::ArcCheck; 186use OpenBSD::Paths; 187use File::Basename; 188 189# Extra stuff needed to archive files 190package OpenBSD::PackingElement; 191sub create_package 192{ 193 my ($self, $state) = @_; 194 195 $self->archive($state); 196 if ($state->verbose) { 197 $self->comment_create_package($state); 198 } 199} 200 201sub pretend_to_archive 202{ 203 my ($self, $state) = @_; 204 $self->comment_create_package($state); 205} 206 207sub record_digest {} 208sub stub_digest {} 209sub archive {} 210sub really_archived { 0 } 211sub comment_create_package {} 212sub grab_manpages {} 213 214sub print_file {} 215 216sub avert_duplicates_and_other_checks 217{ 218 my ($self, $state) = @_; 219 return unless $self->NoDuplicateNames; 220 my $n = $self->fullname; 221 if (defined $state->stash($n)) { 222 $state->error("duplicate item in packing-list #1", $n); 223 } 224 $state->{stash}{$n} = 1; 225} 226 227sub makesum_plist 228{ 229 my ($self, $state, $plist) = @_; 230 $self->add_object($plist); 231} 232 233sub verify_checksum 234{ 235} 236 237sub register_forbidden 238{ 239 my ($self, $state) = @_; 240 if ($self->is_forbidden) { 241 push(@{$state->{forbidden}}, $self); 242 } 243} 244 245sub is_forbidden() { 0 } 246sub resolve_link 247{ 248 my ($filename, $base, $level) = @_; 249 $level //= 0; 250 if (-l $filename) { 251 my $l = readlink($filename); 252 if ($level++ > 14) { 253 return undef; 254 } 255 if ($l =~ m|^/|) { 256 return $base.resolve_link($l, $base, $level); 257 } else { 258 return resolve_link(File::Spec->catfile(File::Basename::dirname($filename),$l), $base, $level); 259 } 260 } else { 261 return $filename; 262 } 263} 264 265sub compute_checksum 266{ 267 my ($self, $result, $state, $base) = @_; 268 my $name = $self->fullname; 269 my $fname = $name; 270 if (defined $base) { 271 $fname = $base.$fname; 272 } 273 for my $field (qw(symlink link size ts)) { # md5 274 if (defined $result->{$field}) { 275 $state->error("User tried to define @#1 for #2", 276 $field, $fname); 277 } 278 } 279 if (defined $self->{wtempname}) { 280 $fname = $self->{wtempname}; 281 } 282 if (-l $fname) { 283 if (!defined $base) { 284 $state->error("special file #1 can't be a symlink", 285 $self->stringize); 286 } 287 my $value = readlink $fname; 288 my $chk = resolve_link($fname, $base); 289 $fname =~ s|^//|/|; # cosmetic 290 if (!defined $chk) { 291 $state->error("bogus symlink: #1 (too deep)", $fname); 292 } elsif (!-e $chk) { 293 push(@{$state->{bad_symlinks}{$chk}}, $fname); 294 } 295 if (defined $state->{wrkobjdir} && 296 $value =~ m/^\Q$state->{wrkobjdir}\E\//) { 297 $state->error( 298 "bad symlink: #1 (points into WRKOBJDIR)", 299 $fname); 300 } 301 $result->make_symlink($value); 302 } elsif (-f _) { 303 my ($dev, $ino, $size, $mtime) = (stat _)[0,1,7, 9]; 304 # XXX when rebuilding packages, tied updates can produce 305 # spurious hardlinks. We also refer to the installed plist 306 # we're rebuilding to know if we must checksum. 307 if (defined $state->stash("$dev/$ino") && !defined $self->{d}) { 308 $result->make_hardlink($state->stash("$dev/$ino")); 309 } else { 310 $state->{stash}{"$dev/$ino"} = $name; 311 $result->add_digest($self->compute_digest($fname)) 312 unless $state->{bad}; 313 $result->add_size($size); 314 $result->add_timestamp($mtime); 315 } 316 } elsif (-d _) { 317 $state->error("#1 should be a file and not a directory", $fname); 318 } else { 319 $state->error("#1 does not exist", $fname); 320 } 321} 322 323sub makesum_plist_with_base 324{ 325 my ($self, $plist, $state, $base) = @_; 326 $self->compute_checksum($self, $state, $base); 327 $self->add_object($plist); 328} 329 330sub verify_checksum_with_base 331{ 332 my ($self, $state, $base) = @_; 333 my $check = ref($self)->new($self->name); 334 $self->compute_checksum($check, $state, $base); 335 336 for my $field (qw(symlink link size)) { # md5 337 if ((defined $check->{$field} && defined $self->{$field} && 338 $check->{$field} ne $self->{$field}) || 339 (defined $check->{$field} xor defined $self->{$field})) { 340 $state->error("#1 inconsistency for #2", 341 $field, $self->fullname); 342 } 343 } 344 if ((defined $check->{d} && defined $self->{d} && 345 !$check->{d}->equals($self->{d})) || 346 (defined $check->{d} xor defined $self->{d})) { 347 $state->error("checksum inconsistency for #1", 348 $self->fullname); 349 } 350} 351 352 353sub prepare_for_archival 354{ 355 my ($self, $state) = @_; 356 357 my $o = $state->{archive}->prepare_long($self); 358 if (!$o->verify_modes($self)) { 359 $state->error("modes don't match for #1", $self->fullname); 360 } 361 if (!$o->is_allowed) { 362 $state->error("can't package #1", $self->fullname); 363 } 364 return $o; 365} 366 367sub discover_directories 368{ 369} 370 371sub check_version 372{ 373} 374 375package OpenBSD::PackingElement::StreamMarker; 376our @ISA = qw(OpenBSD::PackingElement::Meta); 377sub new 378{ 379 my $class = shift; 380 bless {}, $class; 381} 382 383sub comment_create_package 384{ 385 my ($self, $state) = @_; 386 $self->SUPER::comment_create_package($state); 387 $state->say("Gzip: next chunk"); 388} 389 390sub archive 391{ 392 my ($self, $state) = @_; 393 $state->new_gstream; 394} 395 396package OpenBSD::PackingElement::Meta; 397sub record_digest 398{ 399 my ($self, $original, $entries, $new, $tail) = @_; 400 push(@$new, $self); 401} 402 403sub stub_digest 404{ 405 my ($self, $ordered) = @_; 406 push(@$ordered, $self); 407} 408 409package OpenBSD::PackingElement::RcScript; 410sub set_destdir 411{ 412 my ($self, $state) = @_; 413 if ($self->name =~ m/^\//) { 414 $state->{archive}->destdir($state->{base}); 415 } else { 416 $self->SUPER::set_destdir($state); 417 } 418} 419 420package OpenBSD::PackingElement::SpecialFile; 421sub archive 422{ 423 &OpenBSD::PackingElement::FileBase::archive; 424} 425 426sub pretend_to_archive 427{ 428 &OpenBSD::PackingElement::FileBase::pretend_to_archive; 429} 430 431sub set_destdir 432{ 433} 434 435sub may_add 436{ 437 my ($class, $subst, $plist, $opt) = @_; 438 if (defined $opt) { 439 my $o = $class->add($plist); 440 $subst->copy($opt, $o->fullname) if defined $o->fullname; 441 } 442} 443 444sub comment_create_package 445{ 446 my ($self, $state) = @_; 447 $state->say("Adding #1", $self->name); 448} 449 450sub makesum_plist 451{ 452 my ($self, $state, $plist) = @_; 453 $self->makesum_plist_with_base($plist, $state, undef); 454} 455 456sub verify_checksum 457{ 458 my ($self, $state) = @_; 459 $self->verify_checksum_with_base($state, undef); 460} 461 462sub prepare_for_archival 463{ 464 my ($self, $state) = @_; 465 466 my $o = $state->{archive}->prepare_long($self); 467 $o->{uname} = 'root'; 468 $o->{gname} = 'wheel'; 469 $o->{uid} = 0; 470 $o->{gid} = 0; 471 $o->{mode} &= 0555; # zap all write and suid modes 472 return $o; 473} 474 475sub forbidden() { 1 } 476 477sub stub_digest 478{ 479 my ($self, $ordered) = @_; 480 push(@$ordered, $self); 481} 482 483# override for CONTENTS: we cannot checksum this. 484package OpenBSD::PackingElement::FCONTENTS; 485sub makesum_plist 486{ 487} 488 489sub verify_checksum 490{ 491} 492 493sub archive 494{ 495 my ($self, $state) = @_; 496 $self->SUPER::archive($state); 497 $state->new_gstream; 498} 499 500sub comment_create_package 501{ 502 my ($self, $state) = @_; 503 $self->SUPER::comment_create_package($state); 504 $state->say("GZIP: END OF SIGNATURE CHUNK"); 505} 506 507sub stub_digest 508{ 509 my ($self, $ordered) = @_; 510 push(@$ordered, $self); 511} 512 513package OpenBSD::PackingElement::Cwd; 514sub archive 515{ 516 my ($self, $state) = @_; 517} 518 519sub pretend_to_archive 520{ 521 my ($self, $state) = @_; 522 $self->comment_create_package($state); 523} 524 525sub comment_create_package 526{ 527 my ($self, $state) = @_; 528 $state->say("Cwd: #1", $self->name); 529} 530 531package OpenBSD::PackingElement::FileBase; 532 533sub record_digest 534{ 535 my ($self, $original, $entries, $new, $tail) = @_; 536 if (defined $self->{d}) { 537 my $k = $self->{d}->stringize; 538 push(@{$entries->{$k}}, $self); 539 push(@$original, $k); 540 } else { 541 push(@$tail, $self); 542 } 543} 544 545sub set_destdir 546{ 547 my ($self, $state) = @_; 548 549 $state->{archive}->destdir($state->{base}."/".$self->cwd); 550} 551 552sub archive 553{ 554 my ($self, $state) = @_; 555 556 $self->set_destdir($state); 557 my $o = $self->prepare_for_archival($state); 558 559 $o->write unless $state->{bad}; 560} 561 562sub really_archived { 1 } 563sub pretend_to_archive 564{ 565 my ($self, $state) = @_; 566 567 $self->set_destdir($state); 568 $self->prepare_for_archival($state); 569 $self->comment_create_package($state); 570} 571 572sub comment_create_package 573{ 574 my ($self, $state) = @_; 575 $state->say("Adding #1", $self->name); 576} 577 578sub print_file 579{ 580 my ($item) = @_; 581 print '@', $item->keyword, " ", $item->fullname, "\n"; 582} 583 584sub makesum_plist 585{ 586 my ($self, $state, $plist) = @_; 587 $self->makesum_plist_with_base($plist, $state, $state->{base}); 588} 589 590sub verify_checksum 591{ 592 my ($self, $state) = @_; 593 $self->verify_checksum_with_base($state, $state->{base}); 594} 595 596package OpenBSD::PackingElement::Dir; 597sub discover_directories 598{ 599 my ($self, $state) = @_; 600 $state->{known_dirs}->{$self->fullname} = 1; 601} 602 603package OpenBSD::PackingElement::InfoFile; 604sub makesum_plist 605{ 606 my ($self, $state, $plist) = @_; 607 $self->SUPER::makesum_plist($state, $plist); 608 my $fname = $self->fullname; 609 for (my $i = 1; ; $i++) { 610 if (-e "$state->{base}/$fname-$i") { 611 my $e = OpenBSD::PackingElement::File->add($plist, $self->name."-".$i); 612 $e->compute_checksum($e, $state, $state->{base}); 613 } else { 614 last; 615 } 616 } 617} 618 619package OpenBSD::PackingElement::Manpage; 620use File::Basename; 621 622sub grab_manpages 623{ 624 my ($self, $state) = @_; 625 my $filename; 626 if ($self->{wtempname}) { 627 $filename = $self->{wtempname}; 628 } else { 629 $filename = $state->{base}.$self->fullname; 630 } 631 push(@{$state->{manpages}}, $filename); 632} 633 634sub format_source_page 635{ 636 my ($self, $state, $plist) = @_; 637 638 if ($state->{subst}->empty("USE_GROFF") || !$self->is_source) { 639 return 0; 640 } 641 my $dest = $self->source_to_dest; 642 my $fullname = $self->cwd."/".$dest; 643 my $d = dirname($fullname); 644 $state->{mandir} //= OpenBSD::Temp::permanent_dir( 645 $ENV{TMPDIR} // '/tmp', "manpage") or 646 $state->error(OpenBSD::Temp->last_error) and 647 return 0; 648 my $tempname = $state->{mandir}.$fullname; 649 require File::Path; 650 File::Path::make_path($state->{mandir}.$d); 651 open my $fh, ">", $tempname; 652 if (!defined $fh) { 653 $state->error("can't create #1: #2", $tempname, $!); 654 return 0; 655 } 656 chmod 0444, $fh; 657 if (-d $state->{base}.$d) { 658 undef $d; 659 } 660 if (!$self->format($state, $tempname, $fh)) { 661 return 0; 662 } 663 if (-z $tempname) { 664 $state->errsay("groff produced empty result for #1", $dest); 665 $state->errsay("\tkeeping source manpage"); 666 return 0; 667 } 668 if (defined $d && !$state->{known_dirs}->{$d}) { 669 $state->{known_dirs}->{$d} = 1; 670 OpenBSD::PackingElement::Dir->add($plist, dirname($dest)); 671 } 672 my $e = OpenBSD::PackingElement::Manpage->add($plist, $dest); 673 $e->{wtempname} = $tempname; 674 $e->compute_checksum($e, $state, $state->{base}); 675 return 1; 676} 677 678sub makesum_plist 679{ 680 my ($self, $state, $plist) = @_; 681 if (!$self->format_source_page($state, $plist)) { 682 $self->SUPER::makesum_plist($state, $plist); 683 } 684} 685 686 687package OpenBSD::PackingElement::Depend; 688sub avert_duplicates_and_other_checks 689{ 690 my ($self, $state) = @_; 691 if (!$self->spec->is_valid) { 692 $state->error("invalid \@#1 #2 in packing-list", 693 $self->keyword, $self->stringize); 694 } 695 $self->SUPER::avert_duplicates_and_other_checks($state); 696} 697 698sub forbidden() { 1 } 699 700package OpenBSD::PackingElement::Conflict; 701sub avert_duplicates_and_other_checks 702{ 703 $_[1]->{has_conflict}++; 704 &OpenBSD::PackingElement::Depend::avert_duplicates_and_other_checks; 705} 706 707package OpenBSD::PackingElement::AskUpdate; 708sub avert_duplicates_and_other_checks 709{ 710 &OpenBSD::PackingElement::Depend::avert_duplicates_and_other_checks; 711} 712 713package OpenBSD::PackingElement::Dependency; 714sub avert_duplicates_and_other_checks 715{ 716 my ($self, $state) = @_; 717 718 $self->SUPER::avert_duplicates_and_other_checks($state); 719 720 my @issues = OpenBSD::PackageName->from_string($self->{def})->has_issues; 721 if (@issues > 0) { 722 $state->error("\@#1 #2\n #3, #4", 723 $self->keyword, $self->stringize, 724 $self->{def}, join(' ', @issues)); 725 } elsif ($self->spec->is_valid) { 726 my @m = $self->spec->filter($self->{def}); 727 if (@m == 0) { 728 $state->error("\@#1 #2\n pattern #3 doesn't match default #4\n", 729 $self->keyword, $self->stringize, 730 $self->{pattern}, $self->{def}); 731 } 732 } 733} 734 735package OpenBSD::PackingElement::Name; 736sub avert_duplicates_and_other_checks 737{ 738 my ($self, $state) = @_; 739 740 my @issues = OpenBSD::PackageName->from_string($self->name)->has_issues; 741 if (@issues > 0) { 742 $state->error("bad package name #1: ", $self->name, 743 join(' ', @issues)); 744 } 745 $self->SUPER::avert_duplicates_and_other_checks($state); 746} 747 748sub forbidden() { 1 } 749 750package OpenBSD::PackingElement::NoDefaultConflict; 751sub avert_duplicates_and_other_checks 752{ 753 my ($self, $state) = @_; 754 $state->{has_no_default_conflict}++; 755} 756 757package OpenBSD::PackingElement::NewAuth; 758sub avert_duplicates_and_other_checks 759{ 760 my ($self, $state) = @_; 761 my $userlist = $state->{userlist}; 762 if (defined $userlist) { 763 my $entry = $userlist->{$self->{name}}; 764 my $id = $self->id; 765 $id =~ s/^!//; 766 if (!defined $entry) { 767 $state->error("#1 #2: not registered in #3", 768 $self->keyword, $self->{name}, $state->opt('u')); 769 } elsif ($entry != $id) { 770 $state->error( 771 "#1 #2: id mismatch in #3 (#4 vs #5)", 772 $self->keyword, $self->{name}, $state->opt('u'), 773 $entry, $id); 774 } 775 } 776 $self->SUPER::avert_duplicates_and_other_checks($state); 777} 778 779package OpenBSD::PackingElement::NewUser; 780sub id 781{ 782 return shift->{uid}; 783} 784 785package OpenBSD::PackingElement::NewGroup; 786sub id 787{ 788 return shift->{gid}; 789} 790 791package OpenBSD::PackingElement::Lib; 792sub check_version 793{ 794 my ($self, $state, $unsubst) = @_; 795 my @l = $self->parse($self->name); 796 if (defined $l[0]) { 797 if (!$unsubst =~ m/\$\{LIB$l[0]_VERSION\}/) { 798 $state->error("Incorrectly versioned shared library: #1", $unsubst); 799 } 800 } else { 801 $state->error("Invalid shared library #1", $unsubst); 802 } 803 $state->{has_libraries} = 1; 804} 805 806package OpenBSD::PackingElement::DigitalSignature; 807sub is_forbidden() { 1 } 808 809package OpenBSD::PackingElement::Signer; 810sub is_forbidden() { 1 } 811 812package OpenBSD::PackingElement::ExtraInfo; 813sub is_forbidden() { 1 } 814 815package OpenBSD::PackingElement::ManualInstallation; 816sub is_forbidden() { 1 } 817 818package OpenBSD::PackingElement::Firmware; 819sub is_forbidden() { 1 } 820 821package OpenBSD::PackingElement::Url; 822sub is_forbidden() { 1 } 823 824package OpenBSD::PackingElement::Arch; 825sub is_forbidden() { 1 } 826 827package OpenBSD::PackingElement::LocalBase; 828sub is_forbidden() { 1 } 829 830package OpenBSD::PackingElement::Version; 831sub is_forbidden() { 1 } 832 833# put together file and filename, in order to handle fragments simply 834package MyFile; 835sub new 836{ 837 my ($class, $filename) = @_; 838 839 open(my $fh, '<', $filename) or return undef; 840 841 bless { fh => $fh, name => $filename }, (ref($class) || $class); 842} 843 844sub readline 845{ 846 my $self = shift; 847 return readline $self->{fh}; 848} 849 850sub name 851{ 852 my $self = shift; 853 return $self->{name}; 854} 855 856sub close 857{ 858 my $self = shift; 859 close($self->{fh}); 860} 861 862sub deduce_name 863{ 864 my ($self, $frag, $not, $p, $state) = @_; 865 866 my $o = $self->name; 867 my $noto = $o; 868 my $nofrag = "no-$frag"; 869 870 $o =~ s/PFRAG\./PFRAG.$frag-/o or 871 $o =~ s/PLIST/PFRAG.$frag/o; 872 873 $noto =~ s/PFRAG\./PFRAG.no-$frag-/o or 874 $noto =~ s/PLIST/PFRAG.no-$frag/o; 875 unless (-e $o or -e $noto) { 876 $p->missing_fragments($state, $frag, $o, $noto); 877 return; 878 } 879 if ($not) { 880 return $noto if -e $noto; 881 } else { 882 return $o if -e $o; 883 } 884 return; 885} 886 887# special solver class for PkgCreate 888package OpenBSD::Dependencies::CreateSolver; 889our @ISA = qw(OpenBSD::Dependencies::SolverBase); 890 891# we need to "hack" a special set 892sub new 893{ 894 my ($class, $plist) = @_; 895 bless { set => OpenBSD::PseudoSet->new($plist), 896 old_dependencies => {}, bad => [] }, $class; 897} 898 899sub solve_all_depends 900{ 901 my ($solver, $state) = @_; 902 903 $solver->{tag_finder} = OpenBSD::lookup::tag->new($solver, $state); 904 while (1) { 905 my @todo = $solver->solve_depends($state); 906 if (@todo == 0) { 907 return; 908 } 909 if ($solver->solve_wantlibs($state, 0)) { 910 return; 911 } 912 $solver->{set}->add_new(@todo); 913 } 914} 915 916sub solve_wantlibs 917{ 918 my ($solver, $state, $final) = @_; 919 920 my $okay = 1; 921 my $lib_finder = OpenBSD::lookup::library->new($solver); 922 my $h = $solver->{set}{new}[0]; 923 for my $lib (@{$h->{plist}{wantlib}}) { 924 $solver->{localbase} = $h->{plist}->localbase; 925 next if $lib_finder->lookup($solver, 926 $solver->{to_register}{$h}, $state, 927 $lib->spec); 928 $okay = 0; 929 OpenBSD::SharedLibs::report_problem($state, 930 $lib->spec) if $final; 931 } 932 if (!$okay && $final) { 933 $solver->dump($state); 934 $lib_finder->dump($state); 935 } 936 return $okay; 937} 938 939sub really_solve_dependency 940{ 941 my ($self, $state, $dep, $package) = @_; 942 943 $state->progress->message($dep->{pkgpath}); 944 945 # look in installed packages 946 my $v = $self->find_dep_in_installed($state, $dep); 947 if (!defined $v) { 948 $v = $self->find_dep_in_self($state, $dep); 949 } 950 951 # and in portstree otherwise 952 if (!defined $v) { 953 $v = $self->solve_from_ports($state, $dep, $package); 954 } 955 return $v; 956} 957 958sub diskcachename 959{ 960 my ($self, $dep) = @_; 961 962 if ($ENV{_DEPENDS_CACHE}) { 963 my $diskcache = $dep->{pkgpath}; 964 $diskcache =~ s/\//--/g; 965 return $ENV{_DEPENDS_CACHE}."/pkgcreate-".$diskcache; 966 } else { 967 return undef; 968 } 969} 970 971sub to_cache 972{ 973 my ($self, $plist, $final) = @_; 974 # try to cache atomically. 975 # no error if it doesn't work 976 require OpenBSD::MkTemp; 977 my ($fh, $tmp) = OpenBSD::MkTemp::mkstemp( 978 "$ENV{_DEPENDS_CACHE}/my.XXXXXXXXXXX") or return; 979 chmod 0644, $fh; 980 $plist->write($fh); 981 close($fh); 982 rename($tmp, $final); 983 unlink($tmp); 984} 985 986sub ask_tree 987{ 988 my ($self, $state, $pkgpath, $portsdir, $data, @action) = @_; 989 990 my $make = OpenBSD::Paths->make; 991 my $errors = OpenBSD::Temp->file; 992 if (!defined $errors) { 993 $state->fatal(OpenBSD::Temp->last_error); 994 } 995 my $pid = open(my $fh, "-|"); 996 if (!defined $pid) { 997 $state->fatal("cannot fork: #1", $!); 998 } 999 if ($pid == 0) { 1000 $ENV{FULLPATH} = 'Yes'; 1001 delete $ENV{FLAVOR}; 1002 delete $ENV{SUBPACKAGE}; 1003 $ENV{SUBDIR} = $pkgpath; 1004 $ENV{ECHO_MSG} = ':'; 1005 1006 if (!chdir $portsdir) { 1007 $state->errsay("Can't chdir #1: #2", $portsdir, $!); 1008 exit(2); 1009 } 1010 open STDERR, ">>", $errors; 1011 # make sure the child starts with a single identity 1012 $( = $); $< = $>; 1013 # XXX we're already running as ${BUILD_USER} 1014 # so we can't do this again 1015 push(@action, 'PORTS_PRIVSEP=No'); 1016 $DB::inhibit_exit = 0; 1017 exec $make ('make', @action); 1018 } 1019 my $plist = OpenBSD::PackingList->read($fh, $data); 1020 while(<$fh>) { # XXX avoid spurious errors from child 1021 } 1022 close($fh); 1023 if ($? != 0) { 1024 $state->errsay("child running '#2' failed: #1", 1025 $state->child_error, 1026 join(' ', 'make', @action)); 1027 if (open my $fh, '<', $errors) { 1028 while(<$fh>) { 1029 $state->errprint("#1", $_); 1030 } 1031 close($fh); 1032 } 1033 } 1034 unlink($errors); 1035 return $plist; 1036} 1037 1038sub really_solve_from_ports 1039{ 1040 my ($self, $state, $dep, $portsdir) = @_; 1041 1042 my $diskcache = $self->diskcachename($dep); 1043 my $plist; 1044 1045 if (defined $diskcache && -f $diskcache) { 1046 $plist = OpenBSD::PackingList->fromfile($diskcache); 1047 } else { 1048 $plist = $self->ask_tree($state, $dep->{pkgpath}, $portsdir, 1049 \&OpenBSD::PackingList::PrelinkStuffOnly, 1050 'print-plist-libs-with-depends', 1051 'wantlib_args=no-wantlib-args'); 1052 if ($? != 0 || !defined $plist->pkgname) { 1053 return undef; 1054 } 1055 if (defined $diskcache) { 1056 $self->to_cache($plist, $diskcache); 1057 } 1058 } 1059 OpenBSD::SharedLibs::add_libs_from_plist($plist, $state); 1060 $self->{tag_finder}->find_in_plist($plist, $dep->{pkgpath}); 1061 $self->add_dep($plist); 1062 return $plist->pkgname; 1063} 1064 1065my $cache = {}; 1066 1067sub solve_from_ports 1068{ 1069 my ($self, $state, $dep, $package) = @_; 1070 1071 my $portsdir = $state->defines('PORTSDIR'); 1072 return undef unless defined $portsdir; 1073 my $pkgname; 1074 if (defined $cache->{$dep->{pkgpath}}) { 1075 $pkgname = $cache->{$dep->{pkgpath}}; 1076 } else { 1077 $pkgname = $self->really_solve_from_ports($state, $dep, 1078 $portsdir); 1079 $cache->{$dep->{pkgpath}} = $pkgname; 1080 } 1081 if (!defined $pkgname) { 1082 $state->error("Can't obtain dependency #1 from ports tree", 1083 $dep->{pattern}); 1084 return undef; 1085 } 1086 if ($dep->spec->filter($pkgname) == 0) { 1087 $state->error("Dependency #1 doesn't match FULLPKGNAME: #2", 1088 $dep->{pattern}, $pkgname); 1089 return undef; 1090 } 1091 1092 return $pkgname; 1093} 1094 1095# we don't want old libs 1096sub find_old_lib 1097{ 1098 return undef; 1099} 1100 1101package OpenBSD::PseudoHandle; 1102sub new 1103{ 1104 my ($class, $plist) = @_; 1105 bless { plist => $plist}, $class; 1106} 1107 1108sub pkgname 1109{ 1110 my $self = shift; 1111 1112 return $self->{plist}->pkgname; 1113} 1114 1115sub dependency_info 1116{ 1117 my $self = shift; 1118 return $self->{plist}; 1119} 1120 1121package OpenBSD::PseudoSet; 1122sub new 1123{ 1124 my ($class, @elements) = @_; 1125 1126 my $o = bless {}, $class; 1127 $o->add_new(@elements); 1128} 1129 1130sub add_new 1131{ 1132 my ($self, @elements) = @_; 1133 for my $i (@elements) { 1134 push(@{$self->{new}}, OpenBSD::PseudoHandle->new($i)); 1135 } 1136 return $self; 1137} 1138 1139sub newer 1140{ 1141 return @{shift->{new}}; 1142} 1143 1144 1145sub newer_names 1146{ 1147 return map {$_->pkgname} @{shift->{new}}; 1148} 1149 1150sub older 1151{ 1152 return (); 1153} 1154 1155sub older_names 1156{ 1157 return (); 1158} 1159 1160sub kept 1161{ 1162 return (); 1163} 1164 1165sub kept_names 1166{ 1167 return (); 1168} 1169 1170sub print 1171{ 1172 my $self = shift; 1173 return $self->{new}[0]->pkgname; 1174} 1175 1176package OpenBSD::PkgCreate; 1177our @ISA = qw(OpenBSD::AddCreateDelete); 1178 1179sub handle_fragment 1180{ 1181 my ($self, $state, $old, $not, $frag, undef, $cont, $msg) = @_; 1182 my $def = $frag; 1183 if ($state->{subst}->has_fragment($def, $frag, $msg)) { 1184 return undef if defined $not; 1185 } else { 1186 return undef unless defined $not; 1187 } 1188 my $newname = $old->deduce_name($frag, $not, $self, $state); 1189 if (defined $newname) { 1190 $state->set_status("switching to $newname") 1191 unless $state->{silent}; 1192 my $f = $old->new($newname); 1193 if (!defined $f) { 1194 $self->cant_read_fragment($state, $newname); 1195 } else { 1196 return $f; 1197 } 1198 } 1199 return undef; 1200} 1201 1202sub FileClass 1203{ 1204 return "MyFile"; 1205} 1206 1207# hook for update-plist, which wants to record fragment positions 1208sub record_fragment 1209{ 1210} 1211 1212# hook for update-plist, which wants to record original file info 1213sub annotate 1214{ 1215} 1216 1217sub read_fragments 1218{ 1219 my ($self, $state, $plist, $filename) = @_; 1220 1221 my $stack = []; 1222 my $subst = $state->{subst}; 1223 my $main = $self->FileClass->new($filename); 1224 return undef if !defined $main; 1225 push(@$stack, $main); 1226 my $fast = $subst->value("LIBS_ONLY"); 1227 1228 return $plist->read($stack, 1229 sub { 1230 my ($stack, $cont) = @_; 1231 while(my $file = pop @$stack) { 1232 while (my $l = $file->readline) { 1233 $state->progress->working(2048) 1234 unless $state->{silent}; 1235 # add a file name to uncommitted cvs tags so 1236 # that the plist is always the same 1237 if ($l =~m/^(\@comment\s+\$(?:Open)BSD\$)$/o) { 1238 $l = '@comment $'.'OpenBSD: '.basename($file->name).',v$'; 1239 } 1240 if ($l =~ m/^(\!)?\%\%(.*)\%\%$/) { 1241 $self->record_fragment($plist, $1, $2, 1242 $file); 1243 if (my $f2 = $self->handle_fragment($state, $file, $1, $2, $l, $cont, $filename)) { 1244 push(@$stack, $file); 1245 $file = $f2; 1246 } 1247 next; 1248 } 1249 my $s = $subst->do($l); 1250 if ($fast) { 1251 next unless $s =~ m/^\@(?:cwd|lib|libset|define-tag|depend|wantlib)\b/o || $s =~ m/lib.*\.a$/o; 1252 } 1253 # XXX some things, like @comment no checksum, don't produce an object 1254 my $o = &$cont($s); 1255 if (defined $o) { 1256 $o->check_version($state, $s); 1257 $self->annotate($o, $l, $file); 1258 } 1259 } 1260 } 1261 }); 1262} 1263 1264sub add_description 1265{ 1266 my ($state, $plist, $name, $opt_d) = @_; 1267 my $o = OpenBSD::PackingElement::FDESC->add($plist, $name); 1268 my $subst = $state->{subst}; 1269 my $comment = $subst->value('COMMENT'); 1270 if (defined $comment) { 1271 if (length $comment > 60) { 1272 $state->fatal("comment is too long\n#1\n#2\n", 1273 $comment, ' 'x60 . "^" x (length($comment)-60)); 1274 } 1275 } else { 1276 $state->usage("Comment required"); 1277 } 1278 if (!defined $opt_d) { 1279 $state->usage("Description required"); 1280 } 1281 return if defined $state->opt('q'); 1282 1283 open(my $fh, '+>', $o->fullname) or die "Can't write to DESCR: $!"; 1284 if (defined $comment) { 1285 print $fh $subst->do($comment), "\n"; 1286 } 1287 if ($opt_d =~ /^\-(.*)$/o) { 1288 print $fh $1, "\n"; 1289 } else { 1290 $subst->copy_fh($opt_d, $fh); 1291 } 1292 if (defined $comment) { 1293 if ($subst->empty('MAINTAINER')) { 1294 $state->errsay("no MAINTAINER"); 1295 } else { 1296 print $fh "\n", 1297 $subst->do('Maintainer: ${MAINTAINER}'), "\n"; 1298 } 1299 if (!$subst->empty('HOMEPAGE')) { 1300 print $fh "\n", $subst->do('WWW: ${HOMEPAGE}'), "\n"; 1301 } 1302 } 1303 seek($fh, 0, 0) or die "Can't rewind DESCR: $!"; 1304 my $errors = 0; 1305 while (<$fh>) { 1306 chomp; 1307 if ($state->safe($_) ne $_) { 1308 $state->errsay( 1309 "DESCR contains weird characters: #1 on line #2", 1310 $_, $.); 1311 $errors++; 1312 } 1313 } 1314 if ($errors) { 1315 $state->fatal("Can't continue"); 1316 } 1317 close($fh); 1318} 1319 1320sub add_extra_info 1321{ 1322 my ($self, $plist, $state) = @_; 1323 1324 my $subst = $state->{subst}; 1325 my $fullpkgpath = $subst->value('FULLPKGPATH'); 1326 my $cdrom = $subst->value('PERMIT_PACKAGE_CDROM') || 1327 $subst->value('CDROM');; 1328 my $ftp = $subst->value('PERMIT_PACKAGE_FTP') || 1329 $subst->value('FTP'); 1330 if (defined $fullpkgpath || defined $cdrom || defined $ftp) { 1331 $fullpkgpath //= ''; 1332 $ftp //= 'no'; 1333 $ftp = 'yes' if $ftp =~ m/^yes$/io; 1334 $cdrom = 'yes' if defined $cdrom && $cdrom =~ m/^yes$/io; 1335 1336 OpenBSD::PackingElement::ExtraInfo->add($plist, 1337 $fullpkgpath, $cdrom, $ftp); 1338 } else { 1339 $state->errsay("Package without FULLPKGPATH"); 1340 } 1341} 1342 1343sub add_elements 1344{ 1345 my ($self, $plist, $state) = @_; 1346 1347 my $subst = $state->{subst}; 1348 add_description($state, $plist, DESC, $state->opt('d')); 1349 OpenBSD::PackingElement::FDISPLAY->may_add($subst, $plist, 1350 $state->opt('M')); 1351 OpenBSD::PackingElement::FUNDISPLAY->may_add($subst, $plist, 1352 $state->opt('U')); 1353 for my $d (sort keys %{$state->{dependencies}}) { 1354 OpenBSD::PackingElement::Dependency->add($plist, $d); 1355 } 1356 1357 for my $w (sort keys %{$state->{wantlib}}) { 1358 OpenBSD::PackingElement::Wantlib->add($plist, $w); 1359 } 1360 for my $w (sort keys %{$state->{libset}}) { 1361 OpenBSD::PackingElement::Libset->add($plist, $w); 1362 } 1363 1364 if (defined $state->opt('A')) { 1365 OpenBSD::PackingElement::Arch->add($plist, $state->opt('A')); 1366 } 1367 1368 if (defined $state->opt('L')) { 1369 OpenBSD::PackingElement::LocalBase->add($plist, $state->opt('L')); 1370 $state->{groff} = $state->opt('L'). '/bin/groff'; 1371 } 1372 $self->add_extra_info($plist, $state); 1373 if ($state->{system_version}) { 1374 OpenBSD::PackingElement::Version->add($plist, 1375 $state->{system_version}); 1376 } 1377} 1378 1379sub cant_read_fragment 1380{ 1381 my ($self, $state, $frag) = @_; 1382 $state->fatal("can't read packing-list #1", $frag); 1383} 1384 1385sub missing_fragments 1386{ 1387 my ($self, $state, $frag, $o, $noto) = @_; 1388 $state->fatal("Missing fragments for #1: #2 and #3 don't exist", 1389 $frag, $o, $noto); 1390} 1391 1392sub read_all_fragments 1393{ 1394 my ($self, $state, $plist) = @_; 1395 1396 if (defined $state->{prefix}) { 1397 OpenBSD::PackingElement::Cwd->add($plist, $state->{prefix}); 1398 } else { 1399 $state->usage("Prefix required"); 1400 } 1401 for my $contentsfile (@{$state->{contents}}) { 1402 $self->read_fragments($state, $plist, $contentsfile) or 1403 $self->cant_read_fragment($state, $contentsfile); 1404 } 1405 1406 $plist->register_forbidden($state); 1407 if (defined $state->{forbidden}) { 1408 for my $e (@{$state->{forbidden}}) { 1409 $state->errsay("Error: #1 can't be set explicitly", "\@".$e->keyword." ".$e->stringize); 1410 } 1411 $state->fatal("Can't continue"); 1412 } 1413} 1414 1415sub create_plist 1416{ 1417 my ($self, $state, $pkgname) = @_; 1418 1419 my $plist = OpenBSD::PackingList->new; 1420 1421 if ($pkgname =~ m|([^/]+)$|o) { 1422 $pkgname = $1; 1423 $pkgname =~ s/\.tgz$//o; 1424 } 1425 $plist->set_pkgname($pkgname); 1426 unless ($state->{silent}) { 1427 $state->say("Creating package #1", $pkgname) 1428 if defined $state->opt('v'); 1429 $state->set_status("reading plist"); 1430 } 1431 my $dir = OpenBSD::Temp->dir; 1432 if (!$dir) { 1433 $state->fatal(OpenBSD::Temp->last_error); 1434 } 1435 $plist->set_infodir($dir); 1436 if (!defined $state->opt('S')) { 1437 $self->read_all_fragments($state, $plist); 1438 } 1439 $self->add_elements($plist, $state); 1440 1441 return $plist; 1442} 1443 1444sub make_plist_with_sum 1445{ 1446 my ($self, $state, $plist) = @_; 1447 my $p2 = OpenBSD::PackingList->new; 1448 $state->progress->visit_with_count($plist, 'makesum_plist', $p2); 1449 $p2->set_infodir($plist->infodir); 1450 return $p2; 1451} 1452 1453sub read_existing_plist 1454{ 1455 my ($self, $state, $contents) = @_; 1456 1457 my $plist = OpenBSD::PackingList->new; 1458 if (-d $contents && -f $contents.'/'.CONTENTS) { 1459 $plist->set_infodir($contents); 1460 $contents .= '/'.CONTENTS; 1461 } else { 1462 $plist->set_infodir(dirname($contents)); 1463 } 1464 $plist->fromfile($contents) or 1465 $state->fatal("can't read packing-list #1", $contents); 1466 return $plist; 1467} 1468 1469sub create_package 1470{ 1471 my ($self, $state, $plist, $ordered, $wname) = @_; 1472 1473 $state->say("Creating gzip'd tar ball in '#1'", $wname) 1474 if $state->opt('v'); 1475 my $h = sub { 1476 unlink $wname; 1477 my $caught = shift; 1478 $SIG{$caught} = 'DEFAULT'; 1479 kill $caught, $$; 1480 }; 1481 1482 local $SIG{'INT'} = $h; 1483 local $SIG{'QUIT'} = $h; 1484 local $SIG{'HUP'} = $h; 1485 local $SIG{'KILL'} = $h; 1486 local $SIG{'TERM'} = $h; 1487 $state->{archive} = $state->create_archive($wname, $plist->infodir); 1488 $state->set_status("archiving"); 1489 my $p = $state->progress->new_sizer($plist, $state); 1490 for my $e (@$ordered) { 1491 $e->create_package($state); 1492 $p->advance($e); 1493 } 1494 $state->end_status; 1495 $state->{archive}->close; 1496 if ($state->{bad}) { 1497 unlink($wname); 1498 exit(1); 1499 } 1500} 1501 1502sub show_bad_symlinks 1503{ 1504 my ($self, $state) = @_; 1505 for my $dest (sort keys %{$state->{bad_symlinks}}) { 1506 $state->errsay("Warning: symlink(s) point to non-existent #1", 1507 $dest); 1508 for my $link (@{$state->{bad_symlinks}{$dest}}) { 1509 $state->errsay("\t#1", $link); 1510 } 1511 } 1512} 1513 1514sub check_dependencies 1515{ 1516 my ($self, $plist, $state) = @_; 1517 1518 my $solver = OpenBSD::Dependencies::CreateSolver->new($plist); 1519 1520 # look for libraries in the "real" tree 1521 $state->{destdir} = '/'; 1522 1523 $solver->solve_all_depends($state); 1524 if (!$solver->solve_wantlibs($state, 1)) { 1525 $state->{bad}++; 1526 } 1527} 1528 1529sub finish_manpages 1530{ 1531 my ($self, $state, $plist) = @_; 1532 $plist->grab_manpages($state); 1533 if (defined $state->{manpages}) { 1534 $state->run_makewhatis(['-t'], $state->{manpages}); 1535 } 1536 1537 if (defined $state->{mandir}) { 1538 require File::Path; 1539 File::Path::remove_tree($state->{mandir}); 1540 } 1541} 1542 1543sub save_history 1544{ 1545 my ($self, $plist, $dir) = @_; 1546 1547 # grab the old stuff: 1548 # - order 1549 # - and presence 1550 my (%known, %found); 1551 my $fname; 1552 if (defined $dir) { 1553 unless (-d $dir) { 1554 require File::Path; 1555 1556 File::Path::make_path($dir); 1557 } 1558 1559 my $name = $plist->fullpkgpath; 1560 $name =~ s,/,.,g; 1561 my $fname = "$dir/$name"; 1562 my $n = 0; 1563 1564 if (open(my $f, '<', $fname)) { 1565 while (<$f>) { 1566 chomp; 1567 $known{$_} //= $n++; 1568 } 1569 close($f); 1570 } 1571 } 1572 my @new; 1573 my $entries = {}; 1574 my $list = []; 1575 my $tail = []; 1576 $plist->record_digest(\@new, $entries, $list, $tail); 1577 1578 my $f; 1579 if (defined $fname) { 1580 open($f, ">", "$fname.new"); 1581 } 1582 1583 # split list 1584 # - first, unknown stuff 1585 for my $h (@new) { 1586 if ($known{$h}) { 1587 $found{$h} = $known{$h}; 1588 } else { 1589 print $f "$h\n" if defined $f; 1590 push(@$list, (shift @{$entries->{$h}})); 1591 } 1592 } 1593 # - then known stuff, preserve the order 1594 for my $h (sort {$found{$a} <=> $found{$b}} keys %found) { 1595 print $f "$h\n" if defined $f; 1596 push(@$list, @{$entries->{$h}}); 1597 } 1598 if (defined $f) { 1599 close($f); 1600 rename("$fname.new", $fname); 1601 } 1602 # create a new list with check points. 1603 my $l = [@$tail]; 1604 my $i = 0; 1605 my $end_marker = OpenBSD::PackingElement::StreamMarker->new; 1606 while (@$list > 0) { 1607 my $e = pop @$list; 1608 if ($e->really_archived && $i++ % 16 == 0) { 1609 unshift @$l, $end_marker; 1610 } 1611 unshift @$l, $e; 1612 } 1613 # remove extraneous marker if @$tail is empty. 1614 if ($l->[-1] eq $end_marker) { 1615 pop @$l; 1616 } 1617 return $l; 1618} 1619 1620sub run_command 1621{ 1622 my ($self, $state) = @_; 1623 if (defined $state->opt('Q')) { 1624 $state->{opt}{q} = 1; 1625 } 1626 1627 if (!defined $state->{contents}) { 1628 $state->usage("Packing-list required"); 1629 } 1630 1631 my $plist; 1632 if ($state->{regen_package}) { 1633 if (!defined $state->{contents} || @{$state->{contents}} > 1) { 1634 $state->usage("Exactly one single packing-list is required"); 1635 } 1636 $plist = $self->read_existing_plist($state, 1637 $state->{contents}[0]); 1638 } else { 1639 $plist = $self->create_plist($state, $ARGV[0]); 1640 } 1641 1642 1643 if (defined $state->opt('S')) { 1644 print $plist->signature->string, "\n"; 1645 exit 0; 1646 } 1647 $plist->discover_directories($state); 1648 my $ordered = []; 1649 unless (defined $state->opt('q') && defined $state->opt('n')) { 1650 $state->set_status("checking dependencies"); 1651 $self->check_dependencies($plist, $state); 1652 if ($state->defines("stub")) { 1653 $plist->stub_digest($ordered); 1654 } else { 1655 $state->set_status("checksumming"); 1656 if ($state->{regen_package}) { 1657 $state->progress->visit_with_count($plist, 1658 'verify_checksum'); 1659 } else { 1660 $plist = $self->make_plist_with_sum($state, 1661 $plist); 1662 } 1663 $ordered = $self->save_history($plist, 1664 $state->defines('HISTORY_DIR')); 1665 $self->show_bad_symlinks($state); 1666 } 1667 $state->end_status; 1668 } 1669 1670 if (!defined $plist->pkgname) { 1671 $state->fatal("can't write unnamed packing-list"); 1672 } 1673 1674 if (defined $state->opt('q')) { 1675 if (defined $state->opt('Q')) { 1676 $plist->print_file; 1677 } else { 1678 $plist->write(\*STDOUT); 1679 } 1680 return 0 if defined $state->opt('n'); 1681 } 1682 1683 if ($plist->{deprecated}) { 1684 $state->fatal("found obsolete constructs"); 1685 } 1686 1687 $plist->avert_duplicates_and_other_checks($state); 1688 if ($state->{has_no_default_conflict} && !$state->{has_conflict}) { 1689 $state->errsay("Warning: \@option no-default-conflict without \@conflict"); 1690 } 1691 $state->{stash} = {}; 1692 1693 if ($state->{bad} && !$state->defines('REGRESSION_TESTING')) { 1694 $state->fatal("can't continue"); 1695 } 1696 $state->{bad} = 0; 1697 1698 my $wname; 1699 if ($state->{regen_package}) { 1700 $wname = $plist->pkgname.".tgz"; 1701 } else { 1702 $plist->save or $state->fatal("can't write packing-list"); 1703 $wname = $ARGV[0]; 1704 } 1705 1706 if ($state->opt('n')) { 1707 $state->{archive} = OpenBSD::Ustar->new(undef, $state, 1708 $plist->infodir); 1709 $plist->pretend_to_archive($state); 1710 } else { 1711 $self->create_package($state, $plist, $ordered, $wname); 1712 } 1713 if (!$state->defines("stub")) { 1714 $self->finish_manpages($state, $plist); 1715 } 1716} 1717 1718sub parse_and_run 1719{ 1720 my ($self, $cmd) = @_; 1721 1722 my $sign_only = 0; 1723 my $rc = 0; 1724 1725 my $state = OpenBSD::PkgCreate::State->new($cmd); 1726 $state->handle_options; 1727 1728 if (@ARGV == 0) { 1729 $state->{regen_package} = 1; 1730 } elsif (@ARGV != 1) { 1731 if (defined $state->{contents} || 1732 !defined $state->{signature_params}) { 1733 $state->usage("Exactly one single package name is required: #1", join(' ', @ARGV)); 1734 } 1735 } 1736 1737 $self->try_and_run_command($state); 1738 return $state->{bad} != 0; 1739} 1740 17411; 1742