openbsd-ports/infrastructure/bin/dpb

427 lines
9.1 KiB
Perl
Executable File

#! /usr/bin/perl
# ex:ts=8 sw=4:
# $OpenBSD: dpb,v 1.23 2011/07/14 12:44:39 espie Exp $
#
# Copyright (c) 2010 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 strict;
use warnings;
my $ports1;
use FindBin;
BEGIN {
$ports1 = $ENV{PORTSDIR} || '/usr/ports';
}
use lib ("$ports1/infrastructure/lib", "$FindBin::Bin/../lib");
package DPB::State;
our @ISA = qw(OpenBSD::State);
use OpenBSD::State;
use OpenBSD::Paths;
use DPB::Heuristics;
use DPB::PkgPath;
use DPB::Logger;
sub init
{
my $self = shift;
$self->SUPER::init;
$self->{no_exports} = 1;
$self->{heuristics} = DPB::Heuristics->new($self);
$self->{make} = $ENV{MAKE} || OpenBSD::Paths->make;
($self->{ports}, $self->{repo}, $self->{localarch}, $self->{distdir}) =
DPB::Vars->get($self->make,
"PORTSDIR", "PACKAGE_REPOSITORY", "MACHINE_ARCH", "DISTDIR");
$self->{arch} = $self->{localarch};
return $self;
}
sub handle_options
{
my $state = shift;
$state->{opt} = {
a => sub {
$state->{all} = 1;
},
A => sub {
$state->{arch} = shift;
},
L => sub {
$state->{logdir} = shift;
},
r => sub {
$state->heuristics->random;
},
m => sub {
$state->heuristics->set_threshold(shift)
},
P => sub {
my $file = shift;
open my $fh, '<', $file or die "Can't open $file\n";
my $_;
while (<$fh>) {
chomp;
next if m/^\#/;
push @main::ARGV, $_;
}
},
b => sub {
push(@{$state->{build_files}}, shift);
},
S => sub {
$state->parse_size_file(shift, $state->heuristics);
},
};
$state->SUPER::handle_options('aceqrRstuUvh:xA:f:j:m:P:b:L:S:',
"[-acerRsuUvx] [-A arch] [-f N] [-j N] [-P plist] [-h hosts] [-L logdir]",
"[-b log] [-t ctimeout] [-m threshold] [path ...]");
$state->{fullrepo} = join("/", $state->{repo}, $state->arch, "all");
$state->{logdir} //= $ENV{LOGDIR} //
join("/", $state->ports, "logs", $state->arch);
$state->{opt}{f} //= 2;
if (defined $state->opt('j')) {
if ($state->localarch ne $state->arch) {
$state->usage(
"Can't use -j if -A arch is not local architecture");
}
if ($state->opt('j') !~ m/^\d+$/) {
$state->usage("-j takes a numerical argument");
}
}
if ($state->opt('f') !~ m/^\d+$/) {
$state->usage("-f takes a numerical argument");
}
$state->{logger} = DPB::Logger->new($state->logdir, $state->opt('c'));
$state->heuristics->set_logger($state->logger);
$state->{display_timeout} =
$state->{subst}->value('DISPLAY_TIMEOUT') // $state->opt('T') // 10;
$state->{connection_timeout} =
$state->{subst}->value('CONNECTION_TIMEOUT') // $state->opt('t');
$state->{stuck_timeout} = $state->{subst}->value('STUCK_TIMEOUT');
}
sub start_cores
{
my $state = shift;
if ($state->opt('h')) {
DPB::Core->parse_hosts_file($state->opt('h'), $state);
}
my $prop = {};
if ($state->opt('j')) {
$prop->{jobs} = $state->opt('j');
}
if ($state->{stuck_timeout}) {
$prop->{stuck} = $state->{stuck_timeout};
}
if ($state->opt('j') || !$state->opt('h')) {
DPB::Core::Factory->new('localhost', $prop);
}
DPB::Core::Factory->init_cores($state);
}
sub logger
{
return shift->{logger};
}
sub heuristics
{
return shift->{heuristics};
}
sub locker
{
return shift->{locker};
}
sub builder
{
return shift->{builder};
}
sub engine
{
return shift->{engine};
}
sub grabber
{
return shift->{grabber};
}
sub make
{
return shift->{make};
}
sub ports
{
return shift->{ports};
}
sub fullrepo
{
return shift->{fullrepo};
}
sub distdir
{
return shift->{distdir};
}
sub localarch
{
return shift->{localarch};
}
sub arch
{
return shift->{arch};
}
sub logdir
{
return shift->{logdir};
}
sub parse_build_line
{
return split(/\s+/, shift);
}
sub parse_build_file
{
my ($state, $fname, @consumers) = @_;
if (!-f $fname) {
my $arch = $state->arch;
if (-f "$fname/$arch/build.log") {
$fname = "$fname/$arch/build.log";
} elsif (-f "$fname/build.log") {
$fname = "$fname/build.log";
}
}
open my $fh, '<', $fname or
$state->fatal("Couldn't open build file #1: #2", $fname, $!);
my $_;
while (<$fh>) {
chomp;
my ($pkgpath, $host, $time, $sz, @rest) = parse_build_line($_);
next if (!defined $sz) || $sz =~ m/!$/;
my $o = DPB::PkgPath->new_hidden($pkgpath);
for my $c (@consumers) {
$c->add_build_info($o, $host, $time, $sz);
}
}
}
sub handle_build_files
{
my $state = shift;
return unless defined $state->{build_files};
for my $file (@{$state->{build_files}}) {
$state->parse_build_file($file, $state->heuristics,
"DPB::Job::Port");
}
$state->heuristics->finished_parsing;
}
sub parse_size_file
{
my ($state, $fname, @consumers) = @_;
open my $fh, '<', $fname or
$state->fatal("Couldn't open build file #1: #2", $fname, $!);
my $_;
while (<$fh>) {
chomp;
my ($pkgpath, $sz, $sz2) = split(/\s+/, $_);
if (defined $sz2) {
$sz += $sz2;
}
my $o = DPB::PkgPath->new_hidden($pkgpath);
for my $c (@consumers) {
$c->add_size_info($o, $sz);
}
}
}
package main;
use DPB::PkgPath;
use DPB::Core;
use DPB::Vars;
use DPB::PortInfo;
use DPB::Engine;
use DPB::PortBuilder;
use DPB::Reporter;
use OpenBSD::Error;
use DPB::Locks;
use DPB::Job;
use DPB::Grabber;
my @subdirlist;
my $state = DPB::State->new('dpb');
$state->handle_options;
$state->start_cores;
$state->handle_build_files;
if (@ARGV == 0) {
$state->{all} = 1;
}
for my $arg (@ARGV) {
$arg =~ s/\/+$//;
my ($path, $weight) = ($arg, 5000);
if ($arg =~ m/^(.*)\=(\d+)$/) {
($path, $weight) = ($1, $2);
}
if ($arg =~ m/^\./) {
$state->usage("Invalid pkgpath: #1", $arg);
}
my $pkgpath = DPB::PkgPath->new($path);
$state->heuristics->set_weight($pkgpath, $weight);
$pkgpath->add_to_subdirlist(\@subdirlist);
}
$state->{builder} = DPB::PortBuilder->new($state);
$state->{locker} = DPB::Locks->new(join("/", $state->logdir, "locks"));
$state->{engine} = DPB::Engine->new($state);
my $reporter = DPB::Reporter->new($state->opt('x'),
$state->heuristics, "DPB::Core", $state->engine);
while (!DPB::Core->avail) {
DPB::Core->reap;
sleep 1;
}
my $core = DPB::Core->get;
#my $dump = DPB::Util->make_hot($state->logger->open('dump'));
my $keep_going = 1;
my $last_time = time() - $state->{display_timeout};
sub handle_non_waiting_jobs
{
my $need_clock = shift;
my $checked = !$need_clock; # XXX
my $reaped = DPB::Core->reap;
$keep_going = !-e $state->logdir."/stop";
if (DPB::Core->avail > 1) {
$state->engine->recheck_errors;
}
if (DPB::Core->avail) {
$state->engine->check_buildable(0);
$checked = 1;
}
while ($keep_going && DPB::Core->avail && $state->engine->can_build) {
$state->engine->start_new_job;
}
while ($keep_going && DPB::Core::Fetcher->avail &&
$state->engine->can_fetch) {
if (!$checked) {
$state->engine->check_buildable(1);
$checked = 1;
}
$state->engine->start_new_fetch;
}
if ($need_clock) {
my $current = time();
if ($current >= $last_time + $state->{display_timeout} ||
$reaped) {
$reporter->report;
$last_time = $current;
}
} else {
$reporter->report;
}
return $keep_going;
}
$state->{grabber} = DPB::Grabber->new($state,
sub { handle_non_waiting_jobs(1) });
if ($state->{all}) {
# when restarting interrupted dpb,
# find the most important paths first
my $list = $state->engine->find_best($state->logger->logfile("dependencies"), 10);
# if we have them, list them before the full ports tree walk.
if (@$list > 0) {
$state->grabber->grab_subdirs($core, $list);
}
}
if (@subdirlist > 0) {
$state->grabber->grab_subdirs($core, \@subdirlist);
}
$state->grabber->complete_subdirs($core);
if ($state->{all}) {
$state->grabber->grab_subdirs($core);
}
$state->grabber->complete_subdirs($core);
# give back "our" core to the pool.
if (!$state->opt('e')) {
$core->mark_ready;
}
# and let's wait for all jobs now.
if ($state->{all}) {
$state->engine->dump_dependencies;
if ($state->opt('f')) {
DPB::Distfile->dump($state->{logger});
}
}
#$state->engine->dump($dump);
$state->engine->check_buildable(1);
#$state->engine->dump($dump);
DPB::Core->start_clock($state->{display_timeout});
while (1) {
while (1) {
handle_non_waiting_jobs(0);
if (!DPB::Core->running &&
(!$keep_going || !$state->engine->can_build)) {
$state->engine->check_buildable(0);
if (!$state->engine->can_build) {
last;
}
}
if (DPB::Core->running) {
DPB::Core->reap_wait;
}
}
if (!$state->opt('q') || !$state->engine->recheck_errors) {
last;
}
}
$reporter->reset;
DPB::Core->cleanup;
print $state->engine->report;
$state->engine->dump_category('tobuild', $state->logger->open('dump'));