346 lines
7.6 KiB
Perl
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;
|