ports/infrastructure/lib/DPB/SubEngine/Build.pm

346 lines
7.6 KiB
Perl

# ex:ts=8 sw=4:
# $OpenBSD: Build.pm,v 1.25 2023/05/06 05:20:32 espie Exp $
#
# Copyright (c) 2010-2013 Marc Espie <espie@openbsd.org>
#
# 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;
package DPB::SubEngine::Build;
our @ISA = qw(DPB::SubEngine::BuildBase);
sub new($class, $engine, $builder)
{
my $o = $class->SUPER::new($engine, $builder);
$o->{toinstall} = [];
$o->{nfs} = {};
return $o;
}
sub preempt_core($self, $core)
{
if ($self->SUPER::preempt_core($core)) {
return 1;
}
if ($self->start_install($core)) {
return 1;
}
# note we don't actually remove stuff from the queue until needed,
# so mismatches holds a copy of stuff that's still there.
$self->{mismatches} = [];
$self->{tag_mismatches} = [];
$self->{klogged} = {};
return 0;
}
sub can_start_build($self, $v, $core)
{
if ($self->check_for_memory_hogs($v, $core)) {
push(@{$self->{mismatches}}, $v);
return 0;
}
# if the tag mismatch, we keep it for much much later.
# currently, we don't even try to recuperate, so this will
# fail abysmally if there's no junking going on
my $reason = $core->prop->taint_incompatible($v);
if (defined $reason) {
my $core2 = DPB::Core->get_compatible($v);
if (defined $core2) {
if ($self->lock_and_start_build($core2, $v)) {
return 0;
} else {
$core2->mark_ready;
}
}
if (!$self->{klogged}{$v->pkgpath}) {
$self->log('K', $v, " ".$core->hostname." ".$reason);
$self->{klogged}{$v->pkgpath} = 1;
}
push(@{$self->{tag_mismatches}}, $v);
return 0;
}
# keep affinity mismatches for later
if (defined $v->{affinity} && !$core->matches_affinity($v)) {
my $s = " ".$core->hostname;
$s .= " ".$v->{affinity};
$self->log('A', $v, $s);
# try to start them anyways, on the "right" core
my $core2 = DPB::Core->get_affinity($v);
if (defined $core2) {
if ($self->lock_and_start_build($core2, $v)) {
return 0;
} else {
$core2->mark_ready;
}
}
push(@{$self->{mismatches}}, $v);
return 0;
}
# if there's no external lock, we can build
if ($self->lock_and_start_build($core, $v)) {
return 1;
}
}
# we cheat a bit to standardize built paths
sub can_really_start_build($self, $v, $core)
{
for my $w (sort {$a->fullpkgpath cmp $b->fullpkgpath}
$v->build_path_list) {
next unless $self->{queue}->contains($w);
if ($self->SUPER::can_really_start_build($w, $core)) {
for my $k ($w->build_path_list) {
$self->remove($k);
}
return 1;
}
}
return 0;
}
sub check_for_memory_hogs($self, $v, $core)
{
if ($v->{info}->has_property('memoryhog')) {
for my $job ($core->same_host_jobs) {
if ($job->{v}{info}->has_property('memoryhog')) {
return 1;
}
}
}
return 0;
}
sub can_be_junked($self, $v, $core)
{
my $tag = $v->{info}->has_property('tag');
for my $job ($core->same_host_jobs) {
if ($job->{nojunk}) {
return 0;
}
if ($job->{v}{info}->has_property('tag') &&
$job->{v}{info}->has_property('tag') ne $tag) {
return 0;
}
}
my ($nojunk, $h) =
$self->{engine}{locker}->find_dependencies($core->hostname);
if ($nojunk) {
return 0;
}
return 1;
}
sub recheck_mismatches($self, $core)
{
# first let's try to force junking
if (@{$self->{tag_mismatches}} > 0) {
for my $v (@{$self->{tag_mismatches}}) {
# XXX there's probably a race condition there
# we check for junking (which is okay)
# then we FORCE the re-tagging BEFORE junking
# if anything starts up at the same time with nojunk
# then junking won't happen (for instance)
# to fix, it requires a "pseudo" junk first
# to untaint the host, THEN we can try building.
next unless $self->can_be_junked($v, $core);
$self->force_junk($core);
return 1;
}
}
# let's make sure we don't have something else first
if (@{$self->{mismatches}} > 0) {
if ($self->{engine}->check_buildable(1)) {
return $self->use_core($core, 1);
}
}
# second pass, affinity mismatches
for my $v (@{$self->{mismatches}}) {
if (defined $core->prop->taint_incompatible($v)) {
next;
}
if ($self->lock_and_start_build($core, $v)) {
$self->log('Y', $v,
" ".$core->hostname." ".$v->{affinity});
return 1;
}
}
return 0;
}
sub will_install($self, $v)
{
push(@{$self->{toinstall}}, $v);
}
sub start_install($self, $core)
{
return 0 unless $core->is_local;
if (my $v = pop @{$self->{toinstall}}) {
$self->{builder}->install($v, $core);
return 1;
} else {
return 0;
}
}
sub non_empty($self)
{
return $self->SUPER::non_empty || @{$self->{toinstall}} > 0;
}
sub mark_as_done($self, $v)
{
$self->{engine}{affinity}->unmark($v);
delete $self->{engine}{tobuild}{$v};
delete $v->{info}{DIST};
# $self->{heuristics}->done($v);
if (defined $self->{later}{$v}) {
$self->log('V', $v);
delete $self->{later}{$v};
}
if (!defined $self->{engine}{built}{$v}) {
$self->{engine}{built}{$v}= $v;
$self->log('B', $v);
}
$self->remove($v);
}
# special case: some of those paths can't be built
sub remove_stub($self, $v)
{
if ($v->{info}->is_stub) {
$self->{engine}{affinity}->unmark($v);
delete $self->{engine}{tobuild}{$v};
$self->remove($v);
return 1;
}
return 0;
}
sub is_done_or_enqueue($self, $v)
{
my $okay = 1;
for my $w ($v->build_path_list) {
next if $self->remove_stub($w);
if ($self->{builder}->end_check($w)) {
$self->mark_as_done($w);
} else {
$self->{nfs}{$v}{$w} = $w;
$okay = 0;
}
}
return $okay;
}
sub is_done($self, $v)
{
if ($self->{builder}->check($v)) {
for my $w ($v->build_path_list) {
next if $v eq $w;
next if $self->remove_stub($w);
next unless $self->{builder}->check($w);
$self->mark_as_done($w);
}
}
return $self->is_done_quick($v);
}
sub is_done_quick($self, $v)
{
if ($self->{builder}->check($v)) {
$self->mark_as_done($v);
return 1;
} else {
return 0;
}
}
sub key_for_doing($self, $v)
{
return $v->pkgpath;
}
sub already_done($self, $v)
{
$self->{engine}{logger}->make_log_link($v);
}
sub start_build($self, $v, $core, $lock)
{
$self->log('J', $v, " ".$core->hostname);
$core->prop->taint($v);
$self->{builder}->build($v, $core, $lock,
sub($fail) {
$self->end($core, $v, $fail);
});
}
sub start_wipe($self, $v, $core)
{
$self->log('W', $v, " ".$core->hostname);
$self->{builder}->wipe($v, $core,
sub($fail) {
$self->end_build($v);
$self->log('N', $v);
$self->{engine}{locker}->unlock($v);
});
}
sub force_junk($self, $core)
{
my $v = JunkPath->new;
$self->log('J', $v, " ".$core->hostname);
$self->{builder}->force_junk($v, $core,
sub($fail) {
$self->log($fail ? 'E': 'B' , $v, " ".$core->hostname);
});
}
sub end_build($self, $v)
{
$self->{engine}{affinity}->finished($v);
$self->{engine}{sizer}->finished($v);
}
sub sorted($self, $core)
{
return $self->{engine}{affinity}->sorted($self->{queue}, $core);
}
sub add($self, $v)
{
$self->{engine}{affinity}->has_in_queue($v);
$self->SUPER::add($v);
}
package JunkPath;
our @ISA = qw(DPB::PkgPath);
sub new($class)
{
my $v = bless {}, $class;
DPB::PortInfo->new($v);
return $v;
}
sub fullpkgpath($)
{
return "junk-proxy";
}
sub forcejunk($)
{
return 1;
}
1;