openbsd-ports/infrastructure/build/dpb3
espie 61d822e8d5 work-in-progress: new distributed ports builder.
will replace old dpb once we're satisfied it works.
some features:
- no more waiting, starts building right away, the dependencies discovery
process is just another job,
- monitor display that makes it easy to know when jobs get stuck, or machines
get down.
- loads of logs,
- locks that stay around in case of error, and that you can remove when
the error has been fixed (dpb picks it up),
- smart choice of which package to build,
- can take into account previous build times,
- builds on machines of differing speed, offloading "lighter" jobs to slower
boxes.

(again, ports is still locked for most people)
2010-02-24 11:33:31 +00:00

273 lines
6.0 KiB
Perl
Executable File

#! /usr/bin/perl
# ex:ts=8 sw=4:
# $OpenBSD: dpb3,v 1.1 2010/02/24 11:33:31 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;
BEGIN {
$ports1 = $ENV{PORTSDIR} || '/usr/ports';
}
$0 = 'dpb';
use FindBin;
use lib "$FindBin::Bin";
use lib "$ports1/infrastructure/build";
use DPB::PkgPath;
use DPB::Core;
use DPB::Vars;
use DPB::PortInfo;
use DPB::Engine;
use DPB::PortBuilder;
use DPB::Reporter;
use OpenBSD::Getopt;
use OpenBSD::Error;
use DPB::Heuristics;
use DPB::Locks;
use DPB::Logger;
use DPB::Job;
use OpenBSD::Paths;
my $make = $ENV{MAKE} || OpenBSD::Paths->make;
our ($opt_t, $opt_e, $opt_T, $opt_c, $opt_h, $opt_A, $opt_j, $opt_a,
$opt_r,
$opt_L, $opt_m, $opt_f, $opt_x);
my @subdirlist;
sub parse_build_line
{
return split(/\s+/, shift);
}
sub parse_build_file
{
my ($fname, @consumers) = @_;
open my $fh, '<', $fname or die "Couldn't open build file $fname\n";
my $_;
while (<$fh>) {
chomp;
my ($pkgpath, $host, $time, $sz, @rest) = parse_build_line($_);
$sz =~ s/!$//;
my $o = DPB::PkgPath->new_hidden($pkgpath);
for my $c (@consumers) {
$c->add_build_info($o, $host, $time, $sz);
}
}
}
my $parsed = 0;
my $heuristics = DPB::Heuristics->new($opt_r);
set_usage("dpb3 [-acertx] [-A arch] [-j N] [-P plist] [-h hosts] [-L lockdir] [-b log] ",
"[-T timeout] [-m threshold] [path ...]");
try {
getopts('acerh:txA:f:j:m:P:b:L:T:', {
P => sub {
my $file = shift;
open my $fh, '<', $file or die "Can't open $file\n";
my $_;
while (<$fh>) {
chomp;
next if m/^\#/;
unshift @ARGV, $_;
}
},
b => sub {
parse_build_file(shift, $heuristics, "DPB::Job::Port");
$parsed = 1;
},
}
);
} catchall {
Usage($_);
};
if ($parsed) {
$heuristics->finished_parsing;
}
if ($opt_m) {
$heuristics->set_threshold($opt_m);
}
my $dpb = $opt_f ? "fetch" : "normal";
if (@ARGV == 0 && !$opt_a) {
Usage("Need at least one pkgpath to work with");
}
for my $arg (@ARGV) {
my ($path, $weight) = ($arg, 5000);
if ($arg =~ m/^(.*)\=(\d+)$/) {
($path, $weight) = ($1, $2);
}
my $pkgpath = DPB::PkgPath->new($path);
$heuristics->set_weight($pkgpath, $weight);
$pkgpath->add_to_subdirlist(\@subdirlist);
}
my ($ports, $repo, $localarch, $distdir) = DPB::Vars->get($make, "PORTSDIR",
"PACKAGE_REPOSITORY", "MACHINE_ARCH", "DISTDIR");
my $arch = $opt_A // $localarch;
$opt_A //= '';
my $logdir = $ENV{LOGDIR} || "$ports/log/$opt_A";
my $lockdir = $opt_L // "$ports/locks/$opt_A";
my $logger = DPB::Logger->new($logdir, $opt_c);
$heuristics->set_logger($logger);
if (defined $opt_j && $localarch ne $arch) {
Usage("Can't use -j if -A arch is not local architecture");
}
if (defined $opt_j && $opt_j !~ m/^\d+$/) {
Usage("-j takes a numerical argument");
}
my $fullrepo = $opt_t ? "$repo/$arch/test" : "$repo/$arch/all";
if ($opt_t) {
$logdir = "$logdir/test";
}
if ($opt_h) {
DPB::Core->parse_hosts_file($opt_h, $arch);
} else {
$opt_j //= 1;
}
$opt_j //= 0;
for (1 .. $opt_j) {
DPB::Core::Factory->new('localhost');
}
my $builder = ($opt_t ? "DPB::PortBuilder::Test" : "DPB::PortBuilder")->new(
$opt_c, $fullrepo, $logger, $ports, $make, $heuristics);
my $locker = DPB::Locks->new($lockdir);
my $engine = DPB::Engine->new($builder, $heuristics, $logger, $locker);
my $reporter = DPB::Reporter->new($opt_x, "DPB::Core", "DPB::Vars", $engine);
while (!DPB::Core->avail) {
DPB::Core->reap;
sleep 1;
}
my $core = DPB::Core->get;
my $loglist = DPB::Util->make_hot($logger->open("vars"));
#my $dump = DPB::Util->make_hot($logger->open('dump'));
my $keep_going = 1;
$opt_T //= 10;
my $last_time = time() - $opt_T;
sub handle_non_waiting_jobs
{
my $need_clock = shift;
my $reaped = DPB::Core->reap;
$keep_going = !-e "$logdir/stop";
if (DPB::Core->avail) {
$engine->check_buildable;
}
while ($keep_going && DPB::Core->avail && $engine->can_build) {
$engine->start_new_job;
}
if ($need_clock) {
my $current = time();
if ($current >= $last_time + $opt_T || $reaped) {
$reporter->report;
$last_time = $current;
}
} else {
$reporter->report;
}
}
sub finish
{
my $h = shift;
for my $v (values %$h) {
$engine->new_path($v);
}
handle_non_waiting_jobs(1);
}
sub grab_subdirs
{
my $list = shift;
DPB::Vars->grab_list($core, $ports, $make, $list, $loglist, $dpb,
sub {
finish(shift);
});
}
sub complete_subdirs
{
# more passes if necessary
while ($keep_going) {
my @subdirlist = ();
for my $v (DPB::PkgPath->seen) {
next if defined $v->{info};
if (defined $v->{tried}) {
$engine->add_fatal($v);
}
$v->add_to_subdirlist(\@subdirlist);
$v->{tried} = 1;
}
last if @subdirlist == 0;
grab_subdirs(\@subdirlist);
}
}
if (@subdirlist > 0) {
grab_subdirs(\@subdirlist);
}
complete_subdirs();
if ($opt_a) {
grab_subdirs(undef);
}
complete_subdirs();
# give back "our" core to the pool.
if (!$opt_e) {
$core->mark_ready;
}
# and let's wait for all jobs now.
close($loglist);
$engine->finished_scanning;
#$engine->dump($dump);
$engine->check_buildable;
#$engine->dump($dump);
DPB::Core->start_clock($opt_T);
while (1) {
handle_non_waiting_jobs(0);
if (!DPB::Core->running && (!$keep_going || !$engine->can_build)) {
last;
}
if (DPB::Core->running) {
DPB::Core->reap_wait;
}
}
$reporter->reset;
DPB::Core->cleanup;
print $engine->report;
$engine->dump_category('tobuild');