# ex:ts=8 sw=4: # $OpenBSD: PortBuilder.pm,v 1.95 2023/06/21 08:56:17 espie Exp $ # # Copyright (c) 2010-2013 Marc Espie # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. use v5.36; # this object is responsible for launching the build of ports # which mostly includes starting the right jobs package DPB::PortBuilder; use File::Path; use DPB::Util; use DPB::Job::Port; use DPB::Serialize; sub new($class, $state) { if ($state->opt('R')) { require DPB::PortBuilder::Rebuild; $class = $class->rebuild_class; } my $self = bless { state => $state, clean => $state->opt('c'), fetch => $state->opt('f'), wantsize => $state->{wantsize}, fullrepo => $state->fullrepo, realfullrepo => $state->anchor($state->fullrepo), sizer => $state->sizer, heuristics => $state->heuristics}, $class; if ($state->opt('u') || $state->opt('U')) { $self->{update} = 1; } if ($state->opt('U')) { $self->{forceupdate} = 1; } if ($self->{fetch} && $state->defines('NO_CHECKSUM')) { $self->{nochecksum} = 1; } $self->init; return $self; } sub want_size($self, $v, $core) { if (!$self->{wantsize}) { return 0; } # always show for inmem if ($core->{inmem}) { return 1; } # do we already have this stats ? don't run it every time if ($self->{sizer}->match_pkgname($v)) { return rand(10) < 1; } else { return 1; } } sub rebuild_class($) { 'DPB::PortBuilder::Rebuild' } sub ports($self) { return $self->{state}->ports; } sub logger($self) { return $self->{state}->logger; } sub locker($self) { return $self->{state}->locker; } sub dontjunk($self, $v) { $self->{dontjunk}{$v->fullpkgname} = 1; } sub should_clean($self, $v) { my $state = $self->{state}; if ($state->{never_clean}) { return 0; } else { return !$state->{dontclean}{$v->pkgpath}; } } sub make($self) { return $self->{state}->make; } sub make_args($self) { return $self->{state}->make_args; } sub init($self) { my $state = $self->{state}; $state->{build_user}->make_path($self->{realfullrepo}); $state->{fetch_user}->make_path($state->anchor( join("/", $state->{repo}, $state->arch, "cache"))); $self->{global} = $self->logger->append("build"); $self->{lockperf} = DPB::Util->make_hot($self->logger->append("awaiting-locks")); if ($self->{wantsize}) { $self->{logsize} = DPB::Util->make_hot($self->logger->append("size")); } if ($state->defines("WRAP_MAKE")) { $self->{rsslog} = $self->logger->logfile("rss"); $self->{wrapper} = $state->defines("WRAP_MAKE"); } } sub pkgfile($self, $v) { my $name = $v->fullpkgname; return "$self->{realfullrepo}/$name.tgz"; } sub check($self, $v) { return $self->{state}{build_user}->run_as( sub() { return -f $self->pkgfile($v); }); } sub end_check # forwarder { ✓ } sub checks_rebuild($, $) { } sub register_package($, $) { } sub report($self, $v, $job, $core) { return if $job->{signature_only}; my $pkgpath = $v->fullpkgpath; my $host = $core->fullhostname; if ($core->{realjobs}) { $host .= '*'.$core->{realjobs}; } my $log = $self->{global}; my $sz = $self->logger->run_as( sub() { return (stat $self->logger->log_pkgpath($v))[7]; }); if (defined $job->{offset}) { $sz -= $job->{offset}; } print $log "$pkgpath $host ", $job->totaltime, " ", $sz, " ", $job->timings; if ($job->{failed}) { my $fh = $self->logger->open('>>', $job->{log}); print $fh "Error: job failed with $job->{failed} on ", $core->hostname, " at ", CORE::time(), "\n" if defined $fh; print $log "!\n"; } else { print $log "\n"; return unless defined $self->{state}{permanent_log}; my $fh = $self->logger->open('>>', $self->{state}{permanent_log}); return unless defined $fh; print $fh DPB::Serialize::Build->write({ pkgpath => $pkgpath, host => $host, time => $job->totaltime, size => $sz, clean => $job->{fromscratch}}), "\n"; } } sub get($self) { return DPB::Core->get; } sub end_lock($self, $lock, $core, $job) { my $end = CORE::time(); $lock->write("status", $core->{status}); $lock->write("todo", $job->current_task); $lock->write("end", "$end (".DPB::Util->time2string($end).")"); $lock->close; } sub build($self, $v, $core, $lock, $final_sub) { my $start = CORE::time(); my ($log, $fh) = $self->logger->make_logs($v); my $memsize = $self->{sizer}->build_in_memory($fh, $core, $v); my $meminfo; if ($memsize) { $lock->write("mem", $memsize); $meminfo = " in memory"; $core->{inmem} = $memsize; } else { $meminfo = ""; $core->{inmem} = 0; } if (defined (my $t = $v->{info}->has_property('tag'))) { $lock->write("tag", $t); } print $fh ">>> Building on ", $core->hostname, $meminfo, " under "; $v->quick_dump($fh); my $job; # separate decl for closure $job = DPB::Job::Port->new( log => $log, logfh => $fh, v => $v, builder => $self, core => $core, lock => $lock, memsize => $memsize, endcode => sub($core) { $self->end_lock($lock, $core, $job); $self->report($v, $job, $core); &$final_sub($job->{failed}); }); $core->start_job($job); # lonesome takes precedence for swallowing everything if ($job->{lonesome}) { # make it arbitrarily high $core->can_swallow(1000); } elsif ($job->{parallel}) { $core->can_swallow($job->{parallel}-1); } $lock->write("host", $core->hostname); $lock->write("pid", $core->{pid}); $lock->write("start", "$start (".DPB::Util->time2string($start).")"); } sub wipe($self, $v, $core, $final_sub) { my ($log, $fh) = $self->logger->make_logs($v); print $fh ">>> Wiping on ", $core->hostname, " under "; $v->quick_dump($fh); my $job; # separate decl for endcode closure $job = DPB::Job::Port::Wipe->new( log => $log, logfh => $fh, v => $v, builder => $self, core => $core, endcode => sub($core) { $self->report($v, $job, $core); &$final_sub($job->{failed}); }); $core->start_job($job); } sub force_junk($self, $v, $core, $final_sub) { my $log = $self->logger->log_pkgpath($v); my $fh = $self->logger->open('>>', $log); print $fh ">>> Force junking on ", $core->hostname; my $job; # separate decl for endcode closure $job = DPB::Job::Port->new_junk_only( log => $log, logfh => $fh, v => $v, builder => $self, core => $core, endcode => sub($core) { &$final_sub($job->{failed}); $core->mark_ready; }); $core->start_job($job); } sub test($self, $v, $core, $lock, $final_sub) { my $start = CORE::time(); my $log = $self->logger->make_test_logs($v); my $memsize = $self->{sizer}->build_in_memory($core, $v); open my $fh, ">>", $log or DPB::Util->die_bang("can't open $log"); if ($memsize) { $lock->write("mem", $memsize); print $fh ">>> Building in memory under "; $core->{inmem} = $memsize; } else { print $fh ">>> Building under "; $core->{inmem} = 0; } if (defined (my $t = $v->{info}->has_property('tag'))) { $lock->write("tag", $t); } $v->quick_dump($fh); my $job; # separate decl for endcode closure $job = DPB::Job::Port::Test->new( log => $log, logfh => $fh, v => $v, builder => $self, core => $core, lock => $lock, memsize => $memsize, endcode => sub($core) { $self->end_lock($lock, $core, $job); $self->report($v, $job, $core); &$final_sub($job->{failed}); }); $core->start_job($job); $lock->write("host", $core->hostname); $lock->write("pid", $core->{pid}); $lock->write("start", "$start (".DPB::Util->time2string($start).")"); } sub install($self, $v, $core) { my ($log, $fh) = $self->logger->make_logs($v); print $fh ">>> Installing under "; $v->quick_dump($fh); my $job = DPB::Job::Port::Install->new( log => $log, logfh => $fh, v => $v, builder => $self, core => $core, endcode => sub($core) { $core->mark_ready; }); $core->start_job($job); return $core; } 1;