2
# Build.pm: build library for sbuild
3
# Copyright © 2005 Ryan Murray <rmurray@debian.org>
4
# Copyright © 2005-2010 Roger Leigh <rleigh@debian.org>
5
# Copyright © 2008 Simon McVittie <smcv@debian.org>
7
# This program is free software: you can redistribute it and/or modify
8
# it under the terms of the GNU General Public License as published by
9
# the Free Software Foundation, either version 2 of the License, or
10
# (at your option) any later version.
12
# This program is distributed in the hope that it will be useful, but
13
# WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
# General Public License for more details.
17
# You should have received a copy of the GNU General Public License
18
# along with this program. If not, see
19
# <http://www.gnu.org/licenses/>.
21
#######################################################################
23
package Sbuild::Build;
32
use File::Basename qw(basename dirname);
33
use File::Temp qw(tempdir);
36
use File::Copy qw(); # copy is already exported from Sbuild, so don't export
44
use Sbuild qw($devnull binNMU_version copy isin debug df send_mail dsc_files);
46
use Sbuild::ChrootInfoSchroot;
47
use Sbuild::ChrootInfoSudo;
48
use Sbuild::ChrootRoot;
49
use Sbuild::Sysconfig qw($version $release_date);
50
use Sbuild::Sysconfig;
51
use Sbuild::Utility qw(check_url download);
52
use Sbuild::Resolver qw(get_resolver);
53
use Sbuild::Exception;
59
@ISA = qw(Exporter Sbuild::Base);
64
our $saved_stdout = undef;
65
our $saved_stderr = undef;
72
my $self = $class->SUPER::new($conf);
75
$self->set('ABORT', undef);
76
$self->set('Job', $dsc);
77
$self->set('Arch', undef);
78
$self->set('Chroot Dir', '');
79
$self->set('Chroot Build Dir', '');
80
$self->set('Build Dir', '');
81
$self->set('Max Lock Trys', 120);
82
$self->set('Lock Interval', 5);
83
$self->set('Pkg Status', 'pending');
84
$self->set('Pkg Status Trigger', undef);
85
$self->set('Pkg Start Time', 0);
86
$self->set('Pkg End Time', 0);
87
$self->set('Pkg Fail Stage', 'init');
88
$self->set('Build Start Time', 0);
89
$self->set('Build End Time', 0);
90
$self->set('Install Start Time', 0);
91
$self->set('Install End Time', 0);
92
$self->set('This Time', 0);
93
$self->set('This Space', 0);
94
$self->set('This Watches', {});
95
$self->set('Sub Task', 'initialisation');
96
$self->set('Host', Sbuild::ChrootRoot->new($self->get('Config')));
97
# Host execution defaults
98
my $host_defaults = $self->get('Host')->get('Defaults');
99
$host_defaults->{'USER'} = $self->get_conf('USERNAME');
100
$host_defaults->{'DIR'} = $self->get_conf('HOME');
101
$host_defaults->{'STREAMIN'} = $devnull;
102
$host_defaults->{'ENV'}->{'LC_ALL'} = 'POSIX';
103
$host_defaults->{'ENV'}->{'SHELL'} = '/bin/sh';
104
$host_defaults->{'ENV_FILTER'} = $self->get_conf('ENVIRONMENT_FILTER');
105
# Note, this should never fail. But, we should handle failure anyway.
106
$self->get('Host')->begin_session();
108
$self->set('Session', undef);
109
$self->set('Dependency Resolver', undef);
110
$self->set('Log File', undef);
111
$self->set('Log Stream', undef);
112
$self->set('Summary Stats', {});
114
# DSC, package and version information:
115
$self->set_dsc($dsc);
116
my $ver = $self->get('DSC Base');
118
# Note, will be overwritten by Version: in DSC.
119
$self->set_version($ver);
121
# Do we need to download?
122
$self->set('Download', 0);
123
$self->set('Download', 1)
124
if (!($self->get('DSC Base') =~ m/\.dsc$/) || # Use apt to download
125
check_url($self->get('DSC'))); # Valid URL
127
# Can sources be obtained?
128
$self->set('Invalid Source', 0);
129
$self->set('Invalid Source', 1)
130
if ((!$self->get('Download') ||
131
(!($self->get('DSC Base') =~ m/\.dsc$/) &&
132
$self->get('DSC') ne $self->get('Package_OVersion')) ||
133
!defined $self->get('Version')));
135
debug("Download = " . $self->get('Download') . "\n");
136
debug("Invalid Source = " . $self->get('Invalid Source') . "\n");
145
$self->log_error("ABORT: $reason (requesting cleanup and shutdown)\n");
146
$self->set('ABORT', $reason);
152
if ($self->get('ABORT')) {
153
Sbuild::Exception::Build->throw(error => "Aborting build: " .
155
failstage => "abort");
163
debug("Setting DSC: $dsc\n");
165
$self->set('DSC', $dsc);
166
$self->set('Source Dir', dirname($dsc));
167
$self->set('DSC Base', basename($dsc));
169
debug("DSC = " . $self->get('DSC') . "\n");
170
debug("Source Dir = " . $self->get('Source Dir') . "\n");
171
debug("DSC Base = " . $self->get('DSC Base') . "\n");
178
debug("Setting package version: $pkgv\n");
180
my ($pkg, $version) = split /_/, $pkgv;
181
my $pver = Dpkg::Version->new($version, check => 1);
182
return if (!defined($pkg) || !defined($version) || !defined($pver));
183
my ($o_version, $o_revision);
184
$o_version = $pver->version();
185
$o_revision = $pver->revision();
186
$o_revision = "" if $pver->{'no_revision'};
188
# Original version (no binNMU or other addition)
189
my $oversion = $version;
190
# Original version with stripped epoch
191
my $osversion = $o_version;
192
$osversion .= '-' . $o_revision if $o_revision;
194
# Add binNMU to version if needed.
195
if ($self->get_conf('BIN_NMU') || $self->get_conf('APPEND_TO_VERSION')) {
196
$version = binNMU_version($version, $self->get_conf('BIN_NMU_VERSION'),
197
$self->get_conf('APPEND_TO_VERSION'));
200
my $bver = Dpkg::Version->new($version, check => 1);
201
return if (!defined($bver));
202
my ($b_epoch, $b_version, $b_revision);
203
$b_epoch = $bver->epoch();
204
$b_epoch = "" if $bver->{'no_epoch'};
205
$b_version = $bver->version();
206
$b_revision = $bver->revision();
207
$b_revision = "" if $bver->{'no_revision'};
209
# Version with binNMU or other additions and stripped epoch
210
my $sversion = $b_version;
211
$sversion .= '-' . $b_revision if $b_revision;
213
$self->set('Package', $pkg);
214
$self->set('Version', $version);
215
$self->set('Package_Version', "${pkg}_$version");
216
$self->set('Package_OVersion', "${pkg}_$oversion");
217
$self->set('Package_OSVersion', "${pkg}_$osversion");
218
$self->set('Package_SVersion', "${pkg}_$sversion");
219
$self->set('OVersion', $oversion);
220
$self->set('OSVersion', $osversion);
221
$self->set('SVersion', $sversion);
222
$self->set('VersionEpoch', $b_epoch);
223
$self->set('VersionUpstream', $b_version);
224
$self->set('VersionDebian', $b_revision);
225
$self->set('DSC File', "${pkg}_${osversion}.dsc");
226
$self->set('DSC Dir', "${pkg}-${b_version}");
228
debug("Package = " . $self->get('Package') . "\n");
229
debug("Version = " . $self->get('Version') . "\n");
230
debug("Package_Version = " . $self->get('Package_Version') . "\n");
231
debug("Package_OVersion = " . $self->get('Package_OVersion') . "\n");
232
debug("Package_OSVersion = " . $self->get('Package_OSVersion') . "\n");
233
debug("Package_SVersion = " . $self->get('Package_SVersion') . "\n");
234
debug("OVersion = " . $self->get('OVersion') . "\n");
235
debug("OSVersion = " . $self->get('OSVersion') . "\n");
236
debug("SVersion = " . $self->get('SVersion') . "\n");
237
debug("VersionEpoch = " . $self->get('VersionEpoch') . "\n");
238
debug("VersionUpstream = " . $self->get('VersionUpstream') . "\n");
239
debug("VersionDebian = " . $self->get('VersionDebian') . "\n");
240
debug("DSC File = " . $self->get('DSC File') . "\n");
241
debug("DSC Dir = " . $self->get('DSC Dir') . "\n");
248
$self->set('Pkg Status', $status);
249
if (defined($self->get('Pkg Status Trigger'))) {
250
$self->get('Pkg Status Trigger')->($self, $status);
257
return $self->get('Pkg Status');
260
# This function is the main entry point into the package build. It
261
# provides a top-level exception handler and does the initial setup
262
# including initiating logging and creating host chroot. The nested
263
# run_ functions it calls are separate in order to permit running
264
# cleanup tasks in a strict order.
269
$self->check_abort();
271
$self->set_status('building');
273
$self->set('Pkg Start Time', time);
274
$self->set('Pkg End Time', $self->get('Pkg Start Time'));
276
# Acquire the architecture we're building for.
277
$self->set('Arch', $self->get_conf('ARCH'));
279
my $dist = $self->get_conf('DISTRIBUTION');
280
if (!defined($dist) || !$dist) {
281
Sbuild::Exception::Build->throw(error => "No distribution defined",
282
failstage => "init");
285
if ($self->get('Invalid Source')) {
286
Sbuild::Exception::Build->throw(error => "Invalid source " . $self->get('DSC'),
287
failstage => "init");
290
# TODO: Get package name from build object
291
if (!$self->open_build_log()) {
292
Sbuild::Exception::Build->throw(error => "Failed to open build log",
293
failstage => "init");
296
# Set a chroot to run commands in host
297
my $host = $self->get('Host');
299
# Host execution defaults (set streams)
300
my $host_defaults = $host->get('Defaults');
301
$host_defaults->{'STREAMIN'} = $devnull;
302
$host_defaults->{'STREAMOUT'} = $self->get('Log Stream');
303
$host_defaults->{'STREAMERR'} = $self->get('Log Stream');
305
$self->check_abort();
310
if ($e = Exception::Class->caught('Sbuild::Exception::Build')) {
312
$self->set_status($e->status);
314
$self->set_status("failed");
316
$self->set('Pkg Fail Stage', $e->failstage);
321
# Pack up source if needed and then run the main chroot session.
322
# Close log during return/failure.
327
$self->check_abort();
328
$self->run_chroot_session();
331
# Log exception info and set status and fail stage prior to
334
if ($e = Exception::Class->caught('Sbuild::Exception::Build')) {
335
$self->log_error("$e\n");
336
$self->log_info($e->info."\n")
339
$self->set_status($e->status);
341
$self->set_status("failed");
343
$self->set('Pkg Fail Stage', $e->failstage);
346
$self->close_build_log();
353
# Create main chroot session and package resolver. Creates a lock in
354
# the chroot to prevent concurrent chroot usage (only important for
355
# non-snapshot chroots). Ends chroot session on return/failure.
356
sub run_chroot_session {
360
$self->check_abort();
362
if ($self->get_conf('CHROOT_MODE') eq 'schroot') {
363
$chroot_info = Sbuild::ChrootInfoSchroot->new($self->get('Config'));
365
$chroot_info = Sbuild::ChrootInfoSudo->new($self->get('Config'));
368
my $host = $self->get('Host');
370
my $session = $chroot_info->create('chroot',
371
$self->get_conf('DISTRIBUTION'),
372
$self->get_conf('CHROOT'),
373
$self->get_conf('ARCH'));
375
# Run pre build external commands
376
$self->check_abort();
377
$self->run_external_commands("pre-build-commands",
378
$self->get_conf('LOG_EXTERNAL_COMMAND_OUTPUT'),
379
$self->get_conf('LOG_EXTERNAL_COMMAND_ERROR'));
381
$self->check_abort();
382
if (!$session->begin_session()) {
383
Sbuild::Exception::Build->throw(error => "Error creating chroot session: skipping " .
384
$self->get('Package'),
385
failstage => "create-session");
388
$self->set('Session', $session);
390
$self->check_abort();
391
my $chroot_arch = $self->chroot_arch();
392
if ($self->get('Arch') ne $chroot_arch) {
393
Sbuild::Exception::Build->throw(error => "Build architecture (" . $self->get('Arch') .
394
") is not the same as the chroot architecture (" .
396
info => "Please specify the correct architecture with --arch, or use a chroot of the correct architecture",
397
failstage => "create-session");
400
$self->set('Chroot Dir', $session->get('Location'));
401
# TODO: Don't hack the build location in; add a means to customise
402
# the chroot directly. i.e. allow changing of /build location.
403
$self->set('Chroot Build Dir',
404
tempdir($self->get('Package') . '-XXXXXX',
405
DIR => $session->get('Location') . "/build"));
407
$self->set('Build Dir', $session->strip_chroot_path($self->get('Chroot Build Dir')));
410
$self->build_log_colour('red', '^E: ');
411
$self->build_log_colour('yellow', '^W: ');
412
$self->build_log_colour('green', '^I: ');
413
$self->build_log_colour('red', '^Status:');
414
$self->build_log_colour('green', '^Status: successful$');
415
$self->build_log_colour('red', '^Lintian:');
416
$self->build_log_colour('green', '^Lintian: pass$');
420
$filter = $self->get('Build Dir') . '/' . $self->get('DSC Dir');
422
$self->build_log_filter($filter, 'PKGBUILDDIR');
423
$filter = $self->get('Build Dir');
425
$self->build_log_filter($filter, 'BUILDDIR');
426
$filter = $session->get('Location');
428
$self->build_log_filter($filter , 'CHROOT');
430
# Need tempdir to be writable and readable by sbuild group.
431
$self->check_abort();
432
$session->run_command(
433
{ COMMAND => ['chown', $self->get_conf('BUILD_USER') . ':sbuild',
434
$self->get('Build Dir')],
438
Sbuild::Exception::Build->throw(error => "Failed to set sbuild group ownership on chroot build dir",
439
failstage => "create-build-dir");
441
$self->check_abort();
442
$session->run_command(
443
{ COMMAND => ['chmod', '0770', $self->get('Build Dir')],
447
Sbuild::Exception::Build->throw(error => "Failed to set sbuild group ownership on chroot build dir",
448
failstage => "create-build-dir");
451
$self->check_abort();
452
# Needed so chroot commands log to build log
453
$session->set('Log Stream', $self->get('Log Stream'));
454
$host->set('Log Stream', $self->get('Log Stream'));
456
# Chroot execution defaults
457
my $chroot_defaults = $session->get('Defaults');
458
$chroot_defaults->{'DIR'} = $self->get('Build Dir');
459
$chroot_defaults->{'STREAMIN'} = $devnull;
460
$chroot_defaults->{'STREAMOUT'} = $self->get('Log Stream');
461
$chroot_defaults->{'STREAMERR'} = $self->get('Log Stream');
462
$chroot_defaults->{'ENV'}->{'LC_ALL'} = 'POSIX';
463
$chroot_defaults->{'ENV'}->{'SHELL'} = '/bin/sh';
464
$chroot_defaults->{'ENV'}->{'HOME'} = '/sbuild-nonexistent';
465
$chroot_defaults->{'ENV_FILTER'} = $self->get_conf('ENVIRONMENT_FILTER');
467
my $resolver = get_resolver($self->get('Config'), $session, $host);
468
$resolver->set('Log Stream', $self->get('Log Stream'));
469
$resolver->set('Arch', $self->get('Arch'));
470
$resolver->set('Chroot Build Dir', $self->get('Chroot Build Dir'));
471
$self->set('Dependency Resolver', $resolver);
473
# Lock chroot so it won't be tampered with during the build.
474
$self->check_abort();
475
if (!$session->lock_chroot($self->get('Package_SVersion'), $$, $self->get_conf('USERNAME'))) {
476
Sbuild::Exception::Build->throw(error => "Error locking chroot session: skipping " .
477
$self->get('Package'),
478
failstage => "lock-session");
481
$self->check_abort();
482
$self->run_chroot_session_locked();
486
my $session = $self->get('Session');
488
($self->get_conf('PURGE_SESSION') eq 'always' ||
489
($self->get_conf('PURGE_SESSION') eq 'successful' &&
490
$self->get_status() eq 'successful')) ? 1 : 0;
492
$session->end_session();
494
$self->log("Keeping session: " . $session->get('Session ID') . "\n");
497
$self->set('Session', $session);
500
if ($e = Exception::Class->caught('Sbuild::Exception::Build')) {
505
# Run tasks in a *locked* chroot. Update and upgrade packages.
506
# Unlocks chroot on return/failure.
507
sub run_chroot_session_locked {
511
my $session = $self->get('Session');
512
my $resolver = $self->get('Dependency Resolver');
514
$self->check_abort();
517
$self->check_abort();
518
$self->run_chroot_update();
520
$self->check_abort();
521
$self->run_fetch_install_packages();
524
my $session = $self->get('Session');
525
my $resolver = $self->get('Dependency Resolver');
527
$resolver->cleanup();
528
# Unlock chroot now it's cleaned up and ready for other users.
529
$session->unlock_chroot();
532
if ($e = Exception::Class->caught('Sbuild::Exception::Build')) {
537
sub run_chroot_update {
539
my $resolver = $self->get('Dependency Resolver');
541
if ($self->get_conf('APT_CLEAN') || $self->get_conf('APT_UPDATE') ||
542
$self->get_conf('APT_DISTUPGRADE') || $self->get_conf('APT_UPGRADE')) {
543
$self->log_subsection('Update chroot');
547
$self->check_abort();
548
if ($self->get_conf('APT_CLEAN')) {
549
if ($resolver->clean()) {
550
# Since apt-clean was requested specifically, fail on
551
# error when not in buildd mode.
552
$self->log("apt-get clean failed\n");
553
if ($self->get_conf('SBUILD_MODE') ne 'buildd') {
554
Sbuild::Exception::Build->throw(error => "apt-get clean failed",
555
failstage => "apt-get-clean");
561
$self->check_abort();
562
if ($self->get_conf('APT_UPDATE')) {
563
if ($resolver->update()) {
564
# Since apt-update was requested specifically, fail on
565
# error when not in buildd mode.
566
if ($self->get_conf('SBUILD_MODE') ne 'buildd') {
567
Sbuild::Exception::Build->throw(error => "apt-get update failed",
568
failstage => "apt-get-update");
574
$self->check_abort();
575
if ($self->get_conf('APT_DISTUPGRADE')) {
576
if ($self->get_conf('APT_DISTUPGRADE')) {
577
if ($resolver->distupgrade()) {
578
# Since apt-distupgrade was requested specifically, fail on
579
# error when not in buildd mode.
580
if ($self->get_conf('SBUILD_MODE') ne 'buildd') {
581
Sbuild::Exception::Build->throw(error => "apt-get dist-upgrade failed",
582
failstage => "apt-get-dist-upgrade");
586
} elsif ($self->get_conf('APT_UPGRADE')) {
587
if ($self->get_conf('APT_UPGRADE')) {
588
if ($resolver->upgrade()) {
589
# Since apt-upgrade was requested specifically, fail on
590
# error when not in buildd mode.
591
if ($self->get_conf('SBUILD_MODE') ne 'buildd') {
592
Sbuild::Exception::Build->throw(error => "apt-get upgrade failed",
593
failstage => "apt-get-upgrade");
600
# Fetch sources, run setup, fetch and install core and package build
601
# deps, then run build. Cleans up build directory and uninstalls
602
# build depends on return/failure.
603
sub run_fetch_install_packages {
606
$self->check_abort();
608
my $session = $self->get('Session');
609
my $resolver = $self->get('Dependency Resolver');
611
$self->check_abort();
612
if (!$self->fetch_source_files()) {
613
Sbuild::Exception::Build->throw(error => "Failed to fetch source files",
614
failstage => "fetch-src");
617
# Display message about chroot setup script option use being deprecated
618
if ($self->get_conf('CHROOT_SETUP_SCRIPT')) {
619
my $msg = "setup-hook option is deprecated. It has been superceded by ";
620
$msg .= "the chroot-setup-commands feature. setup-hook script will be ";
621
$msg .= "run via chroot-setup-commands.\n";
622
$self->log_warning($msg);
625
# Run specified chroot setup commands
626
$self->check_abort();
627
$self->run_external_commands("chroot-setup-commands",
628
$self->get_conf('LOG_EXTERNAL_COMMAND_OUTPUT'),
629
$self->get_conf('LOG_EXTERNAL_COMMAND_ERROR'));
631
$self->check_abort();
632
$self->set('Install Start Time', time);
633
$self->set('Install End Time', $self->get('Install Start Time'));
634
$resolver->add_dependencies('CORE', join(", ", @{$self->get_conf('CORE_DEPENDS')}) , "", "", "", "", "");
635
if (!$resolver->install_deps('core', 'CORE')) {
636
Sbuild::Exception::Build->throw(error => "Core build dependencies not satisfied; skipping",
637
failstage => "install-deps");
640
$resolver->add_dependencies('ESSENTIAL', $self->read_build_essential(), "", "", "", "", "");
643
$snapshot = "gcc-snapshot" if ($self->get_conf('GCC_SNAPSHOT'));
644
$resolver->add_dependencies('GCC_SNAPSHOT', $snapshot , "", "", "", "", "");
646
# Add additional build dependencies specified on the command-line.
647
# TODO: Split dependencies into an array from the start to save
649
$resolver->add_dependencies('MANUAL',
650
join(", ", @{$self->get_conf('MANUAL_DEPENDS')}),
651
join(", ", @{$self->get_conf('MANUAL_DEPENDS_ARCH')}),
652
join(", ", @{$self->get_conf('MANUAL_DEPENDS_INDEP')}),
653
join(", ", @{$self->get_conf('MANUAL_CONFLICTS')}),
654
join(", ", @{$self->get_conf('MANUAL_CONFLICTS_ARCH')}),
655
join(", ", @{$self->get_conf('MANUAL_CONFLICTS_INDEP')}));
657
$resolver->add_dependencies($self->get('Package'),
658
$self->get('Build Depends'),
659
$self->get('Build Depends Arch'),
660
$self->get('Build Depends Indep'),
661
$self->get('Build Conflicts'),
662
$self->get('Build Conflicts Arch'),
663
$self->get('Build Conflicts Indep'));
665
$self->check_abort();
666
if (!$resolver->install_deps($self->get('Package'),
667
'ESSENTIAL', 'GCC_SNAPSHOT', 'MANUAL',
668
$self->get('Package'))) {
669
Sbuild::Exception::Build->throw(error => "Package build dependencies not satisfied; skipping",
670
failstage => "install-deps");
672
$self->set('Install End Time', time);
674
$self->check_abort();
675
$resolver->dump_build_environment();
677
$self->check_abort();
678
$self->prepare_watches(keys %{$resolver->get('Changes')->{'installed'}});
680
$self->check_abort();
681
if ($self->build()) {
682
$self->set_status('successful');
684
$self->set('Pkg Fail Stage', "build");
685
$self->set_status('failed');
688
# Run specified chroot cleanup commands
689
$self->check_abort();
690
$self->run_external_commands("chroot-cleanup-commands",
691
$self->get_conf('LOG_EXTERNAL_COMMAND_OUTPUT'),
692
$self->get_conf('LOG_EXTERNAL_COMMAND_ERROR'));
694
if ($self->get('Pkg Status') eq "successful") {
695
$self->log_subsection("Post Build");
698
$self->check_abort();
699
$self->run_lintian();
702
$self->check_abort();
703
$self->run_piuparts();
705
# Run post build external commands
706
$self->check_abort();
707
$self->run_external_commands("post-build-commands",
708
$self->get_conf('LOG_EXTERNAL_COMMAND_OUTPUT'),
709
$self->get_conf('LOG_EXTERNAL_COMMAND_ERROR'));
714
$self->log_subsection("Cleanup");
715
my $session = $self->get('Session');
716
my $resolver = $self->get('Dependency Resolver');
718
my $purge_build_directory =
719
($self->get_conf('PURGE_BUILD_DIRECTORY') eq 'always' ||
720
($self->get_conf('PURGE_BUILD_DIRECTORY') eq 'successful' &&
721
$self->get_status() eq 'successful')) ? 1 : 0;
722
my $purge_build_deps =
723
($self->get_conf('PURGE_BUILD_DEPS') eq 'always' ||
724
($self->get_conf('PURGE_BUILD_DEPS') eq 'successful' &&
725
$self->get_status() eq 'successful')) ? 1 : 0;
726
my $is_cloned_session = (defined ($session->get('Session Purged')) &&
727
$session->get('Session Purged') == 1) ? 1 : 0;
729
if ($purge_build_directory) {
730
# Purge package build directory
731
$self->log("Purging " . $self->get('Build Dir') . "\n");
732
$self->get('Session')->run_command(
733
{ COMMAND => ['rm', '-rf', $self->get('Build Dir')],
739
# Purge non-cloned session
740
if ($is_cloned_session) {
741
$self->log("Not cleaning session: cloned chroot in use\n");
743
if ($purge_build_deps) {
744
# Removing dependencies
745
$resolver->uninstall_deps();
747
$self->log("Not removing build depends: as requested\n");
752
if ($e = Exception::Class->caught('Sbuild::Exception::Build')) {
762
$self->check_abort();
763
if (! File::Copy::copy($source, $dest)) {
764
$self->log_error("E: Failed to copy '$source' to '$dest': $!\n");
769
$self->get('Session')->run_command(
770
{ COMMAND => ['chown', $self->get_conf('BUILD_USER') . ':sbuild',
771
$self->get('Session')->strip_chroot_path($dest) . '/' .
776
$self->log_error("E: Failed to set sbuild group ownership on $dest\n");
779
$self->get('Session')->run_command(
780
{ COMMAND => ['chmod', '0664',
781
$self->get('Session')->strip_chroot_path($dest) . '/' .
786
$self->log_error("E: Failed to set 0644 permissions on $dest\n");
792
sub fetch_source_files {
795
my $dir = $self->get('Source Dir');
796
my $dsc = $self->get('DSC File');
797
my $build_dir = $self->get('Chroot Build Dir');
798
my $pkg = $self->get('Package');
799
my $ver = $self->get('OVersion');
800
my $arch = $self->get('Arch');
802
my ($dscarchs, $dscpkg, $dscver, @fetched);
804
my $build_depends = "";
805
my $build_depends_arch = "";
806
my $build_depends_indep = "";
807
my $build_conflicts = "";
808
my $build_conflicts_arch = "";
809
my $build_conflicts_indep = "";
812
$self->log_subsection("Fetch source files");
814
if (!defined($self->get('Package')) ||
815
!defined($self->get('OVersion')) ||
816
!defined($self->get('Source Dir'))) {
817
$self->log("Invalid source: $self->get('DSC')\n");
821
$self->check_abort();
822
if ($self->get('DSC Base') =~ m/\.dsc$/) {
823
# Work with a .dsc file.
824
# $file is the name of the downloaded dsc file written in a tempfile.
826
$file = download($self->get('DSC')) or
827
$self->log_error("Could not download " . $self->get('DSC') . "\n") and
829
my @cwd_files = dsc_files($file);
831
if (-f "$dir/$dsc") {
832
# Copy the local source files into the build directory.
833
$self->log_subsubsection("Local sources");
834
$self->log("$dsc exists in $dir; copying to chroot\n");
835
if (! $self->copy_to_chroot("$dir/$dsc", "$build_dir")) {
838
push(@fetched, "$build_dir/$dsc");
839
foreach (@cwd_files) {
840
if (! $self->copy_to_chroot("$dir/$_", "$build_dir")) {
843
push(@fetched, "$build_dir/$_");
846
# Copy the remote source files into the build directory.
847
$self->log_subsubsection("Remote sources");
848
$self->log("Downloading source files from $dir.\n");
849
if (! File::Copy::copy("$file", "$build_dir/" . $self->get('DSC File'))) {
850
$self->log_error("Could not copy downloaded file $file to $build_dir\n");
853
push(@fetched, "$build_dir/" . $self->get('DSC File'));
854
foreach (@cwd_files) {
855
download("$dir/$_", "$build_dir/$_") or
856
$self->log_error("Could not download $dir/$_") and
858
push(@fetched, "$build_dir/$_");
862
# Use apt to download the source files
863
$self->log_subsubsection("Check APT");
865
my $retried = $self->get_conf('APT_UPDATE'); # Already updated if set
867
$self->log("Checking available source versions...\n");
869
my $pipe = $self->get('Dependency Resolver')->pipe_apt_command(
870
{ COMMAND => [$self->get_conf('APT_CACHE'),
871
'-q', 'showsrc', "$pkg"],
872
USER => $self->get_conf('BUILD_USER'),
876
$self->log("Can't open pipe to ".$self->get_conf('APT_UPDATE').": $!\n");
886
$package = $1 if /^Package:\s+(\S+)\s*$/mi;
887
$ver = $1 if /^Version:\s+(\S+)\s*$/mi;
888
$tfile = $1 if /^Files:\s*\n((\s+.*\s*\n)+)/mi;
889
if (defined $package && defined $ver && defined $tfile) {
890
@{$entries{"$package $ver"}} = map { (split( /\s+/, $_ ))[3] }
891
split( "\n", $tfile );
898
if (! scalar keys %entries) {
899
$self->log($self->get_conf('APT_CACHE') .
900
" returned no information about $pkg source\n");
901
$self->log("Are there any deb-src lines in your /etc/apt/sources.list?\n");
909
$self->log($self->get_conf('APT_CACHE') . " exit status $?: $!\n");
913
if (!defined($entries{"$pkg $ver"})) {
915
$self->log_subsubsection("Update APT");
916
# try to update apt's cache if nothing found
917
$self->get('Dependency Resolver')->update();
921
$self->log("Can't find source for " .
922
$self->get('Package_OVersion') . "\n");
923
$self->log("(only different version(s) ",
924
join( ", ", sort keys %entries), " found)\n")
929
$self->log_subsubsection("Download source files with APT");
931
foreach (@{$entries{"$pkg $ver"}}) {
932
push(@fetched, "$build_dir/$_");
935
my $pipe2 = $self->get('Dependency Resolver')->pipe_apt_command(
936
{ COMMAND => [$self->get_conf('APT_GET'), '--only-source', '-q', '-d', 'source', "$pkg=$ver"],
937
USER => $self->get_conf('BUILD_USER'),
938
PRIORITY => 0}) || return 0;
945
$self->log($self->get_conf('APT_GET') . " for sources failed\n");
948
$self->set_dsc((grep { /\.dsc$/ } @fetched)[0]);
951
my $pdsc = Dpkg::Control->new(type => CTRL_PKG_SRC);
952
$pdsc->set_options(allow_pgp => 1);
953
if (!$pdsc->load("$build_dir/$dsc")) {
954
$self->log("Error parsing $build_dir/$dsc");
958
$build_depends = $pdsc->{'Build-Depends'};
959
$build_depends_arch = $pdsc->{'Build-Depends-Arch'};
960
$build_depends_indep = $pdsc->{'Build-Depends-Indep'};
961
$build_conflicts = $pdsc->{'Build-Conflicts'};
962
$build_conflicts_arch = $pdsc->{'Build-Conflicts-Arch'};
963
$build_conflicts_indep = $pdsc->{'Build-Conflicts-Indep'};
964
$dscarchs = $pdsc->{'Architecture'};
965
$dscpkg = $pdsc->{'Source'};
966
$dscver = $pdsc->{'Version'};
968
$self->set_version("${dscpkg}_${dscver}");
970
$build_depends =~ s/\n\s+/ /g if defined $build_depends;
971
$build_depends_arch =~ s/\n\s+/ /g if defined $build_depends_arch;
972
$build_depends_indep =~ s/\n\s+/ /g if defined $build_depends_indep;
973
$build_conflicts =~ s/\n\s+/ /g if defined $build_conflicts;
974
$build_conflicts_arch =~ s/\n\s+/ /g if defined $build_conflicts_arch;
975
$build_conflicts_indep =~ s/\n\s+/ /g if defined $build_conflicts_indep;
977
$self->log_subsubsection("Check arch");
979
$self->log("$dsc has no Architecture: field -- skipping arch check!\n");
982
for my $a (split(/\s+/, $dscarchs)) {
983
if (Dpkg::Arch::debarch_is($arch, $a)) {
988
if ($dscarchs ne "any" && !($valid_arch) &&
989
!($dscarchs eq "all" && $self->get_conf('BUILD_ARCH_ALL')) ) {
990
my $msg = "$dsc: $arch not in arch list or does not match any arch wildcards: $dscarchs -- skipping\n";
992
Sbuild::Exception::Build->throw(error => "$dsc: $arch not in arch list or does not match any arch wildcards: $dscarchs -- skipping",
994
failstage => "arch-check");
999
debug("Arch check ok ($arch included in $dscarchs)\n");
1001
$self->set('Build Depends', $build_depends);
1002
$self->set('Build Depends Arch', $build_depends_arch);
1003
$self->set('Build Depends Indep', $build_depends_indep);
1004
$self->set('Build Conflicts', $build_conflicts);
1005
$self->set('Build Conflicts Arch', $build_conflicts_arch);
1006
$self->set('Build Conflicts Indep', $build_conflicts_indep);
1011
# Subroutine that runs any command through the system (i.e. not through the
1012
# chroot. It takes a string of a command with arguments to run along with
1013
# arguments whether to save STDOUT and/or STDERR to the log stream
1016
my $command = shift;
1017
my $log_output = shift;
1018
my $log_error = shift;
1021
# Used to determine if we are to log from commands
1022
my ($out, $err, $defaults);
1024
# Run the command and save the exit status
1027
$defaults = $self->get('Host')->{'Defaults'};
1028
$out = $defaults->{'STREAMOUT'} if ($log_output);
1029
$err = $defaults->{'STREAMERR'} if ($log_error);
1030
$self->get('Host')->run_command(
1031
{ COMMAND => \@{$command},
1037
$defaults = $self->get('Session')->{'Defaults'};
1038
$out = $defaults->{'STREAMOUT'} if ($log_output);
1039
$err = $defaults->{'STREAMERR'} if ($log_error);
1040
$self->get('Session')->run_command(
1041
{ COMMAND => \@{$command},
1042
USER => $self->get_conf('BUILD_USER'),
1050
# Check if the command failed
1057
# Subroutine that processes external commands to be run during various stages of
1058
# an sbuild run. We also ask if we want to log any output from the commands
1059
sub run_external_commands {
1062
my $log_output = shift;
1063
my $log_error = shift;
1065
# Return success now unless there are commands to run
1066
return 1 unless (${$self->get_conf('EXTERNAL_COMMANDS')}{$stage});
1068
# Determine which set of commands to run based on the parameter $stage
1069
my @commands = @{${$self->get_conf('EXTERNAL_COMMANDS')}{$stage}};
1070
return 1 if !(@commands);
1072
# Create appropriate log message and determine if the commands are to be
1073
# run inside the chroot or not.
1075
if ($stage eq "pre-build-commands") {
1076
$self->log_subsection("Pre Build Commands");
1077
} elsif ($stage eq "chroot-setup-commands") {
1078
$self->log_subsection("Chroot Setup Commands");
1080
} elsif ($stage eq "chroot-cleanup-commands") {
1081
$self->log_subsection("Chroot Cleanup Commands");
1083
} elsif ($stage eq "post-build-commands") {
1084
$self->log_subsection("Post Build Commands");
1087
# Run each command, substituting the various percent escapes (like
1088
# %SBUILD_DSC) from the commands to run with the appropriate subsitutions.
1089
my $dsc = $self->get('DSC');
1091
$changes = $self->get('Changes File') if ($self->get('Changes File'));
1094
"d" => $dsc, "SBUILD_DSC" => $dsc,
1095
"c" => $changes, "SBUILD_CHANGES" => $changes,
1097
# Our escapes pattern, with longer escapes first, then sorted lexically.
1098
my $keyword_pat = join("|",
1099
sort {length $b <=> length $a || $a cmp $b} keys %percent);
1101
foreach my $command (@commands) {
1102
foreach my $arg (@{$command}) {
1104
# Match a percent followed by a valid keyword
1107
# Substitute with the appropriate value only if it's defined
1111
my $command_str = join(" ", @{$command});
1112
$self->log_subsubsection("$command_str");
1113
$returnval = $self->run_command($command, $log_output, $log_error, $chroot);
1116
$self->log_error("Command '$command_str' failed to run.\n");
1118
$self->log_info("Finished running '$command_str'.\n");
1121
$self->log("\nFinished processing commands.\n");
1129
return 1 unless ($self->get_conf('RUN_LINTIAN'));
1131
$self->log_subsubsection("lintian");
1133
my $lintian = $self->get_conf('LINTIAN');
1134
my @lintian_command = ($lintian);
1135
push @lintian_command, @{$self->get_conf('LINTIAN_OPTIONS')} if
1136
($self->get_conf('LINTIAN_OPTIONS'));
1137
push @lintian_command, $self->get('Changes File');
1138
$self->get('Host')->run_command(
1139
{ COMMAND => \@lintian_command,
1142
my $status = $? >> 8;
1143
$self->set('Lintian Reason', 'pass');
1147
my $why = "unknown reason";
1148
$self->set('Lintian Reason', 'error');
1149
$self->set('Lintian Reason', 'fail') if ($status == 1);
1150
$why = "runtime error" if ($status == 2);
1151
$why = "policy violation" if ($status == 1);
1152
$why = "received signal " . $? & 127 if ($? & 127);
1153
$self->log_error("Lintian run failed ($why)\n");
1158
$self->log_info("Lintian run was successful.\n");
1165
return 1 unless ($self->get_conf('RUN_PIUPARTS'));
1167
$self->log_subsubsection("piuparts");
1169
my $piuparts = $self->get_conf('PIUPARTS');
1170
my @piuparts_command;
1171
if (scalar(@{$self->get_conf('PIUPARTS_ROOT_ARGS')})) {
1172
push @piuparts_command, @{$self->get_conf('PIUPARTS_ROOT_ARGS')};
1174
push @piuparts_command, 'sudo', '--';
1176
push @piuparts_command, $piuparts;
1177
push @piuparts_command, @{$self->get_conf('PIUPARTS_OPTIONS')} if
1178
($self->get_conf('PIUPARTS_OPTIONS'));
1179
push @piuparts_command, $self->get('Changes File');
1180
$self->get('Host')->run_command(
1181
{ COMMAND => \@piuparts_command,
1184
my $status = $? >> 8;
1185
$self->set('Piuparts Reason', 'pass');
1189
$self->log_error("Piuparts run failed.\n");
1190
$self->set('Piuparts Reason', 'fail');
1194
$self->log_info("Piuparts run was successful.\n");
1201
my $dscfile = $self->get('DSC File');
1202
my $dscdir = $self->get('DSC Dir');
1203
my $pkg = $self->get('Package');
1204
my $build_dir = $self->get('Chroot Build Dir');
1205
my $arch = $self->get('Arch');
1207
my( $rv, $changes );
1208
local( *PIPE, *F, *F2 );
1210
$self->log_subsection("Build");
1211
$self->set('This Space', 0);
1213
my $tmpunpackdir = $dscdir;
1214
$tmpunpackdir =~ s/-.*$/.orig.tmp-nest/;
1215
$tmpunpackdir =~ s/_/-/;
1216
$tmpunpackdir = "$build_dir/$tmpunpackdir";
1218
$self->log_subsubsection("Unpack source");
1219
if (-d "$build_dir/$dscdir" && -l "$build_dir/$dscdir") {
1220
# if the package dir already exists but is a symlink, complain
1221
$self->log("Cannot unpack source: a symlink to a directory with the\n".
1222
"same name already exists.\n");
1225
if (! -d "$build_dir/$dscdir") {
1226
$self->set('Sub Task', "dpkg-source");
1227
$self->get('Session')->run_command(
1228
{ COMMAND => [$self->get_conf('DPKG_SOURCE'),
1229
'-x', $dscfile, $dscdir],
1230
USER => $self->get_conf('BUILD_USER'),
1233
$self->log("FAILED [dpkg-source died]\n");
1234
Sbuild::Exception::Build->throw(error => "FAILED [dpkg-source died]",
1235
failstage => "unpack");
1238
$self->get('Session')->run_command(
1239
{ COMMAND => ['chmod', '-R', 'g-s,go+rX', $dscdir],
1240
USER => $self->get_conf('BUILD_USER'),
1243
$self->log("chmod -R g-s,go+rX $dscdir failed.\n");
1244
Sbuild::Exception::Build->throw(error => "chmod -R g-s,go+rX $dscdir failed",
1245
failstage => "unpack");
1248
$dscdir = "$build_dir/$dscdir"
1251
$dscdir = "$build_dir/$dscdir";
1253
$self->log_subsubsection("Check unpacked source");
1254
# check if the unpacked tree is really the version we need
1255
$dscdir = $self->get('Session')->strip_chroot_path($dscdir);
1256
my $pipe = $self->get('Session')->pipe_command(
1257
{ COMMAND => ['dpkg-parsechangelog'],
1258
USER => $self->get_conf('BUILD_USER'),
1261
$self->set('Sub Task', "dpkg-parsechangelog");
1269
$self->log("FAILED [dpkg-parsechangelog died]\n");
1270
Sbuild::Exception::Build->throw(error => "FAILED [dpkg-parsechangelog died]",
1271
failstage => "check-unpacked-version");
1273
if ($clog !~ /^Version:\s*(.+)\s*$/mi) {
1274
$self->log("dpkg-parsechangelog didn't print Version:\n");
1275
Sbuild::Exception::Build->throw(error => "dpkg-parsechangelog didn't print Version:",
1276
failstage => "check-unpacked-version");
1280
$self->log_subsubsection("Check disc space");
1281
my $current_usage = `du -k -s "$dscdir"`;
1282
$current_usage =~ /^(\d+)/;
1283
$current_usage = $1;
1284
if ($current_usage) {
1285
my $free = df($dscdir);
1286
if ($free < 2*$current_usage && $self->get_conf('CHECK_SPACE')) {
1287
Sbuild::Exception::Build->throw(error => "Disc space is probably not sufficient for building.",
1288
info => "Source needs $current_usage KiB, while $free KiB is free.)",
1289
failstage => "check-space");
1291
$self->log("Sufficient free space for build\n");
1295
my $cpipe = $self->get('Session')->pipe_command(
1296
{ COMMAND => ['dpkg-parsechangelog'],
1297
USER => $self->get_conf('BUILD_USER'),
1299
DIR => $self->get('Session')->strip_chroot_path($dscdir) });
1300
my $clog = do { local $/; <$cpipe> };
1303
$self->log("FAILED [dpkg-parsechangelog died]\n");
1307
my ($name) = $clog =~ /^Source:\s*(.*)$/m;
1308
my ($version) = $clog =~ /^Version:\s*(.*)$/m;
1309
my ($dists) = $clog =~ /^Distribution:\s*(.*)$/m;
1310
my ($urgency) = $clog =~ /^Urgency:\s*(.*)$/m;
1311
my ($date) = $clog =~ /^Date:\s*(.*)$/m;
1312
if ($dists ne $self->get_conf('DISTRIBUTION')) {
1313
$self->build_log_colour('yellow',
1314
"^Distribution: " . $self->get_conf('DISTRIBUTION') . "\$");
1317
if ($self->get_conf('BIN_NMU') || $self->get_conf('APPEND_TO_VERSION')) {
1318
if (!$self->get_conf('MAINTAINER_NAME')) {
1319
Sbuild::Exception::Build->throw(error => "No maintainer specified.",
1320
info => 'When making changelog additions for a binNMU or appending a version suffix, a maintainer must be specified for the changelog entry e.g. using $maintainer_name, $uploader_name or $key_id, (or the equivalent command-line options)',
1321
failstage => "check-space");
1324
$self->log_subsubsection("Hack binNMU version");
1325
if (open( F, "<$dscdir/debian/changelog" )) {
1326
my $text = do { local $/; <F> };
1330
my $NMUversion = $self->get('Version');
1331
if (!open( F, ">$dscdir/debian/changelog" )) {
1332
$self->log("Can't open debian/changelog for binNMU hack: $!\n");
1333
Sbuild::Exception::Build->throw(error => "Can't open debian/changelog for binNMU hack: $!",
1334
failstage => "hack-binNMU");
1336
$dists = $self->get_conf('DISTRIBUTION');
1338
print F "$name ($NMUversion) $dists; urgency=low\n\n";
1339
if ($self->get_conf('APPEND_TO_VERSION')) {
1340
print F " * Append ", $self->get_conf('APPEND_TO_VERSION'),
1341
" to version number; no source changes\n";
1343
if ($self->get_conf('BIN_NMU')) {
1344
print F " * Binary-only non-maintainer upload for $arch; ",
1345
"no source changes.\n";
1346
print F " * ", join( " ", split( "\n", $self->get_conf('BIN_NMU') )), "\n";
1350
print F " -- " . $self->get_conf('MAINTAINER_NAME') . " $date\n\n";
1353
$self->log("Created changelog entry for binNMU version $NMUversion\n");
1356
$self->log("Can't open debian/changelog -- no binNMU hack!\n");
1357
Sbuild::Exception::Build->throw(error => "Can't open debian/changelog -- no binNMU hack: $!!",
1358
failstage => "hack-binNMU");
1362
if (-f "$dscdir/debian/files") {
1365
open( FILES, "<$dscdir/debian/files" );
1366
chomp( @lines = <FILES> );
1368
@lines = map { my $ind = 76-length($_);
1369
$ind = 0 if $ind < 0;
1370
"│ $_".(" " x $ind). " │\n"; } @lines;
1372
$self->log_warning("After unpacking, there exists a file debian/files with the contents:\n");
1374
$self->log('┌', '─'x78, '┐', "\n");
1378
$self->log('└', '─'x78, '┘', "\n");
1380
$self->log_info("This should be reported as a bug.\n");
1381
$self->log_info("The file has been removed to avoid dpkg-genchanges errors.\n");
1383
unlink "$dscdir/debian/files";
1386
# Build tree not writable during build (except for the sbuild
1387
# user performing the build).
1388
$self->get('Session')->run_command(
1389
{ COMMAND => ['chmod', '-R', 'go-w', $self->get('Build Dir')],
1393
$self->log("chmod og-w " . $self->get('Build Dir') . " failed.\n");
1397
$self->set('Build Start Time', time);
1398
$self->set('Build End Time', $self->get('Build Start Time'));
1400
my $binopt = $self->get_conf('BUILD_SOURCE') ?
1401
$self->get_conf('FORCE_ORIG_SOURCE') ? "-sa" : "" :
1402
$self->get_conf('BUILD_ARCH_ALL') ? "-b" : "-B";
1404
my $bdir = $self->get('Session')->strip_chroot_path($dscdir);
1405
if (-f "$self->{'Chroot Dir'}/etc/ld.so.conf" &&
1406
! -r "$self->{'Chroot Dir'}/etc/ld.so.conf") {
1407
$self->get('Session')->run_command(
1408
{ COMMAND => ['chmod', 'a+r', '/etc/ld.so.conf'],
1413
$self->log_subsubsection("Fix ld.so");
1414
$self->log("ld.so.conf was not readable! Fixed.\n");
1418
push (@{$buildcmd}, $self->get_conf('BUILD_ENV_CMND'))
1419
if (defined($self->get_conf('BUILD_ENV_CMND')) &&
1420
$self->get_conf('BUILD_ENV_CMND'));
1421
push (@{$buildcmd}, 'dpkg-buildpackage');
1423
if (defined($self->get_conf('PGP_OPTIONS')) &&
1424
$self->get_conf('PGP_OPTIONS')) {
1425
if (ref($self->get_conf('PGP_OPTIONS')) eq 'ARRAY') {
1426
push (@{$buildcmd}, @{$self->get_conf('PGP_OPTIONS')});
1428
push (@{$buildcmd}, $self->get_conf('PGP_OPTIONS'));
1432
if (defined($self->get_conf('SIGNING_OPTIONS')) &&
1433
$self->get_conf('SIGNING_OPTIONS')) {
1434
if (ref($self->get_conf('SIGNING_OPTIONS')) eq 'ARRAY') {
1435
push (@{$buildcmd}, @{$self->get_conf('SIGNING_OPTIONS')});
1437
push (@{$buildcmd}, $self->get_conf('SIGNING_OPTIONS'));
1441
push (@{$buildcmd}, $binopt) if $binopt;
1442
push (@{$buildcmd}, "-r" . $self->get_conf('FAKEROOT'));
1444
if (defined($self->get_conf('DPKG_BUILDPACKAGE_USER_OPTIONS')) &&
1445
$self->get_conf('DPKG_BUILDPACKAGE_USER_OPTIONS')) {
1446
push (@{$buildcmd}, @{$self->get_conf('DPKG_BUILDPACKAGE_USER_OPTIONS')});
1449
# Set up additional build environment variables.
1450
my %buildenv = %{$self->get_conf('BUILD_ENVIRONMENT')};
1451
$buildenv{'PATH'} = $self->get_conf('PATH');
1452
$buildenv{'LD_LIBRARY_PATH'} = $self->get_conf('LD_LIBRARY_PATH')
1453
if defined($self->get_conf('LD_LIBRARY_PATH'));
1455
# Explicitly add any needed environment to the environment filter
1456
# temporarily for dpkg-buildpackage.
1458
foreach my $envvar (keys %buildenv) {
1459
push(@env_filter, "^$envvar\$");
1462
# Dump build environment
1463
$self->log_subsubsection("User Environment");
1465
my $pipe = $self->get('Session')->pipe_command(
1466
{ COMMAND => ['env'],
1468
ENV_FILTER => \@env_filter,
1469
USER => $self->get_conf('BUILD_USER'),
1475
my (@lines) = <$pipe>;
1478
@lines=sort(@lines);
1479
foreach my $line (@lines) {
1480
# $line contains a trailing newline, so don't add one.
1485
$self->log_subsubsection("dpkg-buildpackage");
1488
COMMAND => $buildcmd,
1490
ENV_FILTER => \@env_filter,
1491
USER => $self->get_conf('BUILD_USER'),
1497
my $pipe = $self->get('Session')->pipe_command($command);
1499
$self->set('Sub Task', "dpkg-buildpackage");
1501
# We must send the signal as root, because some subprocesses of
1502
# dpkg-buildpackage could run as root. So we have to use a shell
1503
# command to send the signal... but /bin/kill can't send to
1504
# process groups :-( So start another Perl :-)
1505
my $timeout = $self->get_conf('INDIVIDUAL_STALLED_PKG_TIMEOUT')->{$pkg} ||
1506
$self->get_conf('STALLED_PKG_TIMEOUT');
1509
my(@timeout_times, @timeout_sigs, $last_time);
1511
local $SIG{'ALRM'} = sub {
1512
my $pid = $command->{'PID'};
1513
my $signal = ($timed_out > 0) ? "KILL" : "TERM";
1514
$self->get('Session')->run_command(
1515
{ COMMAND => ['perl',
1517
"kill( \"$signal\", -$pid )"],
1522
$timeout_times[$timed_out] = time - $last_time;
1523
$timeout_sigs[$timed_out] = $signal;
1525
$timeout = 5*60; # only wait 5 minutes until next signal
1532
if ($self->get('ABORT')) {
1533
my $pid = $command->{'PID'};
1534
$self->get('Session')->run_command(
1535
{ COMMAND => ['perl',
1537
"kill( \"TERM\", -$pid )"],
1549
for( $i = 0; $i < $timed_out; ++$i ) {
1550
$self->log("Build killed with signal " . $timeout_sigs[$i] .
1551
" after " . int($timeout_times[$i]/60) .
1552
" minutes of inactivity\n");
1554
$self->set('Build End Time', time);
1555
$self->set('Pkg End Time', time);
1556
$self->write_stats('build-time',
1557
$self->get('Build End Time')-$self->get('Build Start Time'));
1558
$self->write_stats('install-download-time',
1559
$self->get('Install End Time')-$self->get('Install Start Time'));
1560
my $finish_date = strftime("%Y%m%d-%H%M",localtime($self->get('Build End Time')));
1562
$self->log("Build finished at $finish_date\n");
1564
my @space_files = ("$dscdir");
1566
$self->log_subsubsection("Finished");
1568
$self->log_error("Build failure (dpkg-buildpackage died)\n");
1570
$self->log_info("Built successfully\n");
1572
if (-r "$dscdir/debian/files" && $self->get('Chroot Build Dir')) {
1573
my @files = $self->debian_files_list("$dscdir/debian/files");
1576
if (! -f "$build_dir/$_") {
1577
$self->log_error("Package claims to have built ".basename($_).", but did not. This is a bug in the packaging.\n");
1580
if (/_all.u?deb$/ and not $self->get_conf('BUILD_ARCH_ALL')) {
1581
$self->log_error("Package builds ".basename($_)." when binary-indep target is not called. This is a bug in the packaging.\n");
1582
unlink("$build_dir/$_");
1589
# Restore write access to build tree now build is complete.
1590
$self->get('Session')->run_command(
1591
{ COMMAND => ['chmod', '-R', 'g+w', $self->get('Build Dir')],
1595
$self->log("chmod g+w " . $self->get('Build Dir') . " failed.\n");
1599
$self->log_subsection("Changes");
1600
$changes = $self->get('Package_SVersion') . "_$arch.changes";
1602
if (-r "$build_dir/$changes") {
1603
my(@do_dists, @saved_dists);
1604
$self->log_subsubsection("$changes:");
1605
open( F, "<$build_dir/$changes" );
1606
my $sys_build_dir = $self->get_conf('BUILD_DIR');
1607
if (open( F2, ">$sys_build_dir/$changes.new" )) {
1609
if (/^Distribution:\s*(.*)\s*$/ and $self->get_conf('OVERRIDE_DISTRIBUTION')) {
1610
$self->log("Distribution: " . $self->get_conf('DISTRIBUTION') . "\n");
1611
print F2 "Distribution: " . $self->get_conf('DISTRIBUTION') . "\n";
1615
while (length $_ > 989)
1617
my $index = rindex($_,' ',989);
1618
$self->log(substr ($_,0,$index) . "\n");
1619
$_ = ' ' . substr ($_,$index+1);
1622
if (/^ [a-z0-9]{32}/) {
1623
push(@cfiles, (split( /\s+/, $_ ))[5] );
1628
rename("$sys_build_dir/$changes.new", "$sys_build_dir/$changes")
1629
or $self->log("$sys_build_dir/$changes.new could not be " .
1630
"renamed to $sys_build_dir/$changes: $!\n");
1631
$self->set('Changes File', "$sys_build_dir/$changes");
1632
unlink("$build_dir/$changes")
1636
$self->log("Cannot create $sys_build_dir/$changes.new: $!\n");
1637
$self->log("Distribution field may be wrong!!!\n");
1639
system "mv", "-f", "$build_dir/$changes", "."
1640
and $self->log_error("Could not move ".basename($_)." to .\n");
1646
$self->log("Can't find $changes -- can't dump info\n");
1649
$self->log_subsection("Package contents");
1651
my @debcfiles = @cfiles;
1652
foreach (@debcfiles) {
1653
my $deb = "$build_dir/$_";
1654
next if $deb !~ /(\Q$arch\E|all)\.[\w\d.-]*$/;
1656
$self->log_subsubsection("$_");
1657
if (!open( PIPE, "dpkg --info $deb 2>&1 |" )) {
1658
$self->log("Can't spawn dpkg: $! -- can't dump info\n");
1661
$self->log($_) while( <PIPE> );
1665
if (!open( PIPE, "dpkg --contents $deb 2>&1 |" )) {
1666
$self->log("Can't spawn dpkg: $! -- can't dump info\n");
1669
$self->log($_) while( <PIPE> );
1676
push( @space_files, $self->get_conf('BUILD_DIR') . "/$_");
1677
system "mv", "-f", "$build_dir/$_", $self->get_conf('BUILD_DIR')
1678
and $self->log_error("Could not move $_ to .\n");
1682
$self->check_watches();
1683
$self->check_space(@space_files);
1685
return $rv == 0 ? 1 : 0;
1688
# Produce a hash suitable for ENV export
1693
sub _env_loop ($$$$) {
1694
my ($env,$ref,$keysref,$prefix) = @_;
1696
foreach my $key (keys( %{ $keysref } )) {
1697
my $value = $ref->get($key);
1698
next if (!defined($value));
1699
next if (ref($value));
1700
my $name = "${prefix}${key}";
1702
$env->{$name} = $value;
1707
_env_loop($envlist, $self, $self, $prefix);
1708
_env_loop($envlist, $self->get('Config'), $self->get('Config')->{'KEYS'}, "${prefix}CONF_");
1712
sub read_build_essential {
1717
if (open( F, "$self->{'Chroot Dir'}/usr/share/doc/build-essential/essential-packages-list" )) {
1723
push( @essential, $_ ) if $_ !~ /^\s*$/;
1728
warn "Cannot open $self->{'Chroot Dir'}/usr/share/doc/build-essential/essential-packages-list: $!\n";
1731
if (open( F, "$self->{'Chroot Dir'}/usr/share/doc/build-essential/list" )) {
1733
last if $_ eq "BEGIN LIST OF PACKAGES\n";
1737
last if $_ eq "END LIST OF PACKAGES";
1738
next if /^\s/ || /^$/;
1739
push( @essential, $_ );
1744
warn "Cannot open $self->{'Chroot Dir'}/usr/share/doc/build-essential/list: $!\n";
1747
# Workaround http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=602571
1748
# Also works around Ubuntu Lucid shipping with "diff" instead of
1749
# "diffutils": https://bugs.launchpad.net/ubuntu/+source/sbuild/+bug/741897
1750
if (open( F, "$self->{'Chroot Dir'}/etc/lsb-release" )) {
1752
if ($_ eq "DISTRIB_ID=Ubuntu\n") {
1753
@essential = grep(!/^sysvinit$/, @essential);
1755
if ($_ eq "DISTRIB_CODENAME=lucid\n") {
1756
s/^diff$/diffutils/ for (@essential);
1762
return join( ", ", @essential );
1771
my $pipe = $self->get('Host')->pipe_command(
1772
{ COMMAND => ['du', '-k', '-s', $_],
1773
USER => $self->get_conf('USERNAME'),
1778
$self->log("Cannot determine space needed (du failed): $!\n");
1788
$self->set('This Time', $self->get('Pkg End Time') - $self->get('Pkg Start Time'));
1789
$self->get('This Time') = 0 if $self->get('This Time') < 0;
1790
$self->set('This Space', $sum);
1793
sub prepare_watches {
1798
# init %this_watches to names of packages which have not been
1799
# installed as source dependencies
1800
$self->set('This Watches', {});
1801
foreach $pkg (keys %{$self->get_conf('WATCHES')}) {
1802
if (isin( $pkg, @instd )) {
1803
debug("Excluding from watch: $pkg\n");
1806
foreach $prg (@{$self->get_conf('WATCHES')->{$pkg}}) {
1807
# Add /usr/bin to programs without a path
1808
$prg = "/usr/bin/$prg" if $prg !~ m,^/,;
1809
$self->get('This Watches')->{"$self->{'Chroot Dir'}$prg"} = $pkg;
1810
debug("Will watch for $prg ($pkg)\n");
1817
my($prg, @st, %used);
1819
return if (!$self->get_conf('CHECK_WATCHES'));
1821
foreach $prg (keys %{$self->get('This Watches')}) {
1822
if (!(@st = stat( $prg ))) {
1823
debug("Watch: $prg: stat failed\n");
1826
if ($st[8] > $self->get('Build Start Time')) {
1827
my $pkg = $self->get('This Watches')->{$prg};
1828
my $prg2 = $self->get('Session')->strip_chroot_path($prg);
1829
push( @{$used{$pkg}}, $prg2 );
1832
debug("Watch: $prg: untouched\n");
1837
$self->log_warning("NOTE: Binaries from the following packages (access time changed) used\nwithout a source dependency:");
1839
foreach (keys %used) {
1840
$self->log(" $_: @{$used{$_}}\n");
1848
my $for_srcdep = shift;
1849
my $lockfile = "$file.lock";
1853
if (!sysopen( F, $lockfile, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644 )){
1855
# lock file exists, wait
1856
goto repeat if !open( F, "<$lockfile" );
1860
if ($line !~ /^(\d+)\s+([\w\d.-]+)$/) {
1861
$self->log_warning("Bad lock file contents ($lockfile) -- still trying\n");
1864
($pid, $user) = ($1, $2);
1865
if (kill( 0, $pid ) == 0 && $! == ESRCH) {
1866
# process doesn't exist anymore, remove stale lock
1867
$self->log_warning("Removing stale lock file $lockfile ".
1868
"(pid $pid, user $user)\n");
1869
unlink( $lockfile );
1874
if (!$for_srcdep && $try > $self->get_conf('MAX_LOCK_TRYS')) {
1875
$self->log_warning("Lockfile $lockfile still present after " .
1876
$self->get_conf('MAX_LOCK_TRYS') *
1877
$self->get_conf('LOCK_INTERVAL') .
1878
" seconds -- giving up\n");
1881
$self->log("Another sbuild process ($pid by $user) is currently installing or removing packages -- waiting...\n")
1882
if $for_srcdep && $try == 1;
1883
sleep $self->get_conf('LOCK_INTERVAL');
1886
$self->log_warning("Can't create lock file $lockfile: $!\n");
1889
my $username = $self->get_conf('USERNAME');
1890
F->print("$$ $username\n");
1897
my $lockfile = "$file.lock";
1899
unlink( $lockfile );
1907
$self->get('Summary Stats')->{$key} = $value;
1910
sub generate_stats {
1913
$self->add_stat('Job', $self->get('Job'));
1914
$self->add_stat('Package', $self->get('Package'));
1915
$self->add_stat('Version', $self->get('Version'));
1916
$self->add_stat('Source-Version', $self->get('OVersion'));
1917
$self->add_stat('Architecture', $self->get('Arch'));
1918
$self->add_stat('Distribution', $self->get_conf('DISTRIBUTION'));
1919
$self->add_stat('Space', $self->get('This Space'));
1920
$self->add_stat('Build-Time',
1921
$self->get('Build End Time')-$self->get('Build Start Time'));
1922
$self->add_stat('Install-Time',
1923
$self->get('Install End Time')-$self->get('Install Start Time'));
1924
$self->add_stat('Package-Time',
1925
$self->get('Pkg End Time')-$self->get('Pkg Start Time'));
1926
$self->add_stat('Build-Space', $self->get('This Space'));
1927
$self->add_stat('Status', $self->get_status());
1928
$self->add_stat('Fail-Stage', $self->get('Pkg Fail Stage'))
1929
if ($self->get_status() ne "successful");
1930
$self->add_stat('Lintian', $self->get('Lintian Reason'))
1931
if $self->get('Lintian Reason');
1932
$self->add_stat('Piuparts', $self->get('Piuparts Reason'))
1933
if $self->get('Piuparts Reason');
1938
foreach my $stat (sort keys %{$self->get('Summary Stats')}) {
1939
$self->log("${stat}: " . $self->get('Summary Stats')->{$stat} . "\n");
1945
foreach my $stat (sort keys %{$self->get('Summary Stats')}) {
1946
print STDOUT "${stat}: " . $self->get('Summary Stats')->{$stat} . "\n";
1953
return if (!$self->get_conf('BATCH_MODE'));
1955
my $stats_dir = $self->get_conf('STATS_DIR');
1957
return if not defined $stats_dir;
1959
if (! -d $stats_dir &&
1960
!mkdir $stats_dir) {
1961
$self->log_warning("Could not create $stats_dir: $!\n");
1965
my ($cat, $val) = @_;
1968
$self->lock_file($stats_dir, 0);
1969
open( F, ">>$stats_dir/$cat" );
1972
$self->unlock_file($stats_dir);
1975
sub debian_files_list {
1981
debug("Parsing $files\n");
1983
if (-r $files && open( FILES, "<$files" )) {
1986
my $f = (split( /\s+/, $_ ))[0];
1987
push( @list, "$f" );
1990
close( FILES ) or $self->log("Failed to close $files\n") && return 1;
1996
# Figure out chroot architecture
2000
my $pipe = $self->get('Session')->pipe_command(
2001
{ COMMAND => ['dpkg', '--print-architecture'],
2002
USER => $self->get_conf('BUILD_USER'),
2004
DIR => '/' }) || return undef;
2006
chomp(my $chroot_arch = <$pipe>);
2009
Sbuild::Exception::Build->throw(error => "Can't determine architecture of chroot: $!",
2010
failstage => "chroot-arch")
2011
if ($? || !defined($chroot_arch));
2013
return $chroot_arch;
2016
sub build_log_filter {
2019
my $replacement = shift;
2021
if ($self->get_conf('LOG_FILTER')) {
2022
$self->log($self->get('FILTER_PREFIX') . $text . ':' . $replacement . "\n");
2026
sub build_log_colour {
2031
if ($self->get_conf('LOG_COLOUR')) {
2032
$self->log($self->get('COLOUR_PREFIX') . $colour . ':' . $regex . "\n");
2036
sub open_build_log {
2039
my $date = strftime("%Y%m%d-%H%M", localtime($self->get('Pkg Start Time')));
2041
my $filter_prefix = '__SBUILD_FILTER_' . $$ . ':';
2042
$self->set('FILTER_PREFIX', $filter_prefix);
2043
my $colour_prefix = '__SBUILD_COLOUR_' . $$ . ':';
2044
$self->set('COLOUR_PREFIX', $colour_prefix);
2046
my $filename = $self->get_conf('LOG_DIR') . '/' .
2047
$self->get('Package_SVersion') . '-' .
2048
$self->get('Arch') .
2051
open($saved_stdout, ">&STDOUT") or warn "Can't redirect stdout\n";
2052
open($saved_stderr, ">&STDERR") or warn "Can't redirect stderr\n";
2057
($pid = open($PLOG, "|-"));
2058
if (!defined $pid) {
2059
warn "Cannot open pipe to '$filename': $!\n";
2060
} elsif ($pid == 0) {
2061
$SIG{'INT'} = 'IGNORE';
2062
$SIG{'TERM'} = 'IGNORE';
2063
$SIG{'QUIT'} = 'IGNORE';
2064
$SIG{'PIPE'} = 'IGNORE';
2066
$PROGRAM_NAME = 'package log for ' . $self->get('Package_SVersion') . '_' . $self->get('Arch');
2068
if (!$self->get_conf('NOLOG') &&
2069
$self->get_conf('LOG_DIR_AVAILABLE')) {
2070
open( CPLOG, ">$filename" ) or
2071
Sbuild::Exception::Build->throw(error => "Failed to open build log $filename: $!",
2072
failstage => "init");
2073
CPLOG->autoflush(1);
2074
$saved_stdout->autoflush(1);
2076
# Create 'current' symlinks
2077
if ($self->get_conf('SBUILD_MODE') eq 'buildd') {
2078
$self->log_symlink($filename,
2079
$self->get_conf('BUILD_DIR') . '/current-' .
2080
$self->get_conf('DISTRIBUTION'));
2082
$self->log_symlink($filename,
2083
$self->get_conf('BUILD_DIR') . '/' .
2084
$self->get('Package_SVersion') . '_' .
2085
$self->get('Arch') . '.build');
2089
# Cache vars to avoid repeated hash lookups.
2090
my $nolog = $self->get_conf('NOLOG');
2091
my $log = $self->get_conf('LOG_DIR_AVAILABLE');
2092
my $verbose = $self->get_conf('VERBOSE');
2093
my $log_colour = $self->get_conf('LOG_COLOUR');
2096
my ($text, $replacement);
2097
my $filter_regex = "^$filter_prefix(.*):(.*)\$";
2098
my $colour_regex = "^$colour_prefix(.*):(.*)\$";
2102
# Add a replacement pattern to filter (sent from main
2103
# process in log stream).
2104
if (m/$filter_regex/) {
2105
($text,$replacement)=($1,$2);
2106
$replacement = "«$replacement»";
2107
push (@filter, [$text, $replacement]);
2108
$_ = "I: NOTICE: Log filtering will replace '$text' with '$replacement'\n";
2109
} elsif (m/$colour_regex/) {
2110
my ($colour, $regex);
2111
($colour,$regex)=($1,$2);
2112
push (@colour, [$colour, $regex]);
2113
# $_ = "I: NOTICE: Log colouring will colour '$regex' in $colour\n";
2116
# Filter out any matching patterns
2117
foreach my $pattern (@filter) {
2118
($text,$replacement) = @{$pattern};
2119
s/$text/$replacement/g;
2122
if (m/Deprecated key/ || m/please update your configuration/) {
2124
foreach my $ignore (@ignore) {
2125
$skip = 1 if ($ignore eq $_);
2131
if ($nolog || $verbose) {
2132
if (-t $saved_stdout && $log_colour) {
2133
my $colour = 'reset';
2134
foreach my $pattern (@colour) {
2135
if (m/$$pattern[0]/) {
2136
$colour = $$pattern[1];
2139
print $saved_stdout color $colour;
2142
print $saved_stdout $_;
2143
if (-t $saved_stdout && $log_colour) {
2144
print $saved_stdout color 'reset';
2147
# Manual flushing due to Perl 5.10 bug. Should autoflush.
2148
$saved_stdout->flush();
2150
if (!$nolog && $log) {
2159
$PLOG->autoflush(1);
2160
open(STDOUT, '>&', $PLOG) or warn "Can't redirect stdout\n";
2161
open(STDERR, '>&', $PLOG) or warn "Can't redirect stderr\n";
2162
$self->set('Log File', $filename);
2163
$self->set('Log Stream', $PLOG);
2165
my $hostname = $self->get_conf('HOSTNAME');
2166
$self->log("sbuild (Debian sbuild) $version ($release_date) on $hostname\n");
2168
my $head1 = $self->get('Package') . ' ' . $self->get('Version') .
2169
' (' . $self->get('Arch') . ') ';
2170
my $head2 = strftime("%d %b %Y %H:%M",
2171
localtime($self->get('Pkg Start Time')));
2172
my $head = $head1 . ' ' x (80 - 4 - length($head1) - length($head2)) .
2174
$self->log_section($head);
2176
$self->log("Package: " . $self->get('Package') . "\n");
2177
$self->log("Version: " . $self->get('Version') . "\n");
2178
$self->log("Source Version: " . $self->get('OVersion') . "\n");
2179
$self->log("Distribution: " . $self->get_conf('DISTRIBUTION') . "\n");
2180
$self->log("Architecture: " . $self->get('Arch') . "\n");
2184
sub close_build_log {
2187
my $time = $self->get('Pkg End Time');
2191
my $date = strftime("%Y%m%d-%H%M", localtime($time));
2193
if ($self->get_status() eq "successful") {
2194
$self->add_time_entry($self->get('Package_Version'), $self->get('This Time'));
2195
$self->add_space_entry($self->get('Package_Version'), $self->get('This Space'));
2198
my $hours = int($self->get('This Time')/3600);
2199
my $minutes = int(($self->get('This Time')%3600)/60),
2200
my $seconds = int($self->get('This Time')%60),
2201
my $space = $self->get('This Space');
2203
my $filename = $self->get('Log File');
2205
# building status at this point means failure.
2206
if ($self->get_status() eq "building") {
2207
$self->set_status('failed');
2210
$self->log_subsection('Summary');
2211
$self->generate_stats();
2215
$self->log("Finished at ${date}\n");
2216
$self->log(sprintf("Build needed %02d:%02d:%02d, %dk disc space\n",
2217
$hours, $minutes, $seconds, $space));
2219
if ($self->get_status() eq "successful") {
2220
if (defined($self->get_conf('KEY_ID')) && $self->get_conf('KEY_ID')) {
2221
my $key_id = $self->get_conf('KEY_ID');
2222
$self->log(sprintf("Signature with key '%s' requested:\n", $key_id));
2223
my $changes = $self->get('Package_SVersion') . '_' . $self->get('Arch') . '.changes';
2224
system (sprintf('debsign -k%s %s', $key_id, $changes));
2228
my $subject = "Log for " . $self->get_status() .
2229
" build of " . $self->get('Package_Version');
2230
if ($self->get('Arch')) {
2231
$subject .= " on " . $self->get('Arch');
2233
if ($self->get_conf('ARCHIVE')) {
2234
$subject .= " (" . $self->get_conf('ARCHIVE') . "/" . $self->get_conf('DISTRIBUTION') . ")";
2237
$subject .= " (dist=" . $self->get_conf('DISTRIBUTION') . ")";
2240
open(STDERR, '>&', $saved_stderr) or warn "Can't redirect stderr\n"
2241
if defined($saved_stderr);
2242
open(STDOUT, '>&', $saved_stdout) or warn "Can't redirect stdout\n"
2243
if defined($saved_stdout);
2244
$saved_stderr->close();
2245
undef $saved_stderr;
2246
$saved_stdout->close();
2247
undef $saved_stdout;
2248
$self->set('Log File', undef);
2249
if (defined($self->get('Log Stream'))) {
2250
$self->get('Log Stream')->close(); # Close child logger process
2251
$self->set('Log Stream', undef);
2254
$self->send_build_log($self->get_conf('MAILTO'), $subject, $filename)
2255
if (defined($filename) && -f $filename &&
2256
$self->get_conf('MAILTO'));
2259
sub send_build_log {
2262
my $subject = shift;
2263
my $filename = shift;
2265
my $conf = $self->get('Config');
2267
if ($conf->get('MIME_BUILD_LOG_MAILS')) {
2268
return $self->send_mime_build_log($to, $subject, $filename);
2270
return send_mail($conf, $to, $subject, $filename);
2274
sub send_mime_build_log {
2277
my $subject = shift;
2278
my $filename = shift;
2280
my $conf = $self->get('Config');
2281
my $tmp; # Needed for gzip, here for proper scoping.
2283
my $msg = MIME::Lite->new(
2284
From => $conf->get('MAILFROM'),
2286
Subject => $subject,
2287
Type => 'multipart/mixed'
2290
# Add the GPG key ID to the mail if present so that it's clear if the log
2291
# still needs signing or not.
2292
if (defined($self->get_conf('KEY_ID')) && $self->get_conf('KEY_ID')) {
2293
$msg->add('Key-ID', $self->get_conf('KEY_ID'));
2296
if (!$conf->get('COMPRESS_BUILD_LOG_MAILS')) {
2297
my $log_part = MIME::Lite->new(
2298
Type => 'text/plain',
2300
Filename => basename($filename)
2302
$log_part->attr('content-type.charset' => 'UTF-8');
2303
$msg->attach($log_part);
2305
local( *F, *GZFILE );
2307
if (!open( F, "<$filename" )) {
2308
warn "Cannot open $filename for mailing: $!\n";
2312
$tmp = File::Temp->new();
2313
tie *GZFILE, 'IO::Zlib', $tmp->filename, 'wb';
2324
Type => 'application/x-gzip',
2325
Path => $tmp->filename,
2326
Filename => basename($filename) . '.gz'
2330
my $changes = $self->get('Package_SVersion') . '_' . $self->get('Arch') . '.changes';
2331
if ($self->get_status() eq 'successful' && -r $changes) {
2332
my $log_part = MIME::Lite->new(
2333
Type => 'text/plain',
2335
Filename => basename($changes)
2337
$log_part->attr('content-type.charset' => 'UTF-8');
2338
$msg->attach($log_part);
2342
foreach my $stat (sort keys %{$self->get('Summary Stats')}) {
2343
$stats .= sprintf("%s: %s\n", $stat, $self->get('Summary Stats')->{$stat});
2346
Type => 'text/plain',
2347
Filename => basename($filename) . '.summary',
2351
local $SIG{'PIPE'} = 'IGNORE';
2353
if (!open( MAIL, "|" . $conf->get('MAILPROG') . " -oem $to" )) {
2354
warn "Could not open pipe to " . $conf->get('MAILPROG') . ": $!\n";
2359
$msg->print(\*MAIL);
2361
if (!close( MAIL )) {
2362
warn $conf->get('MAILPROG') . " failed (exit status $?)\n";
2373
unlink $dest; # Don't return on failure, since the symlink will fail.
2374
symlink $log, $dest;
2377
sub add_time_entry {
2382
return if !$self->get_conf('AVG_TIME_DB');
2384
if (!tie %db, 'GDBM_File', $self->get_conf('AVG_TIME_DB'), GDBM_WRCREAT, 0664) {
2385
$self->log_warning("Can't open average time db " . $self->get_conf('AVG_TIME_DB') . "\n");
2390
if (exists $db{$pkg}) {
2391
my @times = split( /\s+/, $db{$pkg} );
2394
foreach (@times[1..$#times]) { $sum += $_; }
2395
$times[0] = $sum / (@times-1);
2396
$db{$pkg} = join( ' ', @times );
2399
$db{$pkg} = "$t $t";
2404
sub add_space_entry {
2411
return if !$self->get_conf('AVG_SPACE_DB') || $space == 0;
2413
if (!tie %db, 'GDBM_File', $self->get_conf('AVG_SPACE_DB'), &GDBM_WRCREAT, 0664) {
2414
$self->log_warning("Can't open average space db " . $self->get_conf('AVG_SPACE_DB') . "\n");
2419
if (exists $db{$pkg}) {
2420
my @values = split( /\s+/, $db{$pkg} );
2422
unshift( @values, $space );
2423
pop @values if @values > $keepvals;
2424
my ($sum, $n, $weight, $i) = (0, 0, scalar(@values));
2425
for( $i = 0; $i < @values; ++$i) {
2426
$sum += $values[$i] * $weight;
2429
unshift( @values, $sum/$n );
2430
$db{$pkg} = join( ' ', @values );
2433
$db{$pkg} = "$space $space";