~ubuntu-branches/ubuntu/oneiric/sbuild/oneiric

« back to all changes in this revision

Viewing changes to lib/Sbuild/Build.pm

  • Committer: Bazaar Package Importer
  • Author(s): Lorenzo De Liso
  • Date: 2011-05-01 16:55:16 UTC
  • mfrom: (8.1.19 upstream) (3.3.17 sid)
  • Revision ID: james.westby@ubuntu.com-20110501165516-8g3uwrnhv2bzjt8y
Tags: 0.62.2-1ubuntu1
* Merge from debian unstable, remaining changes:
  - debian/patches/do-not-install-debfoster-into-chroots.patch: 
    do not install debfoster into the chroots because it is in universe and 
    not needed for package building itself.
  - debian/patches/run-pre-build-hooks-as-root.patch: 
    run pre-build hooks as root (Closes: #607228)
* Now that the package uses a patch system, don't modify the files directly;
  instead, put the changes in the respective patches and add the DEP-3
  patch tagging guidelines to them.

Show diffs side-by-side

added added

removed removed

Lines of Context:
25
25
use strict;
26
26
use warnings;
27
27
 
 
28
use English;
28
29
use POSIX;
29
30
use Errno qw(:POSIX);
30
31
use Fcntl;
37
38
use Cwd qw(:DEFAULT abs_path);
38
39
use Dpkg::Arch;
39
40
use Dpkg::Control;
 
41
use MIME::Lite;
 
42
use Term::ANSIColor;
40
43
 
41
 
use Sbuild qw($devnull binNMU_version version_compare split_version copy isin send_build_log debug df);
 
44
use Sbuild qw($devnull binNMU_version version_compare split_version copy isin debug df send_mail);
42
45
use Sbuild::Base;
43
46
use Sbuild::ChrootInfoSchroot;
44
47
use Sbuild::ChrootInfoSudo;
48
51
use Sbuild::Sysconfig;
49
52
use Sbuild::Utility qw(check_url download parse_file dsc_files);
50
53
use Sbuild::Resolver qw(get_resolver);
 
54
use Sbuild::Exception;
51
55
 
52
56
BEGIN {
53
57
    use Exporter ();
66
70
    my $self = $class->SUPER::new($conf);
67
71
    bless($self, $class);
68
72
 
 
73
    $self->set('ABORT', undef);
69
74
    $self->set('Job', $dsc);
70
75
    $self->set('Arch', undef);
71
76
    $self->set('Chroot Dir', '');
76
81
    $self->set('Pkg Status Trigger', undef);
77
82
    $self->set('Pkg Start Time', 0);
78
83
    $self->set('Pkg End Time', 0);
79
 
    $self->set('Pkg Fail Stage', 0);
 
84
    $self->set('Pkg Fail Stage', 'init');
80
85
    $self->set('Build Start Time', 0);
81
86
    $self->set('Build End Time', 0);
82
87
    $self->set('Install Start Time', 0);
130
135
    return $self;
131
136
}
132
137
 
 
138
sub request_abort {
 
139
    my $self = shift;
 
140
    my $reason = shift;
 
141
 
 
142
    $self->log_error("ABORT: $reason (requesting cleanup and shutdown)\n");
 
143
    $self->set('ABORT', $reason);
 
144
}
 
145
 
 
146
sub check_abort {
 
147
    my $self = shift;
 
148
 
 
149
    if ($self->get('ABORT')) {
 
150
        Sbuild::Exception::Build->throw(error => "Aborting build: " .
 
151
                                        $self->get('ABORT'),
 
152
                                        failstage => "abort");
 
153
    }
 
154
}
 
155
 
133
156
sub set_dsc {
134
157
    my $self = shift;
135
158
    my $dsc = shift;
146
169
            });
147
170
 
148
171
        if (!defined($pipe)) {
149
 
            $self->log_error("Could not parse $dsc/debian/changelog: $!");
150
 
            $self->set('Invalid Source', 1);
151
 
            goto set_vars;
 
172
            Sbuild::Exception::Build->throw(error => "Could not parse $dsc/debian/changelog: $!",
 
173
                                            failstage => "set-dsc");
152
174
        }
153
175
 
154
176
        my $stanzas = parse_file($pipe);
158
180
        my $version = ${$stanza}{'Version'};
159
181
 
160
182
        if (!defined($package) || !defined($version)) {
161
 
            $self->log_error("Missing Source or Version in $dsc/debian/changelog");
162
 
            $self->set('Invalid Source', 1);
163
 
            goto set_vars;
 
183
            Sbuild::Exception::Build->throw(error => "Missing Source or Version in $dsc/debian/changelog",
 
184
                                            failstage => "set-dsc");
164
185
        }
165
186
 
166
187
        my $dir = getcwd();
179
200
        $dsc = "$dir/" . $self->get('Package_OSVersion') . ".dsc";
180
201
    }
181
202
 
182
 
set_vars:
183
203
    $self->set('DSC', $dsc);
184
204
    $self->set('Source Dir', dirname($dsc));
185
205
    $self->set('DSC Base', basename($dsc));
261
281
    return $self->get('Pkg Status');
262
282
}
263
283
 
 
284
# This function is the main entry point into the package build.  It
 
285
# provides a top-level exception handler and does the initial setup
 
286
# including initiating logging and creating host chroot.  The nested
 
287
# run_ functions it calls are separate in order to permit running
 
288
# cleanup tasks in a strict order.
264
289
sub run {
265
290
    my $self = shift;
266
291
 
267
 
    $self->set_status('building');
268
 
 
269
 
    $self->set('Pkg Start Time', time);
270
 
    $self->set('Pkg End Time', $self->get('Pkg Start Time'));
271
 
 
272
 
    # Acquire the architecture we're building for.
273
 
    $self->set('Arch', $self->get_conf('ARCH'));
274
 
 
275
 
    my $dist = $self->get_conf('DISTRIBUTION');
276
 
    if (!defined($dist) || !$dist) {
277
 
        $self->log("No distribution defined\n");
278
 
        goto cleanup_skip;
279
 
    }
280
 
 
281
 
    if ($self->get('Invalid Source')) {
282
 
        $self->log("Invalid source: " . $self->get('DSC') . "\n");
283
 
        $self->log("Skipping " . $self->get('Package') . " \n");
284
 
        $self->set_status('failed');
285
 
        goto cleanup_skip;
286
 
    }
287
 
 
288
 
    my $chroot_info;
289
 
    if ($self->get_conf('CHROOT_MODE') eq 'schroot') {
290
 
        $chroot_info = Sbuild::ChrootInfoSchroot->new($self->get('Config'));
291
 
    } else {
292
 
        $chroot_info = Sbuild::ChrootInfoSudo->new($self->get('Config'));
293
 
    }
294
 
 
295
 
    # TODO: Get package name from build object
296
 
    if (!$self->open_build_log()) {
297
 
        goto cleanup_skip;
298
 
    }
299
 
 
300
 
    # Set a chroot to run commands in host
301
 
    my $host = $self->get('Host');
302
 
 
303
 
    # Host execution defaults (set streams)
304
 
    my $host_defaults = $host->get('Defaults');
305
 
    $host_defaults->{'STREAMIN'} = $devnull;
306
 
    $host_defaults->{'STREAMOUT'} = $self->get('Log Stream');
307
 
    $host_defaults->{'STREAMERR'} = $self->get('Log Stream');
308
 
 
 
292
    eval {
 
293
        $self->check_abort();
 
294
 
 
295
        $self->set_status('building');
 
296
 
 
297
        $self->set('Pkg Start Time', time);
 
298
        $self->set('Pkg End Time', $self->get('Pkg Start Time'));
 
299
 
 
300
        # Acquire the architecture we're building for.
 
301
        $self->set('Arch', $self->get_conf('ARCH'));
 
302
 
 
303
        my $dist = $self->get_conf('DISTRIBUTION');
 
304
        if (!defined($dist) || !$dist) {
 
305
            Sbuild::Exception::Build->throw(error => "No distribution defined",
 
306
                                            failstage => "init");
 
307
        }
 
308
 
 
309
        if ($self->get('Invalid Source')) {
 
310
            Sbuild::Exception::Build->throw(error => "Invalid source " . $self->get('DSC'),
 
311
                                            failstage => "init");
 
312
        }
 
313
 
 
314
        # TODO: Get package name from build object
 
315
        if (!$self->open_build_log()) {
 
316
            Sbuild::Exception::Build->throw(error => "Failed to open build log",
 
317
                                            failstage => "init");
 
318
        }
 
319
 
 
320
        # Set a chroot to run commands in host
 
321
        my $host = $self->get('Host');
 
322
 
 
323
        # Host execution defaults (set streams)
 
324
        my $host_defaults = $host->get('Defaults');
 
325
        $host_defaults->{'STREAMIN'} = $devnull;
 
326
        $host_defaults->{'STREAMOUT'} = $self->get('Log Stream');
 
327
        $host_defaults->{'STREAMERR'} = $self->get('Log Stream');
 
328
 
 
329
        $self->check_abort();
 
330
        $self->run_chroot();
 
331
    };
 
332
 
 
333
    my $e;
 
334
    if ($e = Exception::Class->caught('Sbuild::Exception::Build')) {
 
335
        if ($e->status) {
 
336
            $self->set_status($e->status);
 
337
        } else {
 
338
            $self->set_status("failed");
 
339
        }
 
340
        $self->set('Pkg Fail Stage', $e->failstage);
 
341
        $e->rethrow();
 
342
    }
 
343
}
 
344
 
 
345
# Pack up source if needed and then run the main chroot session.
 
346
# Close log during return/failure.
 
347
sub run_chroot {
 
348
    my $self = shift;
 
349
 
 
350
    eval {
 
351
        $self->check_abort();
 
352
        $self->run_pack_source();
 
353
        $self->check_abort();
 
354
        $self->run_chroot_session();
 
355
    };
 
356
 
 
357
    # Log exception info and set status and fail stage prior to
 
358
    # closing build log.
 
359
    my $e;
 
360
    if ($e = Exception::Class->caught('Sbuild::Exception::Build')) {
 
361
        $self->log_error("$e\n");
 
362
        $self->log_info($e->info."\n")
 
363
            if ($e->info);
 
364
        if ($e->status) {
 
365
            $self->set_status($e->status);
 
366
        } else {
 
367
            $self->set_status("failed");
 
368
        }
 
369
        $self->set('Pkg Fail Stage', $e->failstage);
 
370
    }
 
371
 
 
372
    $self->close_build_log();
 
373
 
 
374
    if ($e) {
 
375
        $e->rethrow();
 
376
    }
 
377
}
 
378
 
 
379
# Pack up local source tree
 
380
sub run_pack_source {
 
381
    my $self=shift;
 
382
 
 
383
    $self->check_abort();
309
384
    # Build the source package if given a Debianized source directory
310
385
    if ($self->get('Debian Source Dir')) {
311
 
        $self->set('Pkg Fail Stage', 'pack-source');
312
386
        $self->log_subsection("Build Source Package");
313
387
 
314
388
        $self->log_subsubsection('clean');
320
394
              PRIORITY => 0,
321
395
            });
322
396
        if ($?) {
323
 
            $self->log_error("Failed to clean source directory");
324
 
 
325
 
            goto cleanup_log;
 
397
            Sbuild::Exception::Build->throw(error => "Failed to clean source directory",
 
398
                                            failstage => "pack-source");
326
399
        }
327
400
 
 
401
        $self->check_abort();
 
402
 
328
403
        $self->log_subsubsection('dpkg-source');
329
404
        my @dpkg_source_command = ($self->get_conf('DPKG_SOURCE'), '-b');
330
 
        push @dpkg_source_command, @{$self->get_conf('DPKG_SOURCE_OPTIONS')} if
331
 
            ($self->get_conf('DPKG_SOURCE_OPTIONS'));
 
405
        push @dpkg_source_command, @{$self->get_conf('DPKG_SOURCE_OPTIONS')}
 
406
        if ($self->get_conf('DPKG_SOURCE_OPTIONS'));
332
407
        push @dpkg_source_command, $self->get('Debian Source Dir');
333
408
        $self->get('Host')->run_command(
334
409
            { COMMAND => \@dpkg_source_command,
336
411
              PRIORITY => 0,
337
412
            });
338
413
        if ($?) {
339
 
            $self->log_error("Failed to build source package");
340
 
            goto cleanup_log;
341
 
        }
342
 
    }
343
 
 
344
 
    my $end_session = 1;
345
 
    my $session = $chroot_info->create('chroot',
346
 
                                       $self->get_conf('DISTRIBUTION'),
347
 
                                       $self->get_conf('CHROOT'),
348
 
                                       $self->get_conf('ARCH'));
349
 
 
350
 
    # Run pre build external commands
351
 
    $self->run_external_commands("pre-build-commands",
352
 
                                 $self->get_conf('LOG_EXTERNAL_COMMAND_OUTPUT'),
353
 
                                 $self->get_conf('LOG_EXTERNAL_COMMAND_ERROR'));
354
 
 
355
 
    if (!$session->begin_session()) {
356
 
        $self->log("Error creating chroot session: skipping " .
357
 
                   $self->get('Package') . "\n");
358
 
        $self->set_status('failed');
359
 
        goto cleanup_unlocked_session;
360
 
    }
361
 
 
 
414
            Sbuild::Exception::Build->throw(error => "Failed to clean source directory",
 
415
                                            failstage => "pack-source");
 
416
        }
 
417
    }
 
418
}
 
419
 
 
420
# Create main chroot session and package resolver.  Creates a lock in
 
421
# the chroot to prevent concurrent chroot usage (only important for
 
422
# non-snapshot chroots).  Ends chroot session on return/failure.
 
423
sub run_chroot_session {
 
424
    my $self=shift;
 
425
 
 
426
    eval {
 
427
        $self->check_abort();
 
428
        my $chroot_info;
 
429
        if ($self->get_conf('CHROOT_MODE') eq 'schroot') {
 
430
            $chroot_info = Sbuild::ChrootInfoSchroot->new($self->get('Config'));
 
431
        } else {
 
432
            $chroot_info = Sbuild::ChrootInfoSudo->new($self->get('Config'));
 
433
        }
 
434
 
 
435
        my $host = $self->get('Host');
 
436
 
 
437
        my $session = $chroot_info->create('chroot',
 
438
                                           $self->get_conf('DISTRIBUTION'),
 
439
                                           $self->get_conf('CHROOT'),
 
440
                                           $self->get_conf('ARCH'));
 
441
 
 
442
        # Run pre build external commands
 
443
        $self->check_abort();
 
444
        $self->run_external_commands("pre-build-commands",
 
445
                                     $self->get_conf('LOG_EXTERNAL_COMMAND_OUTPUT'),
 
446
                                     $self->get_conf('LOG_EXTERNAL_COMMAND_ERROR'));
 
447
 
 
448
        $self->check_abort();
 
449
        if (!$session->begin_session()) {
 
450
            Sbuild::Exception::Build->throw(error => "Error creating chroot session: skipping " .
 
451
                                            $self->get('Package'),
 
452
                                            failstage => "create-session");
 
453
        }
 
454
 
 
455
        $self->set('Session', $session);
 
456
 
 
457
        $self->check_abort();
 
458
        my $chroot_arch =  $self->chroot_arch();
 
459
        if ($self->get('Arch') ne $chroot_arch) {
 
460
            Sbuild::Exception::Build->throw(error => "Build architecture (" . $self->get('Arch') .
 
461
                                            ") is not the same as the chroot architecture (" .
 
462
                                            $chroot_arch . ")",
 
463
                                            info => "Please specify the correct architecture with --arch, or use a chroot of the correct architecture",
 
464
                                            failstage => "create-session");
 
465
        }
 
466
 
 
467
        $self->set('Chroot Dir', $session->get('Location'));
 
468
        # TODO: Don't hack the build location in; add a means to customise
 
469
        # the chroot directly.  i.e. allow changing of /build location.
 
470
        $self->set('Chroot Build Dir',
 
471
                   tempdir($self->get('Package') . '-XXXXXX',
 
472
                           DIR =>  $session->get('Location') . "/build"));
 
473
 
 
474
        my $filter;
 
475
        $filter = $session->strip_chroot_path($self->get('Chroot Build Dir'));
 
476
        $filter =~ s;^/;;;
 
477
        $self->build_log_filter($filter, 'BUILDDIR');
 
478
        $filter = $session->get('Location');
 
479
        $filter =~ s;^/;;;
 
480
        $self->build_log_filter($filter , 'CHROOT');
 
481
        # Need tempdir to be writable and readable by sbuild group.
 
482
        $self->check_abort();
 
483
        $session->run_command(
 
484
            { COMMAND => ['chown', 'sbuild:sbuild',
 
485
                          $session->strip_chroot_path($self->get('Chroot Build Dir'))],
 
486
              USER => 'root',
 
487
              DIR => '/' });
 
488
        if ($?) {
 
489
            Sbuild::Exception::Build->throw(error => "Failed to set sbuild group ownership on chroot build dir",
 
490
                                            failstage => "create-build-dir");
 
491
        }
 
492
        $self->check_abort();
 
493
        $session->run_command(
 
494
            { COMMAND => ['chmod', '0770',
 
495
                          $session->strip_chroot_path($self->get('Chroot Build Dir'))],
 
496
              USER => 'root',
 
497
              DIR => '/' });
 
498
        if ($?) {
 
499
            Sbuild::Exception::Build->throw(error => "Failed to set sbuild group ownership on chroot build dir",
 
500
                                            failstage => "create-build-dir");
 
501
        }
 
502
 
 
503
        $self->check_abort();
 
504
        # Needed so chroot commands log to build log
 
505
        $session->set('Log Stream', $self->get('Log Stream'));
 
506
        $host->set('Log Stream', $self->get('Log Stream'));
 
507
 
 
508
        # Chroot execution defaults
 
509
        my $chroot_defaults = $session->get('Defaults');
 
510
        $chroot_defaults->{'DIR'} =
 
511
            $session->strip_chroot_path($self->get('Chroot Build Dir'));
 
512
        $chroot_defaults->{'STREAMIN'} = $devnull;
 
513
        $chroot_defaults->{'STREAMOUT'} = $self->get('Log Stream');
 
514
        $chroot_defaults->{'STREAMERR'} = $self->get('Log Stream');
 
515
        $chroot_defaults->{'ENV'}->{'LC_ALL'} = 'POSIX';
 
516
        $chroot_defaults->{'ENV'}->{'SHELL'} = '/bin/sh';
 
517
 
 
518
        my $resolver = get_resolver($self->get('Config'), $session, $host);
 
519
        $resolver->set('Log Stream', $self->get('Log Stream'));
 
520
        $resolver->set('Arch', $self->get('Arch'));
 
521
        $resolver->set('Chroot Build Dir', $self->get('Chroot Build Dir'));
 
522
        $self->set('Dependency Resolver', $resolver);
 
523
 
 
524
        # Lock chroot so it won't be tampered with during the build.
 
525
        $self->check_abort();
 
526
        if (!$session->lock_chroot($self->get('Package_SVersion'), $$, $self->get_conf('USERNAME'))) {
 
527
            Sbuild::Exception::Build->throw(error => "Error locking chroot session: skipping " .
 
528
                                            $self->get('Package'),
 
529
                                            failstage => "lock-session");
 
530
        }
 
531
 
 
532
        $self->check_abort();
 
533
        $self->run_chroot_session_locked();
 
534
    };
 
535
 
 
536
    # End chroot session
 
537
    my $session = $self->get('Session');
 
538
    my $end_session =
 
539
        ($self->get_conf('PURGE_SESSION') eq 'always' ||
 
540
         ($self->get_conf('PURGE_SESSION') eq 'successful' &&
 
541
          $self->get_status() eq 'successful')) ? 1 : 0;
 
542
    if ($end_session) {
 
543
        $session->end_session();
 
544
    } else {
 
545
        $self->log("Keeping session: " . $session->get('Session ID') . "\n");
 
546
    }
 
547
    $session = undef;
362
548
    $self->set('Session', $session);
363
549
 
364
 
    my $chroot_arch =  $self->chroot_arch();
365
 
    if ($self->get('Arch') ne $chroot_arch) {
366
 
        $self->log_error("Build architecture (" . $self->get('Arch') .
367
 
                         ") is not the same as the chroot architecture (" .
368
 
                         $chroot_arch . ")\n");
369
 
        $self->log_info("Please specify the correct architecture with --arch, or use a chroot of the correct architecture\n");
370
 
        $self->set_status('failed');
371
 
        goto cleanup_unlocked_session;
372
 
    }
373
 
 
374
 
    $self->set('Chroot Dir', $session->get('Location'));
375
 
    # TODO: Don't hack the build location in; add a means to customise
376
 
    # the chroot directly.  i.e. allow changing of /build location.
377
 
    $self->set('Chroot Build Dir',
378
 
               tempdir($self->get_conf('USERNAME') . '-' .
379
 
                       $self->get('Package_SVersion') . '-' .
380
 
                       $self->get('Arch') . '-XXXXXX',
381
 
                       DIR =>  $session->get('Location') . "/build"));
382
 
 
383
 
    # Needed so chroot commands log to build log
384
 
    $session->set('Log Stream', $self->get('Log Stream'));
385
 
    $host->set('Log Stream', $self->get('Log Stream'));
386
 
 
387
 
    # Chroot execution defaults
388
 
    my $chroot_defaults = $session->get('Defaults');
389
 
    $chroot_defaults->{'DIR'} =
390
 
        $session->strip_chroot_path($self->get('Chroot Build Dir'));
391
 
    $chroot_defaults->{'STREAMIN'} = $devnull;
392
 
    $chroot_defaults->{'STREAMOUT'} = $self->get('Log Stream');
393
 
    $chroot_defaults->{'STREAMERR'} = $self->get('Log Stream');
394
 
    $chroot_defaults->{'ENV'}->{'LC_ALL'} = 'POSIX';
395
 
    $chroot_defaults->{'ENV'}->{'SHELL'} = '/bin/sh';
396
 
 
397
 
    my $resolver = get_resolver($self->get('Config'), $session, $host);
398
 
    $resolver->set('Log Stream', $self->get('Log Stream'));
399
 
    $resolver->set('Arch', $self->get('Arch'));
400
 
    $resolver->set('Chroot Build Dir', $self->get('Chroot Build Dir'));
401
 
    $self->set('Dependency Resolver', $resolver);
402
 
 
403
 
    # Lock chroot so it won't be tampered with during the build.
404
 
    if (!$session->lock_chroot($self->get('Package_SVersion'), $$, $self->get_conf('USERNAME'))) {
405
 
        goto cleanup_unlocked_session;
406
 
    }
407
 
 
408
 
    $resolver->setup();
 
550
    my $e;
 
551
    if ($e = Exception::Class->caught('Sbuild::Exception::Build')) {
 
552
        $e->rethrow();
 
553
    }
 
554
}
 
555
 
 
556
# Run tasks in a *locked* chroot.  Update and upgrade packages.
 
557
# Unlocks chroot on return/failure.
 
558
sub run_chroot_session_locked {
 
559
    my $self = shift;
 
560
 
 
561
    eval {
 
562
        my $session = $self->get('Session');
 
563
        my $resolver = $self->get('Dependency Resolver');
 
564
 
 
565
        $self->check_abort();
 
566
        $resolver->setup();
 
567
 
 
568
        $self->check_abort();
 
569
        $self->run_chroot_update();
 
570
 
 
571
        $self->check_abort();
 
572
        $self->run_fetch_install_packages();
 
573
    };
 
574
 
 
575
    my $session = $self->get('Session');
 
576
    my $resolver = $self->get('Dependency Resolver');
 
577
 
 
578
    $resolver->cleanup();
 
579
    # Unlock chroot now it's cleaned up and ready for other users.
 
580
    $session->unlock_chroot();
 
581
 
 
582
    my $e;
 
583
    if ($e = Exception::Class->caught('Sbuild::Exception::Build')) {
 
584
        $e->rethrow();
 
585
    }
 
586
}
 
587
 
 
588
sub run_chroot_update {
 
589
    my $self = shift;
 
590
    my $resolver = $self->get('Dependency Resolver');
 
591
 
 
592
    if ($self->get_conf('APT_CLEAN') || $self->get_conf('APT_UPDATE') ||
 
593
        $self->get_conf('APT_DISTUPGRADE') || $self->get_conf('APT_UPGRADE')) {
 
594
        $self->log_subsection('Update chroot');
 
595
    }
409
596
 
410
597
    # Clean APT cache.
411
 
    $self->set('Pkg Fail Stage', 'apt-get-clean');
 
598
    $self->check_abort();
412
599
    if ($self->get_conf('APT_CLEAN')) {
413
600
        if ($resolver->clean()) {
414
601
            # Since apt-clean was requested specifically, fail on
415
602
            # error when not in buildd mode.
416
603
            $self->log("apt-get clean failed\n");
417
604
            if ($self->get_conf('SBUILD_MODE') ne 'buildd') {
418
 
                $self->set_status('failed');
419
 
                goto cleanup_session;
 
605
                Sbuild::Exception::Build->throw(error => "apt-get clean failed",
 
606
                                                failstage => "apt-get-clean");
420
607
            }
421
608
        }
422
609
    }
423
610
 
424
611
    # Update APT cache.
425
 
    $self->set('Pkg Fail Stage', 'apt-get-update');
 
612
    $self->check_abort();
426
613
    if ($self->get_conf('APT_UPDATE')) {
427
614
        if ($resolver->update()) {
428
615
            # Since apt-update was requested specifically, fail on
429
616
            # error when not in buildd mode.
430
 
            $self->log("apt-get update failed\n");
431
 
            $self->set_status('failed');
432
 
            goto cleanup_session;
 
617
            if ($self->get_conf('SBUILD_MODE') ne 'buildd') {
 
618
                Sbuild::Exception::Build->throw(error => "apt-get update failed",
 
619
                                                failstage => "apt-get-update");
 
620
            }
433
621
        }
434
622
    }
435
623
 
436
624
    # Upgrade using APT.
 
625
    $self->check_abort();
437
626
    if ($self->get_conf('APT_DISTUPGRADE')) {
438
 
        $self->set('Pkg Fail Stage', 'apt-get-distupgrade');
439
627
        if ($self->get_conf('APT_DISTUPGRADE')) {
440
628
            if ($resolver->distupgrade()) {
441
629
                # Since apt-distupgrade was requested specifically, fail on
442
630
                # error when not in buildd mode.
443
 
                $self->log("apt-get dist-upgrade failed\n");
444
631
                if ($self->get_conf('SBUILD_MODE') ne 'buildd') {
445
 
                    $self->set_status('failed');
446
 
                    goto cleanup_session;
 
632
                    Sbuild::Exception::Build->throw(error => "apt-get dist-upgrade failed",
 
633
                                                    failstage => "apt-get-dist-upgrade");
447
634
                }
448
635
            }
449
636
        }
450
637
    } elsif ($self->get_conf('APT_UPGRADE')) {
451
 
        $self->set('Pkg Fail Stage', 'apt-get-upgrade');
452
638
        if ($self->get_conf('APT_UPGRADE')) {
453
639
            if ($resolver->upgrade()) {
454
640
                # Since apt-upgrade was requested specifically, fail on
455
641
                # error when not in buildd mode.
456
 
                $self->log("apt-get upgrade failed\n");
457
642
                if ($self->get_conf('SBUILD_MODE') ne 'buildd') {
458
 
                    $self->set_status('failed');
459
 
                    goto cleanup_session;
 
643
                    Sbuild::Exception::Build->throw(error => "apt-get upgrade failed",
 
644
                                                    failstage => "apt-get-upgrade");
460
645
                }
461
646
            }
462
647
        }
463
648
    }
464
 
 
465
 
    $self->set('Pkg Fail Stage', 'fetch-src');
466
 
    if (!$self->fetch_source_files()) {
467
 
        goto cleanup_packages;
468
 
    }
469
 
 
470
 
    # Display message about chroot setup script option use being deprecated
471
 
    if ($self->get_conf('CHROOT_SETUP_SCRIPT')) {
472
 
        my $msg = "setup-hook option is deprecated. It has been superceded by ";
473
 
        $msg .= "the chroot-setup-commands feature. setup-hook script will be ";
474
 
        $msg .= "run via chroot-setup-commands.\n";
475
 
        $self->log_warning($msg);
476
 
    }
477
 
 
478
 
    # Run specified chroot setup commands
479
 
    $self->run_external_commands("chroot-setup-commands",
480
 
                                 $self->get_conf('LOG_EXTERNAL_COMMAND_OUTPUT'),
481
 
                                 $self->get_conf('LOG_EXTERNAL_COMMAND_ERROR'));
482
 
 
483
 
    $self->set('Install Start Time', time);
484
 
    $self->set('Install End Time', $self->get('Install Start Time'));
485
 
    $resolver->add_dependencies('CORE', join(", ", @{$self->get_conf('CORE_DEPENDS')}) , "", "", "");
486
 
    if (!$resolver->install_deps('core', 'CORE')) {
487
 
        $self->log("Core source dependencies not satisfied; skipping\n");
488
 
        goto cleanup_packages;
489
 
    }
490
 
 
491
 
    $resolver->add_dependencies('ESSENTIAL', $self->read_build_essential(), "", "", "");
492
 
 
493
 
    my $snapshot = "";
494
 
    $snapshot = "gcc-snapshot" if ($self->get_conf('GCC_SNAPSHOT'));
495
 
    $resolver->add_dependencies('GCC_SNAPSHOT', $snapshot , "", "", "");
496
 
 
497
 
    # Add additional build dependencies specified on the command-line.
498
 
    # TODO: Split dependencies into an array from the start to save
499
 
    # lots of joining.
500
 
    $resolver->add_dependencies('MANUAL',
501
 
                                join(", ", @{$self->get_conf('MANUAL_DEPENDS')}),
502
 
                                join(", ", @{$self->get_conf('MANUAL_DEPENDS_INDEP')}),
503
 
                                join(", ", @{$self->get_conf('MANUAL_CONFLICTS')}),
504
 
                                join(", ", @{$self->get_conf('MANUAL_CONFLICTS_INDEP')}));
505
 
 
506
 
    $resolver->add_dependencies($self->get('Package'),
507
 
                                $self->get('Build Depends'),
508
 
                                $self->get('Build Depends Indep'),
509
 
                                $self->get('Build Conflicts'),
510
 
                                $self->get('Build Conflicts Indep'));
511
 
 
512
 
    $self->set('Pkg Fail Stage', 'install-deps');
513
 
    if (!$resolver->install_deps($self->get('Package'),
514
 
                                 'ESSENTIAL', 'GCC_SNAPSHOT', 'MANUAL',
515
 
                                 $self->get('Package'))) {
516
 
        $self->log("Source-dependencies not satisfied; skipping " .
517
 
                   $self->get('Package') . "\n");
518
 
        goto cleanup_packages;
519
 
    }
520
 
    $self->set('Install End Time', time);
521
 
 
522
 
    $resolver->dump_build_environment();
523
 
 
524
 
    $self->prepare_watches(keys %{$resolver->get('Changes')->{'installed'}});
525
 
 
526
 
    if ($self->build()) {
527
 
        $self->set_status('successful');
528
 
    } else {
529
 
        $self->set_status('failed');
530
 
    }
531
 
 
532
 
    # Run specified chroot cleanup commands
533
 
    $self->run_external_commands("chroot-cleanup-commands",
534
 
                                 $self->get_conf('LOG_EXTERNAL_COMMAND_OUTPUT'),
535
 
                                 $self->get_conf('LOG_EXTERNAL_COMMAND_ERROR'));
536
 
 
537
 
    if ($self->get('Pkg Status') eq "successful") {
538
 
        $self->log_subsection("Post Build");
539
 
 
540
 
        # Run lintian.
541
 
        $self->run_lintian();
542
 
 
543
 
        # Run piuparts.
544
 
        $self->run_piuparts();
545
 
 
546
 
        # Run post build external commands
547
 
        $self->run_external_commands("post-build-commands",
548
 
                                     $self->get_conf('LOG_EXTERNAL_COMMAND_OUTPUT'),
549
 
                                     $self->get_conf('LOG_EXTERNAL_COMMAND_ERROR'));
550
 
 
551
 
    }
552
 
 
553
 
  cleanup_packages:
 
649
}
 
650
 
 
651
# Fetch sources, run setup, fetch and install core and package build
 
652
# deps, then run build.  Cleans up build directory and uninstalls
 
653
# build depends on return/failure.
 
654
sub run_fetch_install_packages {
 
655
    my $self = shift;
 
656
 
 
657
    $self->check_abort();
 
658
    eval {
 
659
        my $session = $self->get('Session');
 
660
        my $resolver = $self->get('Dependency Resolver');
 
661
 
 
662
        $self->check_abort();
 
663
        if (!$self->fetch_source_files()) {
 
664
            Sbuild::Exception::Build->throw(error => "Failed to fetch source files",
 
665
                                            failstage => "fetch-src");
 
666
        }
 
667
 
 
668
        # Display message about chroot setup script option use being deprecated
 
669
        if ($self->get_conf('CHROOT_SETUP_SCRIPT')) {
 
670
            my $msg = "setup-hook option is deprecated. It has been superceded by ";
 
671
            $msg .= "the chroot-setup-commands feature. setup-hook script will be ";
 
672
            $msg .= "run via chroot-setup-commands.\n";
 
673
            $self->log_warning($msg);
 
674
        }
 
675
 
 
676
        # Run specified chroot setup commands
 
677
        $self->check_abort();
 
678
        $self->run_external_commands("chroot-setup-commands",
 
679
                                     $self->get_conf('LOG_EXTERNAL_COMMAND_OUTPUT'),
 
680
                                     $self->get_conf('LOG_EXTERNAL_COMMAND_ERROR'));
 
681
 
 
682
        $self->check_abort();
 
683
        $self->set('Install Start Time', time);
 
684
        $self->set('Install End Time', $self->get('Install Start Time'));
 
685
        $resolver->add_dependencies('CORE', join(", ", @{$self->get_conf('CORE_DEPENDS')}) , "", "", "");
 
686
        if (!$resolver->install_deps('core', 'CORE')) {
 
687
            Sbuild::Exception::Build->throw(error => "Core build dependencies not satisfied; skipping",
 
688
                                            failstage => "install-deps");
 
689
        }
 
690
 
 
691
        $resolver->add_dependencies('ESSENTIAL', $self->read_build_essential(), "", "", "");
 
692
 
 
693
        my $snapshot = "";
 
694
        $snapshot = "gcc-snapshot" if ($self->get_conf('GCC_SNAPSHOT'));
 
695
        $resolver->add_dependencies('GCC_SNAPSHOT', $snapshot , "", "", "");
 
696
 
 
697
        # Add additional build dependencies specified on the command-line.
 
698
        # TODO: Split dependencies into an array from the start to save
 
699
        # lots of joining.
 
700
        $resolver->add_dependencies('MANUAL',
 
701
                                    join(", ", @{$self->get_conf('MANUAL_DEPENDS')}),
 
702
                                    join(", ", @{$self->get_conf('MANUAL_DEPENDS_INDEP')}),
 
703
                                    join(", ", @{$self->get_conf('MANUAL_CONFLICTS')}),
 
704
                                    join(", ", @{$self->get_conf('MANUAL_CONFLICTS_INDEP')}));
 
705
 
 
706
        $resolver->add_dependencies($self->get('Package'),
 
707
                                    $self->get('Build Depends'),
 
708
                                    $self->get('Build Depends Indep'),
 
709
                                    $self->get('Build Conflicts'),
 
710
                                    $self->get('Build Conflicts Indep'));
 
711
 
 
712
        $self->check_abort();
 
713
        if (!$resolver->install_deps($self->get('Package'),
 
714
                                     'ESSENTIAL', 'GCC_SNAPSHOT', 'MANUAL',
 
715
                                     $self->get('Package'))) {
 
716
            Sbuild::Exception::Build->throw(error => "Package build dependencies not satisfied; skipping",
 
717
                                            failstage => "install-deps");
 
718
        }
 
719
        $self->set('Install End Time', time);
 
720
 
 
721
        $self->check_abort();
 
722
        $resolver->dump_build_environment();
 
723
 
 
724
        $self->check_abort();
 
725
        $self->prepare_watches(keys %{$resolver->get('Changes')->{'installed'}});
 
726
 
 
727
        $self->check_abort();
 
728
        if ($self->build()) {
 
729
            $self->set_status('successful');
 
730
        } else {
 
731
            $self->set('Pkg Fail Stage', "build");
 
732
            $self->set_status('failed');
 
733
        }
 
734
 
 
735
        # Run specified chroot cleanup commands
 
736
        $self->check_abort();
 
737
        $self->run_external_commands("chroot-cleanup-commands",
 
738
                                     $self->get_conf('LOG_EXTERNAL_COMMAND_OUTPUT'),
 
739
                                     $self->get_conf('LOG_EXTERNAL_COMMAND_ERROR'));
 
740
 
 
741
        if ($self->get('Pkg Status') eq "successful") {
 
742
            $self->log_subsection("Post Build");
 
743
 
 
744
            # Run lintian.
 
745
            $self->check_abort();
 
746
            $self->run_lintian();
 
747
 
 
748
            # Run piuparts.
 
749
            $self->check_abort();
 
750
            $self->run_piuparts();
 
751
 
 
752
            # Run post build external commands
 
753
            $self->check_abort();
 
754
            $self->run_external_commands("post-build-commands",
 
755
                                         $self->get_conf('LOG_EXTERNAL_COMMAND_OUTPUT'),
 
756
                                         $self->get_conf('LOG_EXTERNAL_COMMAND_ERROR'));
 
757
 
 
758
        }
 
759
    };
 
760
 
 
761
    $self->log_subsection("Cleanup");
 
762
    my $session = $self->get('Session');
 
763
    my $resolver = $self->get('Dependency Resolver');
 
764
 
554
765
    my $purge_build_directory =
555
766
        ($self->get_conf('PURGE_BUILD_DIRECTORY') eq 'always' ||
556
767
         ($self->get_conf('PURGE_BUILD_DIRECTORY') eq 'successful' &&
576
787
    # Purge non-cloned session
577
788
    if ($is_cloned_session) {
578
789
        $self->log("Not cleaning session: cloned chroot in use\n");
579
 
        $end_session = 0
580
 
            if ($purge_build_directory == 0 || $purge_build_deps == 0);
581
790
    } else {
582
791
        if ($purge_build_deps) {
583
792
            # Removing dependencies
587
796
        }
588
797
    }
589
798
 
590
 
  cleanup_session:
591
 
    $resolver->cleanup();
592
 
    # Unlock chroot now it's cleaned up and ready for other users.
593
 
    $session->unlock_chroot();
594
 
 
595
 
  cleanup_unlocked_session:
596
 
    # End chroot session
597
 
    if ($end_session == 1) {
598
 
        $session->end_session();
599
 
    } else {
600
 
        $self->log("Keeping session: " . $session->get('Session ID') . "\n");
601
 
    }
602
 
    $session = undef;
603
 
    $self->set('Session', $session);
604
 
 
605
 
  cleanup_log:
606
 
    $self->close_build_log();
607
 
 
608
 
  cleanup_skip:
609
 
}
610
 
 
 
799
    my $e;
 
800
    if ($e = Exception::Class->caught('Sbuild::Exception::Build')) {
 
801
        $e->rethrow();
 
802
    }
 
803
}
 
804
 
 
805
sub copy_to_chroot {
 
806
    my $self = shift;
 
807
    my $source = shift;
 
808
    my $dest = shift;
 
809
 
 
810
    $self->check_abort();
 
811
    if (! File::Copy::copy($source, $dest)) {
 
812
        $self->log_error("E: Failed to copy '$source' to '$dest': $!\n");
 
813
        exit (1);
 
814
        return 0;
 
815
    }
 
816
 
 
817
    $self->get('Session')->run_command(
 
818
        { COMMAND => ['chown', 'sbuild:sbuild',
 
819
                      $self->get('Session')->strip_chroot_path($dest) . '/' .
 
820
                      basename($source)],
 
821
          USER => 'root',
 
822
          DIR => '/' });
 
823
    if ($?) {
 
824
        $self->log_error("E: Failed to set sbuild group ownership on $dest\n");
 
825
        return 0;
 
826
    }
 
827
    $self->get('Session')->run_command(
 
828
        { COMMAND => ['chmod', '0664',
 
829
                      $self->get('Session')->strip_chroot_path($dest) . '/' .
 
830
                      basename($source)],
 
831
          USER => 'root',
 
832
          DIR => '/' });
 
833
    if ($?) {
 
834
        $self->log_error("E: Failed to set 0644 permissions on $dest\n");
 
835
        return 0;
 
836
    }
 
837
 
 
838
    return 1;
 
839
}
611
840
sub fetch_source_files {
612
841
    my $self = shift;
613
842
 
635
864
        return 0;
636
865
    }
637
866
 
 
867
    $self->check_abort();
638
868
    if ($self->get('DSC Base') =~ m/\.dsc$/) {
639
869
        # Work with a .dsc file.
640
870
        # $file is the name of the downloaded dsc file written in a tempfile.
648
878
            # Copy the local source files into the build directory.
649
879
            $self->log_subsubsection("Local sources");
650
880
            $self->log("$dsc exists in $dir; copying to chroot\n");
651
 
            if (! File::Copy::copy("$dir/$dsc", "$build_dir")) {
652
 
                $self->log_error("Could not copy $dir/$dsc to $build_dir\n");
 
881
            if (! $self->copy_to_chroot("$dir/$dsc", "$build_dir")) {
653
882
                return 0;
654
883
            }
655
884
            push(@fetched, "$build_dir/$dsc");
656
885
            foreach (@cwd_files) {
657
 
                if (! File::Copy::copy("$dir/$_", "$build_dir")) {
658
 
                    $self->log_error("Could not copy $dir/$_ to $build_dir\n");
 
886
                if (! $self->copy_to_chroot("$dir/$_", "$build_dir")) {
659
887
                    return 0;
660
888
                }
661
889
                push(@fetched, "$build_dir/$_");
687
915
        my $pipe = $self->get('Dependency Resolver')->pipe_apt_command(
688
916
            { COMMAND => [$self->get_conf('APT_CACHE'),
689
917
                          '-q', 'showsrc', "$pkg"],
690
 
              USER => $self->get_conf('USERNAME'),
 
918
              USER => $self->get_conf('BUILD_USER'),
691
919
              PRIORITY => 0,
692
920
              DIR => '/'});
693
921
        if (!$pipe) {
752
980
 
753
981
        my $pipe2 = $self->get('Dependency Resolver')->pipe_apt_command(
754
982
            { COMMAND => [$self->get_conf('APT_GET'), '--only-source', '-q', '-d', 'source', "$pkg=$ver"],
755
 
              USER => $self->get_conf('USERNAME'),
 
983
              USER => $self->get_conf('BUILD_USER'),
756
984
              PRIORITY => 0}) || return 0;
757
985
 
758
986
        while(<$pipe2>) {
801
1029
        }
802
1030
        if ($dscarchs ne "any" && !($valid_arch) &&
803
1031
            !($dscarchs eq "all" && $self->get_conf('BUILD_ARCH_ALL')) )  {
804
 
            my $msg = "$dsc: $arch not in arch list or does not match any arch ";
805
 
            $msg .= "wildcards: $dscarchs -- skipping\n";
 
1032
            my $msg = "$dsc: $arch not in arch list or does not match any arch wildcards: $dscarchs -- skipping\n";
806
1033
            $self->log($msg);
807
 
            $self->set_status('skipped');
808
 
            $self->set('Pkg Fail Stage', "arch-check");
 
1034
            Sbuild::Exception::Build->throw(error => "$dsc: $arch not in arch list or does not match any arch wildcards: $dscarchs -- skipping",
 
1035
                                            status => "skipped",
 
1036
                                            failstage => "arch-check");
809
1037
            return 0;
810
1038
        }
811
1039
    }
851
1079
            $err = $defaults->{'STREAMERR'} if ($log_error);
852
1080
            $self->get('Session')->run_command(
853
1081
                { COMMAND => \@{$command},
854
 
                    USER => 'root',
 
1082
                    USER => $self->get_conf('BUILD_USER'),
855
1083
                    PRIORITY => 0,
856
1084
                    STREAMOUT => $out,
857
1085
                    STREAMERR => $err,
1029
1257
        return 0;
1030
1258
    }
1031
1259
    if (! -d "$build_dir/$dscdir") {
1032
 
        $self->set('Pkg Fail Stage', "unpack");
1033
 
        # dpkg-source refuses to remove the remanants of an aborted
1034
 
        # dpkg-source extraction, so we will if necessary.
1035
 
        if (-d $tmpunpackdir) {
1036
 
            system ("rm -fr '$tmpunpackdir'");
1037
 
        }
1038
1260
        $self->set('Sub Task', "dpkg-source");
1039
1261
        $self->get('Session')->run_command(
1040
1262
                    { COMMAND => [$self->get_conf('DPKG_SOURCE'),
1041
1263
                                  '-x', $dscfile, $dscdir],
1042
 
                      USER => $self->get_conf('USERNAME'),
 
1264
                      USER => $self->get_conf('BUILD_USER'),
1043
1265
                      PRIORITY => 0});
1044
1266
        if ($?) {
1045
1267
            $self->log("FAILED [dpkg-source died]\n");
1046
 
 
1047
 
            system ("rm -fr '$tmpunpackdir'") if -d $tmpunpackdir;
1048
 
            return 0;
 
1268
            Sbuild::Exception::Build->throw(error => "FAILED [dpkg-source died]",
 
1269
                                            failstage => "unpack");
1049
1270
        }
1050
 
        $dscdir = "$build_dir/$dscdir";
1051
1271
 
1052
 
        if (system( "chmod -R g-s,go+rX $dscdir" ) != 0) {
 
1272
        $self->get('Session')->run_command(
 
1273
            { COMMAND => ['chmod', '-R', 'g-s,go+rX', $dscdir],
 
1274
              USER => $self->get_conf('BUILD_USER'),
 
1275
              PRIORITY => 0});
 
1276
        if ($?) {
1053
1277
            $self->log("chmod -R g-s,go+rX $dscdir failed.\n");
1054
 
            return 0;
 
1278
            Sbuild::Exception::Build->throw(error => "chmod -R g-s,go+rX $dscdir failed",
 
1279
                                            failstage => "unpack");
1055
1280
        }
 
1281
 
 
1282
        $dscdir = "$build_dir/$dscdir"
1056
1283
    }
1057
1284
    else {
1058
1285
        $dscdir = "$build_dir/$dscdir";
1059
1286
 
1060
1287
        $self->log_subsubsection("Check unpacked source");
1061
 
        $self->set('Pkg Fail Stage', "check-unpacked-version");
1062
1288
        # check if the unpacked tree is really the version we need
1063
1289
        $dscdir = $self->get('Session')->strip_chroot_path($dscdir);
1064
1290
        my $pipe = $self->get('Session')->pipe_command(
1065
1291
            { COMMAND => ['dpkg-parsechangelog'],
1066
 
              USER => $self->get_conf('USERNAME'),
 
1292
              USER => $self->get_conf('BUILD_USER'),
1067
1293
              PRIORITY => 0,
1068
1294
              DIR => $dscdir});
1069
1295
        $self->set('Sub Task', "dpkg-parsechangelog");
1075
1301
        close($pipe);
1076
1302
        if ($?) {
1077
1303
            $self->log("FAILED [dpkg-parsechangelog died]\n");
1078
 
            return 0;
 
1304
            Sbuild::Exception::Build->throw(error => "FAILED [dpkg-parsechangelog died]",
 
1305
                                            failstage => "check-unpacked-version");
1079
1306
        }
1080
1307
        if ($clog !~ /^Version:\s*(.+)\s*$/mi) {
1081
1308
            $self->log("dpkg-parsechangelog didn't print Version:\n");
1082
 
            return 0;
 
1309
            Sbuild::Exception::Build->throw(error => "dpkg-parsechangelog didn't print Version:",
 
1310
                                            failstage => "check-unpacked-version");
1083
1311
        }
1084
1312
    }
1085
1313
 
1086
1314
    $self->log_subsubsection("Check disc space");
1087
 
    $self->set('Pkg Fail Stage', "check-space");
1088
1315
    my $current_usage = `du -k -s "$dscdir"`;
1089
1316
    $current_usage =~ /^(\d+)/;
1090
1317
    $current_usage = $1;
1091
1318
    if ($current_usage) {
1092
1319
        my $free = df($dscdir);
1093
1320
        if ($free < 2*$current_usage && $self->get_conf('CHECK_SPACE')) {
1094
 
            $self->log("Disc space is propably not enough for building.\n".
1095
 
                       "(Source needs $current_usage KB, free are $free KB.)\n");
1096
 
            # TODO: Only purge in a single place.
1097
 
            $self->log("Purging $build_dir\n");
1098
 
            $self->get('Session')->run_command(
1099
 
                { COMMAND => ['rm', '-rf', $build_dir],
1100
 
                  USER => 'root',
1101
 
                  PRIORITY => 0,
1102
 
                  DIR => '/' });
1103
 
            return 0;
 
1321
            Sbuild::Exception::Build->throw(error => "Disc space is probably not sufficient for building.",
 
1322
                                            info => "Source needs $current_usage KiB, while $free KiB is free.)",
 
1323
                                            failstage => "check-space");
 
1324
        } else {
 
1325
            $self->log("Sufficient free space for build\n");
1104
1326
        }
1105
1327
    }
1106
1328
 
1107
1329
    if ($self->get_conf('BIN_NMU') || $self->get_conf('APPEND_TO_VERSION')) {
 
1330
        if (!$self->get_conf('MAINTAINER_NAME')) {
 
1331
            Sbuild::Exception::Build->throw(error => "No maintainer specified.",
 
1332
                                            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)',
 
1333
                                            failstage => "check-space");
 
1334
        }
 
1335
 
1108
1336
        $self->log_subsubsection("Hack binNMU version");
1109
 
        $self->set('Pkg Fail Stage', "hack-binNMU");
1110
1337
        if (open( F, "<$dscdir/debian/changelog" )) {
1111
 
            my($firstline, $text);
1112
 
            $firstline = "";
1113
 
            $firstline = <F> while $firstline =~ /^$/;
1114
 
            { local($/); undef $/; $text = <F>; }
 
1338
            my $text = do { local $/; <F> };
1115
1339
            close( F );
1116
 
            $firstline =~ /^(\S+)\s+\((\S+)\)\s+([^;]+)\s*;\s*urgency=(\S+)\s*$/;
1117
 
            my ($name, $version, $dists, $urgent) = ($1, $2, $3, $4);
 
1340
 
 
1341
            my $pipe = $self->get('Session')->pipe_command(
 
1342
                { COMMAND => ['dpkg-parsechangelog'],
 
1343
                  USER => $self->get_conf('BUILD_USER'),
 
1344
                  PRIORITY => 0,
 
1345
                  DIR => $self->get('Session')->strip_chroot_path($dscdir) });
 
1346
            my $clog = do { local $/; <$pipe> };
 
1347
            close($pipe);
 
1348
            if ($?) {
 
1349
                $self->log("FAILED [dpkg-parsechangelog died]\n");
 
1350
                return 0;
 
1351
            }
 
1352
 
 
1353
            my ($name) = $clog =~ /^Source:\s*(.*)$/m;
 
1354
            my ($version) = $clog =~ /^Version:\s*(.*)$/m;
 
1355
            my ($dists) = $clog =~ /^Distribution:\s*(.*)$/m;
 
1356
            my ($urgency) = $clog =~ /^Urgency:\s*(.*)$/m;
 
1357
            my ($date) = $clog =~ /^Date:\s*(.*)$/m;
 
1358
 
1118
1359
            my $NMUversion = $self->get('Version');
1119
 
            chomp( my $date = `date -R` );
1120
1360
            if (!open( F, ">$dscdir/debian/changelog" )) {
1121
1361
                $self->log("Can't open debian/changelog for binNMU hack: $!\n");
1122
 
                return 0;
 
1362
                Sbuild::Exception::Build->throw(error => "Can't open debian/changelog for binNMU hack: $!",
 
1363
                                                failstage => "hack-binNMU");
1123
1364
            }
1124
1365
            $dists = $self->get_conf('DISTRIBUTION');
1125
1366
 
1136
1377
            print F "\n";
1137
1378
 
1138
1379
            print F " -- " . $self->get_conf('MAINTAINER_NAME') . "  $date\n\n";
1139
 
            print F $firstline, $text;
 
1380
            print F $text;
1140
1381
            close( F );
1141
 
            $self->log("*** Created changelog entry for bin-NMU version $NMUversion\n");
 
1382
            $self->log("Created changelog entry for binNMU version $NMUversion\n");
1142
1383
        }
1143
1384
        else {
1144
1385
            $self->log("Can't open debian/changelog -- no binNMU hack!\n");
 
1386
            Sbuild::Exception::Build->throw(error => "Can't open debian/changelog -- no binNMU hack: $!!",
 
1387
                                            failstage => "hack-binNMU");
1145
1388
        }
1146
1389
    }
1147
1390
 
1169
1412
        unlink "$dscdir/debian/files";
1170
1413
    }
1171
1414
 
 
1415
    # Build tree not writable during build (except for the sbuild
 
1416
    # user performing the build).
 
1417
    $self->get('Session')->run_command(
 
1418
        { COMMAND => ['chmod', '-R', 'go-w',
 
1419
                      $self->get('Session')->strip_chroot_path($self->get('Chroot Build Dir'))],
 
1420
          USER => 'root',
 
1421
          PRIORITY => 0});
 
1422
    if ($?) {
 
1423
        $self->log("chmod og-w " . $self->get('Chroot Build Dir') . " failed.\n");
 
1424
        return 0;
 
1425
    }
 
1426
 
1172
1427
    $self->log_subsubsection("dpkg-buildpackage");
1173
1428
    $self->set('Build Start Time', time);
1174
1429
    $self->set('Build End Time', $self->get('Build Start Time'));
1175
 
    $self->set('Pkg Fail Stage', "build");
1176
1430
 
1177
1431
    my $binopt = $self->get_conf('BUILD_SOURCE') ?
1178
1432
        $self->get_conf('FORCE_ORIG_SOURCE') ? "-sa" : "" :
1207
1461
 
1208
1462
    if (defined($self->get_conf('SIGNING_OPTIONS')) &&
1209
1463
        $self->get_conf('SIGNING_OPTIONS')) {
1210
 
        if (ref($self->get_conf('SIGNING_OPTIONS') eq 'ARRAY')) {
 
1464
        if (ref($self->get_conf('SIGNING_OPTIONS')) eq 'ARRAY') {
1211
1465
            push (@{$buildcmd}, @{$self->get_conf('SIGNING_OPTIONS')});
1212
1466
        } else {
1213
1467
            push (@{$buildcmd}, $self->get_conf('SIGNING_OPTIONS'));
1230
1484
    my $command = {
1231
1485
        COMMAND => $buildcmd,
1232
1486
        ENV => $buildenv,
1233
 
        USER => $self->get_conf('USERNAME'),
 
1487
        USER => $self->get_conf('BUILD_USER'),
1234
1488
        SETSID => 1,
1235
1489
        PRIORITY => 0,
1236
1490
        DIR => $bdir
1271
1525
    while(<$pipe>) {
1272
1526
        alarm($timeout);
1273
1527
        $last_time = time;
 
1528
        if ($self->get('ABORT')) {
 
1529
            my $pid = $command->{'PID'};
 
1530
            $self->get('Session')->run_command(
 
1531
                { COMMAND => ['perl',
 
1532
                              '-e',
 
1533
                              "kill( \"TERM\", -$pid )"],
 
1534
                  USER => 'root',
 
1535
                  PRIORITY => 0,
 
1536
                  DIR => '/' });
 
1537
        }
1274
1538
        $self->log($_);
1275
1539
    }
1276
1540
    close($pipe);
1294
1558
    $self->log("Build finished at $date\n");
1295
1559
 
1296
1560
    my @space_files = ("$dscdir");
 
1561
 
 
1562
    $self->log_subsubsection("Finished");
1297
1563
    if ($rv) {
1298
 
        $self->log("FAILED [dpkg-buildpackage died]\n");
 
1564
        $self->log_error("Build failure (dpkg-buildpackage died)\n");
1299
1565
    } else {
 
1566
        $self->log_info("Built successfully\n");
 
1567
 
1300
1568
        if (-r "$dscdir/debian/files" && $self->get('Chroot Build Dir')) {
1301
1569
            my @files = $self->debian_files_list("$dscdir/debian/files");
1302
1570
 
1313
1581
            }
1314
1582
        }
1315
1583
 
 
1584
 
 
1585
        # Restore write access to build tree now build is complete.
 
1586
        $self->get('Session')->run_command(
 
1587
            { COMMAND => ['chmod', '-R', 'g+w',
 
1588
                          $self->get('Session')->strip_chroot_path($self->get('Chroot Build Dir'))],
 
1589
              USER => 'root',
 
1590
              PRIORITY => 0});
 
1591
        if ($?) {
 
1592
            $self->log("chmod g+w " . $self->get('Chroot Build Dir') . " failed.\n");
 
1593
            return 0;
 
1594
        }
 
1595
 
 
1596
        $self->log_subsection("Changes");
1316
1597
        $changes = $self->get('Package_SVersion') . "_$arch.changes";
1317
1598
        my @cfiles;
1318
1599
        if (-r "$build_dir/$changes") {
1319
1600
            my(@do_dists, @saved_dists);
1320
 
            $self->log("\n$changes:\n");
 
1601
            $self->log_subsubsection("$changes:");
1321
1602
            open( F, "<$build_dir/$changes" );
1322
1603
            my $sys_build_dir = $self->get_conf('BUILD_DIR');
1323
1604
            if (open( F2, ">$sys_build_dir/$changes.new" )) {
1393
1674
            system "mv", "-f", "$build_dir/$_", $self->get_conf('BUILD_DIR')
1394
1675
                and $self->log_error("Could not move $_ to .\n");
1395
1676
        }
1396
 
        $self->log_subsection("Finished");
1397
 
        $self->log("Built successfully\n");
1398
1677
    }
1399
1678
 
1400
1679
    $self->check_watches();
1401
1680
    $self->check_space(@space_files);
1402
1681
 
1403
 
    $self->log_sep();
1404
1682
    return $rv == 0 ? 1 : 0;
1405
1683
}
1406
1684
 
1492
1770
    foreach (@files) {
1493
1771
        my $pipe = $self->get('Host')->pipe_command(
1494
1772
            { COMMAND => ['du', '-k', '-s', $_],
1495
 
              USER => $self->get_conf('USERNAME'),
 
1773
              USER => 'root',
1496
1774
              PRIORITY => 0,
1497
1775
              DIR => '/'});
1498
1776
 
1717
1995
 
1718
1996
    my $pipe = $self->get('Session')->pipe_command(
1719
1997
        { COMMAND => ['dpkg', '--print-architecture'],
1720
 
          USER => $self->get_conf('USERNAME'),
 
1998
          USER => $self->get_conf('BUILD_USER'),
1721
1999
          PRIORITY => 0,
1722
2000
          DIR => '/' }) || return undef;
1723
2001
 
1724
2002
    chomp(my $chroot_arch = <$pipe>);
1725
2003
    close($pipe);
1726
2004
 
1727
 
    die "Can't determine architecture of chroot: $!\n"
 
2005
    Sbuild::Exception::Build->throw(error => "Can't determine architecture of chroot: $!",
 
2006
                                    failstage => "chroot-arch")
1728
2007
        if ($? || !defined($chroot_arch));
1729
2008
 
1730
2009
    return $chroot_arch;
1731
2010
}
1732
2011
 
 
2012
sub build_log_filter {
 
2013
    my $self = shift;
 
2014
    my $text = shift;
 
2015
    my $replacement = shift;
 
2016
 
 
2017
    if ($self->get_conf('LOG_FILTER')) {
 
2018
        $self->log($self->get('FILTER_PREFIX') . $text . ':' . $replacement . "\n");
 
2019
    }
 
2020
}
 
2021
 
1733
2022
sub open_build_log {
1734
2023
    my $self = shift;
1735
2024
 
1736
2025
    my $date = strftime("%Y%m%d-%H%M", localtime($self->get('Pkg Start Time')));
1737
2026
 
 
2027
    my $filter_prefix = '__SBUILD_FILTER_' . $$ . ':';
 
2028
    $self->set('FILTER_PREFIX', $filter_prefix);
 
2029
 
1738
2030
    my $filename = $self->get_conf('LOG_DIR') . '/' .
1739
2031
        $self->get('Package_SVersion') . '-' .
1740
2032
        $self->get('Arch') .
1752
2044
        $SIG{'QUIT'} = 'IGNORE';
1753
2045
        $SIG{'PIPE'} = 'IGNORE';
1754
2046
 
 
2047
        $PROGRAM_NAME = 'package log for ' . $self->get('Package_SVersion') . '_' . $self->get('Arch');
 
2048
 
1755
2049
        if (!$self->get_conf('NOLOG') &&
1756
2050
            $self->get_conf('LOG_DIR_AVAILABLE')) {
1757
2051
            open( CPLOG, ">$filename" ) or
1758
 
                die "Can't open logfile $filename: $!\n";
 
2052
                Sbuild::Exception::Build->throw(error => "Failed to open build log $filename: $!",
 
2053
                                                failstage => "init");
1759
2054
            CPLOG->autoflush(1);
1760
2055
            $saved_stdout->autoflush(1);
1761
2056
 
1776
2071
        my $nolog = $self->get_conf('NOLOG');
1777
2072
        my $log = $self->get_conf('LOG_DIR_AVAILABLE');
1778
2073
        my $verbose = $self->get_conf('VERBOSE');
 
2074
        my $log_colour = $self->get_conf('LOG_COLOUR');
 
2075
        my @filter = ();
 
2076
        my ($text, $replacement);
 
2077
        my $filter_regex = "^$filter_prefix(.*):(.*)\$";
 
2078
        my @ignore = ();
1779
2079
 
1780
2080
        while (<STDIN>) {
1781
 
            if ($nolog) {
 
2081
            # Add a replacement pattern to filter (sent from main
 
2082
            # process in log stream).
 
2083
            if (m/$filter_regex/) {
 
2084
                ($text,$replacement)=($1,$2);
 
2085
                $replacement = "«$replacement»";
 
2086
                push (@filter, [$text, $replacement]);
 
2087
                $_ = "I: NOTICE: Log filtering will replace '$text' with '$replacement'\n";
 
2088
            } else {
 
2089
                # Filter out any matching patterns
 
2090
                foreach my $pattern (@filter) {
 
2091
                    ($text,$replacement) = @{$pattern};
 
2092
                    s/$text/$replacement/g;
 
2093
                }
 
2094
            }
 
2095
            if (m/Deprecated key/ || m/please update your configuration/) {
 
2096
                my $skip = 0;
 
2097
                foreach my $ignore (@ignore) {
 
2098
                    $skip = 1 if ($ignore eq $_);
 
2099
                }
 
2100
                next if $skip;
 
2101
                push(@ignore, $_);
 
2102
            }
 
2103
 
 
2104
            if ($nolog || $verbose) {
 
2105
                if (-t $saved_stdout && $log_colour) {
 
2106
                    my $colour = 'reset';
 
2107
                    $colour = 'red' if (m/^E: /);
 
2108
                    $colour = 'yellow' if (m/^W: /);
 
2109
                    $colour = 'green' if (m/^I: /);
 
2110
                    $colour = 'red' if (m/^Status:/);
 
2111
                    $colour = 'green' if (m/^Status: successful$/);
 
2112
                    print $saved_stdout color $colour;
 
2113
                }
 
2114
 
1782
2115
                print $saved_stdout $_;
 
2116
                if (-t $saved_stdout && $log_colour) {
 
2117
                    print $saved_stdout color 'reset';
 
2118
                }
 
2119
 
1783
2120
                # Manual flushing due to Perl 5.10 bug.  Should autoflush.
1784
2121
                $saved_stdout->flush();
1785
 
            } else {
1786
 
                if ($log) {
 
2122
            }
 
2123
            if (!$nolog && $log) {
1787
2124
                    print CPLOG $_;
1788
 
                }
1789
 
                if ($verbose) {
1790
 
                    print $saved_stdout $_;
1791
 
                    # Manual flushing due to Perl 5.10 bug.  Should autoflush.
1792
 
                    $saved_stdout->flush();
1793
 
                }
1794
2125
            }
1795
2126
        }
1796
2127
 
1816
2147
    $self->log("Package: " . $self->get('Package') . "\n");
1817
2148
    $self->log("Version: " . $self->get('Version') . "\n");
1818
2149
    $self->log("Source Version: " . $self->get('OVersion') . "\n");
 
2150
    $self->log("Distribution: " . $self->get_conf('DISTRIBUTION') . "\n");
1819
2151
    $self->log("Architecture: " . $self->get('Arch') . "\n");
 
2152
    $self->log("\n");
1820
2153
}
1821
2154
 
1822
2155
sub close_build_log {
1854
2187
    $self->log(sprintf("Build needed %02d:%02d:%02d, %dk disc space\n",
1855
2188
               $hours, $minutes, $seconds, $space));
1856
2189
 
 
2190
    if ($self->get_status() eq "successful") {
 
2191
        if (defined($self->get_conf('KEY_ID')) && $self->get_conf('KEY_ID')) {
 
2192
            my $key_id = $self->get_conf('KEY_ID');
 
2193
            $self->log(sprintf("Signature with key '%s' requested:\n", $key_id));
 
2194
            my $changes = $self->get('Package_SVersion') . '_' . $self->get('Arch') . '.changes';
 
2195
            system (sprintf('debsign -k%s %s', $key_id, $changes));
 
2196
        }
 
2197
    }
 
2198
 
1857
2199
    my $subject = "Log for " . $self->get_status() .
1858
2200
        " build of " . $self->get('Package_Version');
1859
2201
    if ($self->get('Arch')) {
1865
2207
    else {
1866
2208
            $subject .= " (dist=" . $self->get_conf('DISTRIBUTION') . ")";
1867
2209
    }
1868
 
    send_build_log($self->get('Config'), $self->get_conf('MAILTO'), $subject, $filename)
1869
 
        if (defined($filename) && -f $filename &&
1870
 
            $self->get_conf('MAILTO'));
1871
2210
 
1872
2211
    $self->set('Log File', undef);
1873
2212
    if (defined($self->get('Log Stream'))) {
1874
2213
        $self->get('Log Stream')->close(); # Close child logger process
1875
2214
        $self->set('Log Stream', undef);
1876
2215
    }
 
2216
 
 
2217
    $self->send_build_log($self->get_conf('MAILTO'), $subject, $filename)
 
2218
        if (defined($filename) && -f $filename &&
 
2219
            $self->get_conf('MAILTO'));
 
2220
}
 
2221
 
 
2222
sub send_build_log {
 
2223
    my $self = shift;
 
2224
    my $to = shift;
 
2225
    my $subject = shift;
 
2226
    my $filename = shift;
 
2227
 
 
2228
    my $conf = $self->get('Config');
 
2229
 
 
2230
    if ($conf->get('MIME_BUILD_LOG_MAILS')) {
 
2231
        return $self->send_mime_build_log($to, $subject, $filename);
 
2232
    } else {
 
2233
        return send_mail($conf, $to, $subject, $filename);
 
2234
    }
 
2235
}
 
2236
 
 
2237
sub send_mime_build_log {
 
2238
    my $self = shift;
 
2239
    my $to = shift;
 
2240
    my $subject = shift;
 
2241
    my $filename = shift;
 
2242
 
 
2243
    my $conf = $self->get('Config');
 
2244
    my $tmp; # Needed for gzip, here for proper scoping.
 
2245
 
 
2246
    my $msg = MIME::Lite->new(
 
2247
            From    => $conf->get('MAILFROM'),
 
2248
            To      => $to,
 
2249
            Subject => $subject,
 
2250
            Type    => 'multipart/mixed'
 
2251
            );
 
2252
 
 
2253
    # Add the GPG key ID to the mail if present so that it's clear if the log
 
2254
    # still needs signing or not.
 
2255
    if (defined($self->get_conf('KEY_ID')) && $self->get_conf('KEY_ID')) {
 
2256
        $msg->add('Key-ID', $self->get_conf('KEY_ID'));
 
2257
    }
 
2258
 
 
2259
    if (!$conf->get('COMPRESS_BUILD_LOG_MAILS')) {
 
2260
        my $log_part = MIME::Lite->new(
 
2261
                Type     => 'text/plain',
 
2262
                Path     => $filename,
 
2263
                Filename => basename($filename)
 
2264
                );
 
2265
        $log_part->attr('content-type.charset' => 'UTF-8');
 
2266
        $msg->attach($log_part);
 
2267
    } else {
 
2268
        local( *F, *GZFILE );
 
2269
 
 
2270
        if (!open( F, "<$filename" )) {
 
2271
            warn "Cannot open $filename for mailing: $!\n";
 
2272
            return 0;
 
2273
        }
 
2274
 
 
2275
        $tmp = File::Temp->new();
 
2276
        tie *GZFILE, 'IO::Zlib', $tmp->filename, 'wb';
 
2277
 
 
2278
        while( <F> ) {
 
2279
            print GZFILE $_;
 
2280
        }
 
2281
        untie *GZFILE;
 
2282
 
 
2283
        close F;
 
2284
        close GZFILE;
 
2285
 
 
2286
        $msg->attach(
 
2287
                Type     => 'application/x-gzip',
 
2288
                Path     => $tmp->filename,
 
2289
                Filename => basename($filename) . '.gz'
 
2290
                );
 
2291
    }
 
2292
 
 
2293
    my $changes = $self->get('Package_SVersion') . '_' . $self->get('Arch') . '.changes';
 
2294
    if ($self->get_status() eq 'successful' && -r $changes) {
 
2295
        $msg->attach(
 
2296
                Type     => 'text/plain',
 
2297
                Path     => $changes,
 
2298
                Filename => basename($changes)
 
2299
                );
 
2300
    }
 
2301
 
 
2302
    my $stats = '';
 
2303
    foreach my $stat (sort keys %{$self->get('Summary Stats')}) {
 
2304
        $stats .= sprintf("%s: %s\n", $stat, $self->get('Summary Stats')->{$stat});
 
2305
    }
 
2306
    $msg->attach(
 
2307
        Type => 'text/plain',
 
2308
        Filename => basename($filename) . '.summary',
 
2309
        Data => $stats
 
2310
        );
 
2311
 
 
2312
    local $SIG{'PIPE'} = 'IGNORE';
 
2313
 
 
2314
    if (!open( MAIL, "|" . $conf->get('MAILPROG') . " -oem $to" )) {
 
2315
        warn "Could not open pipe to " . $conf->get('MAILPROG') . ": $!\n";
 
2316
        close( F );
 
2317
        return 0;
 
2318
    }
 
2319
 
 
2320
    $msg->print(\*MAIL);
 
2321
 
 
2322
    if (!close( MAIL )) {
 
2323
        warn $conf->get('MAILPROG') . " failed (exit status $?)\n";
 
2324
        return 0;
 
2325
    }
 
2326
    return 1;
1877
2327
}
1878
2328
 
1879
2329
sub log_symlink {
1893
2343
    return if !$self->get_conf('AVG_TIME_DB');
1894
2344
    my %db;
1895
2345
    if (!tie %db, 'GDBM_File', $self->get_conf('AVG_TIME_DB'), GDBM_WRCREAT, 0664) {
1896
 
        $self->log("Can't open average time db " . $self->get_conf('AVG_TIME_DB') . "\n");
 
2346
        $self->log_warning("Can't open average time db " . $self->get_conf('AVG_TIME_DB') . "\n");
1897
2347
        return;
1898
2348
    }
1899
2349
    $pkg =~ s/_.*//;
1922
2372
    return if !$self->get_conf('AVG_SPACE_DB') || $space == 0;
1923
2373
    my %db;
1924
2374
    if (!tie %db, 'GDBM_File', $self->get_conf('AVG_SPACE_DB'), &GDBM_WRCREAT, 0664) {
1925
 
        $self->log("Can't open average space db " . $self->get_conf('AVG_SPACE_DB') . "\n");
 
2375
        $self->log_warning("Can't open average space db " . $self->get_conf('AVG_SPACE_DB') . "\n");
1926
2376
        return;
1927
2377
    }
1928
2378
    $pkg =~ s/_.*//;